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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt81
-rw-r--r--SConstruct8
-rwxr-xr-xbuild_files/build_environment/install_deps.sh213
-rw-r--r--build_files/buildbot/config/user-config-glibc211-i686.py25
-rw-r--r--build_files/buildbot/config/user-config-glibc211-x86_64.py25
-rw-r--r--build_files/buildbot/config/user-config-player-glibc211-i686.py16
-rw-r--r--build_files/buildbot/config/user-config-player-glibc211-x86_64.py16
-rw-r--r--build_files/cmake/Modules/FindAlembic.cmake103
-rw-r--r--build_files/cmake/Modules/FindHDF5.cmake75
-rw-r--r--build_files/cmake/Modules/FindOpenVDB.cmake74
-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.cmake19
-rw-r--r--build_files/scons/config/darwin-config.py9
-rw-r--r--build_files/scons/config/linux-config.py24
-rw-r--r--build_files/scons/config/win32-mingw-config.py9
-rw-r--r--build_files/scons/config/win32-vc-config.py9
-rw-r--r--build_files/scons/config/win64-mingw-config.py9
-rw-r--r--build_files/scons/config/win64-vc-config.py9
-rw-r--r--build_files/scons/tools/Blender.py44
-rw-r--r--build_files/scons/tools/btools.py25
-rw-r--r--intern/CMakeLists.txt4
-rw-r--r--intern/SConscript2
-rw-r--r--intern/audaspace/intern/AUD_Sequencer.cpp3
-rw-r--r--intern/audaspace/intern/AUD_Sequencer.h2
-rw-r--r--intern/audaspace/intern/AUD_SequencerReader.cpp5
-rw-r--r--intern/cycles/CMakeLists.txt9
-rw-r--r--intern/cycles/SConscript4
-rw-r--r--intern/cycles/app/CMakeLists.txt23
-rw-r--r--intern/cycles/app/cycles_alembic.cpp426
-rw-r--r--intern/cycles/app/cycles_alembic.h36
-rw-r--r--intern/cycles/app/cycles_standalone.cpp54
-rw-r--r--intern/cycles/app/cycles_xml.cpp32
-rw-r--r--intern/cycles/blender/CMakeLists.txt2
-rw-r--r--intern/cycles/blender/addon/properties.py25
-rw-r--r--intern/cycles/blender/addon/ui.py22
-rw-r--r--intern/cycles/blender/blender_curves.cpp717
-rw-r--r--intern/cycles/blender/blender_mesh.cpp120
-rw-r--r--intern/cycles/blender/blender_object.cpp98
-rw-r--r--intern/cycles/blender/blender_session.cpp49
-rw-r--r--intern/cycles/blender/blender_session.h2
-rw-r--r--intern/cycles/blender/blender_shader.cpp57
-rw-r--r--intern/cycles/blender/blender_sync.cpp2
-rw-r--r--intern/cycles/blender/blender_sync.h21
-rw-r--r--intern/cycles/blender/blender_texture.cpp116
-rw-r--r--intern/cycles/blender/blender_texture.h31
-rw-r--r--intern/cycles/blender/blender_util.h42
-rw-r--r--intern/cycles/bvh/bvh.cpp34
-rw-r--r--intern/cycles/bvh/bvh_params.h4
-rw-r--r--intern/cycles/cmake/external_libs.cmake2
-rw-r--r--intern/cycles/kernel/CMakeLists.txt2
-rw-r--r--intern/cycles/kernel/geom/geom_triangle_intersect.h64
-rw-r--r--intern/cycles/kernel/geom/geom_volume.h20
-rw-r--r--intern/cycles/kernel/kernel_compat_cpu.h4
-rw-r--r--intern/cycles/kernel/kernel_globals.h4
-rw-r--r--intern/cycles/kernel/kernel_types.h9
-rw-r--r--intern/cycles/kernel/kernel_volume.h367
-rw-r--r--intern/cycles/kernel/kernels/cpu/kernel.cpp4
-rw-r--r--intern/cycles/kernel/svm/svm.h10
-rw-r--r--intern/cycles/kernel/svm/svm_openvdb.h55
-rw-r--r--intern/cycles/kernel/svm/svm_types.h14
-rw-r--r--intern/cycles/kernel/svm/svm_voxel.h61
-rw-r--r--intern/cycles/render/CMakeLists.txt8
-rw-r--r--intern/cycles/render/attribute.h1
-rw-r--r--intern/cycles/render/mesh.cpp20
-rw-r--r--intern/cycles/render/mesh.h5
-rw-r--r--intern/cycles/render/nodes.cpp180
-rw-r--r--intern/cycles/render/nodes.h40
-rw-r--r--intern/cycles/render/openvdb.cpp276
-rw-r--r--intern/cycles/render/openvdb.h72
-rw-r--r--intern/cycles/render/scene.cpp16
-rw-r--r--intern/cycles/render/scene.h10
-rw-r--r--intern/cycles/render/session.cpp4
-rw-r--r--intern/cycles/render/session.h1
-rw-r--r--intern/cycles/render/svm.cpp5
-rw-r--r--intern/cycles/render/svm.h4
-rw-r--r--intern/cycles/util/CMakeLists.txt7
-rw-r--r--intern/cycles/util/util_openvdb.h415
-rw-r--r--intern/openvdb/CMakeLists.txt61
-rw-r--r--intern/openvdb/SConscript53
-rw-r--r--intern/openvdb/intern/openvdb_dense_convert.cpp263
-rw-r--r--intern/openvdb/intern/openvdb_dense_convert.h140
-rw-r--r--intern/openvdb/intern/openvdb_primitive.cpp92
-rw-r--r--intern/openvdb/intern/openvdb_primitive.h47
-rw-r--r--intern/openvdb/intern/openvdb_reader.cpp107
-rw-r--r--intern/openvdb/intern/openvdb_reader.h51
-rw-r--r--intern/openvdb/intern/openvdb_render.cpp136
-rw-r--r--intern/openvdb/intern/openvdb_render.h15
-rw-r--r--intern/openvdb/intern/openvdb_writer.cpp93
-rw-r--r--intern/openvdb/intern/openvdb_writer.h55
-rw-r--r--intern/openvdb/openvdb_capi.cpp248
-rw-r--r--intern/openvdb/openvdb_capi.h131
-rw-r--r--intern/openvdb/openvdb_util.cpp80
-rw-r--r--intern/openvdb/openvdb_util.h31
-rw-r--r--intern/smoke/extern/smoke_API.h3
-rw-r--r--intern/smoke/intern/WTURBULENCE.cpp100
-rw-r--r--intern/smoke/intern/WTURBULENCE.h7
-rw-r--r--intern/smoke/intern/smoke_API.cpp11
-rw-r--r--release/datafiles/brushicons/hairadd.pngbin0 -> 3341 bytes
-rw-r--r--release/datafiles/brushicons/haircomb.pngbin0 -> 3341 bytes
-rw-r--r--release/datafiles/brushicons/haircut.pngbin0 -> 3341 bytes
-rw-r--r--release/datafiles/brushicons/hairlength.pngbin0 -> 3341 bytes
-rw-r--r--release/datafiles/brushicons/hairpuff.pngbin0 -> 3341 bytes
-rw-r--r--release/datafiles/brushicons/hairsmooth.pngbin0 -> 3341 bytes
-rw-r--r--release/datafiles/brushicons/hairweight.pngbin0 -> 3341 bytes
m---------release/datafiles/locale0
-rw-r--r--release/datafiles/widget_export.py79
m---------release/scripts/addons0
m---------release/scripts/addons_contrib0
-rw-r--r--release/scripts/modules/bpy_extras/anim_utils.py303
-rw-r--r--release/scripts/modules/sys_info.py10
-rw-r--r--release/scripts/startup/bl_operators/anim.py10
-rw-r--r--release/scripts/startup/bl_operators/presets.py153
-rw-r--r--release/scripts/startup/bl_operators/wm.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_data_mesh.py57
-rw-r--r--release/scripts/startup/bl_ui/properties_data_modifier.py23
-rw-r--r--release/scripts/startup/bl_ui/properties_object.py529
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py4
-rw-r--r--release/scripts/startup/bl_ui/properties_particle.py150
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_common.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_smoke.py92
-rw-r--r--release/scripts/startup/bl_ui/properties_texture.py7
-rw-r--r--release/scripts/startup/bl_ui/space_dopesheet.py3
-rw-r--r--release/scripts/startup/bl_ui/space_filebrowser.py4
-rw-r--r--release/scripts/startup/bl_ui/space_graph.py2
-rw-r--r--release/scripts/startup/bl_ui/space_node.py3
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py22
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py5
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py68
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py38
-rw-r--r--release/scripts/startup/nodeitems_builtins.py2
-rw-r--r--source/blender/CMakeLists.txt5
-rw-r--r--source/blender/SConscript3
-rw-r--r--source/blender/blenfont/BLF_translation.h1
-rw-r--r--source/blender/blenkernel/BKE_DerivedMesh.h7
-rw-r--r--source/blender/blenkernel/BKE_anim.h45
-rw-r--r--source/blender/blenkernel/BKE_cache_library.h254
-rw-r--r--source/blender/blenkernel/BKE_context.h2
-rw-r--r--source/blender/blenkernel/BKE_customdata.h3
-rw-r--r--source/blender/blenkernel/BKE_depsgraph.h2
-rw-r--r--source/blender/blenkernel/BKE_editstrands.h104
-rw-r--r--source/blender/blenkernel/BKE_effect.h6
-rw-r--r--source/blender/blenkernel/BKE_facemap.h54
-rw-r--r--source/blender/blenkernel/BKE_key.h34
-rw-r--r--source/blender/blenkernel/BKE_library.h2
-rw-r--r--source/blender/blenkernel/BKE_main.h1
-rw-r--r--source/blender/blenkernel/BKE_mesh.h2
-rw-r--r--source/blender/blenkernel/BKE_mesh_sample.h68
-rw-r--r--source/blender/blenkernel/BKE_node.h12
-rw-r--r--source/blender/blenkernel/BKE_object.h2
-rw-r--r--source/blender/blenkernel/BKE_object_deform.h7
-rw-r--r--source/blender/blenkernel/BKE_particle.h41
-rw-r--r--source/blender/blenkernel/BKE_pointcache.h8
-rw-r--r--source/blender/blenkernel/BKE_scene.h6
-rw-r--r--source/blender/blenkernel/BKE_screen.h3
-rw-r--r--source/blender/blenkernel/BKE_sequencer.h1
-rw-r--r--source/blender/blenkernel/BKE_smoke.h22
-rw-r--r--source/blender/blenkernel/BKE_strands.h358
-rw-r--r--source/blender/blenkernel/BKE_texture.h1
-rw-r--r--source/blender/blenkernel/CMakeLists.txt20
-rw-r--r--source/blender/blenkernel/SConscript5
-rw-r--r--source/blender/blenkernel/intern/bpath.c6
-rw-r--r--source/blender/blenkernel/intern/brush.c2
-rw-r--r--source/blender/blenkernel/intern/cache_library.c2256
-rw-r--r--source/blender/blenkernel/intern/cdderivedmesh.c1
-rw-r--r--source/blender/blenkernel/intern/cloth.c25
-rw-r--r--source/blender/blenkernel/intern/collision.c2
-rw-r--r--source/blender/blenkernel/intern/context.c4
-rw-r--r--source/blender/blenkernel/intern/curve.c2
-rw-r--r--source/blender/blenkernel/intern/customdata.c53
-rw-r--r--source/blender/blenkernel/intern/depsgraph.c150
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c2
-rw-r--r--source/blender/blenkernel/intern/editstrands.c272
-rw-r--r--source/blender/blenkernel/intern/effect.c44
-rw-r--r--source/blender/blenkernel/intern/facemap.c259
-rw-r--r--source/blender/blenkernel/intern/fmodifier.c8
-rw-r--r--source/blender/blenkernel/intern/group.c7
-rw-r--r--source/blender/blenkernel/intern/idcode.c1
-rw-r--r--source/blender/blenkernel/intern/key.c1008
-rw-r--r--source/blender/blenkernel/intern/lattice.c2
-rw-r--r--source/blender/blenkernel/intern/library.c16
-rw-r--r--source/blender/blenkernel/intern/mesh.c62
-rw-r--r--source/blender/blenkernel/intern/mesh_sample.c381
-rw-r--r--source/blender/blenkernel/intern/modifier.c11
-rw-r--r--source/blender/blenkernel/intern/node.c88
-rw-r--r--source/blender/blenkernel/intern/object.c94
-rw-r--r--source/blender/blenkernel/intern/object_dupli.c773
-rw-r--r--source/blender/blenkernel/intern/object_update.c7
-rw-r--r--source/blender/blenkernel/intern/particle.c824
-rw-r--r--source/blender/blenkernel/intern/particle_child.c72
-rw-r--r--source/blender/blenkernel/intern/particle_distribute.c183
-rw-r--r--source/blender/blenkernel/intern/particle_interpolate.c382
-rw-r--r--source/blender/blenkernel/intern/particle_system.c391
-rw-r--r--source/blender/blenkernel/intern/pointcache.c120
-rw-r--r--source/blender/blenkernel/intern/rigidbody.c2
-rw-r--r--source/blender/blenkernel/intern/scene.c43
-rw-r--r--source/blender/blenkernel/intern/screen.c11
-rw-r--r--source/blender/blenkernel/intern/seqcache.c40
-rw-r--r--source/blender/blenkernel/intern/sequencer.c131
-rw-r--r--source/blender/blenkernel/intern/smoke.c694
-rw-r--r--source/blender/blenkernel/intern/softbody.c8
-rw-r--r--source/blender/blenkernel/intern/sound.c2
-rw-r--r--source/blender/blenkernel/intern/strands.c407
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c319
-rw-r--r--source/blender/blenkernel/intern/texture.c10
-rw-r--r--source/blender/blenlib/BLI_fileops.h1
-rw-r--r--source/blender/blenlib/BLI_fileops_types.h18
-rw-r--r--source/blender/blenlib/BLI_math_geom.h2
-rw-r--r--source/blender/blenlib/BLI_path_util.h2
-rw-r--r--source/blender/blenlib/intern/BLI_filelist.c6
-rw-r--r--source/blender/blenlib/intern/math_geom.c65
-rw-r--r--source/blender/blenlib/intern/path_util.c43
-rw-r--r--source/blender/blenloader/intern/readfile.c154
-rw-r--r--source/blender/blenloader/intern/versioning_260.c21
-rw-r--r--source/blender/blenloader/intern/versioning_270.c236
-rw-r--r--source/blender/blenloader/intern/writefile.c117
-rw-r--r--source/blender/bmesh/CMakeLists.txt4
-rw-r--r--source/blender/bmesh/bmesh.h2
-rw-r--r--source/blender/bmesh/bmesh_class.h8
-rw-r--r--source/blender/bmesh/intern/bmesh_interp.c28
-rw-r--r--source/blender/bmesh/intern/bmesh_interp.h6
-rw-r--r--source/blender/bmesh/intern/bmesh_iterators_inline.h3
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_conv.c43
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_conv.h6
-rw-r--r--source/blender/bmesh/intern/bmesh_operators_private.h1
-rw-r--r--source/blender/bmesh/intern/bmesh_strands.c145
-rw-r--r--source/blender/bmesh/intern/bmesh_strands.h215
-rw-r--r--source/blender/bmesh/intern/bmesh_strands_conv.c1314
-rw-r--r--source/blender/bmesh/intern/bmesh_strands_conv.h67
-rw-r--r--source/blender/depsgraph/intern/depsgraph_build_relations.cc2
-rw-r--r--source/blender/editors/CMakeLists.txt1
-rw-r--r--source/blender/editors/SConscript1
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c45
-rw-r--r--source/blender/editors/animation/anim_filter.c7
-rw-r--r--source/blender/editors/animation/anim_ops.c124
-rw-r--r--source/blender/editors/animation/keyframes_general.c79
-rw-r--r--source/blender/editors/armature/armature_utils.c2
-rw-r--r--source/blender/editors/curve/editcurve.c2
-rw-r--r--source/blender/editors/curve/editfont.c2
-rw-r--r--source/blender/editors/datafiles/CMakeLists.txt7
-rw-r--r--source/blender/editors/datafiles/SConscript7
-rw-r--r--source/blender/editors/hair/CMakeLists.txt56
-rw-r--r--source/blender/editors/hair/SConscript54
-rw-r--r--source/blender/editors/hair/hair_cursor.c109
-rw-r--r--source/blender/editors/hair/hair_edit.c459
-rw-r--r--source/blender/editors/hair/hair_intern.h124
-rw-r--r--source/blender/editors/hair/hair_mirror.c210
-rw-r--r--source/blender/editors/hair/hair_object_cachelib.c104
-rw-r--r--source/blender/editors/hair/hair_object_particles.c101
-rw-r--r--source/blender/editors/hair/hair_ops.c143
-rw-r--r--source/blender/editors/hair/hair_select.c502
-rw-r--r--source/blender/editors/hair/hair_stroke.c498
-rw-r--r--source/blender/editors/hair/hair_undo.c190
-rw-r--r--source/blender/editors/include/BIF_glutil.h10
-rw-r--r--source/blender/editors/include/ED_anim_api.h3
-rw-r--r--source/blender/editors/include/ED_datafiles.h21
-rw-r--r--source/blender/editors/include/ED_keyframes_edit.h2
-rw-r--r--source/blender/editors/include/ED_mesh.h5
-rw-r--r--source/blender/editors/include/ED_particle.h2
-rw-r--r--source/blender/editors/include/ED_physics.h26
-rw-r--r--source/blender/editors/include/ED_screen.h7
-rw-r--r--source/blender/editors/include/ED_space_api.h1
-rw-r--r--source/blender/editors/include/ED_transform.h34
-rw-r--r--source/blender/editors/include/ED_util.h3
-rw-r--r--source/blender/editors/include/ED_view3d.h9
-rw-r--r--source/blender/editors/include/UI_icons.h7
-rw-r--r--source/blender/editors/include/UI_interface.h6
-rw-r--r--source/blender/editors/interface/interface_draw.c2
-rw-r--r--source/blender/editors/interface/interface_handlers.c47
-rw-r--r--source/blender/editors/interface/interface_icons.c13
-rw-r--r--source/blender/editors/interface/interface_intern.h5
-rw-r--r--source/blender/editors/interface/interface_ops.c38
-rw-r--r--source/blender/editors/interface/interface_templates.c52
-rw-r--r--source/blender/editors/interface/interface_utils.c20
-rw-r--r--source/blender/editors/interface/view2d.c2
-rw-r--r--source/blender/editors/io/CMakeLists.txt5
-rw-r--r--source/blender/editors/io/SConscript5
-rw-r--r--source/blender/editors/io/io_cache_library.c1005
-rw-r--r--source/blender/editors/io/io_cache_library.h56
-rw-r--r--source/blender/editors/io/io_cache_shapekey.c417
-rw-r--r--source/blender/editors/io/io_ops.c19
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c2
-rw-r--r--source/blender/editors/metaball/mball_edit.c2
-rw-r--r--source/blender/editors/object/CMakeLists.txt9
-rw-r--r--source/blender/editors/object/SConscript4
-rw-r--r--source/blender/editors/object/object_edit.c6
-rw-r--r--source/blender/editors/object/object_fmap.c495
-rw-r--r--source/blender/editors/object/object_intern.h17
-rw-r--r--source/blender/editors/object/object_lamp.c235
-rw-r--r--source/blender/editors/object/object_lattice.c2
-rw-r--r--source/blender/editors/object/object_modifier.c415
-rw-r--r--source/blender/editors/object/object_ops.c15
-rw-r--r--source/blender/editors/object/object_shapekey.c4
-rw-r--r--source/blender/editors/physics/CMakeLists.txt1
-rw-r--r--source/blender/editors/physics/particle_edit.c150
-rw-r--r--source/blender/editors/physics/particle_shapekey.c416
-rw-r--r--source/blender/editors/physics/physics_intern.h9
-rw-r--r--source/blender/editors/physics/physics_ops.c8
-rw-r--r--source/blender/editors/render/render_internal.c4
-rw-r--r--source/blender/editors/render/render_opengl.c1
-rw-r--r--source/blender/editors/screen/area.c147
-rw-r--r--source/blender/editors/screen/glutil.c33
-rw-r--r--source/blender/editors/screen/screen_context.c8
-rw-r--r--source/blender/editors/screen/screen_edit.c4
-rw-r--r--source/blender/editors/screen/screen_ops.c124
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c46
-rw-r--r--source/blender/editors/space_action/action_edit.c9
-rw-r--r--source/blender/editors/space_api/spacetypes.c14
-rw-r--r--source/blender/editors/space_clip/clip_draw.c3
-rw-r--r--source/blender/editors/space_clip/clip_ops.c2
-rw-r--r--source/blender/editors/space_file/file_draw.c112
-rw-r--r--source/blender/editors/space_file/file_ops.c63
-rw-r--r--source/blender/editors/space_file/filelist.c161
-rw-r--r--source/blender/editors/space_file/filelist.h1
-rw-r--r--source/blender/editors/space_file/filesel.c24
-rw-r--r--source/blender/editors/space_file/space_file.c5
-rw-r--r--source/blender/editors/space_graph/graph_buttons.c26
-rw-r--r--source/blender/editors/space_graph/graph_edit.c162
-rw-r--r--source/blender/editors/space_graph/graph_intern.h2
-rw-r--r--source/blender/editors/space_graph/graph_ops.c20
-rw-r--r--source/blender/editors/space_graph/space_graph.c43
-rw-r--r--source/blender/editors/space_image/image_draw.c2
-rw-r--r--source/blender/editors/space_node/drawnode.c111
-rw-r--r--source/blender/editors/space_node/node_draw.c16
-rw-r--r--source/blender/editors/space_node/node_edit.c8
-rw-r--r--source/blender/editors/space_node/node_view.c56
-rw-r--r--source/blender/editors/space_node/space_node.c70
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c31
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c150
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c10
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h5
-rw-r--r--source/blender/editors/space_sequencer/sequencer_ops.c7
-rw-r--r--source/blender/editors/space_sequencer/sequencer_view.c335
-rw-r--r--source/blender/editors/space_sequencer/space_sequencer.c32
-rw-r--r--source/blender/editors/space_view3d/CMakeLists.txt8
-rw-r--r--source/blender/editors/space_view3d/SConscript4
-rw-r--r--source/blender/editors/space_view3d/drawarmature.c9
-rw-r--r--source/blender/editors/space_view3d/drawobject.c662
-rw-r--r--source/blender/editors/space_view3d/drawstrands.c520
-rw-r--r--source/blender/editors/space_view3d/drawvolume.c5
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c184
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c222
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c64
-rw-r--r--source/blender/editors/space_view3d/view3d_header.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_intern.h9
-rw-r--r--source/blender/editors/space_view3d/view3d_ops.c60
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c21
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c39
-rw-r--r--source/blender/editors/transform/SConscript1
-rw-r--r--source/blender/editors/transform/manipulator_widget.c2082
-rw-r--r--source/blender/editors/transform/transform.c6
-rw-r--r--source/blender/editors/transform/transform.h4
-rw-r--r--source/blender/editors/transform/transform_conversions.c237
-rw-r--r--source/blender/editors/transform/transform_generics.c21
-rw-r--r--source/blender/editors/transform/transform_manipulator.c6
-rw-r--r--source/blender/editors/transform/transform_ops.c1
-rw-r--r--source/blender/editors/transform/transform_orientations.c2
-rw-r--r--source/blender/editors/transform/transform_snap.c1
-rw-r--r--source/blender/editors/util/editmode_undo.c21
-rw-r--r--source/blender/editors/util/undo.c12
-rw-r--r--source/blender/gpu/GPU_buffers.h23
-rw-r--r--source/blender/gpu/GPU_renderer.h58
-rw-r--r--source/blender/gpu/intern/gpu_buffers.c185
-rw-r--r--source/blender/gpu/intern/gpu_compositing.c1
-rw-r--r--source/blender/gpu/intern/gpu_renderer.c36
-rw-r--r--source/blender/makesdna/DNA_ID.h1
-rw-r--r--source/blender/makesdna/DNA_action_types.h3
-rw-r--r--source/blender/makesdna/DNA_anim_types.h2
-rw-r--r--source/blender/makesdna/DNA_armature_types.h3
-rw-r--r--source/blender/makesdna/DNA_brush_types.h15
-rw-r--r--source/blender/makesdna/DNA_cache_library_types.h294
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h10
-rw-r--r--source/blender/makesdna/DNA_key_types.h19
-rw-r--r--source/blender/makesdna/DNA_meshdata_types.h7
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h27
-rw-r--r--source/blender/makesdna/DNA_node_types.h69
-rw-r--r--source/blender/makesdna/DNA_object_force.h1
-rw-r--r--source/blender/makesdna/DNA_object_types.h65
-rw-r--r--source/blender/makesdna/DNA_particle_types.h68
-rw-r--r--source/blender/makesdna/DNA_scene_types.h32
-rw-r--r--source/blender/makesdna/DNA_screen_types.h5
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h216
-rw-r--r--source/blender/makesdna/DNA_smoke_types.h33
-rw-r--r--source/blender/makesdna/DNA_space_types.h47
-rw-r--r--source/blender/makesdna/DNA_strands_types.h99
-rw-r--r--source/blender/makesdna/DNA_texture_types.h7
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h19
-rw-r--r--source/blender/makesdna/DNA_widget_types.h50
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h3
-rw-r--r--source/blender/makesdna/intern/makesdna.c6
-rw-r--r--source/blender/makesrna/RNA_access.h7
-rw-r--r--source/blender/makesrna/RNA_enum_types.h7
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt7
-rw-r--r--source/blender/makesrna/intern/makesrna.c3
-rw-r--r--source/blender/makesrna/intern/rna_ID.c3
-rw-r--r--source/blender/makesrna/intern/rna_brush.c28
-rw-r--r--source/blender/makesrna/intern/rna_cache_library.c1049
-rw-r--r--source/blender/makesrna/intern/rna_context.c1
-rw-r--r--source/blender/makesrna/intern/rna_internal.h5
-rw-r--r--source/blender/makesrna/intern/rna_key.c50
-rw-r--r--source/blender/makesrna/intern/rna_main.c7
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c128
-rw-r--r--source/blender/makesrna/intern/rna_mesh_sample.c73
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c104
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c235
-rw-r--r--source/blender/makesrna/intern/rna_object.c433
-rw-r--r--source/blender/makesrna/intern/rna_object_api.c29
-rw-r--r--source/blender/makesrna/intern/rna_object_force.c5
-rw-r--r--source/blender/makesrna/intern/rna_particle.c144
-rw-r--r--source/blender/makesrna/intern/rna_scene.c4
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c68
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c7
-rw-r--r--source/blender/makesrna/intern/rna_smoke.c137
-rw-r--r--source/blender/makesrna/intern/rna_space.c67
-rw-r--r--source/blender/makesrna/intern/rna_strands.c312
-rw-r--r--source/blender/makesrna/intern/rna_texture.c1
-rw-r--r--source/blender/makesrna/intern/rna_ui_api.c13
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c10
-rw-r--r--source/blender/makesrna/intern/rna_wm.c253
-rw-r--r--source/blender/makesrna/intern/rna_wm_api.c33
-rw-r--r--source/blender/modifiers/CMakeLists.txt1
-rw-r--r--source/blender/modifiers/SConscript1
-rw-r--r--source/blender/modifiers/intern/MOD_particleinstance.c143
-rw-r--r--source/blender/modifiers/intern/MOD_surface.c1
-rw-r--r--source/blender/nodes/CMakeLists.txt10
-rw-r--r--source/blender/nodes/NOD_shader.h2
-rw-r--r--source/blender/nodes/NOD_static_types.h2
-rw-r--r--source/blender/nodes/SConscript4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_image.c75
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_openvdb.c146
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.c75
-rw-r--r--source/blender/physics/BPH_mass_spring.h10
-rw-r--r--source/blender/physics/BPH_strands.h44
-rw-r--r--source/blender/physics/CMakeLists.txt3
-rw-r--r--source/blender/physics/SConscript1
-rw-r--r--source/blender/physics/intern/BPH_mass_spring.cpp2841
-rw-r--r--source/blender/physics/intern/eigen_utils.h32
-rw-r--r--source/blender/physics/intern/implicit.h11
-rw-r--r--source/blender/physics/intern/implicit_blender.c62
-rw-r--r--source/blender/physics/intern/implicit_eigen.cpp2
-rw-r--r--source/blender/physics/intern/strands.cpp472
-rw-r--r--source/blender/pointcache/CMakeLists.txt66
-rw-r--r--source/blender/pointcache/PTC_api.cpp423
-rw-r--r--source/blender/pointcache/PTC_api.h138
-rw-r--r--source/blender/pointcache/SConscript58
-rw-r--r--source/blender/pointcache/alembic/CMakeLists.txt75
-rw-r--r--source/blender/pointcache/alembic/SConscript62
-rw-r--r--source/blender/pointcache/alembic/abc_cloth.cpp237
-rw-r--r--source/blender/pointcache/alembic/abc_cloth.h68
-rw-r--r--source/blender/pointcache/alembic/abc_customdata.cpp804
-rw-r--r--source/blender/pointcache/alembic/abc_customdata.h175
-rw-r--r--source/blender/pointcache/alembic/abc_frame_mapper.cpp52
-rw-r--r--source/blender/pointcache/alembic/abc_frame_mapper.h57
-rw-r--r--source/blender/pointcache/alembic/abc_group.cpp770
-rw-r--r--source/blender/pointcache/alembic/abc_group.h226
-rw-r--r--source/blender/pointcache/alembic/abc_info.cpp516
-rw-r--r--source/blender/pointcache/alembic/abc_interpolate.cpp32
-rw-r--r--source/blender/pointcache/alembic/abc_interpolate.h236
-rw-r--r--source/blender/pointcache/alembic/abc_mesh.cpp630
-rw-r--r--source/blender/pointcache/alembic/abc_mesh.h153
-rw-r--r--source/blender/pointcache/alembic/abc_object.cpp161
-rw-r--r--source/blender/pointcache/alembic/abc_object.h84
-rw-r--r--source/blender/pointcache/alembic/abc_particles.cpp1284
-rw-r--r--source/blender/pointcache/alembic/abc_particles.h222
-rw-r--r--source/blender/pointcache/alembic/abc_reader.cpp194
-rw-r--r--source/blender/pointcache/alembic/abc_reader.h108
-rw-r--r--source/blender/pointcache/alembic/abc_schema.h107
-rw-r--r--source/blender/pointcache/alembic/abc_simdebug.cpp185
-rw-r--r--source/blender/pointcache/alembic/abc_simdebug.h82
-rw-r--r--source/blender/pointcache/alembic/abc_split.cpp236
-rw-r--r--source/blender/pointcache/alembic/abc_writer.cpp132
-rw-r--r--source/blender/pointcache/alembic/abc_writer.h130
-rw-r--r--source/blender/pointcache/alembic/alembic.cpp168
-rw-r--r--source/blender/pointcache/alembic/alembic.h48
-rw-r--r--source/blender/pointcache/intern/ptc_types.cpp44
-rw-r--r--source/blender/pointcache/intern/ptc_types.h237
-rw-r--r--source/blender/pointcache/intern/reader.cpp57
-rw-r--r--source/blender/pointcache/intern/reader.h69
-rw-r--r--source/blender/pointcache/intern/writer.cpp56
-rw-r--r--source/blender/pointcache/intern/writer.h59
-rw-r--r--source/blender/pointcache/util/util_error_handler.cpp102
-rw-r--r--source/blender/pointcache/util/util_error_handler.h201
-rw-r--r--source/blender/pointcache/util/util_function.h48
-rw-r--r--source/blender/pointcache/util/util_task.cpp87
-rw-r--r--source/blender/pointcache/util/util_task.h51
-rw-r--r--source/blender/pointcache/util/util_thread.h74
-rw-r--r--source/blender/pointcache/util/util_types.h53
-rw-r--r--source/blender/python/SConscript5
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_customdata.c4
-rw-r--r--source/blender/python/intern/CMakeLists.txt10
-rw-r--r--source/blender/python/intern/bpy_app.c3
-rw-r--r--source/blender/python/intern/bpy_app_build_options.c7
-rw-r--r--source/blender/python/intern/bpy_app_openvdb.c117
-rw-r--r--source/blender/python/intern/bpy_app_openvdb.h38
-rw-r--r--source/blender/render/extern/include/RE_render_ext.h6
-rw-r--r--source/blender/render/intern/source/pipeline.c11
-rw-r--r--source/blender/render/intern/source/pointdensity.c207
-rw-r--r--source/blender/windowmanager/3d_widgets/arrow_widget.c249
-rw-r--r--source/blender/windowmanager/3d_widgets/dial_widget.c779
-rw-r--r--source/blender/windowmanager/3d_widgets/ui_widget_library.h16
-rw-r--r--source/blender/windowmanager/CMakeLists.txt5
-rw-r--r--source/blender/windowmanager/SConscript3
-rw-r--r--source/blender/windowmanager/WM_api.h105
-rw-r--r--source/blender/windowmanager/WM_types.h54
-rw-r--r--source/blender/windowmanager/intern/wm.c2
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c106
-rw-r--r--source/blender/windowmanager/intern/wm_generic_widgets.c1109
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c5
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c44
-rw-r--r--source/blender/windowmanager/intern/wm_widgets.c909
-rw-r--r--source/blender/windowmanager/wm.h84
-rw-r--r--source/blender/windowmanager/wm_event_system.h13
-rw-r--r--source/blender/windowmanager/wm_event_types.h2
-rw-r--r--source/blenderplayer/CMakeLists.txt6
-rw-r--r--source/blenderplayer/bad_level_call_stubs/stubs.c18
-rw-r--r--source/creator/CMakeLists.txt1
-rw-r--r--source/creator/creator.c10
-rw-r--r--source/gameengine/Converter/BL_ShapeDeformer.cpp2
-rw-r--r--source/gameengine/GamePlayer/ghost/CMakeLists.txt1
-rw-r--r--source/gameengine/GamePlayer/ghost/GPG_ghost.cpp3
-rw-r--r--source/gameengine/GamePlayer/ghost/SConscript1
522 files changed, 50901 insertions, 3618 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f710662ec63..4b255ad50f5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -222,6 +222,8 @@ option(WITH_GAMEENGINE "Enable Game Engine" ${_init_GAMEENGINE})
option(WITH_PLAYER "Build Player" OFF)
option(WITH_OPENCOLORIO "Enable OpenColorIO color management" ${_init_OPENCOLORIO})
option(WITH_COMPOSITOR "Enable the tile based nodal compositor" ON)
+option(WITH_OPENVDB "Enable features relying on OpenVDB" OFF)
+option(WITH_OPENVDB_BLOSC "Enable blosc compression for OpenVDB, only enble if OpenVDB was built with blosc support" OFF)
# GHOST Windowing Library Options
option(WITH_GHOST_DEBUG "Enable debugging output for the GHOST library" OFF)
@@ -275,6 +277,10 @@ option(WITH_MOD_REMESH "Enable Remesh Modifier" ON)
# mark_as_advanced(WITH_MOD_CLOTH_ELTOPO)
option(WITH_MOD_OCEANSIM "Enable Ocean Modifier" OFF)
+# Alembic
+option(WITH_ALEMBIC "Enable Alembic Support" OFF)
+option(WITH_HDF5 "Enable HDF5 Support for Alembic" OFF)
+
# Image format support
option(WITH_OPENIMAGEIO "Enable OpenImageIO Support (http://www.openimageio.org)" ON)
option(WITH_IMAGE_OPENEXR "Enable OpenEXR Support (http://www.openexr.com)" ${_init_IMAGE_OPENEXR})
@@ -641,17 +647,23 @@ if(NOT WITH_BOOST)
set_and_warn(WITH_CYCLES OFF)
set_and_warn(WITH_AUDASPACE OFF)
+ set_and_warn(WITH_ALEMBIC OFF)
set_and_warn(WITH_INTERNATIONAL OFF)
set_and_warn(WITH_OPENAL OFF) # depends on AUDASPACE
set_and_warn(WITH_GAMEENGINE OFF) # depends on AUDASPACE
-elseif(WITH_CYCLES OR WITH_OPENIMAGEIO OR WITH_AUDASPACE OR WITH_INTERNATIONAL)
+elseif(WITH_CYCLES OR WITH_OPENIMAGEIO OR WITH_AUDASPACE OR WITH_ALEMBIC OR WITH_INTERNATIONAL)
# Keep enabled
else()
# Enabled but we don't need it
set(WITH_BOOST OFF)
endif()
+# disable hdf5 if Alembic is disabled
+if(NOT WITH_ALEMBIC)
+ set(WITH_HDF5 OFF)
+endif()
+
# auto enable openimageio for cycles
if(WITH_CYCLES)
set(WITH_OPENIMAGEIO ON)
@@ -984,6 +996,24 @@ if(UNIX AND NOT APPLE)
endif()
endif()
+ if(WITH_OPENVDB)
+ find_package_wrapper(OpenVDB)
+
+ set(TBB ${LIBDIR}/tbb)
+ set(TBB_LIBRARIES tbb)
+ set(TBB_LIBPATH ${TBB}/lib)
+
+ set(OPENVDB_LIBRARIES ${OPENVDB_LIBRARIES} ${BOOST_LIBRARIES} ${ZLIB_LIBRARIES} ${TBB_LIBRARIES})
+ set(OPENVDB_LIBPATH) # TODO, remove and reference the absolute path everywhere
+ set(OPENVDB_DEFINITIONS)
+
+ if(NOT OPENVDB_FOUND)
+ set(WITH_OPENVDB OFF)
+ set(WITH_OPENVDB_BLOSC OFF)
+ message(STATUS "OpenVDB not found")
+ endif()
+ endif()
+
if(WITH_BOOST)
# uses in build instructions to override include and library variables
if(NOT BOOST_CUSTOM)
@@ -1004,6 +1034,9 @@ if(UNIX AND NOT APPLE)
if(WITH_CYCLES_NETWORK)
list(APPEND __boost_packages serialization)
endif()
+ if(WITH_OPENVDB)
+ list(APPEND __boost_packages iostreams)
+ endif()
find_package(Boost 1.48 COMPONENTS ${__boost_packages})
if(NOT Boost_FOUND)
# try to find non-multithreaded if -mt not found, this flag
@@ -1078,6 +1111,19 @@ if(UNIX AND NOT APPLE)
set(PLATFORM_LINKFLAGS "${PLATFORM_LINKFLAGS} -Wl,--version-script='${CMAKE_SOURCE_DIR}/source/creator/blender.map'")
endif()
+ if(WITH_ALEMBIC)
+ find_package_wrapper(Alembic)
+ set(ALEMBIC_LIBRARIES ${ALEMBIC_LIBRARIES} ${BOOST_LIBRARIES})
+ if(NOT ALEMBIC_HDF5_FOUND AND WITH_HDF5)
+ message(STATUS "Alembic is compiled without HDF5 support, disabling HDF5 options")
+ set(WITH_HDF5 OFF)
+ endif()
+ endif()
+
+ if(WITH_HDF5)
+ find_package_wrapper(HDF5)
+ endif()
+
# OpenSuse needs lutil, ArchLinux not, for now keep, can avoid by using --as-needed
list(APPEND PLATFORM_LINKLIBS -lutil -lc -lm)
@@ -1485,7 +1531,15 @@ elseif(WIN32)
set(OPENCOLORIO_LIBPATH ${LIBDIR}/opencolorio/lib)
set(OPENCOLORIO_DEFINITIONS)
endif()
-
+
+ if(WITH_OPENVDB)
+ set(OPENVDB ${LIBDIR}/openvdb)
+ set(OPENVDB_INCLUDE_DIRS ${OPENVDB}/include)
+ set(OPENVDB_LIBRARIES openvdb ${TBB_LIBRARIES})
+ set(OPENVDB_LIBPATH ${LIBDIR}/openvdb/lib)
+ set(OPENVDB_DEFINITIONS)
+ add_definitions(-DOPENVDB_USE_BLOSC)
+ endif()
if(WITH_MOD_CLOTH_ELTOPO)
set(LAPACK ${LIBDIR}/lapack)
@@ -1741,6 +1795,14 @@ elseif(WIN32)
set(SDL_LIBPATH ${SDL}/lib)
endif()
+ if(WITH_OPENVDB)
+ set(OPENVDB ${LIBDIR}/openvdb)
+ set(OPENVDB_INCLUDE_DIRS ${OPENVDB}/include)
+ set(OPENVDB_LIBRARIES openvdb ${TBB_LIBRARIES})
+ set(OPENVDB_LIBPATH ${LIBDIR}/openvdb/lib)
+ set(OPENVDB_DEFINITIONS)
+ endif()
+
set(PLATFORM_LINKFLAGS "-Xlinker --stack=2097152")
## DISABLE - causes linking errors
@@ -2035,6 +2097,14 @@ elseif(APPLE)
set(OPENCOLORIO_LIBPATH ${OPENCOLORIO}/lib)
endif()
+ if(WITH_OPENVDB)
+ set(OPENVDB ${LIBDIR}/openvdb)
+ set(OPENVDB_INCLUDE_DIRS ${OPENVDB}/include)
+ set(OPENVDB_LIBRARIES openvdb ${TBB_LIBRARIES})
+ set(OPENVDB_LIBPATH ${LIBDIR}/openvdb/lib)
+ set(OPENVDB_DEFINITIONS)
+ endif()
+
if(WITH_LLVM)
set(LLVM_ROOT_DIR ${LIBDIR}/llvm CACHE PATH "Path to the LLVM installation")
set(LLVM_VERSION "3.4" CACHE STRING "Version of LLVM to use")
@@ -2164,6 +2234,12 @@ if(WITH_CYCLES)
endif()
endif()
+if(WITH_ALEMBIC)
+ if(NOT WITH_BOOST)
+ message(FATAL_ERROR "Alembic requires WITH_BOOST, the library may not have been found. Configure BOOST or disable WITH_ALEMBIC")
+ endif()
+endif()
+
if(WITH_INTERNATIONAL)
if(NOT WITH_BOOST)
message(FATAL_ERROR "Internationalization requires WITH_BOOST, the library may not have been found. Configure BOOST or disable WITH_INTERNATIONAL")
@@ -2801,6 +2877,7 @@ if(FIRST_RUN)
info_cfg_option(WITH_CYCLES)
info_cfg_option(WITH_FREESTYLE)
info_cfg_option(WITH_OPENCOLORIO)
+ info_cfg_option(WITH_OPENVDB)
info_cfg_text("Compiler Options:")
info_cfg_option(WITH_BUILDINFO)
diff --git a/SConstruct b/SConstruct
index 573d4c1934f..184c01cd8ac 100644
--- a/SConstruct
+++ b/SConstruct
@@ -270,6 +270,7 @@ if 'blenderlite' in B.targets:
target_env_defs['WITH_BF_3DMOUSE'] = False
target_env_defs['WITH_BF_LIBMV'] = False
target_env_defs['WITH_BF_FREESTYLE'] = False
+ target_env_defs['WITH_BF_OPENVDB'] = False
# Merge blenderlite, let command line to override
for k,v in target_env_defs.iteritems():
@@ -817,6 +818,13 @@ if B.targets != ['cudakernels']:
data_to_c_simple("release/datafiles/brushicons/fill.png")
data_to_c_simple("release/datafiles/brushicons/flatten.png")
data_to_c_simple("release/datafiles/brushicons/grab.png")
+ data_to_c_simple("release/datafiles/brushicons/hairadd.png")
+ data_to_c_simple("release/datafiles/brushicons/haircomb.png")
+ data_to_c_simple("release/datafiles/brushicons/haircut.png")
+ data_to_c_simple("release/datafiles/brushicons/hairlength.png")
+ data_to_c_simple("release/datafiles/brushicons/hairpuff.png")
+ data_to_c_simple("release/datafiles/brushicons/hairsmooth.png")
+ data_to_c_simple("release/datafiles/brushicons/hairweight.png")
data_to_c_simple("release/datafiles/brushicons/inflate.png")
data_to_c_simple("release/datafiles/brushicons/layer.png")
data_to_c_simple("release/datafiles/brushicons/lighten.png")
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh
index 0d44ef96154..3934d7c2108 100755
--- a/build_files/build_environment/install_deps.sh
+++ b/build_files/build_environment/install_deps.sh
@@ -28,8 +28,8 @@ getopt \
--long source:,install:,tmp:,info:,threads:,help,no-sudo,with-all,with-opencollada,\
ver-ocio:,ver-oiio:,ver-llvm:,ver-osl:,\
force-all,force-python,force-numpy,force-boost,force-ocio,force-oiio,force-llvm,force-osl,force-opencollada,\
-force-ffmpeg,\
-skip-python,skip-numpy,skip-boost,skip-ocio,skip-openexr,skip-oiio,skip-llvm,skip-osl,skip-ffmpeg,skip-opencollada,\
+force-ffmpeg,force-alembic,\
+skip-python,skip-numpy,skip-boost,skip-ocio,skip-openexr,skip-oiio,skip-llvm,skip-osl,skip-ffmpeg,skip-opencollada,skip-alembic,\
required-numpy: \
-- "$@" \
)
@@ -95,6 +95,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
--with-opencollada
Build and install the OpenCOLLADA libraries.
+ --with-alembic
+ Build and install the Alembic library.
+
--ver-ocio=<ver>
Force version of OCIO library.
@@ -144,6 +147,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
--force-ffmpeg
Force the rebuild of FFMpeg.
+ --force-alembic
+ Force the rebuild of Alembic.
+
Note about the --force-foo options:
* They obviously only have an effect if those libraries are built by this script
(i.e. if there is no available and satisfactory package)!
@@ -180,6 +186,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
--skip-ffmpeg
Unconditionally skip FFMpeg installation/building.
+ --skip-alembic
+ Unconditionally skip Alembic installation/building.
+
--required-numpy
Use this in case your distro features a valid python package, but no matching Numpy one.
It will force compilation of both python and numpy\""
@@ -264,6 +273,11 @@ MP3LAME_DEV=""
OPENJPEG_USE=false
OPENJPEG_DEV=""
+ALEMBIC_VERSION="1.5.5"
+ALEMBIC_VERSION_MIN="1.5.5"
+ALEMBIC_FORCE_REBUILD=false
+ALEMBIC_SKIP=false
+
# Switch to english language, else some things (like check_package_DEB()) won't work!
LANG_BACK=$LANG
LANG=""
@@ -423,6 +437,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
;;
@@ -453,6 +470,9 @@ while true; do
--skip-ffmpeg)
FFMPEG_SKIP=true; shift; continue
;;
+ --skip-alembic)
+ ALEMBIC_SKIP=true; shift; continue
+ ;;
--required-numpy)
NUMPY_REQUIRED=true; shift; continue
;;
@@ -513,6 +533,9 @@ OPENCOLLADA_SOURCE=( "https://github.com/KhronosGroup/OpenCOLLADA.git" )
OPENCOLLADA_REPO_UID="3335ac164e68b2512a40914b14c74db260e6ff7d"
FFMPEG_SOURCE=( "http://ffmpeg.org/releases/ffmpeg-$FFMPEG_VERSION.tar.bz2" )
+ALEMBIC_SOURCE=( "https://code.google.com/p/alembic/" )
+ALEMBIC_REPO_UID=( "1_05_05" )
+
##### Generic Helpers #####
@@ -839,6 +862,7 @@ compile_Boost() {
# Rebuild dependecies as well!
OIIO_FORCE_REBUILD=true
OSL_FORCE_REBUILD=true
+ ALEMBIC_FORCE_REBUILD=true
prepare_opt
@@ -1706,6 +1730,165 @@ compile_FFmpeg() {
fi
}
+#### Build ALEMBIC ####
+_init_alembic() {
+ _src=$SRC/Alembic-$ALEMBIC_VERSION
+ _hg=false
+ _inst=$INST/alembic-$ALEMBIC_VERSION
+ _inst_shortcut=$INST/alembic
+}
+
+clean_alembic() {
+ _init_alembic
+ _clean
+}
+
+compile_alembic() {
+ # To be changed each time we make edits that would modify the compiled result!
+ alembic_magic=1
+ _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 ]; then
+ mkdir -p $SRC
+ hg clone -u $ALEMBIC_REPO_UID $ALEMBIC_SOURCE $_src
+ fi
+
+ cd $_src
+
+ # XXX Ugly patching hack!
+ # Alembice cmake files are erratic, to say the least
+ # have to manually disable a bunch of crap here
+ cat << EOF | patch -p1
+--- a/CMakeLists.txt Mon Jul 28 10:09:21 2014 -0700
++++ b/CMakeLists.txt Thu Oct 16 15:03:27 2014 +0200
+@@ -57,9 +57,9 @@
+ ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH} )
+ SET( VERSION ${PROJECT_VERSION} )
+
+-SET( ALEMBIC_NO_TESTS FALSE )
+-SET( ALEMBIC_NO_BOOTSTRAP FALSE )
+-SET( ALEMBIC_NO_OPENGL FALSE )
++SET( ALEMBIC_NO_TESTS TRUE )
++SET( ALEMBIC_NO_BOOTSTRAP TRUE )
++SET( ALEMBIC_NO_OPENGL TRUE )
+
+ MESSAGE(STATUS "CMAKE SYSTEM NAME = ${CMAKE_SYSTEM_NAME}" )
+
+@@ -306,15 +306,15 @@
+ ENDIF()
+
+ # Include PyAlembic stuff
+-IF(DEFINED USE_PYALEMBIC AND NOT USE_PYALEMBIC)
+- MESSAGE(STATUS "Skipping Alembic Python bindings")
+-ELSE()
+- MESSAGE(STATUS "About to include Python cmake files")
+- ADD_SUBDIRECTORY( python )
+-ENDIF()
++#IF(DEFINED USE_PYALEMBIC AND NOT USE_PYALEMBIC)
++# MESSAGE(STATUS "Skipping Alembic Python bindings")
++#ELSE()
++# MESSAGE(STATUS "About to include Python cmake files")
++# ADD_SUBDIRECTORY( python )
++#ENDIF()
+
+ # Example code not supported
+-ADD_SUBDIRECTORY( examples )
++#ADD_SUBDIRECTORY( examples )
+
+ # Uncomment to build python docs Makefile (requires Sphinx)
+ # Run `make docs` from build root
+
+EOF
+
+ cat << EOF | patch -p1
+--- a/lib/Alembic/Util/CMakeLists.txt Mon Jul 28 10:09:21 2014 -0700
++++ b/lib/Alembic/Util/CMakeLists.txt Thu Oct 16 15:03:27 2014 +0200
+@@ -65,7 +65,7 @@
+ DESTINATION include/Alembic/Util
+ PERMISSIONS OWNER_READ GROUP_READ WORLD_READ )
+
+-IF( NOT ALEMBIC_NO_TESTS )
+- ADD_SUBDIRECTORY( Tests )
+-ENDIF()
++#IF( NOT ALEMBIC_NO_TESTS )
++# ADD_SUBDIRECTORY( Tests )
++#ENDIF()
+
+EOF
+
+ cat << EOF | patch -p1
+--- a/python/CMakeLists.txt Mon Jul 28 10:09:21 2014 -0700
++++ b/python/CMakeLists.txt Thu Oct 16 14:20:25 2014 +0200
+@@ -35,4 +35,4 @@
+
+ ADD_SUBDIRECTORY( PyAlembic )
+ ADD_SUBDIRECTORY( PyAbcOpenGL )
+-ADD_SUBDIRECTORY( examples )
++#ADD_SUBDIRECTORY( examples )
+
+EOF
+
+ # Always refresh the whole build!
+ # XXX 'build' directory is included in Alembic sources, don't touch that
+ if [ -d blender_build ]; then
+ rm -rf blender_build
+ fi
+ mkdir blender_build
+ cd blender_build
+
+ # XXX Alembic cmake doesn't take these as options
+ # XXX Alembic creates a subfolder itself ... rather than fix their
+ # stupid build files, just expect this here by using $INST as prefix
+ export ALEMBIC_INSTALL_PREFIX=$INST
+
+ cmake_d="-D CMAKE_BUILD_TYPE=Release"
+ cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$INST"
+ cmake_d="$cmake_d -D BUILD_SHARED_LIBS=ON"
+ cmake_d="$cmake_d -D BUILD_STATIC_LIBS=ON"
+ cmake_d="$cmake_d -D USE_PYTHON=OFF"
+ cmake_d="$cmake_d -D USE_PYALEMBIC=OFF"
+ cmake_d="$cmakd_d -D ALEMBIC_PYTHON_INCLUDE_DIR=/use/include/python2.7" # XXX BAD
+ cmake_d="$cmakd_d -D ALEMBIC_PYTHON_LIBRARY=/use/lib/python2.7" # XXX BAD
+ cmake_d="$cmake_d -D USE_PYILMBASE=OFF"
+ cmake_d="$cmake_d -D ILMBASE_ROOT=$INST/openexr"
+ cmake_d="$cmake_d -D ALEMBIC_ILMBASE_HALF_LIB=$INST/openexr/lib/libHalf.la"
+ cmake_d="$cmake_d -D ALEMBIC_ILMBASE_IEX_LIB=$INST/openexr/lib/libIex-2_1.la"
+ cmake_d="$cmake_d -D ALEMBIC_ILMBASE_ILMTHREAD_LIB=$INST/openexr/lib/libIlmThread-2_1.la"
+ cmake_d="$cmake_d -D ALEMBIC_ILMBASE_IMATH_LIB=$INST/openexr/lib/libImath-2_1.la"
+
+ cmake $cmake_d ../
+
+ make -j$THREADS && make 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
+}
+
#### Install on DEB-like ####
get_package_version_DEB() {
@@ -1812,7 +1995,7 @@ install_DEB() {
OGG_DEV="libogg-dev"
THEORA_DEV="libtheora-dev"
- _packages="gawk cmake cmake-curses-gui scons build-essential libjpeg-dev libpng-dev \
+ _packages="gawk cmake cmake-curses-gui scons mercurial build-essential libjpeg-dev libpng-dev \
libfreetype6-dev libx11-dev libxi-dev wget libsqlite3-dev libbz2-dev \
libncurses5-dev libssl-dev liblzma-dev libreadline-dev $OPENJPEG_DEV \
libopenal-dev libglew-dev libglewmx-dev yasm $THEORA_DEV $VORBIS_DEV $OGG_DEV \
@@ -1992,7 +2175,8 @@ install_DEB() {
if [ $? -eq 0 ]; then
install_packages_DEB libboost-locale$boost_version-dev libboost-filesystem$boost_version-dev \
libboost-regex$boost_version-dev libboost-system$boost_version-dev \
- libboost-thread$boost_version-dev libboost-wave$boost_version-dev
+ libboost-thread$boost_version-dev libboost-python$boost_version-dev \
+ libboost-program-options$boost_version-dev libboost-wave$boost_version-dev
clean_Boost
else
compile_Boost
@@ -2125,6 +2309,14 @@ install_DEB() {
# fi
compile_FFmpeg
fi
+
+ if $ALEMBIC_SKIP; then
+ WARNING "Skipping Alembic installation, as requested..."
+ else
+ install_packages_DEB libhdf5-dev
+ install_packages_DEB libpython2.7-dev # XXX nasty hack, should be done better ...
+ compile_alembic
+ fi
}
@@ -3030,6 +3222,12 @@ print_info() {
fi
fi
+ if [ -d $INST/alembic ]; then
+ _1="-D ALEMBIC_ROOT_DIR=$INST/alembic"
+ PRINT " $_1"
+ _buildargs="$_buildargs $_1"
+ fi
+
PRINT ""
PRINT "Or even simpler, just run (in your blender-source dir):"
PRINT " make -j$THREADS BUILD_CMAKE_ARGS=\"$_buildargs\""
@@ -3099,6 +3297,13 @@ print_info() {
PRINT "BF_FFMPEG_LIB = 'avformat avcodec swscale avutil avdevice `print_info_ffmpeglink`'"
fi
+ if [ "$ALEMBIC_SKIP" = false ]; then
+ PRINT "WITH_BF_ALEMBIC = True"
+ if [ -d $INST/alembic ]; then
+ PRINT "BF_ALEMBIC = '$INST/alembic'"
+ fi
+ fi
+
if [ "$WITH_ALL" = false ]; then
PRINT "WITH_BF_3DMOUSE = False"
# No libspacenav in official arch repos...
diff --git a/build_files/buildbot/config/user-config-glibc211-i686.py b/build_files/buildbot/config/user-config-glibc211-i686.py
index 06c43be32f2..80979c0e329 100644
--- a/build_files/buildbot/config/user-config-glibc211-i686.py
+++ b/build_files/buildbot/config/user-config-glibc211-i686.py
@@ -157,15 +157,36 @@ WITH_BF_STATICBOOST = True
BF_BOOST = '/opt/lib/boost'
BF_BOOST_INC = '${BF_BOOST}/include'
BF_BOOST_LIB_STATIC = '${BF_BOOST_LIBPATH}/libboost_filesystem.a ${BF_BOOST_LIBPATH}/libboost_date_time.a ' + \
- '${BF_BOOST_LIBPATH}/libboost_regex.a ${BF_BOOST_LIBPATH}/libboost_locale.a ${BF_BOOST_LIBPATH}/libboost_system.a \
- ${BF_BOOST_LIBPATH}/libboost_thread.a'
+ '${BF_BOOST_LIBPATH}/libboost_regex.a ${BF_BOOST_LIBPATH}/libboost_locale.a ${BF_BOOST_LIBPATH}/libboost_system.a ' + \
+ '${BF_BOOST_LIBPATH}/libboost_thread.a'
if BF_IS_NEW_OSL:
BF_BOOST_LIB_STATIC += ' ${BF_BOOST_LIBPATH}/libboost_wave.a'
BF_BOOST_LIBPATH = '${BF_BOOST}/lib'
+# OpenVDB
+WITH_BF_OPENVDB = True
+WITH_BF_STATICOPENVDB = True
+BF_OPENVDB = '/opt/lib/openvdb'
+BF_OPENVDB_INC = '${BF_OPENVDB}/include'
+BF_OPENVDB_LIB_PATH = '${BF_OPENVDB}/lib'
+BF_OPENVDB_LIB_STATIC = '${BF_OPENVDB}/lib/libopenvdb.a ${BF_BOOST_LIBPATH}/libboost_iostreams.a /opt/lib/tbb/lib/libtbb.a'
+
# Ocean Simulation
WITH_BF_OCEANSIM = True
+# Alembic
+WITH_BF_HDF5 = False
+WITH_BF_ALEMBIC = True
+WITH_BF_STATICALEMBIC = True
+BF_ALEMBIC = '/opt/lib/alembic'
+BF_ALEMBIC_INC = '${BF_ALEMBIC}/include'
+BF_ALEMBIC_LIBPATH = '${BF_ALEMBIC}/lib/static'
+BF_ALEMBIC_LIB_STATIC = '${BF_ALEMBIC_LIBPATH}/libAlembicAbcGeom.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbc.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicAbcCollection.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbcCoreFactory.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicAbcCoreOgawa.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbcMaterial.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicOgawa.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbcCoreAbstract.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicUtil.a'
+
# Compilation and optimization
BF_DEBUG = False
REL_CCFLAGS = ['-DNDEBUG', '-O2', '-msse', '-msse2'] # C & C++
diff --git a/build_files/buildbot/config/user-config-glibc211-x86_64.py b/build_files/buildbot/config/user-config-glibc211-x86_64.py
index 29f6143f50e..fecb016713c 100644
--- a/build_files/buildbot/config/user-config-glibc211-x86_64.py
+++ b/build_files/buildbot/config/user-config-glibc211-x86_64.py
@@ -157,15 +157,36 @@ WITH_BF_STATICBOOST = True
BF_BOOST = '/opt/lib/boost'
BF_BOOST_INC = '${BF_BOOST}/include'
BF_BOOST_LIB_STATIC = '${BF_BOOST_LIBPATH}/libboost_filesystem.a ${BF_BOOST_LIBPATH}/libboost_date_time.a ' + \
- '${BF_BOOST_LIBPATH}/libboost_regex.a ${BF_BOOST_LIBPATH}/libboost_locale.a ${BF_BOOST_LIBPATH}/libboost_system.a \
- ${BF_BOOST_LIBPATH}/libboost_thread.a'
+ '${BF_BOOST_LIBPATH}/libboost_regex.a ${BF_BOOST_LIBPATH}/libboost_locale.a ${BF_BOOST_LIBPATH}/libboost_system.a ' + \
+ '${BF_BOOST_LIBPATH}/libboost_thread.a'
if BF_IS_NEW_OSL:
BF_BOOST_LIB_STATIC += ' ${BF_BOOST_LIBPATH}/libboost_wave.a'
BF_BOOST_LIBPATH = '${BF_BOOST}/lib'
+# OpenVDB
+WITH_BF_OPENVDB = True
+WITH_BF_STATICOPENVDB = True
+BF_OPENVDB = '/opt/lib/openvdb'
+BF_OPENVDB_INC = '${BF_OPENVDB}/include'
+BF_OPENVDB_LIB_PATH = '${BF_OPENVDB}/lib'
+BF_OPENVDB_LIB_STATIC = '${BF_OPENVDB}/lib/libopenvdb.a ${BF_BOOST_LIBPATH}/libboost_iostreams.a /opt/lib/tbb/lib/libtbb.a'
+
# Ocean Simulation
WITH_BF_OCEANSIM = True
+# Alembic
+WITH_BF_HDF5 = False
+WITH_BF_ALEMBIC = True
+WITH_BF_STATICALEMBIC = True
+BF_ALEMBIC = '/opt/lib/alembic'
+BF_ALEMBIC_INC = '${BF_ALEMBIC}/include'
+BF_ALEMBIC_LIBPATH = '${BF_ALEMBIC}/lib/static'
+BF_ALEMBIC_LIB_STATIC = '${BF_ALEMBIC_LIBPATH}/libAlembicAbcGeom.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbc.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicAbcCollection.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbcCoreFactory.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicAbcCoreOgawa.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbcMaterial.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicOgawa.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbcCoreAbstract.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicUtil.a'
+
# Compilation and optimization
BF_DEBUG = False
REL_CCFLAGS = ['-DNDEBUG', '-O2', '-msse', '-msse2'] # C & C++
diff --git a/build_files/buildbot/config/user-config-player-glibc211-i686.py b/build_files/buildbot/config/user-config-player-glibc211-i686.py
index b3c26ebb310..893eb0bcba6 100644
--- a/build_files/buildbot/config/user-config-player-glibc211-i686.py
+++ b/build_files/buildbot/config/user-config-player-glibc211-i686.py
@@ -120,6 +120,22 @@ WITH_BF_FFTW3 = True
WITH_BF_STATICFFTW3 = True
WITH_BF_OCEANSIM = True
+# Alembic
+WITH_BF_HDF5 = False
+WITH_BF_ALEMBIC = True
+WITH_BF_STATICALEMBIC = True
+BF_ALEMBIC = '/opt/lib/alembic'
+BF_ALEMBIC_INC = '${BF_ALEMBIC}/include'
+BF_ALEMBIC_LIBPATH = '${BF_ALEMBIC}/lib/static'
+BF_ALEMBIC_LIB_STATIC = '${BF_ALEMBIC_LIBPATH}/libAlembicAbcGeom.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbc.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicAbcCollection.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbcCoreFactory.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicAbcCoreOgawa.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbcMaterial.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicOgawa.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbcCoreAbstract.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicUtil.a'
+
+# OpenVDB
+WITH_BF_OPENVDB = False
+
# Compilation and optimization
BF_DEBUG = False
REL_CCFLAGS = ['-DNDEBUG', '-O2', '-msse', '-msse2'] # C & C++
diff --git a/build_files/buildbot/config/user-config-player-glibc211-x86_64.py b/build_files/buildbot/config/user-config-player-glibc211-x86_64.py
index 173e15b08ca..789873896f1 100644
--- a/build_files/buildbot/config/user-config-player-glibc211-x86_64.py
+++ b/build_files/buildbot/config/user-config-player-glibc211-x86_64.py
@@ -120,6 +120,22 @@ WITH_BF_FFTW3 = True
WITH_BF_STATICFFTW3 = True
WITH_BF_OCEANSIM = True
+# Alembic
+WITH_BF_HDF5 = False
+WITH_BF_ALEMBIC = True
+WITH_BF_STATICALEMBIC = True
+BF_ALEMBIC = '/opt/lib/alembic'
+BF_ALEMBIC_INC = '${BF_ALEMBIC}/include'
+BF_ALEMBIC_LIBPATH = '${BF_ALEMBIC}/lib/static'
+BF_ALEMBIC_LIB_STATIC = '${BF_ALEMBIC_LIBPATH}/libAlembicAbcGeom.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbc.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicAbcCollection.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbcCoreFactory.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicAbcCoreOgawa.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbcMaterial.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicOgawa.a ${BF_ALEMBIC_LIBPATH}/libAlembicAbcCoreAbstract.a ' + \
+ '${BF_ALEMBIC_LIBPATH}/libAlembicUtil.a'
+
+# OpenVDB
+WITH_BF_OPENVDB = False
+
# Compilation and optimization
BF_DEBUG = False
REL_CCFLAGS = ['-DNDEBUG', '-O2', '-msse', '-msse2'] # C & C++
diff --git a/build_files/cmake/Modules/FindAlembic.cmake b/build_files/cmake/Modules/FindAlembic.cmake
new file mode 100644
index 00000000000..1fd964026d2
--- /dev/null
+++ b/build_files/cmake/Modules/FindAlembic.cmake
@@ -0,0 +1,103 @@
+# - Find Alembic library
+# Find the native Alembic includes and library
+# This module defines
+# ALEMBIC_INCLUDE_DIRS, where to find Alembic headers.
+# 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.
+# ALEMBIC_HDF5_FOUND, indicates whether Alembic supports HDF5
+
+#=============================================================================
+# Copyright 2013 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
+)
+
+SET(_alembic_FIND_COMPONENTS
+ AlembicAbc
+ AlembicAbcCoreAbstract
+ AlembicAbcGeom
+ AlembicAbcCoreOgawa
+ AlembicOgawa
+ AlembicUtil
+)
+
+FIND_PATH(ALEMBIC_INCLUDE_DIR
+ NAMES
+ Alembic/Abc/All.h
+ HINTS
+ ${_alembic_SEARCH_DIRS}
+ PATH_SUFFIXES
+ include
+)
+
+SET(_alembic_LIBRARIES)
+FOREACH(COMPONENT ${_alembic_FIND_COMPONENTS})
+ STRING(TOUPPER ${COMPONENT} UPPERCOMPONENT)
+
+ FIND_LIBRARY(ALEMBIC_${UPPERCOMPONENT}_LIBRARY
+ NAMES
+ ${COMPONENT}
+ HINTS
+ ${_alembic_SEARCH_DIRS}
+ PATH_SUFFIXES
+ lib
+ lib/static
+ )
+ MARK_AS_ADVANCED(ALEMBIC_${UPPERCOMPONENT}_LIBRARY)
+ LIST(APPEND _alembic_LIBRARIES "${ALEMBIC_${UPPERCOMPONENT}_LIBRARY}")
+ENDFOREACH()
+
+# Sepcial handling of optional libraries
+FIND_LIBRARY(ALEMBIC_ALEMBICABCCOREHDF5_LIBRARY
+ NAMES
+ AlembicAbcCoreHDF5
+ HINTS
+ ${_alembic_SEARCH_DIRS}
+ PATH_SUFFIXES
+ lib
+ lib/static
+ )
+MARK_AS_ADVANCED(ALEMBIC_ALEMBICABCCOREHDF5_LIBRARY)
+IF(ALEMBIC_ALEMBICABCCOREHDF5_LIBRARY)
+ LIST(APPEND _alembic_LIBRARIES "${ALEMBIC_ALEMBICABCCOREHDF5_LIBRARY}")
+ SET(ALEMBIC_HDF5_FOUND TRUE)
+ELSE()
+ SET(ALEMBIC_HDF5_FOUND FALSE)
+ENDIF()
+
+# 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_LIBRARIES ALEMBIC_INCLUDE_DIR)
+
+IF(ALEMBIC_FOUND)
+ SET(ALEMBIC_LIBRARIES ${_alembic_LIBRARIES})
+ SET(ALEMBIC_INCLUDE_DIRS ${ALEMBIC_INCLUDE_DIR})
+ENDIF(ALEMBIC_FOUND)
+
+MARK_AS_ADVANCED(
+ ALEMBIC_INCLUDE_DIR
+ ALEMBIC_LIBRARIES
+)
diff --git a/build_files/cmake/Modules/FindHDF5.cmake b/build_files/cmake/Modules/FindHDF5.cmake
new file mode 100644
index 00000000000..d395519e1fe
--- /dev/null
+++ b/build_files/cmake/Modules/FindHDF5.cmake
@@ -0,0 +1,75 @@
+# - Find HDF5 library
+# Find the native hdf5 includes and library
+# This module defines
+# HDF5_INCLUDE_DIRS, where to find hdf5 headers.
+# 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 2013 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
+)
+
+SET(_hdf5_FIND_COMPONENTS
+ hdf5
+ hdf5_hl
+)
+
+FIND_PATH(_hdf5_INCLUDE_DIRS
+ NAMES
+ hdf5.h
+ HINTS
+ ${_hdf5_SEARCH_DIRS}
+)
+
+SET(_hdf5_LIBRARIES)
+FOREACH(COMPONENT ${_hdf5_FIND_COMPONENTS})
+ STRING(TOUPPER ${COMPONENT} UPPERCOMPONENT)
+
+ FIND_LIBRARY(HDF5_${UPPERCOMPONENT}_LIBRARY
+ NAMES
+ ${COMPONENT}
+ HINTS
+ ${_hdf5_SEARCH_DIRS}
+ )
+ MARK_AS_ADVANCED(HDF5_${UPPERCOMPONENT}_LIBRARY)
+ LIST(APPEND _hdf5_LIBRARIES "${HDF5_${UPPERCOMPONENT}_LIBRARY}")
+ENDFOREACH()
+
+# 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_LIBRARIES _hdf5_INCLUDE_DIRS)
+
+IF(HDF5_FOUND)
+ SET(HDF5_LIBRARIES ${_hdf5_LIBRARIES})
+ SET(HDF5_INCLUDE_DIRS ${_hdf5_INCLUDE_DIRS})
+ENDIF(HDF5_FOUND)
+
+MARK_AS_ADVANCED(
+ HDF5_INCLUDE_DIRS
+ HDF5_LIBRARIES
+)
diff --git a/build_files/cmake/Modules/FindOpenVDB.cmake b/build_files/cmake/Modules/FindOpenVDB.cmake
new file mode 100644
index 00000000000..116f6429116
--- /dev/null
+++ b/build_files/cmake/Modules/FindOpenVDB.cmake
@@ -0,0 +1,74 @@
+# - Find OPENVDB library
+# Find the native OPENVDB includes and library
+# This module defines
+# OPENVDB_INCLUDE_DIRS, where to find openvdb.h, Set when
+# OPENVDB_INCLUDE_DIR is found.
+# OPENVDB_LIBRARIES, libraries to link against to use OPENVDB.
+# OPENVDB_ROOT_DIR, The base directory to search for OPENVDB.
+# This can also be an environment variable.
+# OPENVDB_FOUND, If false, do not try to use OPENVDB.
+#
+# also defined, but not for general use are
+# OPENVDB_LIBRARY, where to find the OPENVDB library.
+
+#=============================================================================
+# Copyright 2015 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 OPENVDB_ROOT_DIR was defined in the environment, use it.
+IF(NOT OPENVDB_ROOT_DIR AND NOT $ENV{OPENVDB_ROOT_DIR} STREQUAL "")
+ SET(OPENVDB_ROOT_DIR $ENV{OPENVDB_ROOT_DIR})
+ENDIF()
+
+SET(_openvdb_SEARCH_DIRS
+ ${OPENVDB_ROOT_DIR}
+ /usr/local
+ /sw # Fink
+ /opt/local # DarwinPorts
+ /opt/csw # Blastwave
+ /opt/lib/openvdb
+)
+
+FIND_PATH(OPENVDB_INCLUDE_DIR
+ NAMES
+ openvdb/openvdb.h
+ HINTS
+ ${_openvdb_SEARCH_DIRS}
+ PATH_SUFFIXES
+ include
+)
+
+FIND_LIBRARY(OPENVDB_LIBRARY
+ NAMES
+ openvdb
+ HINTS
+ ${_openvdb_SEARCH_DIRS}
+ PATH_SUFFIXES
+ lib64 lib
+)
+
+# handle the QUIETLY and REQUIRED arguments and set OPENVDB_FOUND to TRUE if
+# all listed variables are TRUE
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(OPENVDB DEFAULT_MSG
+ OPENVDB_LIBRARY
+ OPENVDB_INCLUDE_DIR)
+
+IF(OPENVDB_FOUND)
+ SET(OPENVDB_LIBRARIES ${OPENVDB_LIBRARY})
+ SET(OPENVDB_INCLUDE_DIRS ${OPENVDB_INCLUDE_DIR})
+ENDIF(OPENVDB_FOUND)
+
+MARK_AS_ADVANCED(
+ OPENVDB_INCLUDE_DIR
+ OPENVDB_LIBRARY
+)
+
+UNSET(_openvdb_SEARCH_DIRS)
diff --git a/build_files/cmake/config/blender_full.cmake b/build_files/cmake/config/blender_full.cmake
index d41f97a8348..76afbb01647 100644
--- a/build_files/cmake/config/blender_full.cmake
+++ b/build_files/cmake/config/blender_full.cmake
@@ -43,6 +43,7 @@ set(WITH_OPENCOLLADA ON CACHE BOOL "" FORCE)
set(WITH_OPENCOLORIO ON CACHE BOOL "" FORCE)
set(WITH_OPENMP ON CACHE BOOL "" FORCE)
set(WITH_OPENNL ON CACHE BOOL "" FORCE)
+set(WITH_OPENVDB ON CACHE BOOL "" FORCE)
set(WITH_PYTHON_INSTALL ON CACHE BOOL "" FORCE)
set(WITH_RAYOPTIMIZATION ON CACHE BOOL "" FORCE)
set(WITH_SDL ON CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake
index 2a57e4c33d8..9d3bbccfa30 100644
--- a/build_files/cmake/config/blender_lite.cmake
+++ b/build_files/cmake/config/blender_lite.cmake
@@ -48,6 +48,7 @@ set(WITH_OPENCOLORIO OFF CACHE BOOL "" FORCE)
set(WITH_OPENIMAGEIO OFF CACHE BOOL "" FORCE)
set(WITH_OPENMP OFF CACHE BOOL "" FORCE)
set(WITH_OPENNL OFF CACHE BOOL "" FORCE)
+set(WITH_OPENVDB OFF CACHE BOOL "" FORCE)
set(WITH_PYTHON_INSTALL OFF CACHE BOOL "" FORCE)
set(WITH_RAYOPTIMIZATION OFF CACHE BOOL "" FORCE)
set(WITH_SDL OFF CACHE BOOL "" FORCE)
diff --git a/build_files/cmake/config/bpy_module.cmake b/build_files/cmake/config/bpy_module.cmake
index b5b13b40987..41140151f04 100644
--- a/build_files/cmake/config/bpy_module.cmake
+++ b/build_files/cmake/config/bpy_module.cmake
@@ -31,3 +31,4 @@ set(WITH_INPUT_NDOF OFF CACHE BOOL "" FORCE)
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)
diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake
index 479dd31b3f8..154b80a43c2 100644
--- a/build_files/cmake/macros.cmake
+++ b/build_files/cmake/macros.cmake
@@ -265,6 +265,9 @@ function(SETUP_LIBDIRS)
if(WITH_OPENCOLORIO)
link_directories(${OPENCOLORIO_LIBPATH})
endif()
+ if(WITH_OPENVDB)
+ link_directories(${OPENVDB_LIBPATH})
+ endif()
if(WITH_IMAGE_OPENJPEG AND WITH_SYSTEM_OPENJPEG)
link_directories(${OPENJPEG_LIBPATH})
endif()
@@ -370,6 +373,9 @@ function(setup_liblinks
if(WITH_OPENCOLORIO)
target_link_libraries(${target} ${OPENCOLORIO_LIBRARIES})
endif()
+ if(WITH_OPENVDB)
+ target_link_libraries(${target} ${OPENVDB_LIBRARIES})
+ endif()
if(WITH_CYCLES_OSL)
target_link_libraries(${target} ${OSL_LIBRARIES})
endif()
@@ -436,6 +442,12 @@ function(setup_liblinks
if(WITH_LLVM)
target_link_libraries(${target} ${LLVM_LIBRARY})
endif()
+ if(WITH_ALEMBIC)
+ target_link_libraries(${target} ${ALEMBIC_LIBRARIES})
+ endif()
+ if(WITH_HDF5)
+ target_link_libraries(${target} ${HDF5_LIBRARIES})
+ endif()
if(WIN32 AND NOT UNIX)
target_link_libraries(${target} ${PTHREADS_LIBRARIES})
endif()
@@ -515,6 +527,7 @@ function(SETUP_BLENDER_SORTED_LIBS)
bf_editor_object
bf_editor_armature
bf_editor_physics
+ bf_editor_hair
bf_editor_render
bf_editor_screen
bf_editor_sculpt_paint
@@ -578,6 +591,8 @@ function(SETUP_BLENDER_SORTED_LIBS)
ge_videotex
bf_dna
bf_blenfont
+ bf_pointcache_alembic
+ bf_pointcache
bf_intern_audaspace
bf_intern_mikktspace
bf_intern_dualcon
@@ -673,6 +688,10 @@ function(SETUP_BLENDER_SORTED_LIBS)
list_insert_after(BLENDER_SORTED_LIBS "ge_logic_ngnetwork" "extern_bullet")
endif()
+ if(WITH_OPENVDB)
+ list(APPEND BLENDER_SORTED_LIBS bf_intern_openvdb)
+ endif()
+
foreach(SORTLIB ${BLENDER_SORTED_LIBS})
set(REMLIB ${SORTLIB})
foreach(SEARCHLIB ${BLENDER_LINK_LIBS})
diff --git a/build_files/scons/config/darwin-config.py b/build_files/scons/config/darwin-config.py
index 1fb6d649ae9..b90b65e9aa2 100644
--- a/build_files/scons/config/darwin-config.py
+++ b/build_files/scons/config/darwin-config.py
@@ -203,6 +203,12 @@ BF_BOOST_LIB = 'boost_date_time-mt boost_filesystem-mt boost_regex-mt boost_syst
BF_BOOST_LIB_INTERNATIONAL = 'boost_locale-mt'
BF_BOOST_LIBPATH = '${BF_BOOST}/lib'
+WITH_BF_OPENVDB = True
+BF_OPENVDB = '/openvdb'
+BF_OPENVDB_INC = '${BF_OPENVDB}/include'
+BF_OPENVDB_LIB = 'openvdb tbb'
+BF_OPENVDB_LIB_PATH = '${BF_OPENVDB}/lib'
+
WITH_BF_CYCLES_CUDA_BINARIES = False
BF_CYCLES_CUDA_NVCC = '/usr/local/cuda/bin/nvcc'
BF_CYCLES_CUDA_BINARIES_ARCH = ['sm_20', 'sm_21', 'sm_30', 'sm_35', 'sm_50', 'sm_52']
@@ -220,6 +226,9 @@ BF_RAYOPTIMIZATION_SSE_FLAGS = []
# SpaceNavigator and related 3D mice, driver must be 3DxWare 10 Beta 4 (Mac OS X) or later !
WITH_BF_3DMOUSE = True
+# Disable OpenVDB, until libraries are available
+WITH_BF_OPENVDB = False
+
#############################################################################
################### various compile settings and flags ##################
#############################################################################
diff --git a/build_files/scons/config/linux-config.py b/build_files/scons/config/linux-config.py
index 9607045662f..9c7b7555ec6 100644
--- a/build_files/scons/config/linux-config.py
+++ b/build_files/scons/config/linux-config.py
@@ -52,9 +52,9 @@ BF_OPENEXR = '/usr'
# BF_OPENEXR_INC = '${BF_OPENEXR}/include/OpenEXR ${BF_OPENEXR}/include'
BF_OPENEXR_INC = '${BF_OPENEXR}/include/OpenEXR'
-BF_OPENEXR_LIB = 'Half IlmImf Iex Imath '
+BF_OPENEXR_LIB = 'Half IlmImf-2_1 Iex-2_1 Imath-2_1 '
BF_OPENEXR_LIB_STATIC = '${BF_OPENEXR}/lib/libHalf.a ${BF_OPENEXR}/lib/libIlmImf.a ${BF_OPENEXR}/lib/libIex.a ${BF_OPENEXR}/lib/libImath.a ${BF_OPENEXR}/lib/libIlmThread.a'
-# BF_OPENEXR_LIBPATH = '${BF_OPENEXR}/lib'
+BF_OPENEXR_LIBPATH = '${BF_OPENEXR}/lib'
WITH_BF_DDS = True
@@ -202,6 +202,13 @@ BF_BOOST_LIB_STATIC = '${BF_BOOST_LIBPATH}/libboost_filesystem.a ${BF_BOOST_LIBP
BF_BOOST_LIB_INTERNATIONAL = 'boost_locale'
BF_BOOST_LIBPATH = '${BF_BOOST}/lib'
+WITH_BF_OPENVDB = True
+BF_OPENVDB = '/usr'
+BF_OPENVDB_INC = '${BF_OPENVDB}/include'
+BF_OPENVDB_LIB = 'openvdb tbb'
+BF_OPENVDB_LIB_PATH = '${BF_OPENVDB}/lib'
+BF_OPENVDB_LIB_STATIC = '${BF_OPENVDB}/lib/libopenvdb.so'
+
WITH_BF_CYCLES = WITH_BF_OIIO and WITH_BF_BOOST
WITH_BF_CYCLES_CUDA_BINARIES = False
@@ -226,6 +233,19 @@ BF_3DMOUSE_LIB_STATIC = '${BF_3DMOUSE_LIBPATH}/libspnav.a'
#Freestyle
WITH_BF_FREESTYLE = True
+# HDF5
+WITH_BF_HDF5 = True
+BF_HDF5 = '/usr'
+BF_HDF5_LIB = 'hdf5 hdf5_hl'
+BF_HDF5_LIBPATH='${BF_HDF5}/lib'
+
+# Alembic
+WITH_BF_ALEMBIC = True
+BF_ALEMBIC = '/opt/lib/alembic'
+BF_ALEMBIC_LIB = 'AlembicAbcGeom AlembicAbc AlembicAbcCollection AlembicAbcCoreFactory AlembicAbcCoreHDF5 AlembicAbcCoreAbstract AlembicAbcCoreOgawa AlembicAbcMaterial AlembicOgawa AlembicUtil'
+BF_ALEMBIC_INC = '${BF_ALEMBIC}/include'
+BF_ALEMBIC_LIBPATH='${BF_ALEMBIC}/lib/static'
+
##
CC = 'gcc'
CXX = 'g++'
diff --git a/build_files/scons/config/win32-mingw-config.py b/build_files/scons/config/win32-mingw-config.py
index cbb7755637a..c89c6eb7676 100644
--- a/build_files/scons/config/win32-mingw-config.py
+++ b/build_files/scons/config/win32-mingw-config.py
@@ -166,6 +166,12 @@ BF_BOOST_LIB = 'boost_date_time-mgw46-mt-s-1_49 boost_filesystem-mgw46-mt-s-1_49
BF_BOOST_LIB_INTERNATIONAL = 'boost_locale-mgw46-mt-s-1_49'
BF_BOOST_LIBPATH = '${BF_BOOST}/lib'
+WITH_BF_OPENVDB = True
+BF_OPENVDB = LIBDIR + '/openvdb'
+BF_OPENVDB_INC = '${BF_OPENVDB}/include'
+BF_OPENVDB_LIB = 'openvdb tbb'
+BF_OPENVDB_LIB_PATH = '${BF_OPENVDB}/lib'
+
#Ray trace optimization
WITH_BF_RAYOPTIMIZATION = True
BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse']
@@ -182,6 +188,9 @@ BF_CYCLES_CUDA_BINARIES_ARCH = ['sm_20', 'sm_21', 'sm_30', 'sm_35', 'sm_50']
#Freestyle
WITH_BF_FREESTYLE = True
+# Disable OpenVDB, until libraries are available
+WITH_BF_OPENVDB = False
+
##
CC = 'gcc'
CXX = 'g++'
diff --git a/build_files/scons/config/win32-vc-config.py b/build_files/scons/config/win32-vc-config.py
index 9f3b3440628..8a358dd0429 100644
--- a/build_files/scons/config/win32-vc-config.py
+++ b/build_files/scons/config/win32-vc-config.py
@@ -196,6 +196,12 @@ BF_BOOST_LIB = 'libboost_date_time-vc120-mt-s-1_55 libboost_filesystem-vc120-mt-
BF_BOOST_LIB_INTERNATIONAL = ' libboost_locale-vc120-mt-s-1_55'
BF_BOOST_LIBPATH = '${BF_BOOST}/lib'
+WITH_BF_OPENVDB = True
+BF_OPENVDB = '${LIBDIR}/openvdb'
+BF_OPENVDB_INC = '${BF_OPENVDB}/include'
+BF_OPENVDB_LIB = 'openvdb tbb'
+BF_OPENVDB_LIB_PATH = '${BF_OPENVDB}/lib'
+
#CUDA
WITH_BF_CYCLES_CUDA_BINARIES = False
#BF_CYCLES_CUDA_NVCC = "" # Path to the nvidia compiler
@@ -208,6 +214,9 @@ BF_RAYOPTIMIZATION_SSE_FLAGS = ['/arch:SSE']
#Freestyle
WITH_BF_FREESTYLE = True
+# Disable OpenVDB, until libraries are available
+WITH_BF_OPENVDB = False
+
WITH_BF_STATICOPENGL = False
BF_OPENGL_INC = '${BF_OPENGL}/include'
BF_OPENGL_LIBINC = '${BF_OPENGL}/lib'
diff --git a/build_files/scons/config/win64-mingw-config.py b/build_files/scons/config/win64-mingw-config.py
index e4def7e5a5c..0133039f1aa 100644
--- a/build_files/scons/config/win64-mingw-config.py
+++ b/build_files/scons/config/win64-mingw-config.py
@@ -165,6 +165,12 @@ BF_BOOST_LIB = 'boost_date_time-mgw47-mt-s-1_49 boost_date_time-mgw47-mt-sd-1_49
BF_BOOST_LIB_INTERNATIONAL = ' boost_locale-mgw47-mt-s-1_49 boost_locale-mgw47-mt-sd-1_49'
BF_BOOST_LIBPATH = '${BF_BOOST}/lib'
+WITH_BF_OPENVDB = True
+BF_OPENVDB = LIBDIR + '/openvdb'
+BF_OPENVDB_INC = '${BF_OPENVDB}/include'
+BF_OPENVDB_LIB = 'openvdb tbb'
+BF_OPENVDB_LIB_PATH = '${BF_OPENVDB}/lib'
+
#Ray trace optimization
WITH_BF_RAYOPTIMIZATION = True
BF_RAYOPTIMIZATION_SSE_FLAGS = ['-mmmx', '-msse', '-msse2']
@@ -176,6 +182,9 @@ WITH_BF_OPENMP = True
#Freestyle
WITH_BF_FREESTYLE = True
+# Disable OpenVDB, until libraries are available
+WITH_BF_OPENVDB = False
+
##
CC = 'gcc'
CXX = 'g++'
diff --git a/build_files/scons/config/win64-vc-config.py b/build_files/scons/config/win64-vc-config.py
index 9ac0173537e..7a3ecc078b7 100644
--- a/build_files/scons/config/win64-vc-config.py
+++ b/build_files/scons/config/win64-vc-config.py
@@ -199,6 +199,12 @@ BF_BOOST_LIB = 'libboost_date_time-vc120-mt-s-1_55 libboost_filesystem-vc120-mt-
BF_BOOST_LIB_INTERNATIONAL = ' libboost_locale-vc120-mt-s-1_55'
BF_BOOST_LIBPATH = '${BF_BOOST}/lib'
+WITH_BF_OPENVDB = True
+BF_OPENVDB = '${LIBDIR}/openvdb'
+BF_OPENVDB_INC = '${BF_OPENVDB}/include'
+BF_OPENVDB_LIB = 'openvdb tbb'
+BF_OPENVDB_LIB_PATH = '${BF_OPENVDB}/lib'
+
#CUDA
WITH_BF_CYCLES_CUDA_BINARIES = False
#BF_CYCLES_CUDA_NVCC = "" # Path to the nvidia compiler
@@ -212,6 +218,9 @@ BF_RAYOPTIMIZATION_SSE_FLAGS = ['']
#Freestyle
WITH_BF_FREESTYLE = True
+# Disable OpenVDB, until libraries are available
+WITH_BF_OPENVDB = False
+
WITH_BF_STATICOPENGL = False
BF_OPENGL_INC = '${BF_OPENGL}/include'
BF_OPENGL_LIBINC = '${BF_OPENGL}/lib'
diff --git a/build_files/scons/tools/Blender.py b/build_files/scons/tools/Blender.py
index 0ed83d57f9f..7c5d0b96041 100644
--- a/build_files/scons/tools/Blender.py
+++ b/build_files/scons/tools/Blender.py
@@ -204,10 +204,20 @@ def setup_staticlibs(lenv):
libincs += Split(lenv['BF_OIIO_LIBPATH'])
if lenv['WITH_BF_STATICOIIO']:
statlibs += Split(lenv['BF_OIIO_LIB_STATIC'])
+
+ if lenv['WITH_BF_HDF5']:
+ libincs += Split(lenv['BF_HDF5_LIBPATH'])
+
+ if lenv['WITH_BF_ALEMBIC']:
+ libincs += Split(lenv['BF_ALEMBIC_LIBPATH'])
+ if lenv['WITH_BF_STATICALEMBIC']:
+ statlibs += Split(lenv['BF_ALEMBIC_LIB_STATIC'])
+
if lenv['WITH_BF_OPENEXR']:
libincs += Split(lenv['BF_OPENEXR_LIBPATH'])
if lenv['WITH_BF_STATICOPENEXR']:
statlibs += Split(lenv['BF_OPENEXR_LIB_STATIC'])
+
if lenv['WITH_BF_ZLIB'] and lenv['WITH_BF_STATICZLIB']:
statlibs += Split(lenv['BF_ZLIB_LIB_STATIC'])
@@ -216,6 +226,11 @@ def setup_staticlibs(lenv):
if lenv['WITH_BF_STATICOCIO']:
statlibs += Split(lenv['BF_OCIO_LIB_STATIC'])
+ if lenv['WITH_BF_OPENVDB']:
+ libincs += Split(lenv['BF_OPENVDB_LIB_PATH'])
+ if lenv['WITH_BF_STATICOPENVDB']:
+ statlibs += Split(lenv['BF_OPENVDB_LIB_STATIC'])
+
if lenv['WITH_BF_CYCLES_OSL']:
libincs += Split(lenv['BF_OSL_LIBPATH'])
if lenv['WITH_BF_STATICOSL']:
@@ -283,8 +298,19 @@ def setup_syslibs(lenv):
if not lenv['WITH_BF_STATICOCIO']:
syslibs += Split(lenv['BF_OCIO_LIB'])
+ if lenv['WITH_BF_HDF5']:
+ syslibs += Split(lenv['BF_HDF5_LIB'])
+
+ if lenv['WITH_BF_ALEMBIC']:
+ if not lenv['WITH_BF_STATICALEMBIC']:
+ syslibs += Split(lenv['BF_ALEMBIC_LIB'])
+
+ if lenv['WITH_BF_OPENVDB'] and not lenv ['WITH_BF_STATICOPENVDB']:
+ syslibs += Split(lenv['BF_OPENVDB_LIB'])
+
if lenv['WITH_BF_OPENEXR'] and not lenv['WITH_BF_STATICOPENEXR']:
syslibs += Split(lenv['BF_OPENEXR_LIB'])
+
if lenv['WITH_BF_ZLIB'] and not lenv['WITH_BF_STATICZLIB']:
syslibs += Split(lenv['BF_ZLIB_LIB'])
if lenv['WITH_BF_TIFF'] and not lenv['WITH_BF_STATICTIFF']:
@@ -372,7 +398,23 @@ def propose_priorities():
def creator(env):
sources = ['creator.c']# + Blender.buildinfo(env, "dynamic") + Blender.resources
- incs = ['#/intern/guardedalloc', '#/source/blender/blenlib', '#/source/blender/blenkernel', '#/source/blender/depsgraph', '#/source/blender/editors/include', '#/source/blender/blenloader', '#/source/blender/imbuf', '#/source/blender/renderconverter', '#/source/blender/render/extern/include', '#/source/blender/windowmanager', '#/source/blender/makesdna', '#/source/blender/makesrna', '#/source/gameengine/BlenderRoutines', '#/extern/glew/include', '#/source/blender/gpu', env['BF_OPENGL_INC']]
+ incs = ['#/intern/guardedalloc',
+ '#/source/blender/blenlib',
+ '#/source/blender/blenkernel',
+ '#/source/blender/depsgraph',
+ '#/source/blender/editors/include',
+ '#/source/blender/blenloader',
+ '#/source/blender/imbuf',
+ '#/source/blender/renderconverter',
+ '#/source/blender/render/extern/include',
+ '#/source/blender/windowmanager',
+ '#/source/blender/makesdna',
+ '#/source/blender/makesrna',
+ '#/source/blender/pointcache',
+ '#/source/gameengine/BlenderRoutines',
+ '#/extern/glew/include',
+ '#/source/blender/gpu',
+ env['BF_OPENGL_INC']]
defs = []
diff --git a/build_files/scons/tools/btools.py b/build_files/scons/tools/btools.py
index 76450fbd223..e888161876c 100644
--- a/build_files/scons/tools/btools.py
+++ b/build_files/scons/tools/btools.py
@@ -180,9 +180,11 @@ def validate_arguments(args, bc):
'WITH_BF_OIIO', 'WITH_BF_STATICOIIO', 'BF_OIIO', 'BF_OIIO_INC', 'BF_OIIO_LIB', 'BF_OIIO_LIB_STATIC', 'BF_OIIO_LIBPATH',
'WITH_BF_OCIO', 'WITH_BF_STATICOCIO', 'BF_OCIO', 'BF_OCIO_INC', 'BF_OCIO_LIB', 'BF_OCIO_LIB_STATIC', 'BF_OCIO_LIBPATH',
'WITH_BF_BOOST', 'WITH_BF_STATICBOOST', 'BF_BOOST', 'BF_BOOST_INC', 'BF_BOOST_LIB', 'BF_BOOST_LIB_INTERNATIONAL', 'BF_BOOST_LIB_STATIC', 'BF_BOOST_LIBPATH',
+ 'WITH_BF_OPENVDB', 'WITH_BF_STATICOPENVDB', 'BF_OPENVDB', 'BF_OPENVDB_INC', 'BF_OPENVDB_LIB', 'BF_OPENVDB_LIB_STATIC', 'BF_OPENVDB_LIB_PATH',
'WITH_BF_LIBMV', 'WITH_BF_LIBMV_SCHUR_SPECIALIZATIONS',
'WITH_BF_CYCLES_OSL', 'WITH_BF_STATICOSL', 'BF_OSL', 'BF_OSL_INC', 'BF_OSL_LIB', 'BF_OSL_LIBPATH', 'BF_OSL_LIB_STATIC', 'BF_OSL_COMPILER',
- 'WITH_BF_LLVM', 'WITH_BF_STATICLLVM', 'BF_LLVM', 'BF_LLVM_LIB', 'BF_LLVM_LIBPATH', 'BF_LLVM_LIB_STATIC', 'BF_PROGRAM_LINKFLAGS'
+ 'WITH_BF_LLVM', 'WITH_BF_STATICLLVM', 'BF_LLVM', 'BF_LLVM_LIB', 'BF_LLVM_LIBPATH', 'BF_LLVM_LIB_STATIC', 'BF_PROGRAM_LINKFLAGS',
+ 'WITH_BF_ALEMBIC', 'BF_ALEMBIC', 'BF_ALEMBIC_INC', 'BF_ALEMBIC_LIB', 'BF_ALEMBIC_LIBPATH',
]
# Have options here that scons expects to be lists
@@ -581,6 +583,19 @@ def read_opts(env, cfg, args):
(BoolVariable('WITH_BF_LIBMV_SCHUR_SPECIALIZATIONS', 'Enable fixed-size schur specializations', True)),
(BoolVariable('WITH_BF_COMPOSITOR', 'Enable the tile based nodal compositor', True)),
+
+ (BoolVariable('WITH_BF_HDF5', 'Use HDF5 if true', False)),
+ ('BF_HDF5', 'HDF5 base path', ''),
+ ('BF_HDF5_LIB', 'HDF5 library', ''),
+ ('BF_HDF5_LIBPATH', 'HDF5 library path', ''),
+
+ (BoolVariable('WITH_BF_ALEMBIC', 'Use Alembic if true', False)),
+ (BoolVariable('WITH_BF_STATICALEMBIC', 'Staticly link to Alembic', False)),
+ ('BF_ALEMBIC', 'Alembic base path', ''),
+ ('BF_ALEMBIC_INC', 'Alembic include path', ''),
+ ('BF_ALEMBIC_LIB', 'Alembic library', ''),
+ ('BF_ALEMBIC_LIB_STATIC', 'Alembic static libraries', ''),
+ ('BF_ALEMBIC_LIBPATH', 'Alembic library path', ''),
) # end of opts.AddOptions()
localopts.AddVariables(
@@ -627,6 +642,14 @@ def read_opts(env, cfg, args):
('BF_OCIO_LIBPATH', 'OCIO library path', ''),
('BF_OCIO_LIB_STATIC', 'OCIO static library', ''),
+ (BoolVariable('WITH_BF_OPENVDB', 'Build with OpenVDB', False)),
+ (BoolVariable('WITH_BF_STATICOPENVDB', 'Staticly link to OpenVDB', False)),
+ ('BF_OPENVDB', 'OpenVDB root path', ''),
+ ('BF_OPENVDB_INC', 'OpenVDB include path', ''),
+ ('BF_OPENVDB_LIB', 'OpenVDB library', ''),
+ ('BF_OPENVDB_LIB_PATH', 'OpenVDB library path', ''),
+ ('BF_OPENVDB_LIB_STATIC', 'OpenVDB static library', ''),
+
(BoolVariable('WITH_BF_BOOST', 'Build with Boost', False)),
(BoolVariable('WITH_BF_STATICBOOST', 'Staticly link to boost', False)),
('BF_BOOST', 'Boost root path', ''),
diff --git a/intern/CMakeLists.txt b/intern/CMakeLists.txt
index 74048c2a4cc..cf7f913d675 100644
--- a/intern/CMakeLists.txt
+++ b/intern/CMakeLists.txt
@@ -82,3 +82,7 @@ if(WIN32)
add_subdirectory(utfconv)
endif()
+if(WITH_OPENVDB)
+ add_subdirectory(openvdb)
+endif()
+
diff --git a/intern/SConscript b/intern/SConscript
index c0dafe37855..d416cab8aa2 100644
--- a/intern/SConscript
+++ b/intern/SConscript
@@ -63,3 +63,5 @@ if env['WITH_BF_BULLET']:
if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'win64-mingw', 'linuxcross', 'win64-vc'):
SConscript(['utfconv/SConscript'])
+if env['WITH_BF_OPENVDB']:
+ SConscript (['openvdb/SConscript'])
diff --git a/intern/audaspace/intern/AUD_Sequencer.cpp b/intern/audaspace/intern/AUD_Sequencer.cpp
index ddcf97e2ea1..a5b70232068 100644
--- a/intern/audaspace/intern/AUD_Sequencer.cpp
+++ b/intern/audaspace/intern/AUD_Sequencer.cpp
@@ -44,7 +44,8 @@ AUD_Sequencer::AUD_Sequencer(AUD_Specs specs, float fps, bool muted) :
m_distance_model(AUD_DISTANCE_MODEL_INVERSE_CLAMPED),
m_volume(1, 1.0f),
m_location(3),
- m_orientation(4)
+ m_orientation(4),
+ m_recursive(false)
{
AUD_Quaternion q;
m_orientation.write(q.get());
diff --git a/intern/audaspace/intern/AUD_Sequencer.h b/intern/audaspace/intern/AUD_Sequencer.h
index 1066eeae8e3..ef68efbbafc 100644
--- a/intern/audaspace/intern/AUD_Sequencer.h
+++ b/intern/audaspace/intern/AUD_Sequencer.h
@@ -201,6 +201,8 @@ public:
* \param entry The entry to remove.
*/
void remove(boost::shared_ptr<AUD_SequencerEntry> entry);
+
+ bool m_recursive;
};
#endif //__AUD_SEQUENCER_H__
diff --git a/intern/audaspace/intern/AUD_SequencerReader.cpp b/intern/audaspace/intern/AUD_SequencerReader.cpp
index aef93cd3896..b893b132fa3 100644
--- a/intern/audaspace/intern/AUD_SequencerReader.cpp
+++ b/intern/audaspace/intern/AUD_SequencerReader.cpp
@@ -78,6 +78,9 @@ AUD_Specs AUD_SequencerReader::getSpecs() const
void AUD_SequencerReader::read(int& length, bool& eos, sample_t* buffer)
{
+ if (m_sequence->m_recursive)
+ return;
+
AUD_MutexLock lock(*m_sequence);
if(m_sequence->m_status != m_status)
@@ -192,7 +195,9 @@ void AUD_SequencerReader::read(int& length, bool& eos, sample_t* buffer)
v2 -= v;
m_device.setListenerVelocity(v2 * m_sequence->m_fps);
+ m_sequence->m_recursive = true;
m_device.read(reinterpret_cast<data_t*>(buffer + specs.channels * pos), len);
+ m_sequence->m_recursive = false;
pos += len;
time += float(len) / float(specs.rate);
diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt
index ed6961f49e0..6c1264ff594 100644
--- a/intern/cycles/CMakeLists.txt
+++ b/intern/cycles/CMakeLists.txt
@@ -146,6 +146,15 @@ if(WITH_CYCLES_OSL)
)
endif()
+if(WITH_OPENVDB)
+ add_definitions(-DWITH_OPENVDB)
+ add_definitions(-DOPENVDB_USE_BLOSC)
+ add_definitions(-DDWREAL_IS_DOUBLE=0)
+ include_directories(
+ ${OPENVDB_INCLUDE_DIRS}
+ )
+endif()
+
add_definitions(
-DWITH_OPENCL
-DWITH_CUDA
diff --git a/intern/cycles/SConscript b/intern/cycles/SConscript
index 99df8c299fc..68eccf96a45 100644
--- a/intern/cycles/SConscript
+++ b/intern/cycles/SConscript
@@ -67,6 +67,10 @@ if env['WITH_BF_CYCLES_OSL']:
defs.append('OSL_STATIC_LIBRARY')
incs.append(cycles['BF_OSL_INC'])
+if env['WITH_BF_OPENVDB']:
+ defs.append('WITH_OPENVDB')
+ incs.append(env['BF_OPENVDB_INC'])
+
if env['WITH_BF_CYCLES_DEBUG']:
defs.append('WITH_CYCLES_DEBUG')
diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt
index b000266cac2..b9a2abf1c9a 100644
--- a/intern/cycles/app/CMakeLists.txt
+++ b/intern/cycles/app/CMakeLists.txt
@@ -96,6 +96,12 @@ macro(cycles_target_link_libraries target)
${CMAKE_DL_LIBS}
${PLATFORM_LINKLIBS}
)
+ if(WITH_ALEMBIC)
+ target_link_libraries(
+ ${target}
+ ${ALEMBIC_LIBRARIES}
+ )
+ endif()
endmacro()
# Application build targets
@@ -106,6 +112,23 @@ if(WITH_CYCLES_STANDALONE)
cycles_xml.cpp
cycles_xml.h
)
+
+ if(WITH_ALEMBIC)
+ list(APPEND SRC
+ cycles_alembic.cpp
+ cycles_alembic.h
+ )
+ add_definitions(-DWITH_ALEMBIC)
+ include_directories(
+ SYSTEM
+ ${ALEMBIC_INCLUDE_DIRS}
+ )
+ endif()
+
+ if(WITH_HDF5)
+ add_definitions(-DWITH_HDF5)
+ endif()
+
add_executable(cycles ${SRC})
cycles_target_link_libraries(cycles)
diff --git a/intern/cycles/app/cycles_alembic.cpp b/intern/cycles/app/cycles_alembic.cpp
new file mode 100644
index 00000000000..3a1e6a6bcaa
--- /dev/null
+++ b/intern/cycles/app/cycles_alembic.cpp
@@ -0,0 +1,426 @@
+/*
+ * Copyright 2015 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#include <sstream>
+#include <algorithm>
+#include <iterator>
+
+#include <Alembic/AbcCoreOgawa/ReadWrite.h>
+#ifdef WITH_HDF5
+#include <Alembic/AbcCoreHDF5/ReadWrite.h>
+#endif
+#include <Alembic/Abc/IArchive.h>
+#include <Alembic/Abc/IObject.h>
+#include <Alembic/Abc/ISampleSelector.h>
+#include <Alembic/Abc/ICompoundProperty.h>
+#include <Alembic/Abc/IScalarProperty.h>
+#include <Alembic/Abc/IArrayProperty.h>
+#include <Alembic/Abc/ArchiveInfo.h>
+#include <Alembic/AbcGeom/IPolyMesh.h>
+
+#include "camera.h"
+#include "film.h"
+#include "graph.h"
+#include "integrator.h"
+#include "light.h"
+#include "mesh.h"
+#include "nodes.h"
+#include "object.h"
+#include "shader.h"
+#include "scene.h"
+
+#include "subd_mesh.h"
+#include "subd_patch.h"
+#include "subd_split.h"
+
+#include "util_debug.h"
+#include "util_foreach.h"
+#include "util_path.h"
+#include "util_transform.h"
+#include "util_xml.h"
+
+#include "cycles_alembic.h"
+
+CCL_NAMESPACE_BEGIN
+
+using namespace Alembic;
+using namespace Abc;
+using namespace AbcGeom;
+
+#define ABC_SAFE_CALL_BEGIN \
+ try {
+
+#define ABC_SAFE_CALL_END \
+ } \
+ catch (Alembic::Util::Exception e) { \
+ printf("%s", e.what()); \
+ }
+
+
+/* File */
+
+static const std::string g_sep(";");
+
+static void visitProperties(std::stringstream &ss, ICompoundProperty, std::string &);
+
+template <class PROP>
+static void visitSimpleArrayProperty(std::stringstream &ss, PROP iProp, const std::string &iIndent)
+{
+ std::string ptype = "ArrayProperty ";
+ size_t asize = 0;
+
+ AbcA::ArraySamplePtr samp;
+ index_t maxSamples = iProp.getNumSamples();
+ for (index_t i = 0 ; i < maxSamples; ++i) {
+ iProp.get(samp, ISampleSelector( i ));
+ asize = samp->size();
+ };
+
+ std::string mdstring = "interpretation=";
+ mdstring += iProp.getMetaData().get("interpretation");
+
+ std::stringstream dtype;
+ dtype << "datatype=";
+ dtype << iProp.getDataType();
+
+ std::stringstream asizestr;
+ asizestr << ";arraysize=";
+ asizestr << asize;
+
+ mdstring += g_sep;
+
+ mdstring += dtype.str();
+
+ mdstring += asizestr.str();
+
+ ss << iIndent << " " << ptype << "name=" << iProp.getName()
+ << g_sep << mdstring << g_sep << "numsamps="
+ << iProp.getNumSamples() << std::endl;
+}
+
+template <class PROP>
+static void visitSimpleScalarProperty(std::stringstream &ss, PROP iProp, const std::string &iIndent)
+{
+ std::string ptype = "ScalarProperty ";
+ size_t asize = 0;
+
+ const AbcA::DataType &dt = iProp.getDataType();
+ const Alembic::Util ::uint8_t extent = dt.getExtent();
+ Alembic::Util::Dimensions dims(extent);
+ AbcA::ArraySamplePtr samp = AbcA::AllocateArraySample( dt, dims );
+ index_t maxSamples = iProp.getNumSamples();
+ for (index_t i = 0 ; i < maxSamples; ++i) {
+ iProp.get(const_cast<void*>(samp->getData()), ISampleSelector( i ));
+ asize = samp->size();
+ };
+
+ std::string mdstring = "interpretation=";
+ mdstring += iProp.getMetaData().get("interpretation");
+
+ std::stringstream dtype;
+ dtype << "datatype=";
+ dtype << dt;
+
+ std::stringstream asizestr;
+ asizestr << ";arraysize=";
+ asizestr << asize;
+
+ mdstring += g_sep;
+
+ mdstring += dtype.str();
+
+ mdstring += asizestr.str();
+
+ ss << iIndent << " " << ptype << "name=" << iProp.getName()
+ << g_sep << mdstring << g_sep << "numsamps="
+ << iProp.getNumSamples() << std::endl;
+}
+
+static void visitCompoundProperty(std::stringstream &ss, ICompoundProperty iProp, std::string &ioIndent)
+{
+ std::string oldIndent = ioIndent;
+ ioIndent += " ";
+
+ std::string interp = "schema=";
+ interp += iProp.getMetaData().get("schema");
+
+ ss << ioIndent << "CompoundProperty " << "name=" << iProp.getName()
+ << g_sep << interp << std::endl;
+
+ visitProperties(ss, iProp, ioIndent);
+
+ ioIndent = oldIndent;
+}
+
+static void visitProperties(std::stringstream &ss, ICompoundProperty iParent, std::string &ioIndent )
+{
+ std::string oldIndent = ioIndent;
+ for (size_t i = 0 ; i < iParent.getNumProperties() ; i++) {
+ PropertyHeader header = iParent.getPropertyHeader(i);
+
+ if (header.isCompound()) {
+ visitCompoundProperty(ss, ICompoundProperty(iParent, header.getName()), ioIndent);
+ }
+ else if (header.isScalar()) {
+ visitSimpleScalarProperty(ss, IScalarProperty(iParent, header.getName()), ioIndent);
+ }
+ else {
+ assert(header.isArray());
+ visitSimpleArrayProperty(ss, IArrayProperty(iParent, header.getName()), ioIndent);
+ }
+ }
+
+ ioIndent = oldIndent;
+}
+
+static void visitObject(std::stringstream &ss, IObject iObj, std::string iIndent, AbcArchiveInfoLevel info_level)
+{
+ // Object has a name, a full name, some meta data,
+ // and then it has a compound property full of properties.
+ std::string path = iObj.getFullName();
+
+ if (iObj.isInstanceRoot()) {
+ if (path != "/") {
+ ss << "Object " << "name=" << path
+ << " [Instance " << iObj.instanceSourcePath() << "]"
+ << std::endl;
+ }
+ }
+ else if (iObj.isInstanceDescendant()) {
+ /* skip non-root instances to avoid repetition */
+ return;
+ }
+ else {
+ if (path != "/") {
+ ss << "Object " << "name=" << path << std::endl;
+ }
+
+ if (info_level >= ABC_INFO_PROPERTIES) {
+ // Get the properties.
+ ICompoundProperty props = iObj.getProperties();
+ visitProperties(ss, props, iIndent);
+ }
+
+ // now the child objects
+ for (size_t i = 0 ; i < iObj.getNumChildren() ; i++) {
+ visitObject(ss, IObject(iObj, iObj.getChildHeader(i).getName()), iIndent, info_level);
+ }
+ }
+}
+
+static std::string abc_archive_info(IArchive &archive, AbcArchiveInfoLevel info_level)
+{
+ std::stringstream ss;
+
+ ss << "Alembic Archive Info for "
+ << Alembic::AbcCoreAbstract::GetLibraryVersion()
+ << std::endl;;
+
+ std::string appName;
+ std::string libraryVersionString;
+ Alembic::Util::uint32_t libraryVersion;
+ std::string whenWritten;
+ std::string userDescription;
+ GetArchiveInfo(archive,
+ appName,
+ libraryVersionString,
+ libraryVersion,
+ whenWritten,
+ userDescription);
+
+ if (appName != "") {
+ ss << " file written by: " << appName << std::endl;
+ ss << " using Alembic : " << libraryVersionString << std::endl;
+ ss << " written on : " << whenWritten << std::endl;
+ ss << " user description : " << userDescription << std::endl;
+ ss << std::endl;
+ }
+ else {
+// ss << argv[1] << std::endl;
+ ss << " (file doesn't have any ArchiveInfo)"
+ << std::endl;
+ ss << std::endl;
+ }
+
+ if (info_level >= ABC_INFO_OBJECTS)
+ visitObject(ss, archive.getTop(), "", info_level);
+
+ return ss.str();
+}
+
+/* ========================================================================= */
+
+struct AbcReadState {
+ Scene *scene; /* scene pointer */
+ float time;
+ Transform tfm; /* current transform state */
+ bool smooth; /* smooth normal state */
+ int shader; /* current shader */
+ string base; /* base path to current file*/
+ float dicing_rate; /* current dicing rate */
+ Mesh::DisplacementMethod displacement_method;
+};
+
+static ISampleSelector get_sample_selector(const AbcReadState &state)
+{
+ return ISampleSelector(state.time, ISampleSelector::kFloorIndex);
+}
+
+static Mesh *add_mesh(Scene *scene, const Transform& tfm)
+{
+ /* create mesh */
+ Mesh *mesh = new Mesh();
+ scene->meshes.push_back(mesh);
+
+ /* create object*/
+ Object *object = new Object();
+ object->mesh = mesh;
+ object->tfm = tfm;
+ scene->objects.push_back(object);
+
+ return mesh;
+}
+
+static void read_mesh(const AbcReadState &state, IPolyMesh object)
+{
+ /* add mesh */
+ Mesh *mesh = add_mesh(state.scene, state.tfm);
+ mesh->used_shaders.push_back(state.shader);
+
+ /* read state */
+ int shader = state.shader;
+ bool smooth = state.smooth;
+
+ mesh->displacement_method = state.displacement_method;
+
+ ISampleSelector ss = get_sample_selector(state);
+ IPolyMeshSchema schema = object.getSchema();
+
+ IPolyMeshSchema::Sample sample;
+ schema.get(sample, ss);
+
+ int totverts = sample.getPositions()->size();
+ int totfaces = sample.getFaceCounts()->size();
+ const V3f *P = sample.getPositions()->get();
+ const int32_t *verts = sample.getFaceIndices()->get();
+ const int32_t *nverts = sample.getFaceCounts()->get();
+
+ /* create vertices */
+ mesh->verts.reserve(totverts);
+ for(int i = 0; i < totverts; i++) {
+ mesh->verts.push_back(make_float3(P[i].x, P[i].y, P[i].z));
+ }
+
+ /* create triangles */
+ int index_offset = 0;
+
+ for(int i = 0; i < totfaces; i++) {
+ int n = nverts[i];
+ /* XXX TODO only supports tris and quads atm,
+ * need a proper tessellation algorithm in cycles.
+ */
+ if (n > 4) {
+ printf("%d-sided face found, only triangles and quads are supported currently", n);
+ n = 4;
+ }
+
+ for(int j = 0; j < n-2; j++) {
+ int v0 = verts[index_offset];
+ int v1 = verts[index_offset + j + 1];
+ int v2 = verts[index_offset + j + 2];
+
+ assert(v0 < (int)totverts);
+ assert(v1 < (int)totverts);
+ assert(v2 < (int)totverts);
+
+ mesh->add_triangle(v0, v1, v2, shader, smooth);
+ }
+
+ index_offset += n;
+ }
+
+ /* temporary for test compatibility */
+ mesh->attributes.remove(ATTR_STD_VERTEX_NORMAL);
+}
+
+static void read_object(const AbcReadState &state, IObject object)
+{
+ for (int i = 0; i < object.getNumChildren(); ++i) {
+ IObject child = object.getChild(i);
+ const MetaData &metadata = child.getMetaData();
+
+ if (IPolyMeshSchema::matches(metadata)) {
+ read_mesh(state, IPolyMesh(child, kWrapExisting));
+ }
+ else {
+ read_object(state, child);
+ }
+ }
+}
+
+static void read_archive(Scene *scene, IArchive archive, const char *filepath)
+{
+ AbcReadState state;
+
+ state.scene = scene;
+ state.time = 0.0f; // TODO
+ state.tfm = transform_identity();
+ state.shader = scene->default_surface;
+ state.smooth = false;
+ state.dicing_rate = 0.1f;
+ state.base = path_dirname(filepath);
+
+ read_object(state, archive.getTop());
+
+ scene->params.bvh_type = SceneParams::BVH_STATIC;
+}
+
+void abc_read_ogawa_file(Scene *scene, const char *filepath, AbcArchiveInfoLevel info_level)
+{
+ IArchive archive;
+ ABC_SAFE_CALL_BEGIN
+ archive = IArchive(AbcCoreOgawa::ReadArchive(), filepath, ErrorHandler::kThrowPolicy);
+ ABC_SAFE_CALL_END
+
+ if (archive) {
+ if (info_level >= ABC_INFO_BASIC)
+ printf("%s", abc_archive_info(archive, info_level).c_str());
+
+ read_archive(scene, archive, filepath);
+ }
+}
+
+void abc_read_hdf5_file(Scene *scene, const char *filepath, AbcArchiveInfoLevel info_level)
+{
+#ifdef WITH_HDF5
+ IArchive archive;
+ ABC_SAFE_CALL_BEGIN
+ archive = IArchive(AbcCoreHDF5::ReadArchive(), filepath, ErrorHandler::kThrowPolicy);
+ ABC_SAFE_CALL_END
+
+ if (archive) {
+ if (info_level >= ABC_INFO_BASIC)
+ printf("%s", abc_archive_info(archive, info_level).c_str());
+
+ read_archive(scene, archive, filepath);
+ }
+#endif
+}
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/app/cycles_alembic.h b/intern/cycles/app/cycles_alembic.h
new file mode 100644
index 00000000000..df17f8b8fbe
--- /dev/null
+++ b/intern/cycles/app/cycles_alembic.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 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 __CYCLES_ALEMBIC_H__
+#define __CYCLES_ALEMBIC_H__
+
+CCL_NAMESPACE_BEGIN
+
+class Scene;
+
+enum AbcArchiveInfoLevel {
+ ABC_INFO_NONE = 0,
+ ABC_INFO_BASIC,
+ ABC_INFO_OBJECTS,
+ ABC_INFO_PROPERTIES,
+};
+
+void abc_read_ogawa_file(Scene *scene, const char *filepath, AbcArchiveInfoLevel info_level = ABC_INFO_NONE);
+void abc_read_hdf5_file(Scene *scene, const char *filepath, AbcArchiveInfoLevel info_level = ABC_INFO_NONE);
+
+CCL_NAMESPACE_END
+
+#endif /* __CYCLES_XML_H__ */
diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp
index b0d49d6ee72..3980ca547c2 100644
--- a/intern/cycles/app/cycles_standalone.cpp
+++ b/intern/cycles/app/cycles_standalone.cpp
@@ -38,13 +38,23 @@
#endif
#include "cycles_xml.h"
+#ifdef WITH_ALEMBIC
+#include "cycles_alembic.h"
+#endif
CCL_NAMESPACE_BEGIN
+enum FileType {
+ FILETYPE_XML = 0,
+ FILETYPE_ABC_HDF5,
+ FILETYPE_ABC_OGAWA,
+};
+
struct Options {
Session *session;
Scene *scene;
string filepath;
+ FileType filetype;
int width, height;
SceneParams scene_params;
SessionParams session_params;
@@ -121,8 +131,22 @@ static void scene_init()
{
options.scene = new Scene(options.scene_params, options.session_params.device);
- /* Read XML */
- xml_read_file(options.scene, options.filepath.c_str());
+ /* Read file */
+ switch (options.filetype) {
+ case FILETYPE_XML:
+ xml_read_file(options.scene, options.filepath.c_str());
+ break;
+#ifdef WITH_ALEMBIC
+ case FILETYPE_ABC_OGAWA:
+ abc_read_ogawa_file(options.scene, options.filepath.c_str());
+ break;
+ case FILETYPE_ABC_HDF5:
+ abc_read_hdf5_file(options.scene, options.filepath.c_str());
+ break;
+#endif
+ default:
+ return;
+ }
/* Camera width/height override? */
if(!(options.width == 0 || options.height == 0)) {
@@ -356,6 +380,16 @@ static void options_parse(int argc, const char **argv)
/* shading system */
string ssname = "svm";
+ /* input file type */
+ string filetypes = "auto, xml";
+#ifdef WITH_ALEMBIC
+ filetypes += ", alembic_ogawa";
+#ifdef WITH_HDF5
+ filetypes += ", alembic_hdf5";
+#endif
+#endif
+ string filetype = "auto";
+
/* parse options */
ArgParse ap;
bool help = false, debug = false;
@@ -371,6 +405,7 @@ static void options_parse(int argc, const char **argv)
"--quiet", &options.quiet, "In background mode, don't print progress messages",
"--samples %d", &options.session_params.samples, "Number of samples to render",
"--output %s", &options.session_params.output_path, "File path to write output image",
+ "--filetype %s", &filetype, ("File type: " + filetypes).c_str(),
"--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",
@@ -416,6 +451,21 @@ static void options_parse(int argc, const char **argv)
else if(ssname == "svm")
options.scene_params.shadingsystem = SHADINGSYSTEM_SVM;
+ if(filetype == "auto") {
+ string extension = options.filepath.substr(options.filepath.find_last_of(".") + 1);
+
+ if (extension == "xml")
+ options.filetype = FILETYPE_XML;
+ else if (extension == "abc")
+ options.filetype = FILETYPE_ABC_OGAWA;
+ }
+ else if(filetype == "xml")
+ options.filetype = FILETYPE_XML;
+ else if(filetype == "alembic_ogawa")
+ options.filetype = FILETYPE_ABC_OGAWA;
+ else if(filetype == "alembic_hdf5")
+ options.filetype = FILETYPE_ABC_HDF5;
+
#ifndef WITH_CYCLES_STANDALONE_GUI
options.session_params.background = true;
#endif
diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp
index edea8cd0ec4..77b0a6454ce 100644
--- a/intern/cycles/app/cycles_xml.cpp
+++ b/intern/cycles/app/cycles_xml.cpp
@@ -43,6 +43,9 @@
#include "util_xml.h"
#include "cycles_xml.h"
+#ifdef WITH_ALEMBIC
+#include "cycles_alembic.h"
+#endif
CCL_NAMESPACE_BEGIN
@@ -735,6 +738,13 @@ static void xml_read_shader_graph(const XMLReadState& state, Shader *shader, pug
xml_read_enum(&vtransform->convert_to, VectorTransformNode::convert_space_enum, node, "convert_to");
snode = vtransform;
}
+ else if(string_iequals(node.name(), "openvdb")) {
+ OpenVDBNode *vdbnode = new OpenVDBNode();
+ xml_read_string(&vdbnode->filename, node, "src");
+ vdbnode->filename = path_join(state.base, vdbnode->filename);
+
+ snode = vdbnode;
+ }
else if(string_iequals(node.name(), "connect")) {
/* connect nodes */
vector<string> from_tokens, to_tokens;
@@ -1188,6 +1198,25 @@ static void xml_read_state(XMLReadState& state, pugi::xml_node node)
state.displacement_method = Mesh::DISPLACE_BOTH;
}
+/* Alembic */
+static void xml_read_alembic(const XMLReadState& state, pugi::xml_node node)
+{
+#ifdef WITH_ALEMBIC
+ string filepath;
+
+ if(xml_read_string(&filepath, node, "file")) {
+ filepath = path_join(state.base, filepath);
+
+ if(xml_equal_string(node, "type", "hdf5"))
+ abc_read_hdf5_file(state.scene, filepath.c_str(), ABC_INFO_BASIC);
+ else if(xml_equal_string(node, "type", "ogawa"))
+ abc_read_ogawa_file(state.scene, filepath.c_str(), ABC_INFO_BASIC);
+ else
+ abc_read_ogawa_file(state.scene, filepath.c_str(), ABC_INFO_BASIC); /* default */
+ }
+#endif
+}
+
/* Scene */
static void xml_read_include(const XMLReadState& state, const string& src);
@@ -1237,6 +1266,9 @@ static void xml_read_scene(const XMLReadState& state, pugi::xml_node scene_node)
if(xml_read_string(&src, node, "src"))
xml_read_include(state, src);
}
+ else if(string_iequals(node.name(), "alembic")) {
+ xml_read_alembic(state, node);
+ }
else
fprintf(stderr, "Unknown node \"%s\".\n", node.name());
}
diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt
index fff9ed20bba..c6a2b919486 100644
--- a/intern/cycles/blender/CMakeLists.txt
+++ b/intern/cycles/blender/CMakeLists.txt
@@ -31,10 +31,12 @@ set(SRC
blender_session.cpp
blender_shader.cpp
blender_sync.cpp
+ blender_texture.cpp
CCL_api.h
blender_sync.h
blender_session.h
+ blender_texture.h
blender_util.h
)
diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 16a807b3af5..0daf6784798 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -463,6 +463,12 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
description="Use BVH spatial splits: longer builder time, faster render",
default=False,
)
+ cls.debug_use_triangle_storage = BoolProperty(
+ name="Use Triangle Storage",
+ description="use special storage with aligned triangle coordinates for faster "
+ "intesection check in expense of higher memory usage",
+ default=True,
+ )
cls.use_cache = BoolProperty(
name="Cache BVH",
description="Cache last built BVH to disk for faster re-render if no geometry changed",
@@ -511,6 +517,19 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
),
)
+ cls.use_camera_cull = BoolProperty(
+ name="Use Camera Cull",
+ description="Allow objects to be culled based on the camera frustum",
+ default=False,
+ )
+
+ cls.camera_cull_margin = FloatProperty(
+ name="Camera Cull Margin",
+ description="Margin for the camera space culling",
+ default=0.1,
+ min=0.0, max=5.0
+ )
+
@classmethod
def unregister(cls):
del bpy.types.Scene.cycles
@@ -890,6 +909,12 @@ class CyclesObjectBlurSettings(bpy.types.PropertyGroup):
default=1,
)
+ cls.use_camera_cull = BoolProperty(
+ name="Use Camera Cull",
+ description="Allow this object and it's duplicators to be culled by camera space culling",
+ default=False,
+ )
+
@classmethod
def unregister(cls):
del bpy.types.Object.cycles
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index f6ff86ac30e..7fcb99d9e15 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -322,6 +322,11 @@ class CyclesRender_PT_performance(CyclesButtonsPanel, Panel):
subsub.enabled = not rd.use_border
subsub.prop(rd, "use_save_buffers")
+ sub.prop(cscene, "use_camera_cull")
+ subsub = col.column()
+ subsub.active = cscene.use_camera_cull
+ subsub.prop(cscene, "camera_cull_margin")
+
col = split.column(align=True)
col.label(text="Viewport:")
@@ -339,6 +344,7 @@ class CyclesRender_PT_performance(CyclesButtonsPanel, Panel):
col.label(text="Acceleration structure:")
col.prop(cscene, "debug_use_spatial_splits")
+ col.prop(cscene, "debug_use_triangle_storage")
class CyclesRender_PT_layer_options(CyclesButtonsPanel, Panel):
@@ -687,8 +693,8 @@ class CyclesObject_PT_motion_blur(CyclesButtonsPanel, Panel):
sub.prop(cob, "motion_steps", text="Steps")
-class CyclesObject_PT_ray_visibility(CyclesButtonsPanel, Panel):
- bl_label = "Ray Visibility"
+class CyclesObject_PT_cycles_settings(CyclesButtonsPanel, Panel):
+ bl_label = "Cycles Settings"
bl_context = "object"
bl_options = {'DEFAULT_CLOSED'}
@@ -703,8 +709,10 @@ class CyclesObject_PT_ray_visibility(CyclesButtonsPanel, Panel):
layout = self.layout
ob = context.object
+ cob = ob.cycles
visibility = ob.cycles_visibility
+ layout.label(text="Ray Visibility:")
flow = layout.column_flow()
flow.prop(visibility, "camera")
@@ -716,6 +724,9 @@ class CyclesObject_PT_ray_visibility(CyclesButtonsPanel, Panel):
if ob.type != 'LAMP':
flow.prop(visibility, "shadow")
+ layout.label(text="Performance:")
+ layout.prop(cob, "use_camera_cull")
+
class CYCLES_OT_use_shading_nodes(Operator):
"""Enable nodes on a material, world or lamp"""
@@ -1225,7 +1236,8 @@ class CyclesTexture_PT_mapping(CyclesButtonsPanel, Panel):
@classmethod
def poll(cls, context):
node = context.texture_node
- return node and CyclesButtonsPanel.poll(context)
+ # TODO(sergey): perform a faster/nicer check?
+ return node and hasattr(node, 'texture_mapping') and CyclesButtonsPanel.poll(context)
def draw(self, context):
layout = self.layout
@@ -1449,7 +1461,8 @@ class CyclesScene_PT_simplify(CyclesButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
- rd = context.scene.render
+ scene = context.scene
+ rd = scene.render
layout.active = rd.use_simplify
split = layout.split()
@@ -1529,6 +1542,7 @@ def get_panels():
"DATA_PT_curve_texture_space",
"DATA_PT_mball_texture_space",
"DATA_PT_vertex_groups",
+ "DATA_PT_face_maps",
"DATA_PT_shape_keys",
"DATA_PT_uv_texture",
"DATA_PT_vertex_colors",
diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp
index dba801fc4df..4ed0b910f35 100644
--- a/intern/cycles/blender/blender_curves.cpp
+++ b/intern/cycles/blender/blender_curves.cpp
@@ -37,15 +37,14 @@ void curveinterp_v3_v3v3v3v3(float3 *p, float3 *v1, float3 *v2, float3 *v3, floa
void interp_weights(float t, float data[4]);
float shaperadius(float shape, float root, float tip, float time);
void InterpolateKeySegments(int seg, int segno, int key, int curve, float3 *keyloc, float *time, ParticleCurveData *CData);
-bool ObtainCacheParticleUV(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int uv_num);
-bool ObtainCacheParticleVcol(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int vcol_num);
-bool ObtainCacheParticleData(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background);
void ExportCurveSegments(Scene *scene, Mesh *mesh, ParticleCurveData *CData);
void ExportCurveTrianglePlanes(Mesh *mesh, ParticleCurveData *CData,
float3 RotCam, bool is_ortho);
void ExportCurveTriangleGeometry(Mesh *mesh, ParticleCurveData *CData, int resolution);
void ExportCurveTriangleUV(ParticleCurveData *CData, int vert_offset, int resol, float3 *uvdata);
+void ExportCurveUV(Mesh *mesh, ParticleCurveData *CData, ustring name, bool active_render, int primitive, int vert_offset, int resol);
void ExportCurveTriangleVcol(ParticleCurveData *CData, int vert_offset, int resol, uchar4 *cdata);
+void ExportCurveVcol(Mesh *mesh, ParticleCurveData *CData, ustring name, int primitive, int vert_offset, int resol);
ParticleCurveData::ParticleCurveData()
{
@@ -119,206 +118,330 @@ void InterpolateKeySegments(int seg, int segno, int key, int curve, float3 *keyl
curveinterp_v3_v3v3v3v3(keyloc, &ckey_loc1, &ckey_loc2, &ckey_loc3, &ckey_loc4, t);
}
-bool ObtainCacheParticleData(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background)
+static void ObtainCacheParticleData(Mesh *mesh, BL::Object b_ob, BL::ParticleSystem b_psys, const Transform &itfm,
+ ParticleCurveData *CData, bool background)
{
- int curvenum = 0;
- int keyno = 0;
+ BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
+ int mi = clamp(b_part.material()-1, 0, mesh->used_shaders.size()-1);
+ int shader = mesh->used_shaders[mi];
+ int draw_step = background ? b_part.render_step() : b_part.draw_step();
+ int totparts = b_psys.particles.length();
+ int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
+ int totcurves = totchild;
+
+ if(b_part.child_type() == 0)
+ totcurves += totparts;
- if(!(mesh && b_mesh && b_ob && CData))
- return false;
+ if(totcurves == 0)
+ return;
- Transform tfm = get_transform(b_ob->matrix_world());
- Transform itfm = transform_quick_inverse(tfm);
+ int ren_step = (1 << draw_step) + 1;
+ if(b_part.kink() == BL::ParticleSettings::kink_SPIRAL)
+ ren_step += b_part.kink_extra_steps();
- BL::Object::modifiers_iterator b_mod;
- for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
- if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (background ? b_mod->show_render() : b_mod->show_viewport())) {
- BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
- BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
- BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
+ PointerRNA cpsys = RNA_pointer_get(&b_part.ptr, "cycles");
+
+ int keyno = CData->curvekey_co.size();
+ int curvenum = CData->curve_keynum.size();
+
+ CData->psys_firstcurve.push_back(curvenum);
+ CData->psys_curvenum.push_back(totcurves);
+ CData->psys_shader.push_back(shader);
+
+ float radius = get_float(cpsys, "radius_scale") * 0.5f;
+
+ CData->psys_rootradius.push_back(radius * get_float(cpsys, "root_width"));
+ CData->psys_tipradius.push_back(radius * get_float(cpsys, "tip_width"));
+ CData->psys_shape.push_back(get_float(cpsys, "shape"));
+ CData->psys_closetip.push_back(get_boolean(cpsys, "use_closetip"));
+
+ int pa_no = 0;
+ if(!(b_part.child_type() == 0))
+ pa_no = totparts;
+
+ int num_add = (totparts+totchild - pa_no);
+ CData->curve_firstkey.reserve(CData->curve_firstkey.size() + num_add);
+ CData->curve_keynum.reserve(CData->curve_keynum.size() + num_add);
+ CData->curve_length.reserve(CData->curve_length.size() + num_add);
+ CData->curvekey_co.reserve(CData->curvekey_co.size() + num_add*ren_step);
+ CData->curvekey_time.reserve(CData->curvekey_time.size() + num_add*ren_step);
+
+ for(; pa_no < totparts+totchild; pa_no++) {
+ int keynum = 0;
+ CData->curve_firstkey.push_back(keyno);
+
+ float curve_length = 0.0f;
+ float3 pcKey;
+ for(int step_no = 0; step_no < ren_step; step_no++) {
+ float nco[3];
+ b_psys.co_hair(b_ob, pa_no, step_no, nco);
+ float3 cKey = make_float3(nco[0], nco[1], nco[2]);
+ cKey = transform_point(&itfm, cKey);
+ if(step_no > 0) {
+ float step_length = len(cKey - pcKey);
+ if(step_length == 0.0f)
+ continue;
+ curve_length += step_length;
+ }
+ CData->curvekey_co.push_back(cKey);
+ CData->curvekey_time.push_back(curve_length);
+ pcKey = cKey;
+ keynum++;
+ }
+ keyno += keynum;
- if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) {
- int mi = clamp(b_part.material()-1, 0, mesh->used_shaders.size()-1);
- int shader = mesh->used_shaders[mi];
- int draw_step = background ? b_part.render_step() : b_part.draw_step();
- int totparts = b_psys.particles.length();
- int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
- int totcurves = totchild;
-
- if(b_part.child_type() == 0)
- totcurves += totparts;
+ CData->curve_keynum.push_back(keynum);
+ CData->curve_length.push_back(curve_length);
+ curvenum++;
+ }
+}
- if(totcurves == 0)
- continue;
+static void ObtainCacheParticleUV(Mesh * /*mesh*/, BL::Object /*b_ob*/, BL::Mesh b_mesh, BL::ParticleSystem b_psys, BL::ParticleSystemModifier b_psmd,
+ ParticleCurveData *CData, bool background, int uv_num)
+{
+ BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
+ int totparts = b_psys.particles.length();
+ int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
+ int totcurves = totchild;
+
+ if(b_part.child_type() == 0)
+ totcurves += totparts;
- int ren_step = (1 << draw_step) + 1;
- if(b_part.kink() == BL::ParticleSettings::kink_SPIRAL)
- ren_step += b_part.kink_extra_steps();
+ if(totcurves == 0)
+ return;
- PointerRNA cpsys = RNA_pointer_get(&b_part.ptr, "cycles");
+ int pa_no = 0;
+ if(!(b_part.child_type() == 0))
+ pa_no = totparts;
- CData->psys_firstcurve.push_back(curvenum);
- CData->psys_curvenum.push_back(totcurves);
- CData->psys_shader.push_back(shader);
+ int num_add = (totparts+totchild - pa_no);
+ CData->curve_uv.reserve(CData->curve_uv.size() + num_add);
- float radius = get_float(cpsys, "radius_scale") * 0.5f;
-
- CData->psys_rootradius.push_back(radius * get_float(cpsys, "root_width"));
- CData->psys_tipradius.push_back(radius * get_float(cpsys, "tip_width"));
- CData->psys_shape.push_back(get_float(cpsys, "shape"));
- CData->psys_closetip.push_back(get_boolean(cpsys, "use_closetip"));
-
- int pa_no = 0;
- if(!(b_part.child_type() == 0))
- pa_no = totparts;
-
- int num_add = (totparts+totchild - pa_no);
- CData->curve_firstkey.reserve(CData->curve_firstkey.size() + num_add);
- CData->curve_keynum.reserve(CData->curve_keynum.size() + num_add);
- CData->curve_length.reserve(CData->curve_length.size() + num_add);
- CData->curvekey_co.reserve(CData->curvekey_co.size() + num_add*ren_step);
- CData->curvekey_time.reserve(CData->curvekey_time.size() + num_add*ren_step);
-
- for(; pa_no < totparts+totchild; pa_no++) {
- int keynum = 0;
- CData->curve_firstkey.push_back(keyno);
-
- float curve_length = 0.0f;
- float3 pcKey;
- for(int step_no = 0; step_no < ren_step; step_no++) {
- float nco[3];
- b_psys.co_hair(*b_ob, pa_no, step_no, nco);
- float3 cKey = make_float3(nco[0], nco[1], nco[2]);
- cKey = transform_point(&itfm, cKey);
- if(step_no > 0) {
- float step_length = len(cKey - pcKey);
- if(step_length == 0.0f)
- continue;
- curve_length += step_length;
- }
- CData->curvekey_co.push_back(cKey);
- CData->curvekey_time.push_back(curve_length);
- pcKey = cKey;
- keynum++;
- }
- keyno += keynum;
+ BL::ParticleSystem::particles_iterator b_pa;
+ b_psys.particles.begin(b_pa);
+ for(; pa_no < totparts+totchild; pa_no++) {
+ /* Add UVs */
+ BL::Mesh::tessface_uv_textures_iterator l;
+ b_mesh.tessface_uv_textures.begin(l);
- CData->curve_keynum.push_back(keynum);
- CData->curve_length.push_back(curve_length);
- curvenum++;
- }
- }
- }
- }
+ float3 uv = make_float3(0.0f, 0.0f, 0.0f);
+ if(b_mesh.tessface_uv_textures.length())
+ b_psys.uv_on_emitter(b_psmd, *b_pa, pa_no, uv_num, &uv.x);
+ CData->curve_uv.push_back(uv);
- return true;
+ if(pa_no < totparts && b_pa != b_psys.particles.end())
+ ++b_pa;
+ }
}
-bool ObtainCacheParticleUV(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int uv_num)
+static void ObtainCacheParticleVcol(Mesh * /*mesh*/, BL::Object /*b_ob*/, BL::Mesh b_mesh, BL::ParticleSystem b_psys, BL::ParticleSystemModifier b_psmd,
+ ParticleCurveData *CData, bool background, int vcol_num)
{
- if(!(mesh && b_mesh && b_ob && CData))
- return false;
+ BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
+ int totparts = b_psys.particles.length();
+ int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
+ int totcurves = totchild;
+
+ if(b_part.child_type() == 0)
+ totcurves += totparts;
- CData->curve_uv.clear();
+ if(totcurves == 0)
+ return;
- BL::Object::modifiers_iterator b_mod;
- for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
- if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (background ? b_mod->show_render() : b_mod->show_viewport())) {
- BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
- BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
- BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
+ int pa_no = 0;
+ if(!(b_part.child_type() == 0))
+ pa_no = totparts;
- if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) {
- int totparts = b_psys.particles.length();
- int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
- int totcurves = totchild;
-
- if(b_part.child_type() == 0)
- totcurves += totparts;
+ int num_add = (totparts+totchild - pa_no);
+ CData->curve_vcol.reserve(CData->curve_vcol.size() + num_add);
- if(totcurves == 0)
- continue;
+ BL::ParticleSystem::particles_iterator b_pa;
+ b_psys.particles.begin(b_pa);
+ for(; pa_no < totparts+totchild; pa_no++) {
+ /* Add vertex colors */
+ BL::Mesh::tessface_vertex_colors_iterator l;
+ b_mesh.tessface_vertex_colors.begin(l);
- int pa_no = 0;
- if(!(b_part.child_type() == 0))
- pa_no = totparts;
+ float3 vcol = make_float3(0.0f, 0.0f, 0.0f);
+ if(b_mesh.tessface_vertex_colors.length())
+ b_psys.mcol_on_emitter(b_psmd, *b_pa, pa_no, vcol_num, &vcol.x);
+ CData->curve_vcol.push_back(vcol);
- int num_add = (totparts+totchild - pa_no);
- CData->curve_uv.reserve(CData->curve_uv.size() + num_add);
+ if(pa_no < totparts && b_pa != b_psys.particles.end())
+ ++b_pa;
+ }
+}
+
+/* A little bit of templated code here to avoid much duplication for parent/child strands:
+ * Most attributes are the same for both types, but some are handled slightly differently.
+ * These attributes are handled by templated utility functions that automatically get selected by type.
+ */
+template <typename StrandsT>
+struct StrandsTraits;
- BL::ParticleSystem::particles_iterator b_pa;
- b_psys.particles.begin(b_pa);
- for(; pa_no < totparts+totchild; pa_no++) {
- /* Add UVs */
- BL::Mesh::tessface_uv_textures_iterator l;
- b_mesh->tessface_uv_textures.begin(l);
+template<>
+struct StrandsTraits<BL::Strands>
+{
+ typedef BL::StrandsCurve curve_t;
+ typedef BL::StrandsVertex vertex_t;
+
+ static float3 get_location(BL::Strands b_strands, int index)
+ {
+ float *co = (b_strands.has_motion_state())? b_strands.motion_state[index].location() : b_strands.vertices[index].location();
+ return make_float3(co[0], co[1], co[2]);
+ }
+ static float3 get_uv(BL::Strands /*b_strands*/, int /*index*/, int /*uv_num*/)
+ {
+ return make_float3(0.0f, 0.0f, 0.0f);
+ }
+ static float3 get_vcol(BL::Strands /*b_strands*/, int /*index*/, int /*vcol_num*/)
+ {
+ return make_float3(0.0f, 0.0f, 0.0f);
+ }
+};
- float3 uv = make_float3(0.0f, 0.0f, 0.0f);
- if(b_mesh->tessface_uv_textures.length())
- b_psys.uv_on_emitter(psmd, *b_pa, pa_no, uv_num, &uv.x);
- CData->curve_uv.push_back(uv);
+template<>
+struct StrandsTraits<BL::StrandsChildren>
+{
+ typedef BL::StrandsChildCurve curve_t;
+ typedef BL::StrandsChildVertex vertex_t;
+
+ static float3 get_location(BL::StrandsChildren b_strands, int index)
+ {
+ float *co = b_strands.vertices[index].location();
+ return make_float3(co[0], co[1], co[2]);
+ }
+ static float3 get_uv(BL::StrandsChildren b_strands, int index, int uv_num)
+ {
+ if (uv_num < b_strands.num_curve_uv_layers()) {
+ size_t offset = uv_num * b_strands.curves.length();
+ float *uv = b_strands.curve_uvs[offset + index].uv();
+ return make_float3(uv[0], uv[1], 0.0f);
+ }
+ else
+ return make_float3(0.0f, 0.0f, 0.0f);
+ }
+ static float3 get_vcol(BL::StrandsChildren b_strands, int index, int vcol_num)
+ {
+ if (vcol_num < b_strands.num_curve_vcol_layers()) {
+ size_t offset = vcol_num * b_strands.curves.length();
+ float *vcol = b_strands.curve_vcols[offset + index].vcol();
+ return make_float3(vcol[0], vcol[1], vcol[2]);
+ }
+ else
+ return make_float3(0.0f, 0.0f, 0.0f);
+ }
+};
- if(pa_no < totparts && b_pa != b_psys.particles.end())
- ++b_pa;
- }
+template <typename StrandsT>
+static bool ObtainCacheStrandsData(Mesh *mesh, BL::Scene /*b_scene*/, BL::Object /*b_parent*/, BL::DupliObject /*b_dupli_ob*/, BL::ParticleSystem b_psys, StrandsT b_strands, const Transform &/*itfm*/,
+ ParticleCurveData *CData, bool /*background*/)
+{
+ typedef StrandsTraits<StrandsT> traits;
+ typedef typename traits::curve_t CurveT;
+
+ BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
+ PointerRNA cpsys = RNA_pointer_get(&b_part.ptr, "cycles");
+
+ int mi = clamp(b_part.material()-1, 0, mesh->used_shaders.size()-1);
+ int shader = mesh->used_shaders[mi];
+
+ int totcurves = b_strands.curves.length();
+ int totvert = b_strands.vertices.length();
+
+ int keyno = CData->curvekey_co.size();
+ int curvenum = CData->curve_keynum.size();
+
+ CData->psys_firstcurve.push_back(curvenum);
+ CData->psys_curvenum.push_back(totcurves);
+ CData->psys_shader.push_back(shader);
+
+ float radius = get_float(cpsys, "radius_scale") * 0.5f;
+
+ CData->psys_rootradius.push_back(radius * get_float(cpsys, "root_width"));
+ CData->psys_tipradius.push_back(radius * get_float(cpsys, "tip_width"));
+ CData->psys_shape.push_back(get_float(cpsys, "shape"));
+ CData->psys_closetip.push_back(get_boolean(cpsys, "use_closetip"));
+
+ CData->curve_firstkey.reserve(CData->curve_firstkey.size() + totcurves);
+ CData->curve_keynum.reserve(CData->curve_keynum.size() + totcurves);
+ CData->curve_length.reserve(CData->curve_length.size() + totcurves);
+ CData->curvekey_co.reserve(CData->curvekey_co.size() + totvert);
+ CData->curvekey_time.reserve(CData->curvekey_time.size() + totvert);
+
+ int icurve = 0;
+ int ivert = 0;
+ for(; icurve < totcurves; ++icurve) {
+ CurveT b_curve = b_strands.curves[icurve];
+ int numverts = b_curve.size();
+ int showverts = b_curve.render_size();
+ int usedverts = 0;
+ CData->curve_firstkey.push_back(keyno);
+
+ float curve_length = 0.0f;
+ float3 pcKey;
+ for(int cvert = 0; cvert < showverts; ++cvert) {
+ float3 cKey = traits::get_location(b_strands, ivert + cvert);
+
+ if(cvert > 0) {
+ float step_length = len(cKey - pcKey);
+ if(step_length == 0.0f)
+ continue;
+ curve_length += step_length;
}
+ CData->curvekey_co.push_back(cKey);
+ CData->curvekey_time.push_back(curve_length);
+ pcKey = cKey;
+ usedverts++;
}
- }
+ keyno += usedverts;
+ ivert += numverts;
+ CData->curve_keynum.push_back(usedverts);
+ CData->curve_length.push_back(curve_length);
+ curvenum++;
+ }
+
return true;
}
-bool ObtainCacheParticleVcol(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int vcol_num)
+template <typename StrandsT>
+static bool ObtainCacheStrandsUV(Mesh * /*mesh*/, BL::Scene /*b_scene*/, BL::Object /*b_parent*/, BL::DupliObject /*b_dupli_ob*/, BL::ParticleSystem /*b_psys*/, StrandsT b_strands,
+ ParticleCurveData *CData, bool /*background*/, int uv_num)
{
- if(!(mesh && b_mesh && b_ob && CData))
- return false;
-
- CData->curve_vcol.clear();
-
- BL::Object::modifiers_iterator b_mod;
- for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) {
- if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (background ? b_mod->show_render() : b_mod->show_viewport())) {
- BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr);
- BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr);
- BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
-
- if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) {
- int totparts = b_psys.particles.length();
- int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f);
- int totcurves = totchild;
-
- if(b_part.child_type() == 0)
- totcurves += totparts;
-
- if(totcurves == 0)
- continue;
-
- int pa_no = 0;
- if(!(b_part.child_type() == 0))
- pa_no = totparts;
-
- int num_add = (totparts+totchild - pa_no);
- CData->curve_vcol.reserve(CData->curve_vcol.size() + num_add);
+ typedef StrandsTraits<StrandsT> traits;
+
+// BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
- BL::ParticleSystem::particles_iterator b_pa;
- b_psys.particles.begin(b_pa);
- for(; pa_no < totparts+totchild; pa_no++) {
- /* Add vertex colors */
- BL::Mesh::tessface_vertex_colors_iterator l;
- b_mesh->tessface_vertex_colors.begin(l);
+ int totcurves = b_strands.curves.length();
- float3 vcol = make_float3(0.0f, 0.0f, 0.0f);
- if(b_mesh->tessface_vertex_colors.length())
- b_psys.mcol_on_emitter(psmd, *b_pa, pa_no, vcol_num, &vcol.x);
- CData->curve_vcol.push_back(vcol);
+ CData->curve_uv.reserve(CData->curve_uv.size() + totcurves);
- if(pa_no < totparts && b_pa != b_psys.particles.end())
- ++b_pa;
- }
- }
- }
+ int icurve = 0;
+ for(; icurve < totcurves; ++icurve) {
+ CData->curve_uv.push_back(traits::get_uv(b_strands, icurve, uv_num));
}
+
+ return true;
+}
+template <typename StrandsT>
+static bool ObtainCacheStrandsVcol(Mesh * /*mesh*/, BL::Scene /*b_scene*/, BL::Object /*b_parent*/, BL::DupliObject /*b_dupli_ob*/, BL::ParticleSystem /*b_psys*/, StrandsT b_strands,
+ ParticleCurveData *CData, bool /*background*/, int vcol_num)
+{
+ typedef StrandsTraits<StrandsT> traits;
+
+// BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
+
+ int totcurves = b_strands.curves.length();
+
+ CData->curve_vcol.reserve(CData->curve_vcol.size() + totcurves);
+
+ int icurve = 0;
+ for(; icurve < totcurves; ++icurve) {
+ CData->curve_vcol.push_back(traits::get_vcol(b_strands, icurve, vcol_num));
+ }
+
return true;
}
@@ -750,6 +873,39 @@ void ExportCurveTriangleUV(ParticleCurveData *CData, int vert_offset, int resol,
}
}
+void ExportCurveUV(Mesh *mesh, ParticleCurveData *CData, ustring name, bool active_render, int primitive, int vert_offset, int resol)
+{
+ AttributeStandard std = (active_render)? ATTR_STD_UV: ATTR_STD_NONE;
+ Attribute *attr_uv;
+
+ if(primitive == CURVE_TRIANGLES) {
+ if(active_render)
+ attr_uv = mesh->attributes.add(std, name);
+ else
+ attr_uv = mesh->attributes.add(name, TypeDesc::TypePoint, ATTR_ELEMENT_CORNER);
+
+ float3 *uv = attr_uv->data_float3();
+
+ ExportCurveTriangleUV(CData, vert_offset, resol, uv);
+ }
+ else {
+ if(active_render)
+ attr_uv = mesh->curve_attributes.add(std, name);
+ else
+ attr_uv = mesh->curve_attributes.add(name, TypeDesc::TypePoint, ATTR_ELEMENT_CURVE);
+
+ float3 *uv = attr_uv->data_float3();
+
+ if(uv) {
+ size_t i = 0;
+
+ for(size_t curve = 0; curve < CData->curve_uv.size(); curve++)
+ if(!(CData->curve_keynum[curve] <= 1 || CData->curve_length[curve] == 0.0f))
+ uv[i++] = CData->curve_uv[curve];
+ }
+ }
+}
+
void ExportCurveTriangleVcol(ParticleCurveData *CData, int vert_offset, int resol, uchar4 *cdata)
{
if(cdata == NULL)
@@ -782,6 +938,31 @@ void ExportCurveTriangleVcol(ParticleCurveData *CData, int vert_offset, int reso
}
}
+void ExportCurveVcol(Mesh *mesh, ParticleCurveData *CData, ustring name, int primitive, int vert_offset, int resol)
+{
+ if(primitive == CURVE_TRIANGLES) {
+ Attribute *attr_vcol = mesh->attributes.add(name, TypeDesc::TypeColor, ATTR_ELEMENT_CORNER_BYTE);
+
+ uchar4 *cdata = attr_vcol->data_uchar4();
+
+ ExportCurveTriangleVcol(CData, vert_offset, resol, cdata);
+ }
+ else {
+ Attribute *attr_vcol = mesh->curve_attributes.add(name, TypeDesc::TypeColor, ATTR_ELEMENT_CURVE);
+
+ float3 *fdata = attr_vcol->data_float3();
+
+ if(fdata) {
+ size_t i = 0;
+
+ for(size_t curve = 0; curve < CData->curve_vcol.size(); curve++)
+ if(!(CData->curve_keynum[curve] <= 1 || CData->curve_length[curve] == 0.0f))
+ fdata[i++] = color_srgb_to_scene_linear(CData->curve_vcol[curve]);
+ }
+ }
+}
+
+
/* Hair Curve Sync */
void BlenderSync::sync_curve_settings()
@@ -856,8 +1037,61 @@ void BlenderSync::sync_curve_settings()
curve_system_manager->tag_update(scene);
}
-void BlenderSync::sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_ob, bool motion, int time_index)
+struct CurvesPSysData {
+ CurvesPSysData(BL::ParticleSystemModifier b_psmd=PointerRNA_NULL, BL::ParticleSystem b_psys=PointerRNA_NULL,
+ BL::Strands b_strands=PointerRNA_NULL, BL::StrandsChildren b_strands_children=PointerRNA_NULL) :
+ b_psmd(b_psmd), b_psys(b_psys), b_strands(b_strands), b_strands_children(b_strands_children)
+ {}
+
+ BL::ParticleSystemModifier b_psmd;
+ BL::ParticleSystem b_psys;
+ BL::Strands b_strands;
+ BL::StrandsChildren b_strands_children;
+};
+
+static void curves_get_psys_data(std::vector<CurvesPSysData> &b_psys_list, BL::Scene b_scene, BL::Object b_ob, BL::Object b_parent, BL::DupliObject b_dupli_ob, bool preview)
+{
+ BL::Object::modifiers_iterator b_mod;
+ for(b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) {
+ if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (preview ? b_mod->show_viewport() : b_mod->show_render())) {
+ BL::ParticleSystemModifier b_psmd((const PointerRNA)b_mod->ptr);
+ BL::ParticleSystem b_psys((const PointerRNA)b_psmd.particle_system().ptr);
+ BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr);
+
+ if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) {
+ int settings = preview ? 1 : 2;
+
+ BL::StrandsChildren b_strands_children = PointerRNA_NULL;
+ BL::Strands b_strands = PointerRNA_NULL;
+
+ if (b_dupli_ob && b_parent) {
+ b_strands_children = b_dupli_ob.strands_children_new(b_scene, b_parent, b_psys, settings);
+ if (!b_strands_children)
+ b_strands = b_dupli_ob.strands_new(b_scene, b_parent, b_psys, settings);
+ }
+
+ b_psys_list.push_back(CurvesPSysData(b_psmd, b_psys, b_strands, b_strands_children));
+ }
+ }
+ }
+}
+
+static void curves_free_psys_data(std::vector<CurvesPSysData> &b_psys_list, BL::DupliObject b_dupli_ob)
+{
+ /* free temporary strands data */
+ for (int i = 0; i < b_psys_list.size(); ++i) {
+ CurvesPSysData &psys_data = b_psys_list[i];
+ if (psys_data.b_strands)
+ b_dupli_ob.strands_free(psys_data.b_strands);
+ if (psys_data.b_strands_children)
+ b_dupli_ob.strands_children_free(psys_data.b_strands_children);
+ }
+}
+
+void BlenderSync::sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_parent, bool motion, int time_index, BL::DupliObject b_dupli_ob)
{
+ BL::Object b_ob = (b_dupli_ob ? b_dupli_ob.object() : b_parent);
+
if(!motion) {
/* Clear stored curve data */
mesh->curve_keys.clear();
@@ -888,27 +1122,46 @@ void BlenderSync::sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_ob, bool
if(!preview)
set_resolution(&b_ob, &b_scene, true);
- ObtainCacheParticleData(mesh, &b_mesh, &b_ob, &CData, !preview);
+ Transform tfm = get_transform(b_ob.matrix_world());
+ Transform itfm = transform_quick_inverse(tfm);
+
+ /* obtain camera parameters */
+ float3 RotCam;
+ Camera *camera = scene->camera;
+ if(camera->type == CAMERA_ORTHOGRAPHIC) {
+ Transform &ctfm = camera->matrix;
+ RotCam = -make_float3(ctfm.x.z, ctfm.y.z, ctfm.z.z);
+ }
+ else {
+ Transform &ctfm = camera->matrix;
+ RotCam = transform_point(&itfm, make_float3(ctfm.x.w, ctfm.y.w, ctfm.z.w));
+ }
+ bool is_ortho_camera = camera->type == CAMERA_ORTHOGRAPHIC;
+
+ std::vector<CurvesPSysData> b_psys_list;
+ curves_get_psys_data(b_psys_list, b_scene, b_ob, b_parent, b_dupli_ob, preview);
+
+ for (int i = 0; i < b_psys_list.size(); ++i) {
+ CurvesPSysData &psys_data = b_psys_list[i];
+ if (psys_data.b_strands_children)
+ /* use child strands cache */
+ ObtainCacheStrandsData(mesh, b_scene, b_parent, b_dupli_ob, psys_data.b_psys, psys_data.b_strands_children,
+ itfm, &CData, !preview);
+ else if (psys_data.b_strands)
+ /* use parent strands cache */
+ ObtainCacheStrandsData(mesh, b_scene, b_parent, b_dupli_ob, psys_data.b_psys, psys_data.b_strands,
+ itfm, &CData, !preview);
+ else {
+ /* use object data */
+ ObtainCacheParticleData(mesh, b_ob, psys_data.b_psys,
+ itfm, &CData, !preview);
+ }
+ }
/* add hair geometry to mesh */
if(primitive == CURVE_TRIANGLES) {
if(triangle_method == CURVE_CAMERA_TRIANGLES) {
- /* obtain camera parameters */
- float3 RotCam;
- Camera *camera = scene->camera;
- Transform &ctfm = camera->matrix;
- if(camera->type == CAMERA_ORTHOGRAPHIC) {
- RotCam = -make_float3(ctfm.x.z, ctfm.y.z, ctfm.z.z);
- }
- else {
- Transform tfm = get_transform(b_ob.matrix_world());
- Transform itfm = transform_quick_inverse(tfm);
- RotCam = transform_point(&itfm, make_float3(ctfm.x.w,
- ctfm.y.w,
- ctfm.z.w));
- }
- bool is_ortho = camera->type == CAMERA_ORTHOGRAPHIC;
- ExportCurveTrianglePlanes(mesh, &CData, RotCam, is_ortho);
+ ExportCurveTrianglePlanes(mesh, &CData, RotCam, is_ortho_camera);
}
else {
ExportCurveTriangleGeometry(mesh, &CData, resolution);
@@ -955,33 +1208,31 @@ void BlenderSync::sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_ob, bool
int vcol_num = 0;
for(b_mesh.tessface_vertex_colors.begin(l); l != b_mesh.tessface_vertex_colors.end(); ++l, vcol_num++) {
+ ustring name = ustring(l->name().c_str());
+
if(!mesh->need_attribute(scene, ustring(l->name().c_str())))
continue;
- ObtainCacheParticleVcol(mesh, &b_mesh, &b_ob, &CData, !preview, vcol_num);
-
- if(primitive == CURVE_TRIANGLES) {
- Attribute *attr_vcol = mesh->attributes.add(
- ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CORNER_BYTE);
-
- uchar4 *cdata = attr_vcol->data_uchar4();
-
- ExportCurveTriangleVcol(&CData, tri_num * 3, used_res, cdata);
- }
- else {
- Attribute *attr_vcol = mesh->curve_attributes.add(
- ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CURVE);
-
- float3 *fdata = attr_vcol->data_float3();
-
- if(fdata) {
- size_t i = 0;
-
- for(size_t curve = 0; curve < CData.curve_vcol.size(); curve++)
- if(!(CData.curve_keynum[curve] <= 1 || CData.curve_length[curve] == 0.0f))
- fdata[i++] = color_srgb_to_scene_linear(CData.curve_vcol[curve]);
+ CData.curve_vcol.clear();
+
+ for (int i = 0; i < b_psys_list.size(); ++i) {
+ CurvesPSysData &psys_data = b_psys_list[i];
+ if (psys_data.b_strands_children)
+ /* use child strands cache */
+ ObtainCacheStrandsVcol(mesh, b_scene, b_parent, b_dupli_ob, psys_data.b_psys, psys_data.b_strands_children,
+ &CData, !preview, vcol_num);
+ else if (psys_data.b_strands)
+ /* use parent strands cache */
+ ObtainCacheStrandsVcol(mesh, b_scene, b_parent, b_dupli_ob, psys_data.b_psys, psys_data.b_strands,
+ &CData, !preview, vcol_num);
+ else {
+ /* use object data */
+ ObtainCacheParticleVcol(mesh, b_ob, b_mesh, psys_data.b_psys, psys_data.b_psmd,
+ &CData, !preview, vcol_num);
}
}
+
+ ExportCurveVcol(mesh, &CData, name, primitive, tri_num * 3, used_res);
}
}
@@ -992,45 +1243,37 @@ void BlenderSync::sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_ob, bool
for(b_mesh.tessface_uv_textures.begin(l); l != b_mesh.tessface_uv_textures.end(); ++l, uv_num++) {
bool active_render = l->active_render();
- AttributeStandard std = (active_render)? ATTR_STD_UV: ATTR_STD_NONE;
ustring name = ustring(l->name().c_str());
/* UV map */
- if(mesh->need_attribute(scene, name) || mesh->need_attribute(scene, std)) {
- Attribute *attr_uv;
-
- ObtainCacheParticleUV(mesh, &b_mesh, &b_ob, &CData, !preview, uv_num);
-
- if(primitive == CURVE_TRIANGLES) {
- if(active_render)
- attr_uv = mesh->attributes.add(std, name);
- else
- attr_uv = mesh->attributes.add(name, TypeDesc::TypePoint, ATTR_ELEMENT_CORNER);
-
- float3 *uv = attr_uv->data_float3();
-
- ExportCurveTriangleUV(&CData, tri_num * 3, used_res, uv);
- }
+ if(!(mesh->need_attribute(scene, name) || (active_render && mesh->need_attribute(scene, ATTR_STD_UV))))
+ continue;
+
+ CData.curve_uv.clear();
+
+ for (int i = 0; i < b_psys_list.size(); ++i) {
+ CurvesPSysData &psys_data = b_psys_list[i];
+ if (psys_data.b_strands_children)
+ /* use child strands cache */
+ ObtainCacheStrandsUV(mesh, b_scene, b_parent, b_dupli_ob, psys_data.b_psys, psys_data.b_strands_children,
+ &CData, !preview, uv_num);
+ else if (psys_data.b_strands)
+ /* use parent strands cache */
+ ObtainCacheStrandsUV(mesh, b_scene, b_parent, b_dupli_ob, psys_data.b_psys, psys_data.b_strands,
+ &CData, !preview, uv_num);
else {
- if(active_render)
- attr_uv = mesh->curve_attributes.add(std, name);
- else
- attr_uv = mesh->curve_attributes.add(name, TypeDesc::TypePoint, ATTR_ELEMENT_CURVE);
-
- float3 *uv = attr_uv->data_float3();
-
- if(uv) {
- size_t i = 0;
-
- for(size_t curve = 0; curve < CData.curve_uv.size(); curve++)
- if(!(CData.curve_keynum[curve] <= 1 || CData.curve_length[curve] == 0.0f))
- uv[i++] = CData.curve_uv[curve];
- }
+ /* use object data */
+ ObtainCacheParticleUV(mesh, b_ob, b_mesh, psys_data.b_psys, psys_data.b_psmd,
+ &CData, !preview, uv_num);
}
}
+
+ ExportCurveUV(mesh, &CData, name, active_render, primitive, tri_num * 3, used_res);
}
}
+ curves_free_psys_data(b_psys_list, b_dupli_ob);
+
if(!preview)
set_resolution(&b_ob, &b_scene, false);
diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp
index d88ebb854d2..3fa80a89947 100644
--- a/intern/cycles/blender/blender_mesh.cpp
+++ b/intern/cycles/blender/blender_mesh.cpp
@@ -229,7 +229,13 @@ static void mikk_compute_tangents(BL::Mesh b_mesh, BL::MeshTextureFaceLayer *b_l
/* Create Volume Attribute */
-static void create_mesh_volume_attribute(BL::Object b_ob, Mesh *mesh, ImageManager *image_manager, AttributeStandard std, float frame)
+static void create_mesh_volume_attribute(BL::Object b_ob,
+ Mesh *mesh,
+ ImageManager *image_manager,
+ AttributeStandard std,
+ float frame,
+ AttributeStandard special_std = ATTR_STD_NONE,
+ bool from_alpha = false)
{
BL::SmokeDomainSettings b_domain = object_smoke_domain_find(b_ob);
@@ -240,17 +246,35 @@ static void create_mesh_volume_attribute(BL::Object b_ob, Mesh *mesh, ImageManag
VoxelAttribute *volume_data = attr->data_voxel();
bool is_float, is_linear;
bool animated = false;
+ AttributeStandard data_attr = (special_std == ATTR_STD_NONE) ? std : special_std;
volume_data->manager = image_manager;
- volume_data->slot = image_manager->add_image(Attribute::standard_name(std),
+ volume_data->slot = image_manager->add_image(Attribute::standard_name(data_attr),
b_ob.ptr.data, animated, frame, is_float, is_linear, INTERPOLATION_LINEAR, true);
+ volume_data->from_alpha = from_alpha;
}
static void create_mesh_volume_attributes(Scene *scene, BL::Object b_ob, Mesh *mesh, float frame)
{
/* for smoke volume rendering */
- if(mesh->need_attribute(scene, ATTR_STD_VOLUME_DENSITY))
- create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_DENSITY, frame);
+ if(mesh->need_attribute(scene, ATTR_STD_VOLUME_DENSITY)) {
+ /* Special case: we re-map density to A channel of Color texture
+ * if both attributes are requested.
+ */
+ AttributeStandard special_std = ATTR_STD_VOLUME_DENSITY;
+ bool from_alpha = false;
+ if(mesh->need_attribute(scene, ATTR_STD_VOLUME_COLOR)) {
+ special_std = ATTR_STD_VOLUME_COLOR;
+ from_alpha = true;
+ }
+ create_mesh_volume_attribute(b_ob,
+ mesh,
+ scene->image_manager,
+ ATTR_STD_VOLUME_DENSITY,
+ frame,
+ special_std,
+ from_alpha);
+ }
if(mesh->need_attribute(scene, ATTR_STD_VOLUME_COLOR))
create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_COLOR, frame);
if(mesh->need_attribute(scene, ATTR_STD_VOLUME_FLAME))
@@ -422,6 +446,52 @@ static void attr_create_pointiness(Scene *scene,
}
}
+/* Create vertex custom attributes. */
+static void attr_create_vertex_properties(Scene *scene,
+ Mesh *mesh,
+ BL::Mesh b_mesh)
+{
+ {
+ BL::Mesh::vertex_layers_float_iterator l;
+ for(b_mesh.vertex_layers_float.begin(l); l != b_mesh.vertex_layers_float.end(); ++l) {
+ if(!mesh->need_attribute(scene, ustring(l->name().c_str())))
+ continue;
+
+ Attribute *attr = mesh->attributes.add(
+ ustring(l->name().c_str()), TypeDesc::TypeFloat, ATTR_ELEMENT_VERTEX);
+
+ BL::MeshVertexFloatPropertyLayer::data_iterator d;
+ float *data = attr->data_float();
+ size_t i = 0;
+
+ for(l->data.begin(d); d != l->data.end(); ++d, ++i) {
+ *data = d->value();
+ data += 1;
+ }
+ }
+ }
+
+ {
+ BL::Mesh::vertex_layers_int_iterator l;
+ for(b_mesh.vertex_layers_int.begin(l); l != b_mesh.vertex_layers_int.end(); ++l) {
+ if(!mesh->need_attribute(scene, ustring(l->name().c_str())))
+ continue;
+
+ Attribute *attr = mesh->attributes.add(
+ ustring(l->name().c_str()), TypeDesc::TypeFloat, ATTR_ELEMENT_VERTEX);
+
+ BL::MeshVertexIntPropertyLayer::data_iterator d;
+ float *data = attr->data_float();
+ size_t i = 0;
+
+ for(l->data.begin(d); d != l->data.end(); ++d, ++i) {
+ *data = (float)d->value();
+ data += 1;
+ }
+ }
+ }
+}
+
/* Create Mesh */
static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, const vector<uint>& used_shaders)
@@ -528,6 +598,7 @@ static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, const vector<
*/
attr_create_vertex_color(scene, mesh, b_mesh, nverts);
attr_create_uv_map(scene, mesh, b_mesh, nverts);
+ attr_create_vertex_properties(scene, mesh, b_mesh);
/* for volume objects, create a matrix to transform from object space to
* mesh texture space. this does not work with deformations but that can
@@ -587,8 +658,10 @@ static void create_subd_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, PointerR
/* Sync */
-Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tris)
+Mesh *BlenderSync::sync_mesh(BL::Object b_parent, bool object_updated, bool hide_tris, BL::DupliObject b_dupli_ob)
{
+ BL::Object b_ob = (b_dupli_ob ? b_dupli_ob.object() : b_parent);
+
/* When viewport display is not needed during render we can force some
* caches to be releases from blender side in order to reduce peak memory
* footprint during synchronization process.
@@ -599,7 +672,6 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tri
/* test if we can instance or if the object is modified */
BL::ID b_ob_data = b_ob.data();
- BL::ID key = (BKE_object_is_modified(b_ob))? b_ob: b_ob_data;
BL::Material material_override = render_layer.material_override;
/* find shader indices */
@@ -630,7 +702,24 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tri
}
Mesh *mesh;
- if(!mesh_map.sync(&mesh, key)) {
+ bool need_update;
+ BL::CacheLibrary b_cachelib = b_parent.cache_library();
+ bool use_dupli_override = b_dupli_ob && b_cachelib &&
+ (b_cachelib.source_mode() == BL::CacheLibrary::source_mode_CACHE ||
+ b_cachelib.display_mode() == BL::CacheLibrary::display_mode_RESULT);
+ if (use_dupli_override) {
+ /* if a dupli override (cached data) is used, identify the mesh by object and parent together,
+ * so that individual per-dupli overrides are possible.
+ */
+ MeshKey key = MeshKey(b_parent, b_ob);
+ need_update = mesh_map.sync(&mesh, b_ob, b_parent, key);
+ }
+ else {
+ BL::ID key = (BKE_object_is_modified(b_ob))? b_ob: b_ob_data;
+ need_update = mesh_map.sync(&mesh, key);
+ }
+
+ if(!need_update) {
/* if transform was applied to mesh, need full update */
if(object_updated && mesh->transform_applied);
/* test if shaders changed, these can be object level so mesh
@@ -681,7 +770,9 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tri
b_ob.update_from_editmode();
bool need_undeformed = mesh->need_attribute(scene, ATTR_STD_GENERATED);
- BL::Mesh b_mesh = object_to_mesh(b_data, b_ob, b_scene, true, !preview, need_undeformed);
+ BL::Mesh b_mesh = (use_dupli_override)?
+ dupli_to_mesh(b_data, b_scene, b_parent, b_dupli_ob, !preview, need_undeformed):
+ object_to_mesh(b_data, b_ob, b_scene, true, !preview, need_undeformed);
if(b_mesh) {
if(render_layer.use_surfaces && !hide_tris) {
@@ -694,10 +785,10 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tri
}
if(render_layer.use_hair)
- sync_curves(mesh, b_mesh, b_ob, false);
+ sync_curves(mesh, b_mesh, b_parent, false, 0, b_dupli_ob);
if(can_free_caches) {
- b_ob.cache_release();
+ b_ob.cache_release(false);
}
/* free derived mesh */
@@ -740,8 +831,9 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tri
return mesh;
}
-void BlenderSync::sync_mesh_motion(BL::Object b_ob, Object *object, float motion_time)
+void BlenderSync::sync_mesh_motion(BL::Object b_parent, Object *object, float motion_time, BL::DupliObject b_dupli_ob)
{
+ BL::Object b_ob = (b_dupli_ob ? b_dupli_ob.object() : b_parent);
/* ensure we only sync instanced meshes once */
Mesh *mesh = object->mesh;
@@ -800,7 +892,9 @@ void BlenderSync::sync_mesh_motion(BL::Object b_ob, Object *object, float motion
if(ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview)) {
/* get derived mesh */
- b_mesh = object_to_mesh(b_data, b_ob, b_scene, true, !preview, false);
+ b_mesh = (b_dupli_ob && b_parent)?
+ dupli_to_mesh(b_data, b_scene, b_parent, b_dupli_ob, !preview, false):
+ object_to_mesh(b_data, b_ob, b_scene, true, !preview, false);
}
if(!b_mesh) {
@@ -890,7 +984,7 @@ void BlenderSync::sync_mesh_motion(BL::Object b_ob, Object *object, float motion
/* hair motion */
if(numkeys)
- sync_curves(mesh, b_mesh, b_ob, true, time_index);
+ sync_curves(mesh, b_mesh, b_parent, true, time_index, b_dupli_ob);
/* free derived mesh */
b_data.meshes.remove(b_mesh);
diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp
index 1e17d306fd0..6afcc2231aa 100644
--- a/intern/cycles/blender/blender_object.cpp
+++ b/intern/cycles/blender/blender_object.cpp
@@ -234,8 +234,53 @@ void BlenderSync::sync_background_light(bool use_portal)
/* Object */
-Object *BlenderSync::sync_object(BL::Object b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::DupliObject b_dupli_ob,
- Transform& tfm, uint layer_flag, float motion_time, bool hide_tris, bool *use_portal)
+static bool object_boundbox_clip(Scene *scene,
+ BL::Object b_ob,
+ Transform& tfm,
+ float margin)
+{
+ Camera *cam = scene->camera;
+ Transform& worldtondc = cam->worldtondc;
+ BL::Array<float, 24> boundbox = b_ob.bound_box();
+ float3 bb_min = make_float3(FLT_MAX, FLT_MAX, FLT_MAX),
+ bb_max = make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
+ bool all_behind = true;
+ for(int i = 0; i < 8; ++i) {
+ float3 p = make_float3(boundbox[3 * i + 0],
+ boundbox[3 * i + 1],
+ boundbox[3 * i + 2]);
+ p = transform_point(&tfm, p);
+ p = transform_point(&worldtondc, p);
+ if(p.z >= -margin) {
+ all_behind = false;
+ }
+ p /= p.z;
+ bb_min = min(bb_min, p);
+ bb_max = max(bb_max, p);
+ }
+ if(!all_behind) {
+ if(bb_min.x >= 1.0f + margin ||
+ bb_min.y >= 1.0f + margin ||
+ bb_max.x <= -margin ||
+ bb_max.y <= -margin)
+ {
+ return true;
+ }
+ return false;
+ }
+ return true;
+}
+
+Object *BlenderSync::sync_object(BL::Object b_parent,
+ int persistent_id[OBJECT_PERSISTENT_ID_SIZE],
+ BL::DupliObject b_dupli_ob,
+ Transform& tfm,
+ uint layer_flag,
+ float motion_time,
+ bool hide_tris,
+ bool use_camera_cull,
+ float camera_cull_margin,
+ bool *use_portal)
{
BL::Object b_ob = (b_dupli_ob ? b_dupli_ob.object() : b_parent);
bool motion = motion_time != 0.0f;
@@ -253,6 +298,11 @@ Object *BlenderSync::sync_object(BL::Object b_parent, int persistent_id[OBJECT_P
if(!object_is_mesh(b_ob))
return NULL;
+ /* Perform camera space culling. */
+ if(use_camera_cull && object_boundbox_clip(scene, b_ob, tfm, camera_cull_margin)) {
+ return NULL;
+ }
+
/* key to lookup object */
ObjectKey key(b_parent, persistent_id, b_ob);
Object *object;
@@ -279,7 +329,7 @@ Object *BlenderSync::sync_object(BL::Object b_parent, int persistent_id[OBJECT_P
/* mesh deformation */
if(object->mesh)
- sync_mesh_motion(b_ob, object, motion_time);
+ sync_mesh_motion(b_parent, object, motion_time, b_dupli_ob);
}
return object;
@@ -294,7 +344,10 @@ Object *BlenderSync::sync_object(BL::Object b_parent, int persistent_id[OBJECT_P
bool use_holdout = (layer_flag & render_layer.holdout_layer) != 0;
/* mesh sync */
- object->mesh = sync_mesh(b_ob, object_updated, hide_tris);
+ if (b_dupli_ob)
+ object->mesh = sync_mesh(b_parent, object_updated, hide_tris, b_dupli_ob);
+ else
+ object->mesh = sync_mesh(b_ob, object_updated, hide_tris);
/* special case not tracked by object update flags */
@@ -480,6 +533,15 @@ void BlenderSync::sync_objects(BL::SpaceView3D b_v3d, float motion_time)
mesh_motion_synced.clear();
}
+ PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
+ bool allow_camera_cull = scene->camera->type != CAMERA_PANORAMA &&
+ !b_scene.render().use_multiview() &&
+ get_boolean(cscene, "use_camera_cull");
+ float camera_cull_margin = 0.0f;
+ if(allow_camera_cull) {
+ camera_cull_margin = get_float(cscene, "camera_cull_margin");
+ }
+
/* object loop */
BL::Scene::object_bases_iterator b_base;
BL::Scene b_sce = b_scene;
@@ -502,6 +564,12 @@ void BlenderSync::sync_objects(BL::SpaceView3D b_v3d, float motion_time)
if(!hide) {
progress.set_sync_status("Synchronizing object", b_ob.name());
+ PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles");
+ bool use_camera_cull = allow_camera_cull && get_boolean(cobject, "use_camera_cull");
+ if(use_camera_cull) {
+ /* Need to have proper projection matrix. */
+ scene->camera->update();
+ }
if(b_ob.is_duplicator() && !object_render_hide_duplis(b_ob)) {
/* dupli objects */
b_ob.dupli_list_create(b_scene, dupli_settings);
@@ -521,7 +589,16 @@ void BlenderSync::sync_objects(BL::SpaceView3D b_v3d, float motion_time)
BL::Array<int, OBJECT_PERSISTENT_ID_SIZE> persistent_id = b_dup->persistent_id();
/* sync object and mesh or light data */
- Object *object = sync_object(b_ob, persistent_id.data, *b_dup, tfm, ob_layer, motion_time, hide_tris, &use_portal);
+ Object *object = sync_object(b_ob,
+ persistent_id.data,
+ *b_dup,
+ tfm,
+ ob_layer,
+ motion_time,
+ hide_tris,
+ use_camera_cull,
+ camera_cull_margin,
+ &use_portal);
/* sync possible particle data, note particle_id
* starts counting at 1, first is dummy particle */
@@ -541,7 +618,16 @@ void BlenderSync::sync_objects(BL::SpaceView3D b_v3d, float motion_time)
if(!object_render_hide(b_ob, true, true, hide_tris)) {
/* object itself */
Transform tfm = get_transform(b_ob.matrix_world());
- sync_object(b_ob, NULL, PointerRNA_NULL, tfm, ob_layer, motion_time, hide_tris, &use_portal);
+ sync_object(b_ob,
+ NULL,
+ PointerRNA_NULL,
+ tfm,
+ ob_layer,
+ motion_time,
+ hide_tris,
+ use_camera_cull,
+ camera_cull_margin,
+ &use_portal);
}
}
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
index cdc44748923..9a1db703836 100644
--- a/intern/cycles/blender/blender_session.cpp
+++ b/intern/cycles/blender/blender_session.cpp
@@ -101,7 +101,8 @@ void BlenderSession::create_session()
start_resize_time = 0.0;
/* create scene */
- scene = new Scene(scene_params, session_params.device);
+ bool free_data_after_update = background && !scene_params.persistent_data;
+ scene = new Scene(scene_params, session_params.device, free_data_after_update);
/* setup callbacks for builtin image support */
scene->image_manager->builtin_image_info_cb = function_bind(&BlenderSession::builtin_image_info, this, _1, _2, _3, _4, _5, _6, _7);
@@ -413,6 +414,7 @@ void BlenderSession::render()
/* set callback to write out render results */
session->write_render_tile_cb = function_bind(&BlenderSession::write_render_tile, this, _1);
session->update_render_tile_cb = function_bind(&BlenderSession::update_render_tile, this, _1);
+ session->clear_database_cb = function_bind(&BlenderSession::clear_blender_database, this);
/* get buffer parameters */
SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background);
@@ -511,6 +513,7 @@ void BlenderSession::render()
/* clear callback */
session->write_render_tile_cb = function_null;
session->update_render_tile_cb = function_null;
+ session->clear_database_cb = function_null;
/* free all memory used (host and device), so we wouldn't leave render
* engine with extra memory allocated
@@ -1012,6 +1015,18 @@ void BlenderSession::builtin_image_info(const string &builtin_name, void *builti
is_float = true;
}
+ else {
+ /* TODO(sergey): Check we're indeed in shader node tree. */
+ PointerRNA ptr;
+ RNA_pointer_create(NULL, &RNA_Node, builtin_data, &ptr);
+ BL::Node b_node(ptr);
+ if(b_node.is_a(&RNA_ShaderNodeTexPointDensity)) {
+ BL::ShaderNodeTexPointDensity b_point_density_node(b_node);
+ channels = 4;
+ width = height = depth = b_point_density_node.resolution();
+ is_float = true;
+ }
+ }
}
bool BlenderSession::builtin_image_pixels(const string &builtin_name, void *builtin_data, unsigned char *pixels)
@@ -1154,9 +1169,41 @@ bool BlenderSession::builtin_image_float_pixels(const string &builtin_name, void
fprintf(stderr, "Cycles error: unexpected smoke volume resolution, skipping\n");
}
+ else {
+ /* TODO(sergey): Check we're indeed in shader node tree. */
+ PointerRNA ptr;
+ RNA_pointer_create(NULL, &RNA_Node, builtin_data, &ptr);
+ BL::Node b_node(ptr);
+ if(b_node.is_a(&RNA_ShaderNodeTexPointDensity)) {
+ BL::ShaderNodeTexPointDensity b_point_density_node(b_node);
+ int length;
+ b_point_density_node.calc_point_density(b_scene, &length, &pixels);
+ }
+ }
return false;
}
+void BlenderSession::clear_blender_database()
+{
+ const bool is_interface_locked = b_engine.render() &&
+ b_engine.render().use_lock_interface();
+ const bool can_free_database = BlenderSession::headless || is_interface_locked;
+ if(!can_free_database) {
+ /* Database might be used by the interface, can not free it at all. */
+ return;
+ }
+ for(BL::Scene b_sce = b_scene; b_sce; b_sce = b_sce.background_set()) {
+ BL::Scene::object_bases_iterator b_base;
+ for(b_sce.object_bases.begin(b_base);
+ b_base != b_sce.object_bases.end();
+ ++b_base)
+ {
+ BL::Object b_ob = b_base->object();
+ b_ob.cache_release(true);
+ }
+ }
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h
index 708776dc8ca..5498548eb74 100644
--- a/intern/cycles/blender/blender_session.h
+++ b/intern/cycles/blender/blender_session.h
@@ -109,6 +109,8 @@ protected:
void builtin_image_info(const string &builtin_name, void *builtin_data, bool &is_float, int &width, int &height, int &depth, int &channels);
bool builtin_image_pixels(const string &builtin_name, void *builtin_data, unsigned char *pixels);
bool builtin_image_float_pixels(const string &builtin_name, void *builtin_data, float *pixels);
+
+ void clear_blender_database();
};
CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp
index 2b0e8acae38..140560bed0f 100644
--- a/intern/cycles/blender/blender_shader.cpp
+++ b/intern/cycles/blender/blender_shader.cpp
@@ -22,6 +22,7 @@
#include "scene.h"
#include "shader.h"
+#include "blender_texture.h"
#include "blender_sync.h"
#include "blender_util.h"
@@ -736,6 +737,62 @@ static ShaderNode *add_node(Scene *scene,
uvm->from_dupli = b_uvmap_node.from_dupli();
node = uvm;
}
+ else if(b_node.is_a(&RNA_ShaderNodeTexPointDensity)) {
+ BL::ShaderNodeTexPointDensity b_point_density_node(b_node);
+ PointDensityTextureNode *point_density = new PointDensityTextureNode();
+ point_density->filename = b_point_density_node.name();
+ point_density->space =
+ PointDensityTextureNode::space_enum[(int)b_point_density_node.space()];
+ point_density->interpolation =
+ (InterpolationType)b_point_density_node.interpolation();
+ point_density->builtin_data = b_point_density_node.ptr.data;
+
+ /* Transformation form world space to texture space. */
+ BL::Object b_ob(b_point_density_node.object());
+ if(b_ob) {
+ float3 loc, size;
+ point_density_texture_space(b_point_density_node, loc, size);
+ point_density->tfm =
+ transform_translate(-loc) * transform_scale(size) *
+ transform_inverse(get_transform(b_ob.matrix_world()));
+ }
+
+ /* TODO(sergey): Use more proper update flag. */
+ if(true) {
+ scene->image_manager->tag_reload_image(point_density->filename,
+ point_density->builtin_data,
+ point_density->interpolation);
+ }
+ node = point_density;
+ }
+ else if(b_node.is_a(&RNA_ShaderNodeOpenVDB)) {
+ BL::ShaderNodeOpenVDB b_vdb_node(b_node);
+ OpenVDBNode *vdb_node = new OpenVDBNode();
+ vdb_node->filename = b_vdb_node.filename();
+ vdb_node->sampling = b_vdb_node.sampling();
+
+ /* TODO(kevin) */
+ if(b_vdb_node.source() == 1) {
+ string filename = b_vdb_node.filename();
+ string basename = filename.substr(0, filename.size() - 8);
+ stringstream ss;
+ ss << b_scene.frame_current();
+ string frame = ss.str();
+ frame.insert(frame.begin(), 4 - frame.size(), '0');
+
+ vdb_node->filename = ustring::format("%s%s.vdb", basename, frame);
+ }
+
+ BL::Node::outputs_iterator b_output;
+
+ for(b_vdb_node.outputs.begin(b_output); b_output != b_vdb_node.outputs.end(); ++b_output) {
+ vdb_node->output_names.push_back(ustring(b_output->name()));
+ vdb_node->add_output(vdb_node->output_names.back().c_str(),
+ convert_socket_type(*b_output));
+ }
+
+ node = vdb_node;
+ }
if(node)
graph->add(node);
diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp
index c5fb7925306..87e2932d6b1 100644
--- a/intern/cycles/blender/blender_sync.cpp
+++ b/intern/cycles/blender/blender_sync.cpp
@@ -435,6 +435,8 @@ SceneParams BlenderSync::get_scene_params(BL::Scene b_scene, bool background, bo
params.use_qbvh = false;
}
+ params.use_bvh_triangle_storage = RNA_boolean_get(&cscene, "debug_use_triangle_storage");
+
return params;
}
diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h
index 89d93e19e9f..ef279a4fe63 100644
--- a/intern/cycles/blender/blender_sync.h
+++ b/intern/cycles/blender/blender_sync.h
@@ -83,13 +83,22 @@ private:
void sync_curve_settings();
void sync_nodes(Shader *shader, BL::ShaderNodeTree b_ntree);
- Mesh *sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tris);
- void sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_ob, bool motion, int time_index = 0);
- Object *sync_object(BL::Object b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::DupliObject b_dupli_ob,
- Transform& tfm, uint layer_flag, float motion_time, bool hide_tris, bool *use_portal);
+ Mesh *sync_mesh(BL::Object b_parent, bool object_updated, bool hide_tris, BL::DupliObject b_dupli_ob = PointerRNA_NULL);
+ void sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_parent, bool motion, int time_index = 0, BL::DupliObject b_dupli_ob = PointerRNA_NULL);
+ Object *sync_object(BL::Object b_parent,
+ int persistent_id[OBJECT_PERSISTENT_ID_SIZE],
+ BL::DupliObject b_dupli_ob,
+ Transform& tfm,
+ uint layer_flag,
+ float motion_time,
+ bool hide_tris,
+ bool use_camera_cull,
+ float camera_cull_margin,
+ bool *use_portal);
void sync_light(BL::Object b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::Object b_ob, Transform& tfm, bool *use_portal);
void sync_background_light(bool use_portal);
- void sync_mesh_motion(BL::Object b_ob, Object *object, float motion_time);
+ void sync_mesh_motion(BL::Object b_parent, Object *object, float motion_time, BL::DupliObject b_dupli_ob = PointerRNA_NULL);
+
void sync_camera_motion(BL::Object b_ob, float motion_time);
/* particles */
@@ -111,7 +120,7 @@ private:
id_map<void*, Shader> shader_map;
id_map<ObjectKey, Object> object_map;
- id_map<void*, Mesh> mesh_map;
+ id_map<MeshKey, Mesh> mesh_map;
id_map<ObjectKey, Light> light_map;
id_map<ParticleSystemKey, ParticleSystem> particle_system_map;
set<Mesh*> mesh_synced;
diff --git a/intern/cycles/blender/blender_texture.cpp b/intern/cycles/blender/blender_texture.cpp
new file mode 100644
index 00000000000..cb4dd1792d0
--- /dev/null
+++ b/intern/cycles/blender/blender_texture.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2011-2015 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "blender_texture.h"
+
+CCL_NAMESPACE_BEGIN
+
+namespace {
+
+/* Point density helpers. */
+
+static void density_texture_space_invert(float3& loc,
+ float3& size)
+{
+ if(size.x != 0.0f) size.x = 0.5f/size.x;
+ if(size.y != 0.0f) size.y = 0.5f/size.y;
+ if(size.z != 0.0f) size.z = 0.5f/size.z;
+
+ loc = loc*size - make_float3(0.5f, 0.5f, 0.5f);
+}
+
+static void density_object_texture_space(BL::Object b_ob,
+ float radius,
+ float3& loc,
+ float3& size)
+{
+ if(b_ob.type() == BL::Object::type_MESH) {
+ BL::Mesh b_mesh(b_ob.data());
+ loc = get_float3(b_mesh.texspace_location());
+ size = get_float3(b_mesh.texspace_size());
+ }
+ else {
+ /* TODO(sergey): Not supported currently. */
+ }
+ /* Adjust texture space to include density points on the boundaries. */
+ size = size + make_float3(radius, radius, radius);
+ density_texture_space_invert(loc, size);
+}
+
+static void density_particle_system_texture_space(
+ BL::Object b_ob,
+ BL::ParticleSystem b_particle_system,
+ float radius,
+ float3& loc,
+ float3& size)
+{
+ if(b_particle_system.settings().type() == BL::ParticleSettings::type_HAIR) {
+ /* TODO(sergey): Not supported currently. */
+ return;
+ }
+ Transform tfm = get_transform(b_ob.matrix_world());
+ Transform itfm = transform_inverse(tfm);
+ float3 min = make_float3(FLT_MAX, FLT_MAX, FLT_MAX),
+ max = make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
+ float3 particle_size = make_float3(radius, radius, radius);
+ for(int i = 0; i < b_particle_system.particles.length(); ++i) {
+ BL::Particle particle = b_particle_system.particles[i];
+ float3 location = get_float3(particle.location());
+ location = transform_point(&itfm, location);
+ min = ccl::min(min, location - particle_size);
+ max = ccl::max(max, location + particle_size);
+ }
+ /* Calculate texture space from the particle bounds. */
+ loc = (min + max) * 0.5f;
+ size = (max - min) * 0.5f;
+ density_texture_space_invert(loc, size);
+}
+
+} /* namespace */
+
+void point_density_texture_space(BL::ShaderNodeTexPointDensity b_point_density_node,
+ float3& loc,
+ float3& size)
+{
+ /* Fallback values. */
+ loc = make_float3(0.0f, 0.0f, 0.0f);
+ size = make_float3(0.0f, 0.0f, 0.0f);
+ BL::Object b_ob(b_point_density_node.object());
+ if(!b_ob) {
+ return;
+ }
+ if(b_point_density_node.point_source() ==
+ BL::ShaderNodeTexPointDensity::point_source_PARTICLE_SYSTEM)
+ {
+ BL::ParticleSystem b_particle_system(
+ b_point_density_node.particle_system());
+ if(b_particle_system) {
+ density_particle_system_texture_space(b_ob,
+ b_particle_system,
+ b_point_density_node.radius(),
+ loc,
+ size);
+ }
+ }
+ else {
+ density_object_texture_space(b_ob,
+ b_point_density_node.radius(),
+ loc,
+ size);
+ }
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/blender/blender_texture.h b/intern/cycles/blender/blender_texture.h
new file mode 100644
index 00000000000..74fbca02a9e
--- /dev/null
+++ b/intern/cycles/blender/blender_texture.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011-2015 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 __BLENDER_TEXTURE_H__
+#define __BLENDER_TEXTURE_H__
+
+#include <stdlib.h>
+#include "blender_sync.h"
+
+CCL_NAMESPACE_BEGIN
+
+void point_density_texture_space(BL::ShaderNodeTexPointDensity b_point_density_node,
+ float3& loc,
+ float3& size);
+
+CCL_NAMESPACE_END
+
+#endif /* __BLENDER_TEXTURE_H__ */
diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h
index 165242d0dff..b7a5e0f3c3a 100644
--- a/intern/cycles/blender/blender_util.h
+++ b/intern/cycles/blender/blender_util.h
@@ -52,6 +52,18 @@ static inline BL::Mesh object_to_mesh(BL::BlendData data, BL::Object object, BL:
return me;
}
+static inline BL::Mesh dupli_to_mesh(BL::BlendData data, BL::Scene scene, BL::Object parent, BL::DupliObject dob, bool render, bool calc_undeformed)
+{
+ BL::Mesh me = data.meshes.new_from_dupli(scene, parent, dob, (render)? 2: 1, false, calc_undeformed);
+ if ((bool)me) {
+ if (me.use_auto_smooth()) {
+ me.calc_normals_split();
+ }
+ me.calc_tessface(true);
+ }
+ return me;
+}
+
static inline void colorramp_to_array(BL::ColorRamp ramp, float4 *data, int size)
{
for(int i = 0; i < size; i++) {
@@ -586,6 +598,36 @@ struct ObjectKey {
}
};
+/* Mesh Key */
+
+struct MeshKey {
+ void *parent;
+ void *mesh;
+
+ MeshKey(void *mesh_)
+ : parent(NULL), mesh(mesh_)
+ {
+ }
+
+ MeshKey(void *parent_, void *mesh_)
+ : parent(parent_), mesh(mesh_)
+ {
+ }
+
+ bool operator<(const MeshKey& k) const
+ {
+ if(mesh < k.mesh) {
+ return true;
+ }
+ else if(mesh == k.mesh) {
+ return parent < k.parent;
+ return true;
+ }
+
+ return false;
+ }
+};
+
/* Particle System Key */
struct ParticleSystemKey {
diff --git a/intern/cycles/bvh/bvh.cpp b/intern/cycles/bvh/bvh.cpp
index 350ca16f6e2..9b808203694 100644
--- a/intern/cycles/bvh/bvh.cpp
+++ b/intern/cycles/bvh/bvh.cpp
@@ -278,28 +278,34 @@ void BVH::pack_triangle(int idx, float4 woop[3])
void BVH::pack_primitives()
{
- int nsize = TRI_NODE_SIZE;
+ const int nsize = TRI_NODE_SIZE;
+ const bool use_triangle_storage = params.use_triangle_storage;
size_t tidx_size = pack.prim_index.size();
pack.tri_woop.clear();
- pack.tri_woop.resize(tidx_size * nsize);
+ if (use_triangle_storage) {
+ pack.tri_woop.resize(tidx_size * nsize);
+ }
+ else {
+ pack.tri_woop.resize(0);
+ }
pack.prim_visibility.clear();
pack.prim_visibility.resize(tidx_size);
for(unsigned int i = 0; i < tidx_size; i++) {
if(pack.prim_index[i] != -1) {
- float4 woop[3];
-
- if(pack.prim_type[i] & PRIMITIVE_TRIANGLE) {
- pack_triangle(i, woop);
- }
- else {
- /* Avoid use of uninitialized memory. */
- memset(&woop, 0, sizeof(woop));
+ if(use_triangle_storage) {
+ float4 woop[3];
+ if(pack.prim_type[i] & PRIMITIVE_TRIANGLE) {
+ pack_triangle(i, woop);
+ }
+ else {
+ /* Avoid use of uninitialized memory. */
+ memset(&woop, 0, sizeof(woop));
+ }
+ memcpy(&pack.tri_woop[i * nsize], woop, sizeof(float4)*3);
}
- memcpy(&pack.tri_woop[i * nsize], woop, sizeof(float4)*3);
-
int tob = pack.prim_object[i];
Object *ob = objects[tob];
pack.prim_visibility[i] = ob->visibility;
@@ -308,7 +314,9 @@ void BVH::pack_primitives()
pack.prim_visibility[i] |= PATH_RAY_CURVE;
}
else {
- memset(&pack.tri_woop[i * nsize], 0, sizeof(float4)*3);
+ if(use_triangle_storage) {
+ memset(&pack.tri_woop[i * nsize], 0, sizeof(float4)*3);
+ }
pack.prim_visibility[i] = 0;
}
}
diff --git a/intern/cycles/bvh/bvh_params.h b/intern/cycles/bvh/bvh_params.h
index af8d8eeb3ee..892fd906e77 100644
--- a/intern/cycles/bvh/bvh_params.h
+++ b/intern/cycles/bvh/bvh_params.h
@@ -49,6 +49,9 @@ public:
/* QBVH */
bool use_qbvh;
+ /* Use pre-aligned tringle storage for faster lookup. */
+ bool use_triangle_storage;
+
/* fixed parameters */
enum {
MAX_DEPTH = 64,
@@ -73,6 +76,7 @@ public:
top_level = false;
use_cache = false;
use_qbvh = false;
+ use_triangle_storage = true;
}
/* SAH costs */
diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake
index d7c59f42a5e..13196e2ba6b 100644
--- a/intern/cycles/cmake/external_libs.cmake
+++ b/intern/cycles/cmake/external_libs.cmake
@@ -30,7 +30,7 @@ if(NOT CYCLES_STANDALONE_REPOSITORY)
set(GLEW_INCLUDE_DIR "${GLEW_INCLUDE_PATH}")
endif()
-if(WITH_CYCLES_STANDALONE AND WITH_CYCLES_STANDALONE_GUI)
+if(WITH_CYCLES_STANDALONE)
set(CYCLES_APP_GLEW_LIBRARY ${BLENDER_GLEW_LIBRARIES})
endif()
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index d0279b27046..872d5ab4af5 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -115,6 +115,7 @@ set(SRC_SVM_HEADERS
svm/svm_noise.h
svm/svm_noisetex.h
svm/svm_normal.h
+ svm/svm_openvdb.h
svm/svm_ramp.h
svm/svm_sepcomb_hsv.h
svm/svm_sepcomb_vector.h
@@ -125,6 +126,7 @@ set(SRC_SVM_HEADERS
svm/svm_value.h
svm/svm_vector_transform.h
svm/svm_voronoi.h
+ svm/svm_voxel.h
svm/svm_wave.h
)
diff --git a/intern/cycles/kernel/geom/geom_triangle_intersect.h b/intern/cycles/kernel/geom/geom_triangle_intersect.h
index 3ef918dc842..ee5d4df2889 100644
--- a/intern/cycles/kernel/geom/geom_triangle_intersect.h
+++ b/intern/cycles/kernel/geom/geom_triangle_intersect.h
@@ -119,9 +119,19 @@ ccl_device_inline bool triangle_intersect(KernelGlobals *kg,
const float Sz = isect_precalc->Sz;
/* Calculate vertices relative to ray origin. */
- const float4 tri_a = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+0),
- tri_b = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+1),
- tri_c = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+2);
+ float4 tri_a, tri_b, tri_c;
+ if (kernel_data.bvh.use_tri_storage) {
+ tri_a = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+0);
+ tri_b = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+1);
+ tri_c = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+2);
+ }
+ else {
+ const int prim = kernel_tex_fetch(__prim_index, triAddr);
+ const float4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim);
+ tri_a = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.x));
+ tri_b = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.y));
+ tri_c = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.z));
+ }
const float3 A = make_float3(tri_a.x - P.x, tri_a.y - P.y, tri_a.z - P.z);
const float3 B = make_float3(tri_b.x - P.x, tri_b.y - P.y, tri_b.z - P.z);
const float3 C = make_float3(tri_c.x - P.x, tri_c.y - P.y, tri_c.z - P.z);
@@ -212,9 +222,19 @@ ccl_device_inline void triangle_intersect_subsurface(
const float Sz = isect_precalc->Sz;
/* Calculate vertices relative to ray origin. */
- const float4 tri_a = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+0),
- tri_b = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+1),
- tri_c = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+2);
+ float4 tri_a, tri_b, tri_c;
+ if (kernel_data.bvh.use_tri_storage) {
+ tri_a = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+0);
+ tri_b = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+1);
+ tri_c = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+2);
+ }
+ else {
+ const int prim = kernel_tex_fetch(__prim_index, triAddr);
+ const float4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim);
+ tri_a = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.x));
+ tri_b = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.y));
+ tri_c = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.z));
+ }
const float3 A = make_float3(tri_a.x - P.x, tri_a.y - P.y, tri_a.z - P.z);
const float3 B = make_float3(tri_b.x - P.x, tri_b.y - P.y, tri_b.z - P.z);
const float3 C = make_float3(tri_c.x - P.x, tri_c.y - P.y, tri_c.z - P.z);
@@ -327,9 +347,19 @@ ccl_device_inline float3 triangle_refine(KernelGlobals *kg,
P = P + D*t;
- const float4 tri_a = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+0),
- tri_b = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+1),
- tri_c = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+2);
+ float4 tri_a, tri_b, tri_c;
+ if (kernel_data.bvh.use_tri_storage) {
+ tri_a = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+0);
+ tri_b = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+1);
+ tri_c = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+2);
+ }
+ else {
+ const int prim = kernel_tex_fetch(__prim_index, isect->prim);
+ const float4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim);
+ tri_a = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.x));
+ tri_b = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.y));
+ tri_c = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.z));
+ }
float3 edge1 = make_float3(tri_a.x - tri_c.x, tri_a.y - tri_c.y, tri_a.z - tri_c.z);
float3 edge2 = make_float3(tri_b.x - tri_c.x, tri_b.y - tri_c.y, tri_b.z - tri_c.z);
float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z);
@@ -384,9 +414,19 @@ ccl_device_inline float3 triangle_refine_subsurface(KernelGlobals *kg,
P = P + D*t;
- const float4 tri_a = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+0),
- tri_b = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+1),
- tri_c = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+2);
+ float4 tri_a, tri_b, tri_c;
+ if (kernel_data.bvh.use_tri_storage) {
+ tri_a = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+0);
+ tri_b = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+1);
+ tri_c = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+2);
+ }
+ else {
+ const int prim = kernel_tex_fetch(__prim_index, isect->prim);
+ const float4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim);
+ tri_a = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.x));
+ tri_b = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.y));
+ tri_c = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.z));
+ }
float3 edge1 = make_float3(tri_a.x - tri_c.x, tri_a.y - tri_c.y, tri_a.z - tri_c.z);
float3 edge2 = make_float3(tri_b.x - tri_c.x, tri_b.y - tri_c.y, tri_b.z - tri_c.z);
float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z);
diff --git a/intern/cycles/kernel/geom/geom_volume.h b/intern/cycles/kernel/geom/geom_volume.h
index c72afa2a3a4..24430dd223b 100644
--- a/intern/cycles/kernel/geom/geom_volume.h
+++ b/intern/cycles/kernel/geom/geom_volume.h
@@ -53,17 +53,21 @@ ccl_device float volume_attribute_float(KernelGlobals *kg, const ShaderData *sd,
float4 r = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
#else
float4 r;
+ int slot = id >> 1;
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(slot, 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(slot, P.x, P.y, P.z);
#endif
if(dx) *dx = 0.0f;
if(dy) *dy = 0.0f;
/* todo: support float textures to lower memory usage for single floats */
- return average(float4_to_float3(r));
+ if(id & 1)
+ return r.w;
+ else
+ 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)
@@ -73,16 +77,20 @@ ccl_device float3 volume_attribute_float3(KernelGlobals *kg, const ShaderData *s
float4 r = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
#else
float4 r;
+ int slot = id >> 1;
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(slot, 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(slot, P.x, P.y, P.z);
#endif
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(r);
+ if(id & 1)
+ return make_float3(r.w, r.w, r.w);
+ else
+ return float4_to_float3(r);
}
#endif
diff --git a/intern/cycles/kernel/kernel_compat_cpu.h b/intern/cycles/kernel/kernel_compat_cpu.h
index 0bf1ed36d1e..8a1ad7052f8 100644
--- a/intern/cycles/kernel/kernel_compat_cpu.h
+++ b/intern/cycles/kernel/kernel_compat_cpu.h
@@ -40,6 +40,8 @@
#include "util_simd.h"
#include "util_half.h"
#include "util_types.h"
+#include "util_openvdb.h"
+#include "util_vector.h"
#define ccl_addr_space
@@ -416,6 +418,8 @@ typedef texture_image<uchar4> texture_image_uchar4;
#define kernel_tex_image_interp(tex, x, y) ((tex < MAX_FLOAT_IMAGES) ? kg->texture_float_images[tex].interp(x, y) : kg->texture_byte_images[tex - MAX_FLOAT_IMAGES].interp(x, y))
#define kernel_tex_image_interp_3d(tex, x, y, z) ((tex < MAX_FLOAT_IMAGES) ? kg->texture_float_images[tex].interp_3d(x, y, z) : kg->texture_byte_images[tex - MAX_FLOAT_IMAGES].interp_3d(x, y, z))
#define kernel_tex_image_interp_3d_ex(tex, x, y, z, interpolation) ((tex < MAX_FLOAT_IMAGES) ? kg->texture_float_images[tex].interp_3d_ex(x, y, z, interpolation) : kg->texture_byte_images[tex - MAX_FLOAT_IMAGES].interp_3d_ex(x, y, z, interpolation))
+#define kernel_tex_voxel_float(tex, x, y, z, sampling) (kg->float_volumes[tex]->sample(x, y, z, sampling))
+#define kernel_tex_voxel_float3(tex, x, y, z, sampling) (kg->float3_volumes[tex]->sample(x, y, z, sampling))
#define kernel_data (kg->__data)
diff --git a/intern/cycles/kernel/kernel_globals.h b/intern/cycles/kernel/kernel_globals.h
index 17fa18909c4..157720d6679 100644
--- a/intern/cycles/kernel/kernel_globals.h
+++ b/intern/cycles/kernel/kernel_globals.h
@@ -33,11 +33,15 @@ struct OSLShadingSystem;
#define MAX_BYTE_IMAGES 1024
#define MAX_FLOAT_IMAGES 1024
+#define MAX_VOLUME 1024
typedef struct KernelGlobals {
texture_image_uchar4 texture_byte_images[MAX_BYTE_IMAGES];
texture_image_float4 texture_float_images[MAX_FLOAT_IMAGES];
+ float_volume *float_volumes[MAX_VOLUME];
+ float3_volume *float3_volumes[MAX_VOLUME];
+
#define KERNEL_TEX(type, ttype, name) ttype name;
#define KERNEL_IMAGE_TEX(type, ttype, name)
#include "kernel_textures.h"
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index 2a70bfcb8f0..c071be5419a 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -67,6 +67,9 @@ CCL_NAMESPACE_BEGIN
#ifdef WITH_OSL
#define __OSL__
#endif
+#ifdef WITH_OPENVDB
+#define __OPENVDB__
+#endif
#define __SUBSURFACE__
#define __CMJ__
#define __VOLUME__
@@ -952,7 +955,8 @@ typedef struct KernelBVH {
int have_curves;
int have_instancing;
int use_qbvh;
- int pad1, pad2;
+ int use_tri_storage;
+ int pad1;
} KernelBVH;
typedef enum CurveFlag {
@@ -976,7 +980,8 @@ typedef struct KernelCurves {
typedef struct KernelTables {
int beckmann_offset;
- int pad1, pad2, pad3;
+ int num_volumes;
+ int density_index, pad2;
} KernelTables;
typedef struct KernelData {
diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h
index e06568457c6..cc958a09a46 100644
--- a/intern/cycles/kernel/kernel_volume.h
+++ b/intern/cycles/kernel/kernel_volume.h
@@ -21,7 +21,8 @@ CCL_NAMESPACE_BEGIN
typedef enum VolumeIntegrateResult {
VOLUME_PATH_SCATTERED = 0,
VOLUME_PATH_ATTENUATED = 1,
- VOLUME_PATH_MISSED = 2
+ VOLUME_PATH_MISSED = 2,
+ VOLUME_PATH_CONTINUE = 3,
} VolumeIntegrateResult;
/* Volume shader properties
@@ -161,6 +162,38 @@ ccl_device void kernel_volume_shadow_homogeneous(KernelGlobals *kg, PathState *s
*throughput *= volume_color_transmittance(sigma_t, ray->t);
}
+ccl_device_inline bool kernel_volume_integrate_shadow_ray(
+ KernelGlobals *kg, PathState *state, Ray *ray, ShaderData *sd,
+ float3 *tp, float t, float new_t, float random_jitter_offset,
+ float3 *sum, float tp_eps, int i)
+{
+ float dt = new_t - t;
+
+ /* use random position inside this segment to sample shader */
+ if(new_t == ray->t)
+ random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt;
+
+ float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
+ float3 sigma_t;
+
+ /* compute attenuation over segment */
+ if(volume_shader_extinction_sample(kg, sd, state, new_P, &sigma_t)) {
+ /* Compute expf() only for every Nth step, to save some calculations
+ * because exp(a)*exp(b) = exp(a+b), also do a quick tp_eps check then. */
+
+ *sum += (-sigma_t * (new_t - t));
+ if((i & 0x07) == 0) { /* ToDo: Other interval? */
+ *tp = *tp * make_float3(expf(sum->x), expf(sum->y), expf(sum->z));
+
+ /* stop if nearly all light is blocked */
+ if(tp->x < tp_eps && tp->y < tp_eps && tp->z < tp_eps)
+ return true;
+ }
+ }
+
+ return false;
+}
+
/* heterogeneous volume: integrate stepping through the volume until we
* reach the end, get absorbed entirely, or run out of iterations */
ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg, PathState *state, Ray *ray, ShaderData *sd, float3 *throughput)
@@ -178,42 +211,66 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg, PathState
float3 sum = make_float3(0.0f, 0.0f, 0.0f);
- for(int i = 0; i < max_steps; i++) {
- /* advance to new position */
- float new_t = min(ray->t, (i+1) * step);
- float dt = new_t - t;
-
- /* use random position inside this segment to sample shader */
- if(new_t == ray->t)
- random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt;
-
- float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
- float3 sigma_t;
-
- /* compute attenuation over segment */
- if(volume_shader_extinction_sample(kg, sd, state, new_P, &sigma_t)) {
- /* Compute expf() only for every Nth step, to save some calculations
- * because exp(a)*exp(b) = exp(a+b), also do a quick tp_eps check then. */
+#ifdef __OPENVDB__
+ int vdb_index = kernel_data.tables.density_index;
+ bool has_vdb_volume = kernel_data.tables.num_volumes > 0;
+ float t1 = 0.0f;
+
+ if(has_vdb_volume && vdb_index >= 0 && kg->float_volumes[vdb_index]->has_uniform_voxels()) {
+ /* TODO(kevin): this call should be moved out of here, all it does is
+ * checking if we have an intersection with the boundbox of the volumue
+ * which in most cases corresponds to the boundbox of the object that has
+ * this volume. Also it initializes the rays for the ray marching. */
+ if(!kg->float_volumes[vdb_index]->intersect(ray, NULL)) {
+ return;
+ }
- sum += (-sigma_t * (new_t - t));
- if((i & 0x07) == 0) { /* ToDo: Other interval? */
- tp = *throughput * make_float3(expf(sum.x), expf(sum.y), expf(sum.z));
+ /* t and t1 represent the entry and exit points for each leaf node or tile
+ * containing active voxels. If we don't have any active node in the current
+ * ray path (i.e. empty space) the ray march loop is not executed,
+ * otherwise we loop through all leaves until the end of the volume. */
+ while(kg->float_volumes[vdb_index]->march(&t, &t1)) {
+ int i = 0;
+
+ /* Perform small steps through the current leaf or tile. */
+ for(float new_t = step * ceilf(t / step); new_t <= t1; new_t += step) {
+ bool ok = kernel_volume_integrate_shadow_ray(
+ kg, state, ray, sd, &tp, t, new_t, random_jitter_offset,
+ &sum, tp_eps, i);
+
+ if (ok) {
+ *throughput = tp;
+ return;
+ }
- /* stop if nearly all light is blocked */
- if(tp.x < tp_eps && tp.y < tp_eps && tp.z < tp_eps)
- break;
+ /* stop if at the end of the volume */
+ t = new_t;
+ i++;
}
}
-
- /* stop if at the end of the volume */
- t = new_t;
- if(t == ray->t) {
- /* Update throughput in case we haven't done it above */
- tp = *throughput * make_float3(expf(sum.x), expf(sum.y), expf(sum.z));
- break;
+ }
+ else
+#endif
+ {
+ for(int i = 0; i < max_steps; i++) {
+ /* advance to new position */
+ float new_t = min(ray->t, (i+1) * step);
+
+ bool ok = kernel_volume_integrate_shadow_ray(
+ kg, state, ray, sd, &tp, t, new_t, random_jitter_offset,
+ &sum, tp_eps, i);
+
+ /* stop if at the end of the volume */
+ t = new_t;
+ if(ok || t == ray->t) {
+ break;
+ }
}
}
+ /* Update throughput in case we haven't done it above */
+ tp = *throughput * make_float3(expf(sum.x), expf(sum.y), expf(sum.z));
+
*throughput = tp;
}
@@ -420,6 +477,112 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_homogeneous(KernelGloba
return VOLUME_PATH_ATTENUATED;
}
+ccl_device_inline VolumeIntegrateResult kernel_volume_integrate_ray(
+ KernelGlobals *kg, PathState *state, Ray *ray, ShaderData *sd,
+ PathRadiance *L, float3 *throughput, float t, float new_t,
+ float random_jitter_offset, bool has_scatter, float3 *accum_transmittance,
+ int channel, const float tp_eps, float *xi)
+{
+ float dt = new_t - t;
+ float3 tp = *throughput;
+
+ /* use random position inside this segment to sample shader */
+ if(new_t == ray->t)
+ random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt;
+
+ float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
+ VolumeShaderCoefficients coeff;
+
+ /* compute segment */
+ if(volume_shader_sample(kg, sd, state, new_P, &coeff)) {
+ int closure_flag = sd->flag;
+ float3 new_tp;
+ float3 transmittance;
+ bool scatter = false;
+
+ /* distance sampling */
+#ifdef __VOLUME_SCATTER__
+ if((closure_flag & SD_SCATTER) || (has_scatter && (closure_flag & SD_ABSORPTION))) {
+ has_scatter = true;
+
+ float3 sigma_t = coeff.sigma_a + coeff.sigma_s;
+ float3 sigma_s = coeff.sigma_s;
+
+ /* compute transmittance over full step */
+ transmittance = volume_color_transmittance(sigma_t, dt);
+
+ /* decide if we will scatter or continue */
+ float sample_transmittance = kernel_volume_channel_get(transmittance, channel);
+
+ if(1.0f - *xi >= sample_transmittance) {
+ /* compute sampling distance */
+ float sample_sigma_t = kernel_volume_channel_get(sigma_t, channel);
+ float new_dt = -logf(1.0f - *xi)/sample_sigma_t;
+ new_t = t + new_dt;
+
+ /* transmittance and pdf */
+ float3 new_transmittance = volume_color_transmittance(sigma_t, new_dt);
+ float3 pdf = sigma_t * new_transmittance;
+
+ /* throughput */
+ new_tp = tp * sigma_s * new_transmittance / average(pdf);
+ scatter = true;
+ }
+ else {
+ /* throughput */
+ float pdf = average(transmittance);
+ new_tp = tp * transmittance / pdf;
+
+ /* remap xi so we can reuse it and keep thing stratified */
+ *xi = 1.0f - (1.0f - *xi)/sample_transmittance;
+ }
+ }
+ else
+#endif
+ if(closure_flag & SD_ABSORPTION) {
+ /* absorption only, no sampling needed */
+ float3 sigma_a = coeff.sigma_a;
+
+ transmittance = volume_color_transmittance(sigma_a, dt);
+ new_tp = tp * transmittance;
+ }
+
+ /* integrate emission attenuated by absorption */
+ if(L && (closure_flag & SD_EMISSION)) {
+ float3 emission = kernel_volume_emission_integrate(&coeff, closure_flag, transmittance, dt);
+ path_radiance_accum_emission(L, tp, emission, state->bounce);
+ }
+
+ /* modify throughput */
+ if(closure_flag & (SD_ABSORPTION|SD_SCATTER)) {
+ tp = new_tp;
+
+ /* stop if nearly all light blocked */
+ if(tp.x < tp_eps && tp.y < tp_eps && tp.z < tp_eps) {
+ tp = make_float3(0.0f, 0.0f, 0.0f);
+ *throughput = tp;
+ return VOLUME_PATH_ATTENUATED;
+ }
+ }
+
+ /* prepare to scatter to new direction */
+ if(scatter) {
+ /* adjust throughput and move to new location */
+ sd->P = ray->P + new_t*ray->D;
+ *throughput = tp;
+
+ return VOLUME_PATH_SCATTERED;
+ }
+ else {
+ /* accumulate transmittance */
+ *accum_transmittance *= transmittance;
+ }
+ }
+
+ *throughput = tp;
+ return VOLUME_PATH_CONTINUE;
+}
+
/* heterogeneous volume distance sampling: integrate stepping through the
* volume until we reach the end, get absorbed entirely, or run out of
* iterations. this does probalistically scatter or get transmitted through
@@ -427,7 +590,7 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_homogeneous(KernelGloba
ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance(KernelGlobals *kg,
PathState *state, Ray *ray, ShaderData *sd, PathRadiance *L, float3 *throughput, RNG *rng)
{
- float3 tp = *throughput;
+ VolumeIntegrateResult result = VOLUME_PATH_MISSED;
const float tp_eps = 1e-6f; /* todo: this is likely not the right value */
/* prepare for stepping */
@@ -436,7 +599,7 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance(
float random_jitter_offset = lcg_step_float(&state->rng_congruential) * step_size;
/* compute coefficients at the start */
- float t = 0.0f;
+ float t = 0.0f, t1 = 0.0f;
float3 accum_transmittance = make_float3(1.0f, 1.0f, 1.0f);
/* pick random color channel, we use the Veach one-sample
@@ -446,113 +609,65 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance(
int channel = (int)(rphase*3.0f);
sd->randb_closure = rphase*3.0f - channel;
bool has_scatter = false;
+ bool path_missed = true;
+
+#ifdef __OPENVDB__
+ int vdb_index = kernel_data.tables.density_index;
+ bool has_vdb_volume = kernel_data.tables.num_volumes > 0;
+
+ if(has_vdb_volume && vdb_index >= 0 && kg->float_volumes[vdb_index]->has_uniform_voxels()) {
+ /* TODO(kevin): this call should be moved out of here, all it does is
+ * checking if we have an intersection with the boundbox of the volumue
+ * which in most cases corresponds to the boundbox of the object that has
+ * this volume. Also it initializes the rays for the ray marching. */
+ if(!kg->float_volumes[vdb_index]->intersect(ray, NULL)) {
+ return VOLUME_PATH_MISSED;
+ }
- for(int i = 0; i < max_steps; i++) {
- /* advance to new position */
- float new_t = min(ray->t, (i+1) * step_size);
- float dt = new_t - t;
-
- /* use random position inside this segment to sample shader */
- if(new_t == ray->t)
- random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt;
-
- float3 new_P = ray->P + ray->D * (t + random_jitter_offset);
- VolumeShaderCoefficients coeff;
-
- /* compute segment */
- if(volume_shader_sample(kg, sd, state, new_P, &coeff)) {
- int closure_flag = sd->flag;
- float3 new_tp;
- float3 transmittance;
- bool scatter = false;
-
- /* distance sampling */
-#ifdef __VOLUME_SCATTER__
- if((closure_flag & SD_SCATTER) || (has_scatter && (closure_flag & SD_ABSORPTION))) {
- has_scatter = true;
-
- float3 sigma_t = coeff.sigma_a + coeff.sigma_s;
- float3 sigma_s = coeff.sigma_s;
-
- /* compute transmittance over full step */
- transmittance = volume_color_transmittance(sigma_t, dt);
-
- /* decide if we will scatter or continue */
- float sample_transmittance = kernel_volume_channel_get(transmittance, channel);
-
- if(1.0f - xi >= sample_transmittance) {
- /* compute sampling distance */
- float sample_sigma_t = kernel_volume_channel_get(sigma_t, channel);
- float new_dt = -logf(1.0f - xi)/sample_sigma_t;
- new_t = t + new_dt;
+ /* t and t1 represent the entry and exit points for each leaf node or tile
+ * containing active voxels. If we don't have any active node in the current
+ * ray path (i.e. empty space) the ray march loop is not executed,
+ * otherwise we loop through all leaves until the end of the volume. */
+ while(kg->float_volumes[vdb_index]->march(&t, &t1)) {
+ path_missed = false;
- /* transmittance and pdf */
- float3 new_transmittance = volume_color_transmittance(sigma_t, new_dt);
- float3 pdf = sigma_t * new_transmittance;
+ /* Perform small steps through the current leaf or tile. */
+ for(float new_t = step_size * ceilf(t / step_size); new_t <= t1; new_t += step_size) {
+ result = kernel_volume_integrate_ray(kg, state, ray, sd, L, throughput, t, new_t,
+ random_jitter_offset, has_scatter,
+ &accum_transmittance, channel, tp_eps, &xi);
- /* throughput */
- new_tp = tp * sigma_s * new_transmittance / average(pdf);
- scatter = true;
- }
- else {
- /* throughput */
- float pdf = average(transmittance);
- new_tp = tp * transmittance / pdf;
+ if(result != VOLUME_PATH_CONTINUE)
+ return result;
- /* remap xi so we can reuse it and keep thing stratified */
- xi = 1.0f - (1.0f - xi)/sample_transmittance;
- }
+ t = new_t;
}
- else
+ }
+ }
+ else
#endif
- if(closure_flag & SD_ABSORPTION) {
- /* absorption only, no sampling needed */
- float3 sigma_a = coeff.sigma_a;
-
- transmittance = volume_color_transmittance(sigma_a, dt);
- new_tp = tp * transmittance;
- }
-
- /* integrate emission attenuated by absorption */
- if(L && (closure_flag & SD_EMISSION)) {
- float3 emission = kernel_volume_emission_integrate(&coeff, closure_flag, transmittance, dt);
- path_radiance_accum_emission(L, tp, emission, state->bounce);
- }
+ {
+ path_missed = false;
- /* modify throughput */
- if(closure_flag & (SD_ABSORPTION|SD_SCATTER)) {
- tp = new_tp;
+ for(int i = 0; i < max_steps; i++) {
+ /* advance to new position */
+ float new_t = min(ray->t, (i+1) * step_size);
- /* stop if nearly all light blocked */
- if(tp.x < tp_eps && tp.y < tp_eps && tp.z < tp_eps) {
- tp = make_float3(0.0f, 0.0f, 0.0f);
- break;
- }
- }
+ result = kernel_volume_integrate_ray(kg, state, ray, sd, L, throughput, t, new_t,
+ random_jitter_offset, has_scatter,
+ &accum_transmittance, channel, tp_eps, &xi);
- /* prepare to scatter to new direction */
- if(scatter) {
- /* adjust throughput and move to new location */
- sd->P = ray->P + new_t*ray->D;
- *throughput = tp;
+ if(result != VOLUME_PATH_CONTINUE)
+ return result;
- return VOLUME_PATH_SCATTERED;
- }
- else {
- /* accumulate transmittance */
- accum_transmittance *= transmittance;
- }
+ /* stop if at the end of the volume */
+ t = new_t;
+ if(t == ray->t)
+ break;
}
-
- /* stop if at the end of the volume */
- t = new_t;
- if(t == ray->t)
- break;
}
- *throughput = tp;
-
- return VOLUME_PATH_ATTENUATED;
+ return (path_missed) ? VOLUME_PATH_MISSED : VOLUME_PATH_ATTENUATED;
}
/* get the volume attenuation and emission over line segment defined by
diff --git a/intern/cycles/kernel/kernels/cpu/kernel.cpp b/intern/cycles/kernel/kernels/cpu/kernel.cpp
index 37a73ab2f04..500aef3fe19 100644
--- a/intern/cycles/kernel/kernels/cpu/kernel.cpp
+++ b/intern/cycles/kernel/kernels/cpu/kernel.cpp
@@ -34,6 +34,10 @@ void kernel_const_copy(KernelGlobals *kg, const char *name, void *host, size_t s
{
if(strcmp(name, "__data") == 0)
memcpy(&kg->__data, host, size);
+ else if(strcmp(name, "__float_volume") == 0)
+ kg->float_volumes[size] = (float_volume *)host;
+ else if(strcmp(name, "__float3_volume") == 0)
+ kg->float3_volumes[size] = (float3_volume *)host;
else
assert(0);
}
diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h
index 15ac6519780..21604e78f7d 100644
--- a/intern/cycles/kernel/svm/svm.h
+++ b/intern/cycles/kernel/svm/svm.h
@@ -168,6 +168,7 @@ CCL_NAMESPACE_END
#include "svm_wave.h"
#include "svm_math.h"
#include "svm_mix.h"
+#include "svm_openvdb.h"
#include "svm_ramp.h"
#include "svm_sepcomb_hsv.h"
#include "svm_sepcomb_vector.h"
@@ -179,6 +180,7 @@ CCL_NAMESPACE_END
#include "svm_checker.h"
#include "svm_brick.h"
#include "svm_vector_transform.h"
+#include "svm_voxel.h"
CCL_NAMESPACE_BEGIN
@@ -390,6 +392,9 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, Shade
case NODE_TEX_BRICK:
svm_node_tex_brick(kg, sd, stack, node, &offset);
break;
+ case NODE_TEX_VOXEL:
+ svm_node_tex_voxel(kg, sd, stack, node, &offset);
+ break;
# endif /* __TEXTURES__ */
# ifdef __EXTRA_NODES__
case NODE_NORMAL:
@@ -447,6 +452,11 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, Shade
break;
# endif /* __EXTRA_NODES__ */
#endif /* NODES_GROUP(NODE_GROUP_LEVEL_3) */
+#ifdef __OPENVDB__
+ case NODE_OPENVDB:
+ svm_node_openvdb(kg, sd, stack, node, &offset);
+ break;
+#endif
case NODE_END:
return;
default:
diff --git a/intern/cycles/kernel/svm/svm_openvdb.h b/intern/cycles/kernel/svm/svm_openvdb.h
new file mode 100644
index 00000000000..8820dae42c4
--- /dev/null
+++ b/intern/cycles/kernel/svm/svm_openvdb.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+CCL_NAMESPACE_BEGIN
+
+#ifdef __OPENVDB__
+
+ccl_device void svm_node_openvdb(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset)
+{
+ uint slot = node.y;
+ uint type, out_offset, sampling, co_offset;
+ decode_node_uchar4(node.z, &type, &co_offset, &out_offset, &sampling);
+
+ float3 co = stack_load_float3(stack, co_offset);
+
+ Transform tfm;
+ tfm.x = read_node_float(kg, offset);
+ tfm.y = read_node_float(kg, offset);
+ tfm.z = read_node_float(kg, offset);
+ tfm.w = read_node_float(kg, offset);
+ co = transform_point(&tfm, co);
+
+ if(type == NODE_VDB_FLOAT) {
+ float out = kernel_tex_voxel_float(slot, co.x, co.y, co.z, sampling);
+
+ if(stack_valid(out_offset)) {
+ stack_store_float(stack, out_offset, out);
+ }
+ }
+ else if(type == NODE_VDB_FLOAT3) {
+ float3 out = kernel_tex_voxel_float3(slot, co.x, co.y, co.z, sampling);
+
+ if(stack_valid(out_offset)) {
+ stack_store_float3(stack, out_offset, out);
+ }
+ }
+}
+
+#endif
+
+CCL_NAMESPACE_END
+
diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h
index 009e91192eb..28e55a1e0d5 100644
--- a/intern/cycles/kernel/svm/svm_types.h
+++ b/intern/cycles/kernel/svm/svm_types.h
@@ -125,7 +125,9 @@ typedef enum NodeType {
NODE_TANGENT,
NODE_NORMAL_MAP,
NODE_HAIR_INFO,
- NODE_UVMAP
+ NODE_UVMAP,
+ NODE_TEX_VOXEL,
+ NODE_OPENVDB,
} NodeType;
typedef enum NodeAttributeType {
@@ -349,6 +351,16 @@ typedef enum NodeBumpOffset {
NODE_BUMP_OFFSET_DY,
} NodeBumpOffset;
+typedef enum NodeTexVoxelSpace {
+ NODE_TEX_VOXEL_SPACE_OBJECT = 0,
+ NODE_TEX_VOXEL_SPACE_WORLD = 1,
+} NodeTexVoxelSpace;
+
+typedef enum NodeOpenVDBType {
+ NODE_VDB_FLOAT = 0,
+ NODE_VDB_FLOAT3 = 1,
+} NodeOpenVDBType;
+
typedef enum ShaderType {
SHADER_TYPE_SURFACE,
SHADER_TYPE_VOLUME,
diff --git a/intern/cycles/kernel/svm/svm_voxel.h b/intern/cycles/kernel/svm/svm_voxel.h
new file mode 100644
index 00000000000..4838926503e
--- /dev/null
+++ b/intern/cycles/kernel/svm/svm_voxel.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011-2015 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+CCL_NAMESPACE_BEGIN
+
+ccl_device void svm_node_tex_voxel(KernelGlobals *kg,
+ ShaderData *sd,
+ float *stack,
+ uint4 node,
+ int *offset)
+{
+ int id = node.y;
+ uint co_offset, density_out_offset, color_out_offset, space;
+ decode_node_uchar4(node.z, &co_offset, &density_out_offset, &color_out_offset, &space);
+ float3 co = stack_load_float3(stack, co_offset);
+ if(space == NODE_TEX_VOXEL_SPACE_OBJECT) {
+ co = volume_normalized_position(kg, sd, co);
+ }
+ else {
+ kernel_assert(space == NODE_TEX_VOXEL_SPACE_WORLD);
+ Transform tfm;
+ tfm.x = read_node_float(kg, offset);
+ tfm.y = read_node_float(kg, offset);
+ tfm.z = read_node_float(kg, offset);
+ tfm.w = read_node_float(kg, offset);
+ co = transform_point(&tfm, co);
+ }
+ if(co.x < 0.0f || co.y < 0.0f || co.z < 0.0f ||
+ co.x > 1.0f || co.y > 1.0f || co.z > 1.0f)
+ {
+ if (stack_valid(density_out_offset))
+ stack_store_float(stack, density_out_offset, 0.0f);
+ if (stack_valid(color_out_offset))
+ stack_store_float3(stack, color_out_offset, make_float3(0.0f, 0.0f, 0.0f));
+ return;
+ }
+#ifdef __KERNEL_GPU__
+ float4 r = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
+#else
+ float4 r = kernel_tex_image_interp_3d(id, co.x, co.y, co.z);
+#endif
+ if (stack_valid(density_out_offset))
+ stack_store_float(stack, density_out_offset, r.w);
+ if (stack_valid(color_out_offset))
+ stack_store_float3(stack, color_out_offset, make_float3(r.x, r.y, r.z));
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt
index 4e8a1794813..e85b7e67f6a 100644
--- a/intern/cycles/render/CMakeLists.txt
+++ b/intern/cycles/render/CMakeLists.txt
@@ -14,6 +14,12 @@ set(INC_SYS
${GLEW_INCLUDE_DIR}
)
+if(WITH_OPENVDB)
+ list(APPEND INC_SYS
+ ${OPENVDB_INCLUDE_DIRS}
+ )
+endif()
+
set(SRC
attribute.cpp
background.cpp
@@ -29,6 +35,7 @@ set(SRC
mesh_displace.cpp
nodes.cpp
object.cpp
+ openvdb.cpp
osl.cpp
particles.cpp
curves.cpp
@@ -56,6 +63,7 @@ set(SRC_HEADERS
mesh.h
nodes.h
object.h
+ openvdb.h
osl.h
particles.h
curves.h
diff --git a/intern/cycles/render/attribute.h b/intern/cycles/render/attribute.h
index bbc6cf7f65f..b49f0ffe387 100644
--- a/intern/cycles/render/attribute.h
+++ b/intern/cycles/render/attribute.h
@@ -39,6 +39,7 @@ struct Transform;
struct VoxelAttribute {
ImageManager *manager;
int slot;
+ int from_alpha;
};
/* Attribute
diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp
index 45685fe5927..fdd1e3ea32c 100644
--- a/intern/cycles/render/mesh.cpp
+++ b/intern/cycles/render/mesh.cpp
@@ -515,6 +515,7 @@ void Mesh::compute_bvh(SceneParams *params, Progress *progress, int n, int total
BVHParams bparams;
bparams.use_cache = params->use_bvh_cache;
bparams.use_spatial_split = params->use_bvh_spatial_split;
+ bparams.use_triangle_storage = params->use_bvh_triangle_storage;
bparams.use_qbvh = params->use_qbvh;
delete bvh;
@@ -554,9 +555,10 @@ bool Mesh::has_motion_blur() const
/* Mesh Manager */
-MeshManager::MeshManager()
+MeshManager::MeshManager(const bool free_data_after_update_)
{
bvh = NULL;
+ free_data_after_update = free_data_after_update_;
need_update = true;
need_flags_update = true;
}
@@ -816,7 +818,10 @@ static void update_attribute_element_offset(Mesh *mesh,
if(mattr->element == ATTR_ELEMENT_VOXEL) {
/* store slot in offset value */
VoxelAttribute *voxel_data = mattr->data_voxel();
- offset = voxel_data->slot;
+ offset = voxel_data->slot << 1;
+ if(voxel_data->from_alpha) {
+ ++offset;
+ }
}
else if(mattr->element == ATTR_ELEMENT_CORNER_BYTE) {
uchar4 *data = mattr->data_uchar4();
@@ -1085,6 +1090,7 @@ void MeshManager::device_update_bvh(Device *device, DeviceScene *dscene, Scene *
bparams.use_qbvh = scene->params.use_qbvh;
bparams.use_spatial_split = scene->params.use_bvh_spatial_split;
bparams.use_cache = scene->params.use_bvh_cache;
+ bparams.use_triangle_storage = scene->params.use_bvh_triangle_storage;
delete bvh;
bvh = BVH::create(bparams, scene->objects);
@@ -1132,6 +1138,7 @@ void MeshManager::device_update_bvh(Device *device, DeviceScene *dscene, Scene *
dscene->data.bvh.root = pack.root_index;
dscene->data.bvh.use_qbvh = scene->params.use_qbvh;
+ dscene->data.bvh.use_tri_storage = scene->params.use_bvh_triangle_storage? 1: 0;
}
void MeshManager::device_update_flags(Device * /*device*/,
@@ -1320,6 +1327,15 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen
device_update_bvh(device, dscene, scene, progress);
+ if(free_data_after_update) {
+ foreach(Object *object, scene->objects) {
+ if(object->mesh->bvh != NULL) {
+ delete object->mesh->bvh;
+ object->mesh->bvh = NULL;
+ }
+ }
+ }
+
need_update = false;
if(need_displacement_images) {
diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h
index 76c186a3feb..887ef65004e 100644
--- a/intern/cycles/render/mesh.h
+++ b/intern/cycles/render/mesh.h
@@ -152,7 +152,10 @@ public:
bool need_update;
bool need_flags_update;
- MeshManager();
+ /* Free memory used by BVH after device update. */
+ bool free_data_after_update;
+
+ MeshManager(const bool free_data_after_update);
~MeshManager();
bool displace(Device *device, DeviceScene *dscene, Scene *scene, Mesh *mesh, Progress& progress);
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index 69ae2078216..7a9ed1006ce 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -16,6 +16,7 @@
#include "image.h"
#include "nodes.h"
+#include "openvdb.h"
#include "svm.h"
#include "svm_math_util.h"
#include "osl.h"
@@ -1297,6 +1298,118 @@ void BrickTextureNode::compile(OSLCompiler& compiler)
compiler.add(this, "node_brick_texture");
}
+/* Point Density Texture */
+
+static ShaderEnum point_density_space_init()
+{
+ ShaderEnum enm;
+
+ enm.insert("Object", NODE_TEX_VOXEL_SPACE_OBJECT);
+ enm.insert("World", NODE_TEX_VOXEL_SPACE_WORLD);
+
+ return enm;
+}
+
+ShaderEnum PointDensityTextureNode::space_enum = point_density_space_init();
+
+PointDensityTextureNode::PointDensityTextureNode()
+: ShaderNode("point_density")
+{
+ image_manager = NULL;
+ slot = -1;
+ filename = "";
+ space = ustring("Object");
+ builtin_data = NULL;
+ interpolation = INTERPOLATION_LINEAR;
+
+ tfm = transform_identity();
+
+ add_input("Vector", SHADER_SOCKET_POINT, ShaderInput::POSITION);
+ add_output("Density", SHADER_SOCKET_FLOAT);
+ add_output("Color", SHADER_SOCKET_COLOR);
+}
+
+PointDensityTextureNode::~PointDensityTextureNode()
+{
+ if(image_manager)
+ image_manager->remove_image(filename, builtin_data, interpolation);
+}
+
+ShaderNode *PointDensityTextureNode::clone() const
+{
+ PointDensityTextureNode *node = new PointDensityTextureNode(*this);
+ node->image_manager = NULL;
+ node->slot = -1;
+ return node;
+}
+
+void PointDensityTextureNode::attributes(Shader *shader,
+ AttributeRequestSet *attributes)
+{
+ if(shader->has_volume)
+ attributes->add(ATTR_STD_GENERATED_TRANSFORM);
+
+ ShaderNode::attributes(shader, attributes);
+}
+
+void PointDensityTextureNode::compile(SVMCompiler& compiler)
+{
+ ShaderInput *vector_in = input("Vector");
+ ShaderOutput *density_out = output("Density");
+ ShaderOutput *color_out = output("Color");
+
+ bool use_density = !density_out->links.empty();
+ bool use_color = !color_out->links.empty();
+
+ image_manager = compiler.image_manager;
+
+ if (use_density || use_color) {
+ if (use_density)
+ compiler.stack_assign(density_out);
+ if (use_color)
+ compiler.stack_assign(color_out);
+
+ if(slot == -1) {
+ bool is_float, is_linear;
+ slot = image_manager->add_image(filename, builtin_data,
+ false, 0,
+ is_float, is_linear,
+ interpolation,
+ true);
+ }
+
+ if(slot != -1) {
+ compiler.stack_assign(vector_in);
+ compiler.add_node(NODE_TEX_VOXEL,
+ slot,
+ compiler.encode_uchar4(vector_in->stack_offset,
+ density_out->stack_offset,
+ color_out->stack_offset,
+ space_enum[space]));
+ if(space == "World") {
+ compiler.add_node(tfm.x);
+ compiler.add_node(tfm.y);
+ compiler.add_node(tfm.z);
+ compiler.add_node(tfm.w);
+ }
+ }
+ else {
+ compiler.add_node(NODE_VALUE_F,
+ __float_as_int(0.0f),
+ density_out->stack_offset);
+ compiler.add_node(NODE_VALUE_V, color_out->stack_offset);
+ compiler.add_node(NODE_VALUE_V, make_float3(TEX_IMAGE_MISSING_R,
+ TEX_IMAGE_MISSING_G,
+ TEX_IMAGE_MISSING_B));
+ }
+ }
+}
+
+void PointDensityTextureNode::compile(OSLCompiler& /*compiler*/)
+{
+ /* TODO(sergey): To be supported. */
+}
+
/* Normal */
NormalNode::NormalNode()
@@ -4367,4 +4480,71 @@ void TangentNode::compile(OSLCompiler& compiler)
compiler.add(this, "node_tangent");
}
+OpenVDBNode::OpenVDBNode()
+: ShaderNode("openvdb")
+{
+ filename = "";
+ volume_manager = NULL;
+ sampling = OPENVDB_SAMPLE_POINT;
+ tfm = transform_identity();
+
+ add_input("Vector", SHADER_SOCKET_POINT, ShaderInput::POSITION);
+}
+
+void OpenVDBNode::attributes(Shader *shader, AttributeRequestSet *attributes)
+{
+ if(shader->has_volume)
+ attributes->add(ATTR_STD_GENERATED_TRANSFORM);
+
+ ShaderNode::attributes(shader, attributes);
+}
+
+void OpenVDBNode::compile(SVMCompiler& compiler)
+{
+ ShaderInput *vector_in = input("Vector");
+ volume_manager = compiler.volume_manager;
+
+ compiler.stack_assign(vector_in);
+
+ for(size_t i = 0; i < outputs.size(); ++i) {
+ ShaderOutput *out = outputs[i];
+
+ if(out->links.empty()) {
+ continue;
+ }
+
+ int type = NODE_VDB_FLOAT;
+
+ if(out->type == SHADER_SOCKET_VECTOR || out->type == SHADER_SOCKET_COLOR) {
+ type = NODE_VDB_FLOAT3;
+ }
+
+ grid_slot = volume_manager->add_volume(filename.string(),
+ output_names[i].string(),
+ sampling, type);
+
+ if(grid_slot == -1) {
+ continue;
+ }
+
+ compiler.stack_assign(out);
+
+ compiler.add_node(NODE_OPENVDB,
+ grid_slot,
+ compiler.encode_uchar4(type,
+ vector_in->stack_offset,
+ out->stack_offset,
+ sampling));
+
+ compiler.add_node(tfm.x);
+ compiler.add_node(tfm.y);
+ compiler.add_node(tfm.z);
+ compiler.add_node(tfm.w);
+ }
+}
+
+void OpenVDBNode::compile(OSLCompiler& /*compiler*/)
+{
+}
+
CCL_NAMESPACE_END
diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h
index 7ec20f0879b..c839152ec2f 100644
--- a/intern/cycles/render/nodes.h
+++ b/intern/cycles/render/nodes.h
@@ -25,6 +25,7 @@ CCL_NAMESPACE_BEGIN
class ImageManager;
class Shader;
+class VolumeManager;
/* Texture Mapping */
@@ -213,6 +214,29 @@ public:
virtual int get_group() { return NODE_GROUP_LEVEL_2; }
};
+class PointDensityTextureNode : public ShaderNode {
+public:
+ SHADER_NODE_NO_CLONE_CLASS(PointDensityTextureNode)
+
+ ~PointDensityTextureNode();
+ ShaderNode *clone() const;
+ void attributes(Shader *shader, AttributeRequestSet *attributes);
+
+ bool has_spatial_varying() { return true; }
+ bool has_object_dependency() { return true; }
+
+ ImageManager *image_manager;
+ int slot;
+ string filename;
+ ustring space;
+ void *builtin_data;
+ InterpolationType interpolation;
+
+ Transform tfm;
+
+ static ShaderEnum space_enum;
+};
+
class MappingNode : public ShaderNode {
public:
SHADER_NODE_CLASS(MappingNode)
@@ -734,6 +758,22 @@ public:
ustring attribute;
};
+class OpenVDBNode : public ShaderNode {
+public:
+ SHADER_NODE_CLASS(OpenVDBNode)
+ void attributes(Shader *shader, AttributeRequestSet *attributes);
+ bool has_spatial_varying() { return true; }
+
+ ustring filename;
+ VolumeManager *volume_manager;
+
+ int grid_slot;
+ int sampling;
+ vector<ustring> output_names;
+
+ Transform tfm;
+};
+
CCL_NAMESPACE_END
#endif /* __NODES_H__ */
diff --git a/intern/cycles/render/openvdb.cpp b/intern/cycles/render/openvdb.cpp
new file mode 100644
index 00000000000..44468f4876e
--- /dev/null
+++ b/intern/cycles/render/openvdb.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2015 Blender Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "openvdb.h"
+#include "scene.h"
+#include "util_logging.h"
+#include "util_progress.h"
+
+CCL_NAMESPACE_BEGIN
+
+#define MAX_VOLUME 1024
+
+VolumeManager::VolumeManager()
+{
+#ifdef WITH_OPENVDB
+ openvdb::initialize();
+
+ scalar_grids.reserve(64);
+ vector_grids.reserve(64);
+ current_grids.reserve(64);
+ float_volumes.reserve(64);
+ float3_volumes.reserve(64);
+#endif
+
+ need_update = true;
+}
+
+VolumeManager::~VolumeManager()
+{
+#ifdef WITH_OPENVDB
+ scalar_grids.clear();
+ vector_grids.clear();
+ current_grids.clear();
+ float_volumes.clear();
+ float3_volumes.clear();
+#endif
+}
+
+static inline void catch_exceptions()
+{
+#ifdef WITH_OPENVDB
+ try {
+ throw;
+ }
+ catch (const openvdb::IoError& e) {
+ std::cerr << e.what() << "\n";
+ }
+#endif
+}
+
+int VolumeManager::add_volume(const string& filename, const string& name, int sampling, int grid_type)
+{
+ size_t slot = -1;
+
+ if((slot = find_existing_slot(filename, name, sampling, grid_type)) != -1) {
+ return slot;
+ }
+
+ try {
+ if(is_openvdb_file(filename)) {
+ slot = add_openvdb_volume(filename, name, sampling, grid_type);
+ }
+
+ add_grid_description(filename, name, sampling, slot);
+
+ need_update = true;
+ }
+ catch (...) {
+ catch_exceptions();
+ need_update = false;
+ slot = -1;
+ }
+
+ return slot;
+}
+
+int VolumeManager::find_existing_slot(const string& filename, const string& name, int sampling, int grid_type)
+{
+ for(size_t i = 0; i < current_grids.size(); ++i) {
+ GridDescription grid = current_grids[i];
+
+ if(grid.filename == filename && grid.name == name) {
+ if(grid.sampling == sampling) {
+ return grid.slot;
+ }
+ else {
+ /* sampling was changed, remove the sampler */
+ if(grid_type == NODE_VDB_FLOAT) {
+ delete float_volumes[grid.slot];
+ float_volumes[grid.slot] = NULL;
+ }
+ else {
+ delete float3_volumes[grid.slot];
+ float3_volumes[grid.slot] = NULL;
+ }
+
+ /* remove the grid description too */
+ std::swap(current_grids[i], current_grids.back());
+ current_grids.pop_back();
+ break;
+ }
+ }
+ }
+
+ return -1;
+}
+
+int VolumeManager::find_density_slot()
+{
+ /* first try finding a matching grid name */
+ for (size_t i = 0; i < current_grids.size(); ++i) {
+ GridDescription grid = current_grids[i];
+
+ if (string_iequals(grid.name, "density") || string_iequals(grid.name, "density high"))
+ return grid.slot;
+ }
+
+ /* try using the first scalar float grid instead */
+ if (!float_volumes.empty()) {
+ return 0;
+ }
+
+ return -1;
+}
+
+bool VolumeManager::is_openvdb_file(const string& filename) const
+{
+ return string_endswith(filename, ".vdb");
+}
+
+template <typename Container>
+size_t find_empty_slot(Container container)
+{
+ size_t slot = 0;
+
+ for(; slot < container.size(); ++slot) {
+ if(!container[slot]) {
+ break;
+ }
+ }
+
+ if(slot == container.size()) {
+ if(slot == MAX_VOLUME) {
+ printf("VolumeManager::add_volume: volume sampler limit reached %d!\n",
+ MAX_VOLUME);
+ return -1;
+ }
+
+ container.resize(slot + 1);
+ }
+
+ return slot;
+}
+
+size_t VolumeManager::add_openvdb_volume(const std::string& filename, const std::string& name, int /*sampling*/, int grid_type)
+{
+ size_t slot = -1;
+
+#ifdef WITH_OPENVDB
+ using namespace openvdb;
+
+ io::File file(filename);
+ file.open();
+
+ if (!file.hasGrid(name)) return -1;
+
+ if(grid_type == NODE_VDB_FLOAT) {
+ slot = find_empty_slot(float_volumes);
+
+ if(slot == -1) return -1;
+
+ if (!file.hasGrid(name)) return -1;
+ FloatGrid::Ptr grid = gridPtrCast<FloatGrid>(file.readGrid(name));
+ vdb_float_volume *sampler = new vdb_float_volume(grid);
+
+ float_volumes.insert(float_volumes.begin() + slot, sampler);
+ scalar_grids.push_back(grid);
+ }
+ else if(grid_type == NODE_VDB_FLOAT3) {
+ slot = find_empty_slot(float3_volumes);
+
+ if(slot == -1) return -1;
+
+ if (!file.hasGrid(name)) return -1;
+ Vec3SGrid::Ptr grid = gridPtrCast<Vec3SGrid>(file.readGrid(name));
+ vdb_float3_volume *sampler = new vdb_float3_volume(grid);
+
+ float3_volumes.insert(float3_volumes.begin() + slot, sampler);
+ vector_grids.push_back(grid);
+ }
+#else
+ (void)filename;
+ (void)name;
+ (void)grid_type;
+#endif
+
+ return slot;
+}
+
+void VolumeManager::add_grid_description(const string& filename, const string& name, int sampling, int slot)
+{
+ GridDescription descr;
+ descr.filename = filename;
+ descr.name = name;
+ descr.sampling = sampling;
+ descr.slot = slot;
+
+ current_grids.push_back(descr);
+}
+
+void VolumeManager::device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress)
+{
+ (void)scene;
+
+ if(!need_update) {
+ return;
+ }
+
+ device_free(device, dscene);
+ progress.set_status("Updating OpenVDB volumes", "Sending samplers to device.");
+
+ for(size_t i = 0; i < float_volumes.size(); ++i) {
+ if(!float_volumes[i]) {
+ continue;
+ }
+ device->const_copy_to("__float_volume", float_volumes[i], i);
+ }
+
+ for(size_t i = 0; i < float3_volumes.size(); ++i) {
+ if(!float3_volumes[i]) {
+ continue;
+ }
+ device->const_copy_to("__float3_volume", float3_volumes[i], i);
+ }
+
+ if(progress.get_cancel()) {
+ return;
+ }
+
+ dscene->data.tables.num_volumes = float_volumes.size() + float3_volumes.size();
+ dscene->data.tables.density_index = find_density_slot();
+
+ VLOG(1) << "Volume samplers allocate: __float_volume, " << float_volumes.size() * sizeof(float_volume) << " bytes";
+ VLOG(1) << "Volume samplers allocate: __float3_volume, " << float3_volumes.size() * sizeof(float3_volume) << " bytes";
+
+#ifdef WITH_OPENVDB
+ for(size_t i = 0; i < scalar_grids.size(); ++i) {
+ VLOG(1) << scalar_grids[i]->getName() << " memory usage: " << scalar_grids[i]->memUsage() / 1024.0f << " kilobytes.\n";
+ }
+
+ for(size_t i = 0; i < vector_grids.size(); ++i) {
+ VLOG(1) << vector_grids[i]->getName() << " memory usage: " << vector_grids[i]->memUsage() / 1024.0f << " kilobytes.\n";
+ }
+#endif
+
+ need_update = false;
+}
+
+void VolumeManager::device_free(Device */*device*/, DeviceScene */*dscene*/)
+{
+}
+
+CCL_NAMESPACE_END
diff --git a/intern/cycles/render/openvdb.h b/intern/cycles/render/openvdb.h
new file mode 100644
index 00000000000..2aa2162c0ff
--- /dev/null
+++ b/intern/cycles/render/openvdb.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 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 __VOLUMEMANAGER_H__
+#define __VOLUMEMANAGER_H__
+
+#include "util_openvdb.h"
+#include "util_string.h"
+#include "util_types.h"
+
+CCL_NAMESPACE_BEGIN
+
+class Device;
+class DeviceScene;
+class Progress;
+class Scene;
+
+class VolumeManager {
+ struct GridDescription {
+ string filename;
+ string name;
+ int sampling;
+ int slot;
+ };
+
+ vector<GridDescription> current_grids;
+
+#ifdef WITH_OPENVDB
+ vector<openvdb::FloatGrid::Ptr> scalar_grids;
+ vector<openvdb::Vec3SGrid::Ptr> vector_grids;
+#endif
+
+ void delete_volume(int grid_type, int sampling, size_t slot);
+
+ void add_grid_description(const string& filename, const string& name, int sampling, int slot);
+ int find_existing_slot(const string& filename, const string& name, int sampling, int grid_type);
+
+ bool is_openvdb_file(const string& filename) const;
+ size_t add_openvdb_volume(const string& filename, const string& name, int sampling, int grid_type);
+
+public:
+ VolumeManager();
+ ~VolumeManager();
+
+ int add_volume(const string& filename, const string& name, int sampling, int grid_type);
+ int find_density_slot();
+
+ void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress);
+ void device_free(Device *device, DeviceScene *dscene);
+
+ bool need_update;
+
+ vector<float_volume*> float_volumes;
+ vector<float3_volume*> float3_volumes;
+};
+
+CCL_NAMESPACE_END
+
+#endif /* __VOLUMEMANAGER_H__ */
diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp
index 19d715d834b..feffff30e89 100644
--- a/intern/cycles/render/scene.cpp
+++ b/intern/cycles/render/scene.cpp
@@ -26,6 +26,7 @@
#include "light.h"
#include "mesh.h"
#include "object.h"
+#include "openvdb.h"
#include "osl.h"
#include "particles.h"
#include "scene.h"
@@ -43,7 +44,9 @@
CCL_NAMESPACE_BEGIN
-Scene::Scene(const SceneParams& params_, const DeviceInfo& device_info_)
+Scene::Scene(const SceneParams& params_,
+ const DeviceInfo& device_info_,
+ const bool free_data_after_update)
: params(params_)
{
device = NULL;
@@ -54,13 +57,14 @@ Scene::Scene(const SceneParams& params_, const DeviceInfo& device_info_)
film = new Film();
background = new Background();
light_manager = new LightManager();
- mesh_manager = new MeshManager();
+ mesh_manager = new MeshManager(free_data_after_update);
object_manager = new ObjectManager();
integrator = new Integrator();
image_manager = new ImageManager();
particle_system_manager = new ParticleSystemManager();
curve_system_manager = new CurveSystemManager();
bake_manager = new BakeManager();
+ volume_manager = new VolumeManager();
/* OSL only works on the CPU */
if(device_info_.type == DEVICE_CPU)
@@ -118,6 +122,7 @@ void Scene::free_memory(bool final)
image_manager->device_free_builtin(device, &dscene);
lookup_tables->device_free(device, &dscene);
+ volume_manager->device_free(device, &dscene);
}
if(final) {
@@ -134,6 +139,7 @@ void Scene::free_memory(bool final)
delete curve_system_manager;
delete image_manager;
delete bake_manager;
+ delete volume_manager;
}
}
@@ -240,6 +246,11 @@ void Scene::device_update(Device *device_, Progress& progress)
if(progress.get_cancel() || device->have_error()) return;
+ progress.set_status("Updating OpenVDB Volumes");
+ volume_manager->device_update(device, &dscene, this, progress);
+
+ if(progress.get_cancel() || device->have_error()) return;
+
if(device->have_error() == false) {
progress.set_status("Updating Device", "Writing constant memory");
device->const_copy_to("__data", &dscene.data, sizeof(dscene.data));
@@ -300,6 +311,7 @@ bool Scene::need_reset()
|| particle_system_manager->need_update
|| curve_system_manager->need_update
|| bake_manager->need_update
+ || volume_manager->need_update
|| film->need_update);
}
diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h
index 851e5ac0b72..e7460c348ee 100644
--- a/intern/cycles/render/scene.h
+++ b/intern/cycles/render/scene.h
@@ -24,6 +24,7 @@
#include "kernel_types.h"
+#include "util_openvdb.h"
#include "util_param.h"
#include "util_string.h"
#include "util_system.h"
@@ -55,6 +56,7 @@ class ShaderManager;
class Progress;
class BakeManager;
class BakeData;
+class VolumeManager;
/* Scene Device Data */
@@ -128,6 +130,7 @@ public:
enum BVHType { BVH_DYNAMIC, BVH_STATIC } bvh_type;
bool use_bvh_cache;
bool use_bvh_spatial_split;
+ bool use_bvh_triangle_storage;
bool use_qbvh;
bool persistent_data;
@@ -137,6 +140,7 @@ public:
bvh_type = BVH_DYNAMIC;
use_bvh_cache = false;
use_bvh_spatial_split = false;
+ use_bvh_triangle_storage = true;
use_qbvh = false;
persistent_data = false;
}
@@ -146,6 +150,7 @@ public:
&& bvh_type == params.bvh_type
&& use_bvh_cache == params.use_bvh_cache
&& use_bvh_spatial_split == params.use_bvh_spatial_split
+ && use_bvh_triangle_storage == params.use_bvh_triangle_storage
&& use_qbvh == params.use_qbvh
&& persistent_data == params.persistent_data); }
};
@@ -177,6 +182,7 @@ public:
ParticleSystemManager *particle_system_manager;
CurveSystemManager *curve_system_manager;
BakeManager *bake_manager;
+ VolumeManager *volume_manager;
/* default shaders */
int default_surface;
@@ -194,7 +200,9 @@ public:
/* mutex must be locked manually by callers */
thread_mutex mutex;
- Scene(const SceneParams& params, const DeviceInfo& device_info);
+ Scene(const SceneParams& params,
+ const DeviceInfo& device_info,
+ const bool free_data_after_update = false);
~Scene();
void device_update(Device *device, Progress& progress);
diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp
index 57c1500628a..6408ba3adb9 100644
--- a/intern/cycles/render/session.cpp
+++ b/intern/cycles/render/session.cpp
@@ -812,6 +812,10 @@ void Session::update_scene()
progress.set_status("Updating Scene");
scene->device_update(device, progress);
}
+
+ if(clear_database_cb) {
+ clear_database_cb();
+ }
}
void Session::update_status_time(bool show_pause, bool show_done)
diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h
index c669bccd34b..ab9f2ddb0f2 100644
--- a/intern/cycles/render/session.h
+++ b/intern/cycles/render/session.h
@@ -128,6 +128,7 @@ public:
function<void(RenderTile&)> write_render_tile_cb;
function<void(RenderTile&)> update_render_tile_cb;
+ function<void(void)> clear_database_cb;
Session(const SessionParams& params);
~Session();
diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp
index d0bd34915df..1281beaac46 100644
--- a/intern/cycles/render/svm.cpp
+++ b/intern/cycles/render/svm.cpp
@@ -76,7 +76,7 @@ void SVMShaderManager::device_update(Device *device, DeviceScene *dscene, Scene
if(shader->use_mis && shader->has_surface_emission)
scene->light_manager->need_update = true;
- SVMCompiler compiler(scene->shader_manager, scene->image_manager);
+ SVMCompiler compiler(scene->shader_manager, scene->image_manager, scene->volume_manager);
compiler.background = ((int)i == scene->default_background);
compiler.compile(shader, svm_nodes, i);
}
@@ -104,10 +104,11 @@ void SVMShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *s
/* Graph Compiler */
-SVMCompiler::SVMCompiler(ShaderManager *shader_manager_, ImageManager *image_manager_)
+SVMCompiler::SVMCompiler(ShaderManager *shader_manager_, ImageManager *image_manager_, VolumeManager *volume_manager_)
{
shader_manager = shader_manager_;
image_manager = image_manager_;
+ volume_manager = volume_manager_;
max_stack_use = 0;
current_type = SHADER_TYPE_SURFACE;
current_shader = NULL;
diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h
index 4b390fb88f9..db07dd5b9e2 100644
--- a/intern/cycles/render/svm.h
+++ b/intern/cycles/render/svm.h
@@ -34,6 +34,7 @@ class ShaderGraph;
class ShaderInput;
class ShaderNode;
class ShaderOutput;
+class VolumeManager;
/* Shader Manager */
@@ -52,7 +53,7 @@ public:
class SVMCompiler {
public:
- SVMCompiler(ShaderManager *shader_manager, ImageManager *image_manager);
+ SVMCompiler(ShaderManager *shader_manager, ImageManager *image_manager, VolumeManager *volume_manager_);
void compile(Shader *shader, vector<int4>& svm_nodes, int index);
void stack_assign(ShaderOutput *output);
@@ -74,6 +75,7 @@ public:
ShaderType output_type() { return current_type; }
ImageManager *image_manager;
+ VolumeManager *volume_manager;
ShaderManager *shader_manager;
bool background;
diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt
index 0acb9e9304c..a9217c65901 100644
--- a/intern/cycles/util/CMakeLists.txt
+++ b/intern/cycles/util/CMakeLists.txt
@@ -52,6 +52,7 @@ set(SRC_HEADERS
util_math_fast.h
util_md5.h
util_opengl.h
+ util_openvdb.h
util_optimization.h
util_param.h
util_path.h
@@ -83,6 +84,12 @@ if(WITH_CYCLES_DEBUG)
)
endif()
+if(WITH_OPENVDB)
+ add_definitions(
+ -DWITH_OPENVDB
+ )
+endif()
+
include_directories(${INC})
include_directories(SYSTEM ${INC_SYS})
diff --git a/intern/cycles/util/util_openvdb.h b/intern/cycles/util/util_openvdb.h
new file mode 100644
index 00000000000..e262ec94c30
--- /dev/null
+++ b/intern/cycles/util/util_openvdb.h
@@ -0,0 +1,415 @@
+#ifndef __UTIL_OPENVDB_H__
+#define __UTIL_OPENVDB_H__
+
+#ifdef WITH_OPENVDB
+# include "util_map.h"
+#endif
+
+#include "util_types.h"
+
+#include "kernel_types.h"
+
+CCL_NAMESPACE_BEGIN
+
+struct Ray;
+struct Intersection;
+
+enum {
+ OPENVDB_SAMPLE_POINT = 0,
+ OPENVDB_SAMPLE_BOX = 1,
+};
+
+class float_volume {
+public:
+ virtual ~float_volume() {}
+ virtual float sample(float x, float y, float z, int sampling) = 0;
+ virtual bool intersect(const Ray *ray, Intersection *isect) = 0;
+ virtual bool march(float *t0, float *t1) = 0;
+ virtual bool has_uniform_voxels() = 0;
+};
+
+class float3_volume {
+public:
+ virtual ~float3_volume() {}
+ virtual float3 sample(float x, float y, float z, int sampling) = 0;
+ virtual bool intersect(const Ray *ray, Intersection *isect) = 0;
+ virtual bool march(float *t0, float *t1) = 0;
+ virtual bool has_uniform_voxels() = 0;
+};
+
+CCL_NAMESPACE_END
+
+#ifdef WITH_OPENVDB
+
+/* They are too many implicit float conversions happening in OpenVDB, disabling
+ * errors for now (kevin) */
+#ifdef __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wfloat-conversion"
+# pragma GCC diagnostic ignored "-Wdouble-promotion"
+#endif
+
+#include <openvdb/openvdb.h>
+#include <openvdb/tools/Interpolation.h>
+#include <openvdb/tools/RayIntersector.h>
+
+#ifdef __GNUC__
+# pragma GCC diagnostic pop
+#endif
+
+CCL_NAMESPACE_BEGIN
+
+#if defined(HAS_CPP11_FEATURES)
+using std::isfinite;
+#else
+using boost::math::isfinite;
+#endif
+
+class vdb_float_volume : public float_volume {
+ typedef openvdb::tools::GridSampler<openvdb::FloatGrid::ConstAccessor, openvdb::tools::PointSampler> point_sampler_t;
+ typedef openvdb::tools::GridSampler<openvdb::FloatGrid::ConstAccessor, openvdb::tools::BoxSampler> box_sampler_t;
+ typedef openvdb::tools::VolumeRayIntersector<openvdb::FloatGrid> isector_t;
+ typedef isector_t::RayType vdb_ray_t;
+
+ /* mainly used to ensure thread safety for the accessors */
+ typedef unordered_map<pthread_t, isector_t *> isect_map;
+ typedef unordered_map<pthread_t, point_sampler_t *> point_map;
+ typedef unordered_map<pthread_t, box_sampler_t *> box_map;
+ isect_map isectors;
+ point_map point_samplers;
+ box_map box_samplers;
+
+ openvdb::FloatGrid::ConstAccessor *accessor;
+ openvdb::math::Transform *transfrom;
+
+ /* Main intersector, its purpose is to initialize the voxels' bounding box
+ * so the ones for the various threads do not do this, rather they are
+ * generated from a copy of it */
+ isector_t *main_isector;
+
+ bool uniform_voxels;
+
+public:
+ vdb_float_volume(openvdb::FloatGrid::Ptr grid)
+ : transfrom(&grid->transform())
+ {
+ accessor = new openvdb::FloatGrid::ConstAccessor(grid->getConstAccessor());
+
+ /* only grids with uniform voxels can be used with VolumeRayIntersector */
+ if(grid->hasUniformVoxels()) {
+ uniform_voxels = true;
+ main_isector = new isector_t(*grid);
+ }
+ else {
+ uniform_voxels = false;
+ main_isector = NULL;
+ }
+ }
+
+ ~vdb_float_volume()
+ {
+ for(point_map::iterator iter = point_samplers.begin();
+ iter != point_samplers.end();
+ ++iter)
+ {
+ delete iter->second;
+ }
+
+ for(box_map::iterator iter = box_samplers.begin();
+ iter != box_samplers.end();
+ ++iter)
+ {
+ delete iter->second;
+ }
+
+ if(uniform_voxels) {
+ delete main_isector;
+
+ for(isect_map::iterator iter = isectors.begin();
+ iter != isectors.end();
+ ++iter)
+ {
+ delete iter->second;
+ }
+ }
+ }
+
+ ccl_always_inline float sample(float x, float y, float z, int sampling)
+ {
+ pthread_t thread = pthread_self();
+
+ if(sampling == OPENVDB_SAMPLE_POINT) {
+ point_map::iterator iter = point_samplers.find(thread);
+ point_sampler_t *sampler;
+
+ if(iter == point_samplers.end()) {
+ openvdb::FloatGrid::ConstAccessor *acc = new openvdb::FloatGrid::ConstAccessor(*accessor);
+ sampler = new point_sampler_t(*acc, *transfrom);
+ pair<pthread_t, point_sampler_t *> sampl(thread, sampler);
+ point_samplers.insert(sampl);
+ }
+ else {
+ sampler = iter->second;
+ }
+
+ return sampler->wsSample(openvdb::Vec3d(x, y, z));
+ }
+ else {
+ box_map::iterator iter = box_samplers.find(thread);
+ box_sampler_t *sampler;
+
+ if(iter == box_samplers.end()) {
+ openvdb::FloatGrid::ConstAccessor *acc = new openvdb::FloatGrid::ConstAccessor(*accessor);
+ sampler = new box_sampler_t(*acc, *transfrom);
+ pair<pthread_t, box_sampler_t *> sampl(thread, sampler);
+ box_samplers.insert(sampl);
+ }
+ else {
+ sampler = iter->second;
+ }
+
+ return sampler->wsSample(openvdb::Vec3d(x, y, z));
+ }
+ }
+
+ ccl_always_inline bool intersect(const Ray *ray, Intersection */*isect*/)
+ {
+ pthread_t thread = pthread_self();
+ isect_map::iterator iter = isectors.find(thread);
+ isector_t *vdb_isect;
+
+ if(iter == isectors.end()) {
+ vdb_isect = new isector_t(*main_isector);
+ pair<pthread_t, isector_t *> inter(thread, vdb_isect);
+ isectors.insert(inter);
+ }
+ else {
+ vdb_isect = iter->second;
+ }
+
+ vdb_ray_t::Vec3Type P(ray->P.x, ray->P.y, ray->P.z);
+ vdb_ray_t::Vec3Type D(ray->D.x, ray->D.y, ray->D.z);
+ D.normalize();
+
+ vdb_ray_t vdb_ray(P, D, 1e-5f, ray->t);
+
+ if(vdb_isect->setWorldRay(vdb_ray)) {
+ // TODO
+// isect->t = vdb_ray.t1(); // (kevin) is this correct?
+// isect->u = isect->v = 1.0f;
+// isect->type = ;
+// isect->shad = shader;
+// isect->norm = ;
+// isect->prim = 0;
+// isect->object = 0;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ ccl_always_inline bool march(float *t0, float *t1)
+ {
+ pthread_t thread = pthread_self();
+ isect_map::iterator iter = isectors.find(thread);
+ isector_t *vdb_isect = iter->second;
+
+ openvdb::Real vdb_t0(*t0), vdb_t1(*t1);
+
+ if(vdb_isect->march(vdb_t0, vdb_t1)) {
+ *t0 = (float)vdb_isect->getWorldTime(vdb_t0);
+ *t1 = (float)vdb_isect->getWorldTime(vdb_t1);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ ccl_always_inline bool has_uniform_voxels()
+ {
+ return uniform_voxels;
+ }
+};
+
+/* Same as above, except for vector grids */
+/* TODO(kevin): staggered velocity grid sampling */
+class vdb_float3_volume : public float3_volume {
+ typedef openvdb::tools::GridSampler<openvdb::Vec3SGrid::ConstAccessor, openvdb::tools::PointSampler> point_sampler_t;
+ typedef openvdb::tools::GridSampler<openvdb::Vec3SGrid::ConstAccessor, openvdb::tools::BoxSampler> box_sampler_t;
+
+ typedef openvdb::tools::VolumeRayIntersector<openvdb::Vec3SGrid> isector_t;
+ typedef isector_t::RayType vdb_ray_t;
+
+ /* mainly used to ensure thread safety for the accessors */
+ typedef unordered_map<pthread_t, isector_t *> isect_map;
+ typedef unordered_map<pthread_t, point_sampler_t *> point_map;
+ typedef unordered_map<pthread_t, box_sampler_t *> box_map;
+ isect_map isectors;
+ point_map point_samplers;
+ box_map box_samplers;
+
+ openvdb::Vec3SGrid::ConstAccessor *accessor;
+ openvdb::math::Transform *transfrom;
+
+ /* Main intersector, its purpose is to initialize the voxels' bounding box
+ * so the ones for the various threads do not do this, rather they are
+ * generated from a copy of it. */
+ isector_t *main_isector;
+
+ bool uniform_voxels;
+
+public:
+ vdb_float3_volume(openvdb::Vec3SGrid::Ptr grid)
+ : transfrom(&grid->transform())
+ {
+ accessor = new openvdb::Vec3SGrid::ConstAccessor(grid->getConstAccessor());
+
+ /* only grids with uniform voxels can be used with VolumeRayIntersector */
+ if(grid->hasUniformVoxels()) {
+ uniform_voxels = true;
+ main_isector = new isector_t(*grid);
+ }
+ else {
+ uniform_voxels = false;
+ main_isector = NULL;
+ }
+ }
+
+ ~vdb_float3_volume()
+ {
+ for(point_map::iterator iter = point_samplers.begin();
+ iter != point_samplers.end();
+ ++iter)
+ {
+ delete iter->second;
+ }
+
+ for(box_map::iterator iter = box_samplers.begin();
+ iter != box_samplers.end();
+ ++iter)
+ {
+ delete iter->second;
+ }
+
+ if(uniform_voxels) {
+ delete main_isector;
+
+ for(isect_map::iterator iter = isectors.begin();
+ iter != isectors.end();
+ ++iter)
+ {
+ delete iter->second;
+ }
+ }
+ }
+
+ ccl_always_inline float3 sample(float x, float y, float z, int sampling)
+ {
+ openvdb::Vec3s r;
+ pthread_t thread = pthread_self();
+
+ if(sampling == OPENVDB_SAMPLE_POINT) {
+ point_map::iterator iter = point_samplers.find(thread);
+ point_sampler_t *sampler;
+
+ if(iter == point_samplers.end()) {
+ openvdb::Vec3SGrid::ConstAccessor *acc = new openvdb::Vec3SGrid::ConstAccessor(*accessor);
+ sampler = new point_sampler_t(*acc, *transfrom);
+ pair<pthread_t, point_sampler_t *> sampl(thread, sampler);
+ point_samplers.insert(sampl);
+ }
+ else {
+ sampler = iter->second;
+ }
+
+ r = sampler->wsSample(openvdb::Vec3d(x, y, z));
+ }
+ else {
+ box_map::iterator iter = box_samplers.find(thread);
+ box_sampler_t *sampler;
+
+ if(iter == box_samplers.end()) {
+ openvdb::Vec3SGrid::ConstAccessor *acc = new openvdb::Vec3SGrid::ConstAccessor(*accessor);
+ sampler = new box_sampler_t(*acc, *transfrom);
+ pair<pthread_t, box_sampler_t *> sampl(thread, sampler);
+ box_samplers.insert(sampl);
+ }
+ else {
+ sampler = iter->second;
+ }
+
+ r = sampler->wsSample(openvdb::Vec3d(x, y, z));
+ }
+
+ return make_float3(r.x(), r.y(), r.z());
+ }
+
+ ccl_always_inline bool intersect(const Ray *ray, Intersection */*isect*/)
+ {
+ pthread_t thread = pthread_self();
+ isect_map::iterator iter = isectors.find(thread);
+ isector_t *vdb_isect;
+
+ if(iter == isectors.end()) {
+ vdb_isect = new isector_t(*main_isector);
+ pair<pthread_t, isector_t *> inter(thread, vdb_isect);
+ isectors.insert(inter);
+ }
+ else {
+ vdb_isect = iter->second;
+ }
+
+ vdb_ray_t::Vec3Type P(ray->P.x, ray->P.y, ray->P.z);
+ vdb_ray_t::Vec3Type D(ray->D.x, ray->D.y, ray->D.z);
+ D.normalize();
+
+ vdb_ray_t vdb_ray(P, D, 1e-5f, ray->t);
+
+ if(vdb_isect->setWorldRay(vdb_ray)) {
+ // TODO
+// isect->t = vdb_ray.t1(); // (kevin) is this correct?
+// isect->u = isect->v = 1.0f;
+// isect->type = ;
+// isect->shad = shader;
+// isect->norm = ;
+// isect->prim = 0;
+// isect->object = 0;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ ccl_always_inline bool march(float *t0, float *t1)
+ {
+ pthread_t thread = pthread_self();
+ isect_map::iterator iter = isectors.find(thread);
+ isector_t *vdb_isect = iter->second;
+
+ openvdb::Real vdb_t0(*t0), vdb_t1(*t1);
+
+ if(vdb_isect->march(vdb_t0, vdb_t1)) {
+ *t0 = (float)vdb_isect->getWorldTime(vdb_t0);
+ *t1 = (float)vdb_isect->getWorldTime(vdb_t1);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ ccl_always_inline bool has_uniform_voxels()
+ {
+ return uniform_voxels;
+ }
+};
+
+CCL_NAMESPACE_END
+
+#endif /* WITH_OPENVDB */
+
+#endif /* __UTIL_OPENVDB_H__ */
+
diff --git a/intern/openvdb/CMakeLists.txt b/intern/openvdb/CMakeLists.txt
new file mode 100644
index 00000000000..d7186338a16
--- /dev/null
+++ b/intern/openvdb/CMakeLists.txt
@@ -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.
+#
+# The Original Code is Copyright (C) 2015, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): Kevin Dietrich.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ intern
+)
+
+set(INC_SYS
+ ${GLEW_INCLUDE_PATH}
+)
+
+set(SRC
+ openvdb_capi.h
+)
+
+if(WITH_OPENVDB)
+ add_definitions(
+ -DWITH_OPENVDB
+ -DOPENVDB_USE_BLOSC
+ )
+
+ list(APPEND INC_SYS
+ ${OPENEXR_INCLUDE_DIRS}
+ ${OPENVDB_INCLUDE_DIRS}
+ )
+
+ list(APPEND SRC
+ intern/openvdb_dense_convert.cpp
+ intern/openvdb_primitive.cpp
+ intern/openvdb_reader.cpp
+ intern/openvdb_render.cpp
+ intern/openvdb_writer.cpp
+ openvdb_capi.cpp
+ openvdb_util.cpp
+ )
+endif()
+
+blender_add_lib(bf_intern_openvdb "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/intern/openvdb/SConscript b/intern/openvdb/SConscript
new file mode 100644
index 00000000000..b05437b36fe
--- /dev/null
+++ b/intern/openvdb/SConscript
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+#
+# ***** 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) 2015, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): Kevin Dietrich
+#
+# ***** END GPL LICENSE BLOCK *****
+
+Import ('env')
+
+sources = []
+
+defs = []
+incs = [
+ '.',
+ 'intern',
+ '../guardedalloc',
+ '../../source/blender/blenkernel',
+ '../../source/blender/blenlib',
+ '../../source/blender/makesdna',
+ '../../source/blender/makesrna',
+ env['BF_OPENVDB_INC'],
+ env['BF_BOOST_INC'],
+ env['BF_GLEW_INC']
+]
+
+incs.append(env['BF_OPENEXR_INC'].split())
+
+if env['WITH_BF_OPENVDB']:
+ defs.append('WITH_OPENVDB')
+ defs.append('DDF_DEBUG=0')
+ sources = env.Glob('*.cpp') + env.Glob('intern/*.cpp')
+
+env.BlenderLib ( libname = 'bf_intern_openvdb', sources = sources, includes = Split(incs), defines = defs, libtype=['core'], priority = [364] )
diff --git a/intern/openvdb/intern/openvdb_dense_convert.cpp b/intern/openvdb/intern/openvdb_dense_convert.cpp
new file mode 100644
index 00000000000..d7a9896675e
--- /dev/null
+++ b/intern/openvdb/intern/openvdb_dense_convert.cpp
@@ -0,0 +1,263 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <openvdb/tools/ValueTransformer.h>
+
+#include "openvdb_capi.h"
+#include "openvdb_dense_convert.h"
+#include "openvdb_writer.h"
+
+using namespace openvdb;
+
+namespace internal {
+
+class MergeScalarGrids {
+ tree::ValueAccessor<const FloatTree> m_acc_x, m_acc_y, m_acc_z;
+
+public:
+ MergeScalarGrids(const FloatTree *x_tree, const FloatTree *y_tree, const FloatTree *z_tree)
+ : m_acc_x(*x_tree)
+ , m_acc_y(*y_tree)
+ , m_acc_z(*z_tree)
+ {}
+
+ MergeScalarGrids(const MergeScalarGrids &other)
+ : m_acc_x(other.m_acc_x)
+ , m_acc_y(other.m_acc_y)
+ , m_acc_z(other.m_acc_z)
+ {}
+
+ void operator()(const Vec3STree::ValueOnIter &it) const
+ {
+ const math::Coord xyz = it.getCoord();
+ float x = m_acc_x.getValue(xyz);
+ float y = m_acc_y.getValue(xyz);
+ float z = m_acc_z.getValue(xyz);
+
+ it.setValue(Vec3s(x, y, z));
+ }
+};
+
+GridBase *OpenVDB_export_vector_grid(OpenVDBWriter *writer,
+ const std::string &name,
+ const float *data_x, const float *data_y, const float *data_z,
+ const int res[3],
+ float fluid_mat[4][4],
+ VecType vec_type,
+ const bool is_color,
+ const FloatGrid *mask)
+{
+
+ math::CoordBBox bbox(Coord(0), Coord(res[0] - 1, res[1] - 1, res[2] - 1));
+
+ Mat4R mat = Mat4R(
+ fluid_mat[0][0], fluid_mat[0][1], fluid_mat[0][2], fluid_mat[0][3],
+ fluid_mat[1][0], fluid_mat[1][1], fluid_mat[1][2], fluid_mat[1][3],
+ fluid_mat[2][0], fluid_mat[2][1], fluid_mat[2][2], fluid_mat[2][3],
+ fluid_mat[3][0], fluid_mat[3][1], fluid_mat[3][2], fluid_mat[3][3]);
+
+ math::Transform::Ptr transform = math::Transform::createLinearTransform(mat);
+
+ FloatGrid::Ptr grid[3];
+
+ grid[0] = FloatGrid::create(0.0f);
+ tools::Dense<const float, tools::LayoutXYZ> dense_grid_x(bbox, data_x);
+ tools::copyFromDense(dense_grid_x, grid[0]->tree(), TOLERANCE);
+
+ grid[1] = FloatGrid::create(0.0f);
+ tools::Dense<const float, tools::LayoutXYZ> dense_grid_y(bbox, data_y);
+ tools::copyFromDense(dense_grid_y, grid[1]->tree(), TOLERANCE);
+
+ grid[2] = FloatGrid::create(0.0f);
+ tools::Dense<const float, tools::LayoutXYZ> dense_grid_z(bbox, data_z);
+ tools::copyFromDense(dense_grid_z, grid[2]->tree(), TOLERANCE);
+
+ Vec3SGrid::Ptr vecgrid = Vec3SGrid::create(Vec3s(0.0f));
+
+ /* Activate voxels in the vector grid based on the scalar grids to ensure
+ * thread safety later on */
+ for (int i = 0; i < 3; ++i) {
+ vecgrid->tree().topologyUnion(grid[i]->tree());
+ }
+
+ MergeScalarGrids op(&(grid[0]->tree()), &(grid[1]->tree()), &(grid[2]->tree()));
+ tools::foreach(vecgrid->beginValueOn(), op, true, false);
+
+ vecgrid->setTransform(transform);
+
+ if (mask) {
+ vecgrid = tools::clip(*vecgrid, *mask);
+ }
+
+ vecgrid->setName(name);
+ vecgrid->setIsInWorldSpace(false);
+ vecgrid->setVectorType(vec_type);
+ vecgrid->insertMeta("is_color", BoolMetadata(is_color));
+
+ writer->insert(vecgrid);
+
+ return vecgrid.get();
+}
+
+class SplitVectorGrid {
+ FloatGrid::Ptr m_grid_x, m_grid_y, m_grid_z;
+
+public:
+ SplitVectorGrid()
+ {}
+
+ void operator()(const Vec3SGrid::Ptr &vecgrid)
+ {
+ Vec3s bg = vecgrid->background();
+ m_grid_x = FloatGrid::create(bg.x());
+ m_grid_y = FloatGrid::create(bg.y());
+ m_grid_z = FloatGrid::create(bg.z());
+
+ if (math::Transform::Ptr xform = vecgrid->transform().copy()) {
+ m_grid_x->setTransform(xform);
+ m_grid_y->setTransform(xform);
+ m_grid_z->setTransform(xform);
+ }
+
+ FloatGrid::Accessor acc_x = m_grid_x->getAccessor(),
+ acc_y = m_grid_y->getAccessor(),
+ acc_z = m_grid_z->getAccessor();
+
+ CoordBBox bbox;
+ for (Vec3SGrid::ValueOnCIter it = vecgrid->cbeginValueOn(); it; ++it) {
+ if (!it.getBoundingBox(bbox)) continue;
+
+ const Vec3s &val = it.getValue();
+
+ if (it.isTileValue()) {
+ m_grid_x->fill(bbox, val.x());
+ m_grid_y->fill(bbox, val.y());
+ m_grid_z->fill(bbox, val.z());
+ }
+ else {
+ acc_x.setValueOn(bbox.min(), val.x());
+ acc_y.setValueOn(bbox.min(), val.y());
+ acc_z.setValueOn(bbox.min(), val.z());
+ }
+ }
+ }
+
+ const FloatGrid::Ptr &grid_x() { return m_grid_x; }
+ const FloatGrid::Ptr &grid_y() { return m_grid_y; }
+ const FloatGrid::Ptr &grid_z() { return m_grid_z; }
+};
+
+void OpenVDB_import_grid_vector(OpenVDBReader *reader,
+ const std::string &name,
+ float **data_x, float **data_y, float **data_z,
+ const int res[3])
+{
+ Vec3SGrid::Ptr vgrid = gridPtrCast<Vec3SGrid>(reader->getGrid(name));
+
+#if 0
+ SplitVectorGrid vector_split;
+ vector_split(vgrid);
+
+ FloatGrid::Ptr grid[3];
+ math::CoordBBox bbox(Coord(0), Coord(res[0] - 1, res[1] - 1, res[2] - 1));
+
+ grid[0] = vector_split.grid_x();
+ tools::Dense<float, tools::LayoutXYZ> dense_grid_x(bbox);
+ tools::copyToDense(*grid[0], dense_grid_x);
+ *data_x = dense_grid_x.data();
+
+ grid[1] = vector_split.grid_y();
+ tools::Dense<float, tools::LayoutXYZ> dense_grid_y(bbox);
+ tools::copyToDense(*grid[1], dense_grid_y);
+ *data_y = dense_grid_y.data();
+
+ grid[2] = vector_split.grid_z();
+ tools::Dense<float, tools::LayoutXYZ> dense_grid_z(bbox);
+ tools::copyToDense(*grid[2], dense_grid_z);
+ *data_z = dense_grid_z.data();
+#else
+ Vec3SGrid::Accessor acc = vgrid->getAccessor();
+ math::Coord xyz;
+ int &x = xyz[0], &y = xyz[1], &z = xyz[2];
+
+ int index = 0;
+ for (z = 0; z < res[2]; ++z) {
+ for (y = 0; y < res[1]; ++y) {
+ for (x = 0; x < res[0]; ++x, ++index) {
+ math::Vec3s value = acc.getValue(xyz);
+ (*data_x)[index] = value.x();
+ (*data_y)[index] = value.y();
+ (*data_z)[index] = value.z();
+ }
+ }
+ }
+#endif
+}
+
+void OpenVDB_update_fluid_transform(const char *filename, float matrix[4][4], float matrix_high[4][4])
+{
+ /* TODO(kevin): deduplicate this call */
+ initialize();
+
+ Mat4R fluid_mat = Mat4R(
+ matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3],
+ matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3],
+ matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3],
+ matrix[3][0], matrix[3][1], matrix[3][2], matrix[3][3]);
+
+ Mat4R fluid_matBig = Mat4R(
+ matrix_high[0][0], matrix_high[0][1], matrix_high[0][2], matrix_high[0][3],
+ matrix_high[1][0], matrix_high[1][1], matrix_high[1][2], matrix_high[1][3],
+ matrix_high[2][0], matrix_high[2][1], matrix_high[2][2], matrix_high[2][3],
+ matrix_high[3][0], matrix_high[3][1], matrix_high[3][2], matrix_high[3][3]);
+
+ math::Transform::Ptr transform = math::Transform::createLinearTransform(fluid_mat);
+ math::Transform::Ptr transformBig = math::Transform::createLinearTransform(fluid_matBig);
+
+ io::File file(filename);
+ file.open();
+ GridPtrVecPtr grids = file.getGrids();
+ GridBase::Ptr grid;
+
+ for (size_t i = 0; i < grids->size(); ++i) {
+ grid = (*grids)[i];
+
+ const std::string name = grid->getName();
+ size_t found = name.find("High");
+
+ if (found != std::string::npos) {
+ grid->setTransform(transformBig);
+ }
+ else {
+ grid->setTransform(transform);
+ }
+ }
+
+ file.close();
+}
+
+} // namespace internal
diff --git a/intern/openvdb/intern/openvdb_dense_convert.h b/intern/openvdb/intern/openvdb_dense_convert.h
new file mode 100644
index 00000000000..d0243decf53
--- /dev/null
+++ b/intern/openvdb/intern/openvdb_dense_convert.h
@@ -0,0 +1,140 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __OPENVDB_DENSE_CONVERT_H__
+#define __OPENVDB_DENSE_CONVERT_H__
+
+#include <openvdb/openvdb.h>
+#include <openvdb/tools/Dense.h>
+#include <openvdb/tools/Clip.h>
+
+#include "openvdb_primitive.h"
+#include "openvdb_reader.h"
+#include "openvdb_writer.h"
+
+#define TOLERANCE 1e-3f
+
+namespace internal {
+
+template <typename GridType, typename T>
+GridType *OpenVDB_export_grid(OpenVDBWriter *writer,
+ const std::string &name,
+ const T *data,
+ const int res[3],
+ float fluid_mat[4][4],
+ const openvdb::FloatGrid *mask)
+{
+ using namespace openvdb;
+
+ math::CoordBBox bbox(Coord(0), Coord(res[0] - 1, res[1] - 1, res[2] - 1));
+
+ Mat4R mat = Mat4R(
+ fluid_mat[0][0], fluid_mat[0][1], fluid_mat[0][2], fluid_mat[0][3],
+ fluid_mat[1][0], fluid_mat[1][1], fluid_mat[1][2], fluid_mat[1][3],
+ fluid_mat[2][0], fluid_mat[2][1], fluid_mat[2][2], fluid_mat[2][3],
+ fluid_mat[3][0], fluid_mat[3][1], fluid_mat[3][2], fluid_mat[3][3]);
+
+ math::Transform::Ptr transform = math::Transform::createLinearTransform(mat);
+
+ typename GridType::Ptr grid = GridType::create(T(0));
+
+ tools::Dense<const T, openvdb::tools::LayoutXYZ> dense_grid(bbox, data);
+ tools::copyFromDense(dense_grid, grid->tree(), TOLERANCE);
+
+ grid->setTransform(transform);
+
+ if (mask) {
+ grid = tools::clip(*grid, *mask);
+ }
+
+ grid->setName(name);
+ grid->setIsInWorldSpace(false);
+
+ writer->insert(grid);
+
+ return grid.get();
+}
+
+template <typename GridType, typename T>
+OpenVDBPrimitive *OpenVDB_import_grid(OpenVDBReader *reader,
+ const std::string &name,
+ T **data,
+ const int res[3])
+{
+ using namespace openvdb;
+
+ typename GridType::Ptr grid_tmp = gridPtrCast<GridType>(reader->getGrid(name));
+#if 0
+ math::CoordBBox bbox(Coord(0), Coord(res[0] - 1, res[1] - 1, res[2] - 1));
+
+ tools::Dense<T, tools::LayoutXYZ> dense_grid(bbox);
+ tools::copyToDense(*grid_tmp, dense_grid);
+ memcpy(*data, dense_grid.data(), sizeof(T) * res[0] * res[1] * res[2]);
+#else
+ typename GridType::Accessor acc = grid_tmp->getAccessor();
+ math::Coord xyz;
+ int &x = xyz[0], &y = xyz[1], &z = xyz[2];
+
+ int index = 0;
+ for (z = 0; z < res[2]; ++z) {
+ for (y = 0; y < res[1]; ++y) {
+ for (x = 0; x < res[0]; ++x, ++index) {
+ (*data)[index] = acc.getValue(xyz);
+ }
+ }
+ }
+#endif
+
+ OpenVDBPrimitive *vdb_prim = new OpenVDBPrimitive();
+ vdb_prim->setGrid(grid_tmp);
+
+ return vdb_prim;
+}
+
+openvdb::GridBase *OpenVDB_export_vector_grid(OpenVDBWriter *writer,
+ const std::string &name,
+ const float *data_x, const float *data_y, const float *data_z,
+ const int res[3],
+ float fluid_mat[4][4],
+ openvdb::VecType vec_type,
+ const bool is_color,
+ const openvdb::FloatGrid *mask);
+
+
+void OpenVDB_import_grid_vector(OpenVDBReader *reader,
+ const std::string &name,
+ float **data_x, float **data_y, float **data_z,
+ const int res[3]);
+
+void OpenVDB_update_fluid_transform(const char *filename,
+ float matrix[4][4],
+ float matrix_high[4][4]);
+
+}
+
+#endif /* __OPENVDB_DENSE_CONVERT_H__ */
+
diff --git a/intern/openvdb/intern/openvdb_primitive.cpp b/intern/openvdb/intern/openvdb_primitive.cpp
new file mode 100644
index 00000000000..301a1a5fcbe
--- /dev/null
+++ b/intern/openvdb/intern/openvdb_primitive.cpp
@@ -0,0 +1,92 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "openvdb_primitive.h"
+
+OpenVDBPrimitive::OpenVDBPrimitive()
+{}
+
+OpenVDBPrimitive::~OpenVDBPrimitive()
+{}
+
+openvdb::GridBase &OpenVDBPrimitive::getGrid()
+{
+ return *m_grid;
+}
+
+const openvdb::GridBase &OpenVDBPrimitive::getConstGrid() const
+{
+ return *m_grid;
+}
+
+openvdb::GridBase::Ptr OpenVDBPrimitive::getGridPtr()
+{
+ return m_grid;
+}
+
+openvdb::GridBase::ConstPtr OpenVDBPrimitive::getConstGridPtr() const
+{
+ return m_grid;
+}
+
+void OpenVDBPrimitive::setGrid(openvdb::GridBase::Ptr grid)
+{
+ m_grid = grid->copyGrid();
+}
+
+static openvdb::Mat4R convertMatrix(const float mat[4][4])
+{
+ return openvdb::Mat4R(
+ mat[0][0], mat[0][1], mat[0][2], mat[0][3],
+ mat[1][0], mat[1][1], mat[1][2], mat[1][3],
+ mat[2][0], mat[2][1], mat[2][2], mat[2][3],
+ mat[3][0], mat[3][1], mat[3][2], mat[3][3]);
+}
+
+/* A simple protection to avoid crashes for cases when something goes wrong for
+ * some reason in the matrix creation. */
+static openvdb::math::MapBase::Ptr createAffineMap(const float mat[4][4])
+{
+ using namespace openvdb::math;
+ MapBase::Ptr transform;
+
+ try {
+ transform.reset(new AffineMap(convertMatrix(mat)));
+ }
+ catch (const openvdb::ArithmeticError &e) {
+ std::cerr << e.what() << "\n";
+ transform.reset(new AffineMap());
+ }
+
+ return transform;
+}
+
+void OpenVDBPrimitive::setTransform(const float mat[4][4])
+{
+ using namespace openvdb::math;
+
+ Transform::Ptr transform = Transform::Ptr(new Transform(createAffineMap(mat)));
+ m_grid->setTransform(transform);
+}
diff --git a/intern/openvdb/intern/openvdb_primitive.h b/intern/openvdb/intern/openvdb_primitive.h
new file mode 100644
index 00000000000..65633dfa86a
--- /dev/null
+++ b/intern/openvdb/intern/openvdb_primitive.h
@@ -0,0 +1,47 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __OPENVDB_PRIMITIVE_H__
+#define __OPENVDB_PRIMITIVE_H__
+
+#include <openvdb/openvdb.h>
+
+class OpenVDBPrimitive {
+ openvdb::GridBase::Ptr m_grid;
+
+public:
+ OpenVDBPrimitive();
+ ~OpenVDBPrimitive();
+
+ openvdb::GridBase &getGrid();
+ const openvdb::GridBase &getConstGrid() const;
+ openvdb::GridBase::Ptr getGridPtr();
+ openvdb::GridBase::ConstPtr getConstGridPtr() const;
+
+ void setGrid(openvdb::GridBase::Ptr grid);
+ void setTransform(const float mat[4][4]);
+};
+
+#endif /* __OPENVDB_PRIMITIVE_H__ */
diff --git a/intern/openvdb/intern/openvdb_reader.cpp b/intern/openvdb/intern/openvdb_reader.cpp
new file mode 100644
index 00000000000..1ed080e7574
--- /dev/null
+++ b/intern/openvdb/intern/openvdb_reader.cpp
@@ -0,0 +1,107 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "openvdb_reader.h"
+
+#define COPY_MAX_BYTES 10485760 /* 10 Mb */
+
+OpenVDBReader::OpenVDBReader()
+ : m_meta_map(new openvdb::MetaMap)
+{
+ /* Although it is safe, it may not be good to have this here... */
+ openvdb::initialize();
+ m_file = NULL;
+}
+
+OpenVDBReader::~OpenVDBReader()
+{
+ if (m_file) {
+ m_file->close();
+ delete m_file;
+ }
+}
+
+void OpenVDBReader::open(const std::string &filename)
+{
+ if (m_file) {
+ m_file->close();
+ delete m_file;
+ }
+
+ m_file = new openvdb::io::File(filename);
+ m_file->setCopyMaxBytes(COPY_MAX_BYTES);
+ m_file->open();
+
+ m_meta_map = m_file->getMetadata();
+}
+
+void OpenVDBReader::floatMeta(const std::string &name, float &value)
+{
+ value = m_meta_map->metaValue<float>(name);
+}
+
+void OpenVDBReader::intMeta(const std::string &name, int &value)
+{
+ value = m_meta_map->metaValue<int>(name);
+}
+
+void OpenVDBReader::vec3sMeta(const std::string &name, float value[3])
+{
+ openvdb::Vec3s meta_val = m_meta_map->metaValue<openvdb::Vec3s>(name);
+
+ value[0] = meta_val.x();
+ value[1] = meta_val.y();
+ value[2] = meta_val.z();
+}
+
+void OpenVDBReader::vec3IMeta(const std::string &name, int value[3])
+{
+ openvdb::Vec3i meta_val = m_meta_map->metaValue<openvdb::Vec3i>(name);
+
+ value[0] = meta_val.x();
+ value[1] = meta_val.y();
+ value[2] = meta_val.z();
+}
+
+void OpenVDBReader::mat4sMeta(const std::string &name, float value[4][4])
+{
+ openvdb::Mat4s meta_val = m_meta_map->metaValue<openvdb::Mat4s>(name);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ value[i][j] = meta_val[i][j];
+ }
+ }
+}
+
+openvdb::GridBase::Ptr OpenVDBReader::getGrid(const std::string &name)
+{
+ return m_file->readGrid(name);
+}
+
+size_t OpenVDBReader::numGrids() const
+{
+ return m_file->getGrids()->size();
+}
diff --git a/intern/openvdb/intern/openvdb_reader.h b/intern/openvdb/intern/openvdb_reader.h
new file mode 100644
index 00000000000..ae79b63c9c7
--- /dev/null
+++ b/intern/openvdb/intern/openvdb_reader.h
@@ -0,0 +1,51 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __OPENVDB_READER_H__
+#define __OPENVDB_READER_H__
+
+#include <openvdb/openvdb.h>
+
+class OpenVDBReader {
+ openvdb::MetaMap::Ptr m_meta_map;
+ openvdb::io::File *m_file;
+
+public:
+ OpenVDBReader();
+ ~OpenVDBReader();
+
+ void open(const std::string &filename);
+
+ void floatMeta(const std::string &name, float &value);
+ void intMeta(const std::string &name, int &value);
+ void vec3sMeta(const std::string &name, float value[3]);
+ void vec3IMeta(const std::string &name, int value[3]);
+ void mat4sMeta(const std::string &name, float value[4][4]);
+
+ openvdb::GridBase::Ptr getGrid(const std::string &name);
+ size_t numGrids() const;
+};
+
+#endif /* __OPENVDB_READER_H__ */
diff --git a/intern/openvdb/intern/openvdb_render.cpp b/intern/openvdb/intern/openvdb_render.cpp
new file mode 100644
index 00000000000..51aa43676c9
--- /dev/null
+++ b/intern/openvdb/intern/openvdb_render.cpp
@@ -0,0 +1,136 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <GL/glew.h>
+#include <openvdb/openvdb.h>
+
+#include "openvdb_primitive.h"
+#include "openvdb_render.h"
+
+namespace internal {
+
+void OpenVDBPrimitive_draw_tree(OpenVDBPrimitive *vdb_prim, const bool draw_root, const bool draw_level_1, const bool draw_level_2, const bool draw_leaves)
+{
+ using namespace openvdb;
+ using namespace openvdb::math;
+
+ FloatGrid::Ptr grid = gridPtrCast<FloatGrid>(vdb_prim->getGridPtr());
+
+ math::Vec3d wmin, wmax;
+ math::Vec3s color(0.0f);
+
+ math::Vec3s node_color[4] = {
+ math::Vec3s(0.0060f, 0.2790f, 0.6250f), // leaf nodes
+ math::Vec3s(0.8710f, 0.3940f, 0.0191f), // intermediate internal node levels
+ math::Vec3s(0.0432f, 0.3300f, 0.0411f), // first internal node level
+ math::Vec3s(0.0450f, 0.0450f, 0.0450f) // root
+ };
+
+ math::CoordBBox bbox;
+
+ glBegin(GL_LINES);
+
+ for (FloatTree::NodeCIter node_iter = grid->tree().cbeginNode(); node_iter; ++node_iter) {
+ node_iter.getBoundingBox(bbox);
+
+ const Vec3d min(bbox.min().x() - 0.5, bbox.min().y() - 0.5, bbox.min().z() - 0.5);
+ const Vec3d max(bbox.max().x() + 0.5, bbox.max().y() + 0.5, bbox.max().z() + 0.5);
+
+ wmin = grid->indexToWorld(min);
+ wmax = grid->indexToWorld(max);
+
+ const int level = node_iter.getLevel();
+
+ if (level == 0) {
+ if (!draw_leaves) {
+ continue;
+ }
+ color = node_color[0];
+ }
+
+ if (level == 1) {
+ if (!draw_level_2) {
+ continue;
+ }
+ color = node_color[1];
+ }
+
+ if (level == 2) {
+ if (!draw_level_1) {
+ continue;
+ }
+ color = node_color[2];
+ }
+
+ if (level == 3) {
+ if (!draw_root) {
+ continue;
+ }
+ color = node_color[3];
+ }
+
+ glColor3f(color[0], color[1], color[2]);
+
+ glVertex3f(wmin.x(), wmax.y(), wmax.z());
+ glVertex3f(wmax.x(), wmax.y(), wmax.z());
+
+ glVertex3f(wmin.x(), wmax.y(), wmin.z());
+ glVertex3f(wmax.x(), wmax.y(), wmin.z());
+
+ glVertex3f(wmin.x(), wmin.y(), wmax.z());
+ glVertex3f(wmax.x(), wmin.y(), wmax.z());
+
+ glVertex3f(wmin.x(), wmin.y(), wmin.z());
+ glVertex3f(wmax.x(), wmin.y(), wmin.z());
+
+ glVertex3f(wmin.x(), wmin.y(), wmin.z());
+ glVertex3f(wmin.x(), wmin.y(), wmax.z());
+
+ glVertex3f(wmin.x(), wmax.y(), wmin.z());
+ glVertex3f(wmin.x(), wmax.y(), wmax.z());
+
+ glVertex3f(wmax.x(), wmin.y(), wmin.z());
+ glVertex3f(wmax.x(), wmin.y(), wmax.z());
+
+ glVertex3f(wmax.x(), wmax.y(), wmin.z());
+ glVertex3f(wmax.x(), wmax.y(), wmax.z());
+
+ glVertex3f(wmin.x(), wmin.y(), wmin.z());
+ glVertex3f(wmin.x(), wmax.y(), wmin.z());
+
+ glVertex3f(wmax.x(), wmin.y(), wmin.z());
+ glVertex3f(wmax.x(), wmax.y(), wmin.z());
+
+ glVertex3f(wmax.x(), wmin.y(), wmax.z());
+ glVertex3f(wmax.x(), wmax.y(), wmax.z());
+
+ glVertex3f(wmin.x(), wmin.y(), wmax.z());
+ glVertex3f(wmin.x(), wmax.y(), wmax.z());
+ }
+
+ glEnd();
+}
+
+}
diff --git a/intern/openvdb/intern/openvdb_render.h b/intern/openvdb/intern/openvdb_render.h
new file mode 100644
index 00000000000..06bff3540b7
--- /dev/null
+++ b/intern/openvdb/intern/openvdb_render.h
@@ -0,0 +1,15 @@
+#ifndef __OPENVDB_RENDER_H__
+#define __OPENVDB_RENDER_H__
+
+struct OpenVDBPrimitive;
+
+namespace internal {
+
+void OpenVDBPrimitive_draw_tree(OpenVDBPrimitive *vdb_prim, const bool draw_root,
+ const bool draw_level_1, const bool draw_level_2,
+ const bool draw_leaves);
+
+}
+
+#endif /* __OPENVDB_RENDER_H__ */
+
diff --git a/intern/openvdb/intern/openvdb_writer.cpp b/intern/openvdb/intern/openvdb_writer.cpp
new file mode 100644
index 00000000000..9c2ad40c425
--- /dev/null
+++ b/intern/openvdb/intern/openvdb_writer.cpp
@@ -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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "openvdb_writer.h"
+
+OpenVDBWriter::OpenVDBWriter()
+ : m_grids(new openvdb::GridPtrVec())
+ , m_meta_map(new openvdb::MetaMap())
+{
+ m_meta_map->insertMeta("creator", openvdb::StringMetadata("Blender/OpenVDBWriter"));
+}
+
+OpenVDBWriter::~OpenVDBWriter()
+{}
+
+void OpenVDBWriter::insert(const openvdb::GridBase::Ptr &grid)
+{
+ m_grids->push_back(grid);
+}
+
+void OpenVDBWriter::insert(const openvdb::GridBase &grid)
+{
+ m_grids->push_back(grid.copyGrid());
+}
+
+void OpenVDBWriter::insertFloatMeta(const std::string &name, const float value)
+{
+ m_meta_map->insertMeta(name, openvdb::FloatMetadata(value));
+}
+
+void OpenVDBWriter::insertIntMeta(const std::string &name, const int value)
+{
+ m_meta_map->insertMeta(name, openvdb::Int32Metadata(value));
+}
+
+void OpenVDBWriter::insertVec3sMeta(const std::string &name, const openvdb::Vec3s value)
+{
+ m_meta_map->insertMeta(name, openvdb::Vec3SMetadata(value));
+}
+
+void OpenVDBWriter::insertVec3IMeta(const std::string &name, const openvdb::Vec3I value)
+{
+ m_meta_map->insertMeta(name, openvdb::Vec3IMetadata(value));
+}
+
+void OpenVDBWriter::insertMat4sMeta(const std::string &name, const float value[4][4])
+{
+ openvdb::Mat4s mat = openvdb::Mat4s(
+ value[0][0], value[0][1], value[0][2], value[0][3],
+ value[1][0], value[1][1], value[1][2], value[1][3],
+ value[2][0], value[2][1], value[2][2], value[2][3],
+ value[3][0], value[3][1], value[3][2], value[3][3]);
+
+ m_meta_map->insertMeta(name, openvdb::Mat4SMetadata(mat));
+}
+
+void OpenVDBWriter::setFileCompression(const int flags)
+{
+ m_flags = flags;
+}
+
+void OpenVDBWriter::write(const std::string &filename) const
+{
+ openvdb::io::File file(filename);
+ file.setCompression(m_flags);
+ file.write(*m_grids, *m_meta_map);
+ file.close();
+
+ /* Should perhaps be an option at some point */
+ m_grids->clear();
+}
diff --git a/intern/openvdb/intern/openvdb_writer.h b/intern/openvdb/intern/openvdb_writer.h
new file mode 100644
index 00000000000..6f36d6233a3
--- /dev/null
+++ b/intern/openvdb/intern/openvdb_writer.h
@@ -0,0 +1,55 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __OPENVDB_WRITER_H__
+#define __OPENVDB_WRITER_H__
+
+#include <openvdb/openvdb.h>
+
+class OpenVDBWriter {
+ openvdb::GridPtrVecPtr m_grids;
+ openvdb::MetaMap::Ptr m_meta_map;
+
+ int m_flags;
+
+public:
+ OpenVDBWriter();
+ ~OpenVDBWriter();
+
+ void insert(const openvdb::GridBase::Ptr &grid);
+ void insert(const openvdb::GridBase &grid);
+
+ void insertFloatMeta(const std::string &name, const float value);
+ void insertIntMeta(const std::string &name, const int value);
+ void insertVec3sMeta(const std::string &name, const openvdb::Vec3s value);
+ void insertVec3IMeta(const std::string &name, const openvdb::Vec3I value);
+ void insertMat4sMeta(const std::string &name, const float value[4][4]);
+
+ void setFileCompression(const int flags);
+
+ void write(const std::string &filename) const;
+};
+
+#endif /* __OPENVDB_WRITER_H__ */
diff --git a/intern/openvdb/openvdb_capi.cpp b/intern/openvdb/openvdb_capi.cpp
new file mode 100644
index 00000000000..64df4b94c3d
--- /dev/null
+++ b/intern/openvdb/openvdb_capi.cpp
@@ -0,0 +1,248 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "openvdb_capi.h"
+#include "openvdb_dense_convert.h"
+#include "openvdb_primitive.h"
+#include "openvdb_render.h"
+#include "openvdb_util.h"
+
+using namespace openvdb;
+
+struct OpenVDBFloatGrid { int unused; };
+struct OpenVDBIntGrid { int unused; };
+struct OpenVDBVectorGrid { int unused; };
+
+int OpenVDB_getVersionHex()
+{
+ return OPENVDB_LIBRARY_VERSION;
+}
+
+void OpenVDB_get_grid_info(const char *filename, OpenVDBGridInfoCallback cb, void *userdata)
+{
+ int ret = OPENVDB_NO_ERROR;
+ initialize();
+
+ try {
+ io::File file(filename);
+ file.open();
+
+ GridPtrVecPtr grids = file.getGrids();
+ int grid_num = grids->size();
+
+ for (size_t i = 0; i < grid_num; ++i) {
+ GridBase::ConstPtr grid = (*grids)[i];
+
+ std::string name = grid->getName();
+ std::string value_type = grid->valueType();
+ bool is_color = false;
+ if (grid->getMetadata< TypedMetadata<bool> >("is_color"))
+ is_color = grid->metaValue<bool>("is_color");
+
+ cb(userdata, name.c_str(), value_type.c_str(), is_color);
+ }
+ }
+ catch (...) {
+ catch_exception(ret);
+ }
+}
+
+void OpenVDB_update_fluid_transform(const char *filename,
+ float matrix[4][4],
+ float matrix_high[4][4])
+{
+ int ret = OPENVDB_NO_ERROR;
+
+ try {
+ internal::OpenVDB_update_fluid_transform(filename, matrix, matrix_high);
+ }
+ catch (...) {
+ catch_exception(ret);
+ }
+}
+
+OpenVDBFloatGrid *OpenVDB_export_grid_fl(OpenVDBWriter *writer,
+ const char *name, float *data,
+ const int res[3], float matrix[4][4],
+ OpenVDBFloatGrid *mask)
+{
+ OpenVDBFloatGrid *grid =
+ (OpenVDBFloatGrid *)internal::OpenVDB_export_grid<FloatGrid>(writer, name, data, res, matrix, (FloatGrid *)mask);
+ return grid;
+}
+
+OpenVDBIntGrid *OpenVDB_export_grid_ch(OpenVDBWriter *writer,
+ const char *name, unsigned char *data,
+ const int res[3], float matrix[4][4],
+ OpenVDBFloatGrid *mask)
+{
+ OpenVDBIntGrid *grid =
+ (OpenVDBIntGrid *)internal::OpenVDB_export_grid<Int32Grid>(writer, name, data, res, matrix, (FloatGrid *)mask);
+ return grid;
+}
+
+OpenVDBVectorGrid *OpenVDB_export_grid_vec(struct OpenVDBWriter *writer,
+ const char *name,
+ const float *data_x, const float *data_y, const float *data_z,
+ const int res[3], float matrix[4][4], short vec_type,
+ const bool is_color, OpenVDBFloatGrid *mask)
+{
+ OpenVDBVectorGrid *grid =
+ (OpenVDBVectorGrid *)internal::OpenVDB_export_vector_grid(writer, name,
+ data_x, data_y, data_z, res, matrix,
+ static_cast<openvdb::VecType>(vec_type),
+ is_color, (FloatGrid *)mask);
+ return grid;
+}
+
+OpenVDBPrimitive *OpenVDB_import_grid_fl(OpenVDBReader *reader,
+ const char *name, float **data,
+ const int res[3])
+{
+ return internal::OpenVDB_import_grid<FloatGrid>(reader, name, data, res);
+}
+
+void OpenVDB_import_grid_ch(OpenVDBReader *reader,
+ const char *name, unsigned char **data,
+ const int res[3])
+{
+ internal::OpenVDB_import_grid<Int32Grid>(reader, name, data, res);
+}
+
+void OpenVDB_import_grid_vec(struct OpenVDBReader *reader,
+ const char *name,
+ float **data_x, float **data_y, float **data_z,
+ const int res[3])
+{
+ internal::OpenVDB_import_grid_vector(reader, name, data_x, data_y, data_z, res);
+}
+
+OpenVDBWriter *OpenVDBWriter_create()
+{
+ return new OpenVDBWriter();
+}
+
+void OpenVDBWriter_free(OpenVDBWriter *writer)
+{
+ delete writer;
+ writer = NULL;
+}
+
+void OpenVDBWriter_set_compression(OpenVDBWriter *writer, const int flag)
+{
+ int compression_flags = io::COMPRESS_ACTIVE_MASK;
+
+ if (flag == 0) {
+ compression_flags |= io::COMPRESS_ZIP;
+ }
+ else if (flag == 1) {
+ compression_flags |= io::COMPRESS_BLOSC;
+ }
+ else {
+ compression_flags = io::COMPRESS_NONE;
+ }
+
+ writer->setFileCompression(compression_flags);
+}
+
+void OpenVDBWriter_add_meta_fl(OpenVDBWriter *writer, const char *name, const float value)
+{
+ writer->insertFloatMeta(name, value);
+}
+
+void OpenVDBWriter_add_meta_int(OpenVDBWriter *writer, const char *name, const int value)
+{
+ writer->insertIntMeta(name, value);
+}
+
+void OpenVDBWriter_add_meta_v3(OpenVDBWriter *writer, const char *name, const float value[3])
+{
+ writer->insertVec3sMeta(name, value);
+}
+
+void OpenVDBWriter_add_meta_v3_int(OpenVDBWriter *writer, const char *name, const int value[3])
+{
+ writer->insertVec3IMeta(name, value);
+}
+
+void OpenVDBWriter_add_meta_mat4(OpenVDBWriter *writer, const char *name, float value[4][4])
+{
+ writer->insertMat4sMeta(name, value);
+}
+
+void OpenVDBWriter_write(OpenVDBWriter *writer, const char *filename)
+{
+ writer->write(filename);
+}
+
+OpenVDBReader *OpenVDBReader_create()
+{
+ return new OpenVDBReader();
+}
+
+void OpenVDBReader_free(OpenVDBReader *reader)
+{
+ delete reader;
+ reader = NULL;
+}
+
+void OpenVDBReader_open(OpenVDBReader *reader, const char *filename)
+{
+ reader->open(filename);
+}
+
+void OpenVDBReader_get_meta_fl(OpenVDBReader *reader, const char *name, float *value)
+{
+ reader->floatMeta(name, *value);
+}
+
+void OpenVDBReader_get_meta_int(OpenVDBReader *reader, const char *name, int *value)
+{
+ reader->intMeta(name, *value);
+}
+
+void OpenVDBReader_get_meta_v3(OpenVDBReader *reader, const char *name, float value[3])
+{
+ reader->vec3sMeta(name, value);
+}
+
+void OpenVDBReader_get_meta_v3_int(OpenVDBReader *reader, const char *name, int value[3])
+{
+ reader->vec3IMeta(name, value);
+}
+
+void OpenVDBReader_get_meta_mat4(OpenVDBReader *reader, const char *name, float value[4][4])
+{
+ reader->mat4sMeta(name, value);
+}
+
+void OpenVDB_draw_primitive(OpenVDBPrimitive *vdb_prim,
+ const bool draw_root,
+ const bool draw_level_1,
+ const bool draw_level_2,
+ const bool draw_leaves)
+{
+ internal::OpenVDBPrimitive_draw_tree(vdb_prim, draw_root, draw_level_1, draw_level_2, draw_leaves);
+}
diff --git a/intern/openvdb/openvdb_capi.h b/intern/openvdb/openvdb_capi.h
new file mode 100644
index 00000000000..d0d4d80cb19
--- /dev/null
+++ b/intern/openvdb/openvdb_capi.h
@@ -0,0 +1,131 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __OPENVDB_CAPI_H__
+#define __OPENVDB_CAPI_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct OpenVDBReader;
+struct OpenVDBWriter;
+struct OpenVDBFloatGrid;
+struct OpenVDBIntGrid;
+struct OpenVDBPrimitive;
+struct OpenVDBVectorGrid;
+
+int OpenVDB_getVersionHex(void);
+
+typedef void (*OpenVDBGridInfoCallback)(void *userdata, const char *name, const char *value_type, bool is_color);
+void OpenVDB_get_grid_info(const char *filename, OpenVDBGridInfoCallback cb, void *userdata);
+
+enum {
+ OPENVDB_NO_ERROR = 0,
+ OPENVDB_ARITHM_ERROR = 1,
+ OPENVDB_ILLEGAL_ERROR = 2,
+ OPENVDB_INDEX_ERROR = 3,
+ OPENVDB_IO_ERROR = 4,
+ OPENVDB_KEY_ERROR = 5,
+ OPENVDB_LOOKUP_ERROR = 6,
+ OPENVDB_IMPL_ERROR = 7,
+ OPENVDB_REF_ERROR = 8,
+ OPENVDB_TYPE_ERROR = 9,
+ OPENVDB_VALUE_ERROR = 10,
+ OPENVDB_UNKNOWN_ERROR = 11,
+};
+
+enum {
+ VEC_INVARIANT = 0,
+ VEC_COVARIANT = 1,
+ VEC_COVARIANT_NORMALIZE = 2,
+ VEC_CONTRAVARIANT_RELATIVE = 3,
+ VEC_CONTRAVARIANT_ABSOLUTE = 4,
+};
+
+void OpenVDB_update_fluid_transform(const char *filename,
+ float matrix[4][4],
+ float matrix_high[4][4]);
+
+struct OpenVDBFloatGrid *OpenVDB_export_grid_fl(struct OpenVDBWriter *writer,
+ const char *name, float *data,
+ const int res[3], float matrix[4][4],
+ struct OpenVDBFloatGrid *mask);
+
+struct OpenVDBIntGrid *OpenVDB_export_grid_ch(struct OpenVDBWriter *writer,
+ const char *name, unsigned char *data,
+ const int res[3], float matrix[4][4],
+ struct OpenVDBFloatGrid *mask);
+
+struct OpenVDBVectorGrid *OpenVDB_export_grid_vec(struct OpenVDBWriter *writer,
+ const char *name,
+ const float *data_x, const float *data_y, const float *data_z,
+ const int res[3], float matrix[4][4], short vec_type,
+ const bool is_color,
+ struct OpenVDBFloatGrid *mask);
+
+struct OpenVDBPrimitive *OpenVDB_import_grid_fl(struct OpenVDBReader *reader,
+ const char *name, float **data,
+ const int res[3]);
+
+void OpenVDB_import_grid_ch(struct OpenVDBReader *reader,
+ const char *name, unsigned char **data,
+ const int res[3]);
+
+void OpenVDB_import_grid_vec(struct OpenVDBReader *reader,
+ const char *name,
+ float **data_x, float **data_y, float **data_z,
+ const int res[3]);
+
+struct OpenVDBWriter *OpenVDBWriter_create(void);
+void OpenVDBWriter_free(struct OpenVDBWriter *writer);
+void OpenVDBWriter_set_compression(struct OpenVDBWriter *writer, const int flag);
+void OpenVDBWriter_add_meta_fl(struct OpenVDBWriter *writer, const char *name, const float value);
+void OpenVDBWriter_add_meta_int(struct OpenVDBWriter *writer, const char *name, const int value);
+void OpenVDBWriter_add_meta_v3(struct OpenVDBWriter *writer, const char *name, const float value[3]);
+void OpenVDBWriter_add_meta_v3_int(struct OpenVDBWriter *writer, const char *name, const int value[3]);
+void OpenVDBWriter_add_meta_mat4(struct OpenVDBWriter *writer, const char *name, float value[4][4]);
+void OpenVDBWriter_write(struct OpenVDBWriter *writer, const char *filename);
+
+struct OpenVDBReader *OpenVDBReader_create(void);
+void OpenVDBReader_free(struct OpenVDBReader *reader);
+void OpenVDBReader_open(struct OpenVDBReader *reader, const char *filename);
+void OpenVDBReader_get_meta_fl(struct OpenVDBReader *reader, const char *name, float *value);
+void OpenVDBReader_get_meta_int(struct OpenVDBReader *reader, const char *name, int *value);
+void OpenVDBReader_get_meta_v3(struct OpenVDBReader *reader, const char *name, float value[3]);
+void OpenVDBReader_get_meta_v3_int(struct OpenVDBReader *reader, const char *name, int value[3]);
+void OpenVDBReader_get_meta_mat4(struct OpenVDBReader *reader, const char *name, float value[4][4]);
+
+void OpenVDB_draw_primitive(struct OpenVDBPrimitive *vdb_prim,
+ const bool draw_root,
+ const bool draw_level_1,
+ const bool draw_level_2,
+ const bool draw_leaves);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OPENVDB_CAPI_H__ */
diff --git a/intern/openvdb/openvdb_util.cpp b/intern/openvdb/openvdb_util.cpp
new file mode 100644
index 00000000000..4caa62c6395
--- /dev/null
+++ b/intern/openvdb/openvdb_util.cpp
@@ -0,0 +1,80 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <openvdb/Exceptions.h>
+
+#include "openvdb_capi.h"
+#include "openvdb_util.h"
+
+void catch_exception(int &ret)
+{
+ try {
+ throw;
+ }
+ catch (const openvdb::ArithmeticError &e) {
+ std::cerr << e.what() << std::endl;
+ ret = OPENVDB_ARITHM_ERROR;
+ }
+ catch (const openvdb::IllegalValueException &e) {
+ std::cerr << e.what() << std::endl;
+ ret = OPENVDB_ILLEGAL_ERROR;
+ }
+ catch (const openvdb::IndexError &e) {
+ std::cerr << e.what() << std::endl;
+ ret = OPENVDB_INDEX_ERROR;
+ }
+ catch (const openvdb::IoError &e) {
+ std::cerr << e.what() << std::endl;
+ ret = OPENVDB_IO_ERROR;
+ }
+ catch (const openvdb::KeyError &e) {
+ std::cerr << e.what() << std::endl;
+ ret = OPENVDB_KEY_ERROR;
+ }
+ catch (const openvdb::LookupError &e) {
+ std::cerr << e.what() << std::endl;
+ ret = OPENVDB_LOOKUP_ERROR;
+ }
+ catch (const openvdb::NotImplementedError &e) {
+ std::cerr << e.what() << std::endl;
+ ret = OPENVDB_IMPL_ERROR;
+ }
+ catch (const openvdb::ReferenceError &e) {
+ std::cerr << e.what() << std::endl;
+ ret = OPENVDB_REF_ERROR;
+ }
+ catch (const openvdb::TypeError &e) {
+ std::cerr << e.what() << std::endl;
+ ret = OPENVDB_TYPE_ERROR;
+ }
+ catch (const openvdb::ValueError &e) {
+ std::cerr << e.what() << std::endl;
+ ret = OPENVDB_VALUE_ERROR;
+ }
+ catch (...) {
+ std::cerr << "Unknown error in OpenVDB library..." << std::endl;
+ ret = OPENVDB_UNKNOWN_ERROR;
+ }
+}
diff --git a/intern/openvdb/openvdb_util.h b/intern/openvdb/openvdb_util.h
new file mode 100644
index 00000000000..56ed9ce412b
--- /dev/null
+++ b/intern/openvdb/openvdb_util.h
@@ -0,0 +1,31 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __OPENVDB_UTIL_H__
+#define __OPENVDB_UTIL_H__
+
+void catch_exception(int &ret);
+
+#endif /* __OPENVDB_UTIL_H__ */
diff --git a/intern/smoke/extern/smoke_API.h b/intern/smoke/extern/smoke_API.h
index 08dbded176e..8fa7daaad79 100644
--- a/intern/smoke/extern/smoke_API.h
+++ b/intern/smoke/extern/smoke_API.h
@@ -74,7 +74,7 @@ size_t smoke_get_index2d(int x, int max_x, int y);
void smoke_dissolve(struct FLUID_3D *fluid, int speed, int log);
// wavelet turbulence functions
-struct WTURBULENCE *smoke_turbulence_init(int *res, int amplify, int noisetype, const char *noisefile_path, int use_fire, int use_colors);
+struct WTURBULENCE *smoke_turbulence_init(int *res, int amplify, int noisetype, const char *noisefile_path, int use_fire, int use_colors, int use_sim);
void smoke_turbulence_free(struct WTURBULENCE *wt);
void smoke_turbulence_step(struct WTURBULENCE *wt, struct FLUID_3D *fluid);
@@ -109,6 +109,7 @@ int smoke_has_colors(struct FLUID_3D *fluid);
int smoke_turbulence_has_fuel(struct WTURBULENCE *wt);
int smoke_turbulence_has_colors(struct WTURBULENCE *wt);
+void smoke_ensure_simulation(struct FLUID_3D *fluid, struct WTURBULENCE *wt);
void smoke_ensure_heat(struct FLUID_3D *fluid);
void smoke_ensure_fire(struct FLUID_3D *fluid, struct WTURBULENCE *wt);
void smoke_ensure_colors(struct FLUID_3D *fluid, struct WTURBULENCE *wt, float init_r, float init_g, float init_b);
diff --git a/intern/smoke/intern/WTURBULENCE.cpp b/intern/smoke/intern/WTURBULENCE.cpp
index 3d712d2124a..2260057c0d2 100644
--- a/intern/smoke/intern/WTURBULENCE.cpp
+++ b/intern/smoke/intern/WTURBULENCE.cpp
@@ -51,8 +51,10 @@ static const float persistence = 0.56123f;
//////////////////////////////////////////////////////////////////////
// constructor
//////////////////////////////////////////////////////////////////////
-WTURBULENCE::WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int noisetype, const char *noisefile_path, int init_fire, int init_colors)
+WTURBULENCE::WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int noisetype, const char *noisefile_path, int init_fire, int init_colors, int init_sim)
{
+ _need_sim_data = init_sim != 0;
+
// if noise magnitude is below this threshold, its contribution
// is negilgible, so stop evaluating new octaves
_cullingThreshold = 1e-3;
@@ -87,11 +89,14 @@ WTURBULENCE::WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int no
// allocate high resolution density field
_totalStepsBig = 0;
_densityBig = new float[_totalCellsBig];
- _densityBigOld = new float[_totalCellsBig];
-
- for(int i = 0; i < _totalCellsBig; i++) {
- _densityBig[i] =
- _densityBigOld[i] = 0.;
+ memset(_densityBig, 0, sizeof(*_densityBig) * _totalCellsBig);
+
+ if (_need_sim_data) {
+ _densityBigOld = new float[_totalCellsBig];
+ memset(_densityBigOld, 0, sizeof(*_densityBigOld) * _totalCellsBig);
+ }
+ else {
+ _densityBigOld = NULL;
}
/* fire */
@@ -112,7 +117,6 @@ WTURBULENCE::WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int no
_tcU = new float[_totalCellsSm];
_tcV = new float[_totalCellsSm];
_tcW = new float[_totalCellsSm];
- _tcTemp = new float[_totalCellsSm];
// map all
const float dx = 1.0f/(float)(_resSm[0]);
@@ -126,29 +130,72 @@ WTURBULENCE::WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int no
_tcU[index] = x*dx;
_tcV[index] = y*dy;
_tcW[index] = z*dz;
- _tcTemp[index] = 0.;
}
+ if (_need_sim_data) {
+ _tcTemp = new float[_totalCellsSm];
+ memset(_tcTemp, 0, sizeof(*_tcTemp) * _totalCellsSm);
+ }
+ else {
+ _tcTemp = NULL;
+ }
+
// noise tiles
_noiseTile = new float[noiseTileSize * noiseTileSize * noiseTileSize];
setNoise(noisetype, noisefile_path);
}
+void WTURBULENCE::initSimulation()
+{
+ if (_need_sim_data) {
+ return;
+ }
+
+ if(_densityBigOld == NULL) {
+ _densityBigOld = new float[_totalCellsBig];
+ memset(_densityBigOld, 0, sizeof(*_densityBigOld) * _totalCellsBig);
+ }
+
+ if (_tcTemp == NULL) {
+ _tcTemp = new float[_totalCellsSm];
+ memset(_tcTemp, 0, sizeof(*_tcTemp) * _totalCellsSm);
+ }
+
+ if (_fuelBig != NULL) {
+ if (_fuelBigOld == NULL) {
+ _fuelBigOld = new float[_totalCellsBig];
+ _reactBigOld = new float[_totalCellsBig];
+ memset(_fuelBigOld, 0, sizeof(*_fuelBigOld) * _totalCellsBig);
+ memset(_reactBigOld, 0, sizeof(*_reactBigOld) * _totalCellsBig);
+ }
+ }
+
+ if (_color_rBig != NULL) {
+ if (_color_rBigOld == NULL) {
+ _color_rBigOld = new float[_totalCellsBig];
+ _color_gBigOld = new float[_totalCellsBig];
+ _color_bBigOld = new float[_totalCellsBig];
+ memset(_color_rBigOld, 0, sizeof(*_color_rBigOld) * _totalCellsBig);
+ memset(_color_gBigOld, 0, sizeof(*_color_gBigOld) * _totalCellsBig);
+ memset(_color_bBigOld, 0, sizeof(*_color_bBigOld) * _totalCellsBig);
+ }
+ }
+}
+
void WTURBULENCE::initFire()
{
if (!_fuelBig) {
_flameBig = new float[_totalCellsBig];
_fuelBig = new float[_totalCellsBig];
- _fuelBigOld = new float[_totalCellsBig];
_reactBig = new float[_totalCellsBig];
- _reactBigOld = new float[_totalCellsBig];
-
- for(int i = 0; i < _totalCellsBig; i++) {
- _flameBig[i] =
- _fuelBig[i] =
- _fuelBigOld[i] = 0.;
- _reactBig[i] =
- _reactBigOld[i] = 0.;
+ memset(_flameBig, 0, sizeof(*_flameBig) * _totalCellsBig);
+ memset(_fuelBig, 0, sizeof(*_fuelBig) * _totalCellsBig);
+ memset(_reactBig, 0, sizeof(*_reactBig) * _totalCellsBig);
+ if (_need_sim_data) {
+ _fuelBigOld = new float[_totalCellsBig];
+ _reactBigOld = new float[_totalCellsBig];
+ memset(_fuelBigOld, 0, sizeof(*_fuelBigOld) * _totalCellsBig);
+ memset(_reactBigOld, 0, sizeof(*_reactBigOld) * _totalCellsBig);
}
}
}
@@ -157,19 +204,20 @@ void WTURBULENCE::initColors(float init_r, float init_g, float init_b)
{
if (!_color_rBig) {
_color_rBig = new float[_totalCellsBig];
- _color_rBigOld = new float[_totalCellsBig];
_color_gBig = new float[_totalCellsBig];
- _color_gBigOld = new float[_totalCellsBig];
_color_bBig = new float[_totalCellsBig];
- _color_bBigOld = new float[_totalCellsBig];
-
for(int i = 0; i < _totalCellsBig; i++) {
_color_rBig[i] = _densityBig[i] * init_r;
- _color_rBigOld[i] = 0.0f;
_color_gBig[i] = _densityBig[i] * init_g;
- _color_gBigOld[i] = 0.0f;
_color_bBig[i] = _densityBig[i] * init_b;
- _color_bBigOld[i] = 0.0f;
+ }
+ if (_need_sim_data) {
+ _color_rBigOld = new float[_totalCellsBig];
+ _color_gBigOld = new float[_totalCellsBig];
+ _color_bBigOld = new float[_totalCellsBig];
+ memset(_color_rBigOld, 0, sizeof(*_color_rBigOld) * _totalCellsBig);
+ memset(_color_gBigOld, 0, sizeof(*_color_gBigOld) * _totalCellsBig);
+ memset(_color_bBigOld, 0, sizeof(*_color_bBigOld) * _totalCellsBig);
}
}
}
@@ -179,7 +227,7 @@ void WTURBULENCE::initColors(float init_r, float init_g, float init_b)
//////////////////////////////////////////////////////////////////////
WTURBULENCE::~WTURBULENCE() {
delete[] _densityBig;
- delete[] _densityBigOld;
+ if (_densityBigOld) delete[] _densityBigOld;
if (_flameBig) delete[] _flameBig;
if (_fuelBig) delete[] _fuelBig;
if (_fuelBigOld) delete[] _fuelBigOld;
@@ -196,7 +244,7 @@ WTURBULENCE::~WTURBULENCE() {
delete[] _tcU;
delete[] _tcV;
delete[] _tcW;
- delete[] _tcTemp;
+ if (_tcTemp) delete[] _tcTemp;
delete[] _noiseTile;
}
diff --git a/intern/smoke/intern/WTURBULENCE.h b/intern/smoke/intern/WTURBULENCE.h
index 36635325f62..fbc3e9eb8ba 100644
--- a/intern/smoke/intern/WTURBULENCE.h
+++ b/intern/smoke/intern/WTURBULENCE.h
@@ -36,11 +36,14 @@ struct WTURBULENCE
{
public:
// both config files can be NULL, altCfg might override values from noiseCfg
- WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int noisetype, const char *noisefile_path, int init_fire, int init_colors);
+ WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int noisetype, const char *noisefile_path, int init_fire, int init_colors, int init_sim);
/// destructor
virtual ~WTURBULENCE();
+ // Ensure data needed for simulation is allocated
+ void initSimulation();
+
void initFire();
void initColors(float init_r, float init_g, float init_b);
@@ -144,6 +147,8 @@ struct WTURBULENCE
void computeEigenvalues(float *_eigMin, float *_eigMax);
void decomposeEnergy(float *energy, float *_highFreqEnergy);
+
+ bool _need_sim_data;
};
#endif // WTURBULENCE_H
diff --git a/intern/smoke/intern/smoke_API.cpp b/intern/smoke/intern/smoke_API.cpp
index d79aaf76d56..28b39531b91 100644
--- a/intern/smoke/intern/smoke_API.cpp
+++ b/intern/smoke/intern/smoke_API.cpp
@@ -44,10 +44,10 @@ extern "C" FLUID_3D *smoke_init(int *res, float dx, float dtdef, int use_heat, i
return fluid;
}
-extern "C" WTURBULENCE *smoke_turbulence_init(int *res, int amplify, int noisetype, const char *noisefile_path, int use_fire, int use_colors)
+extern "C" WTURBULENCE *smoke_turbulence_init(int *res, int amplify, int noisetype, const char *noisefile_path, int use_fire, int use_colors, int use_sim)
{
if (amplify)
- return new WTURBULENCE(res[0],res[1],res[2], amplify, noisetype, noisefile_path, use_fire, use_colors);
+ return new WTURBULENCE(res[0],res[1],res[2], amplify, noisetype, noisefile_path, use_fire, use_colors, use_sim);
else
return NULL;
}
@@ -480,6 +480,13 @@ extern "C" int smoke_turbulence_has_colors(WTURBULENCE *wt)
}
/* additional field initialization */
+extern "C" void smoke_ensure_simulation(FLUID_3D * /*fluid*/, WTURBULENCE *wt)
+{
+ if (wt) {
+ wt->initSimulation();
+ }
+}
+
extern "C" void smoke_ensure_heat(FLUID_3D *fluid)
{
if (fluid) {
diff --git a/release/datafiles/brushicons/hairadd.png b/release/datafiles/brushicons/hairadd.png
new file mode 100644
index 00000000000..074111a5a0b
--- /dev/null
+++ b/release/datafiles/brushicons/hairadd.png
Binary files differ
diff --git a/release/datafiles/brushicons/haircomb.png b/release/datafiles/brushicons/haircomb.png
new file mode 100644
index 00000000000..074111a5a0b
--- /dev/null
+++ b/release/datafiles/brushicons/haircomb.png
Binary files differ
diff --git a/release/datafiles/brushicons/haircut.png b/release/datafiles/brushicons/haircut.png
new file mode 100644
index 00000000000..074111a5a0b
--- /dev/null
+++ b/release/datafiles/brushicons/haircut.png
Binary files differ
diff --git a/release/datafiles/brushicons/hairlength.png b/release/datafiles/brushicons/hairlength.png
new file mode 100644
index 00000000000..074111a5a0b
--- /dev/null
+++ b/release/datafiles/brushicons/hairlength.png
Binary files differ
diff --git a/release/datafiles/brushicons/hairpuff.png b/release/datafiles/brushicons/hairpuff.png
new file mode 100644
index 00000000000..074111a5a0b
--- /dev/null
+++ b/release/datafiles/brushicons/hairpuff.png
Binary files differ
diff --git a/release/datafiles/brushicons/hairsmooth.png b/release/datafiles/brushicons/hairsmooth.png
new file mode 100644
index 00000000000..074111a5a0b
--- /dev/null
+++ b/release/datafiles/brushicons/hairsmooth.png
Binary files differ
diff --git a/release/datafiles/brushicons/hairweight.png b/release/datafiles/brushicons/hairweight.png
new file mode 100644
index 00000000000..074111a5a0b
--- /dev/null
+++ b/release/datafiles/brushicons/hairweight.png
Binary files differ
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject c651e63a9a537624f639950f3127a1dee29205d
+Subproject 6f3437adf6d8bfa29c910e4c6c21580aed61cb3
diff --git a/release/datafiles/widget_export.py b/release/datafiles/widget_export.py
new file mode 100644
index 00000000000..c700f25b623
--- /dev/null
+++ b/release/datafiles/widget_export.py
@@ -0,0 +1,79 @@
+import bpy
+from bpy.types import Operator
+from bpy.props import StringProperty
+from bpy_extras.io_utils import ExportHelper
+
+def mesh_triangulate(me):
+ import bmesh
+ bm = bmesh.new()
+ bm.from_mesh(me)
+ bmesh.ops.triangulate(bm, faces=bm.faces)
+ bm.to_mesh(me)
+ bm.free()
+
+
+class ExportWidget(Operator, ExportHelper):
+ """Export a widget mesh as a C file"""
+ bl_idname = "export_scene.widget"
+ bl_label = "Export Widget"
+ bl_options = {'PRESET', 'UNDO'}
+
+ filename_ext = ".c"
+ filter_glob = StringProperty(
+ default="*.c;",
+ options={'HIDDEN'},
+ )
+ @classmethod
+ def poll(cls, context):
+ obj = context.active_object
+ return (obj and obj.type == 'MESH')
+
+ def execute(self, context):
+ ob = context.active_object
+ scene = context.scene
+
+ try:
+ me = ob.to_mesh(scene, True, 'PREVIEW', calc_tessface=False)
+ except RuntimeError:
+ me = None
+
+ if me is None:
+ return {'CANCELLED'}
+
+ mesh_triangulate(me)
+
+ name = ob.name
+ f = open(self.filepath, 'w')
+ f.write("int _WIDGET_nverts_%s = %d;\n" % (name, len(me.vertices)))
+ f.write("int _WIDGET_ntris_%s = %d;\n\n" % (name, len(me.polygons)))
+ f.write("float _WIDGET_verts_%s[][3] = {\n" % name)
+ for v in me.vertices:
+ f.write(" {%.6f, %.6f, %.6f},\n" % v.co[:])
+ f.write("};\n\n")
+ f.write("float _WIDGET_normals_%s[][3] = {\n" % name)
+ for v in me.vertices:
+ f.write(" {%.6f, %.6f, %.6f},\n" % v.normal[:])
+ f.write("};\n\n")
+ f.write("unsigned short _WIDGET_indices_%s[] = {\n" % name)
+ for p in me.polygons:
+ f.write(" %d, %d, %d,\n" % p.vertices[:])
+ f.write("};\n")
+ f.close()
+
+ return {'FINISHED'}
+
+def menu_func_export(self, context):
+ self.layout.operator(ExportWidget.bl_idname, text="Widget (.c)")
+
+
+def register():
+ bpy.utils.register_module(__name__)
+ bpy.types.INFO_MT_file_export.append(menu_func_export)
+
+
+def unregister():
+ bpy.utils.unregister_module(__name__)
+ bpy.types.INFO_MT_file_export.remove(menu_func_export)
+
+if __name__ == "__main__":
+ register()
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject 3fc5b82c6bdba2f9c954fbf497621b9bb794a1b
+Subproject 04c5015d0349bdf7f274a3893664fd5e523d22c
diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib
-Subproject cf842d8bb7b0033ca4fa99f7ebedcbd3810fd27
+Subproject 3537c4ac7db88ecfe1c307268cd4eb0548ad32e
diff --git a/release/scripts/modules/bpy_extras/anim_utils.py b/release/scripts/modules/bpy_extras/anim_utils.py
index 4ee5e685668..435169c39b0 100644
--- a/release/scripts/modules/bpy_extras/anim_utils.py
+++ b/release/scripts/modules/bpy_extras/anim_utils.py
@@ -25,6 +25,304 @@ __all__ = (
import bpy
+def frange(start, stop, step=1.0):
+ while start < stop:
+ yield start
+ start += step
+
+
+def ref_curve_eval(curve, frame_start, frame_stop, frame_step, x):
+ fac = (x - frame_start) / frame_step
+ idx = int(fac)
+ fac = abs(fac - idx)
+ if idx < 0:
+ return curve[0]
+ elif idx + 1 >= len(curve):
+ return curve[-1]
+ return (1.0 - fac) * curve[idx] + fac * curve[idx + 1]
+
+
+def bezt_optimize(points, threshold, res, steps, org_ref_curve, frame_start, frame_stop, frame_step):
+ """
+ Try to optimize given pair of Bezier segments (triplet of contiguous control points).
+ """
+ # Trying to remove the center point and adjusting relevant handles of each end points.
+ # If resulting curve gives error below threshold (i.e. average difference between y-values of original
+ # and simplified curve is small enough), we keep it (i.e. remove its center point).
+
+ from mathutils.geometry import interpolate_bezier
+ from math import sqrt
+
+ def correct_bezpart(points):
+ # Same as in C code...
+ h1 = points[0] - points[1]
+ h2 = points[3] - points[2]
+ d = points[3].x - points[0].x
+ d1 = abs(h1.x)
+ d2 = abs(h2.x)
+
+ if d != 0.0 and d1 + d2 > d:
+ fac = d / (d1 + d2)
+ points[1] = points[0] - h1 * fac
+ points[2] = points[3] - h2 * fac
+
+ def bez_diff(ref_curve, cur_curve, res):
+ # start and end values shall be the same!
+ start_diff = end_diff = 0
+ for i, (ref_v, cur_pt) in enumerate(zip(ref_curve[1:-1], cur_curve[1:-1])):
+ # Note we give much higher importance (quadratic rate) to difference near matching end.
+ start_fac = (i + 1) / res
+ end_fac = 1.0 - start_fac
+ start_diff += (cur_pt.y - ref_v) / (start_fac * start_fac)
+ end_diff += (cur_pt.y - ref_v) / (end_fac * end_fac)
+ return start_diff / (res - 2), end_diff / (res - 2)
+
+ correct_bezpart(points)
+
+ start_vec = points[1] - points[0]
+ end_vec = points[2] - points[3]
+
+ neg_slope = points[1].y < points[0].y if points[1].y != points[0].y else points[2].y < points[0].y if points[2].y != points[0].y else points[3].y < points[0].y
+
+ cur_curve = interpolate_bezier(points[0], points[1], points[2], points[3], res)
+ ref_curve = [ref_curve_eval(org_ref_curve, frame_start, frame_stop, frame_step, pt.x) for pt in cur_curve]
+
+ start_diff, end_diff = bez_diff(ref_curve, cur_curve, res)
+ prev_start_diff, prev_end_diff = start_diff, end_diff
+ do_start = 0
+ #~ print(points)
+ #~ print(start_diff, end_diff)
+
+ f = 1.0
+ for i in range(steps):
+ error = max(abs(start_diff), abs(end_diff))
+ if error < threshold:
+ return error
+
+ prev_points = list(points)
+ prev_start_vec, prev_end_vec = start_vec.copy(), end_vec.copy()
+
+ if do_start > 0 or (do_start == 0 and abs(start_diff) > abs(end_diff)):
+ do_start += 1
+ if neg_slope:
+ if start_diff > 0.0:
+ start_vec /= 1 + start_diff * f
+ else:
+ start_vec *= 1 - start_diff * f
+ else:
+ if start_diff < 0.0:
+ start_vec /= 1 - start_diff * f
+ else:
+ start_vec *= 1 + start_diff * f
+ points[1] = points[0] + start_vec
+ else:
+ do_start -= 1
+ if neg_slope:
+ if end_diff > 0.0:
+ end_vec *= 1 + end_diff * f
+ else:
+ end_vec /= 1 - end_diff * f
+ else:
+ if end_diff < 0.0:
+ end_vec *= 1 - end_diff * f
+ else:
+ end_vec /= 1 + end_diff * f
+ points[2] = points[3] + end_vec
+
+ correct_bezpart(points)
+ cur_curve = interpolate_bezier(points[0], points[1], points[2], points[3], res)
+ ref_curve = [ref_curve_eval(org_ref_curve, frame_start, frame_stop, frame_step, pt.x) for pt in cur_curve]
+
+ start_diff, end_diff = bez_diff(ref_curve, cur_curve, res)
+ #~ print(points)
+ #~ print(start_diff, end_diff, f, do_start, neg_slope)
+
+ if ((do_start > 0 and abs(start_diff) > abs(prev_start_diff)) or
+ (do_start < 0 and abs(end_diff) > abs(prev_end_diff))):
+ #~ print("WRONG!!!", (start_diff, prev_start_diff) if do_start > 0 else (end_diff, prev_end_diff))
+ points[:] = prev_points
+ start_diff, end_diff = prev_start_diff, prev_end_diff
+ start_vec, end_vec = prev_start_vec, prev_end_vec
+ do_start *= -1
+ if not (do_start % 2):
+ f /= 2
+ else:
+ do_start = 0
+ prev_start_diff, prev_end_diff = start_diff, end_diff
+
+ return max(abs(start_diff), abs(end_diff))
+
+
+def simplify_fcurve(fcurve, frame_start, frame_stop, threshold):
+ """
+ This function simplifies given fcurve, removing some existing control points and adjusting the others' handles.
+ Note that it does not remove non-aligned (or auto) points, nor any using something else than Bezier interpolation.
+
+ :arg frame_start: First frame to simplify.
+ :type frame_start: int
+ :arg frame_stop: Last frame to simplify (excluded).
+ :type frame_stop: int
+ :arg threshold: Precision of simplification
+ (the smaller the more precise, never zero, typically 0.1 gives best results).
+ :type threshold: float
+
+ :return: The number of deleted keyframes.
+ :rtype: int
+ """
+
+ # * We make several passes on the curve, removing each time at most (n - 1) / 2 of its control points.
+ # * End points are never removed.
+ # * Points which do not have aligned handles are never removed, neither are points using non-Bezier interpolation.
+ # * Each set of contiguous, aligned/auto points define a single curve segment.
+ # * At each pass, for each segment, we check a set of triplets, and try to optimize it.
+
+ SIMPLIFIED_TYPES_AUTO = {'AUTO', 'AUTO_CLAMPED'}
+ SIMPLIFIED_TYPES = {'ALIGNED'} | SIMPLIFIED_TYPES_AUTO
+ SIMPLIFIED_INTERPOLATION = {'BEZIER'}
+
+ frame_step = max(0.001, threshold / 10.0)
+ res = min(1000, int(1 / threshold * 10))
+ steps = min(100, int(1 / threshold * 5))
+
+ ref_curve = [fcurve.evaluate(x) for x in frange(frame_start, frame_stop, frame_step)]
+
+ curves = [[[], False]]
+ for pt in fcurve.keyframe_points:
+ if pt.co.x < frame_start:
+ continue
+ if pt.co.x >= frame_stop:
+ break
+ if pt.interpolation not in SIMPLIFIED_INTERPOLATION:
+ # 'Break' point.
+ if len(curves[-1][0]) > 2:
+ curves.append([[], False])
+ else: # Current curve segment is too short to be simplifiable, simply ignore it!
+ curves[-1][0][:] = []
+ #~ print("breaking")
+ continue
+ if pt.handle_left_type not in SIMPLIFIED_TYPES or pt.handle_right_type not in SIMPLIFIED_TYPES:
+ # 'Break' point.
+ if len(curves[-1][0]) > 1:
+ curves[-1][0].append([[pt.handle_left, pt.co, pt.handle_right], False, pt])
+ curves.append([[], False])
+ else: # Current curve segment is too short to be simplifiable, simply ignore it!
+ curves[-1][0][:] = []
+ #~ print("breaking")
+ curves[-1][0].append([[pt.handle_left, pt.co, pt.handle_right], False, pt])
+
+ if not curves[-1][0]:
+ del curves[-1] # Cleanup.
+
+ if not curves:
+ return 0
+
+ del_keyframes = []
+ step_simplified = True
+ while step_simplified:
+ step_simplified = False
+ for crv in curves:
+ if crv[1]:
+ continue # that whole segment of curve is considered impossible to simplify further.
+
+ curve = crv[0]
+ curve_len = len(curve)
+ new_curve1 = curve[0:1]
+ del_keyframes1 = []
+ simplified1 = 0
+ tot_error1 = 0.0
+ if curve_len <= 2:
+ continue
+
+ for i in range(0, curve_len - 2, 2):
+ if curve[i + 1][1]:
+ # Center knot of this triplet is locked (marked as not removable), skip.
+ new_curve1 += curve[i + 1:i + 3]
+ points = [curve[i][0][1].copy(), curve[i][0][2].copy(), curve[i + 2][0][0].copy(), curve[i + 2][0][1].copy()]
+ error = bezt_optimize(points, threshold, res, steps, ref_curve, frame_start, frame_stop, frame_step)
+ #~ print(error)
+ if (error < threshold):
+ del_keyframes1.append(curve[i + 1][2])
+ tot_error1 += error
+ # Center points of knots do not change - ever!
+ new_curve1[-1][0][2] = points[1]
+ new_curve1.append(curve[i + 2])
+ new_curve1[-1][0][0] = points[2]
+ simplified1 += 1
+ else:
+ new_curve1 += curve[i + 1:i + 3] # Mere copy of org curve...
+ #~ new_curve1[-2][1] = True # Lock that center knot from now on.
+ step_simplified = step_simplified or (simplified1 > 0)
+
+ if curve_len > 3:
+ # If we have four or more control points, we also have to check the other possible set of triplets...
+ new_curve2 = curve[0:1]
+ del_keyframes2 = []
+ simplified2 = 0
+ tot_error2 = 0.0
+
+ for i in range(1, curve_len - 2, 2):
+ if curve[i + 1][1]:
+ # Center knot of this triplet is locked (marked as not removable), skip.
+ new_curve2 += curve[i + 1:i + 3]
+ points = [curve[i][0][1].copy(), curve[i][0][2].copy(), curve[i + 2][0][0].copy(), curve[i + 2][0][1].copy()]
+ error = bezt_optimize(points, threshold, res, steps, ref_curve, frame_start, frame_stop, frame_step)
+ #~ print(error)
+ if (error < threshold):
+ del_keyframes2.append(curve[i + 1][2])
+ tot_error2 += error
+ # Center points of knots do not change - ever!
+ new_curve2[-1][0][2] = points[1]
+ new_curve2.append(curve[i + 2])
+ new_curve2[-1][0][0] = points[2]
+ simplified2 += 1
+ else:
+ new_curve2 += curve[i + 1:i + 3] # Mere copy of org curve...
+ #~ new_curve2[-2][1] = True # Lock that center knot from now on.
+
+ if (simplified2 > simplified1) or (simplified2 and ((tot_error2 < tot_error1) or not simplified1)):
+ new_curve1 = new_curve2
+ del_keyframes1 = del_keyframes2
+ step_simplified = step_simplified or (simplified2 > 0)
+
+ if (len(new_curve1) < curve_len):
+ curve[:] = new_curve1
+ del_keyframes += del_keyframes1
+ else:
+ crv[1] = True # That segment of curve cannot be simplified further.
+
+ ret = len(del_keyframes)
+ if not del_keyframes:
+ return ret
+
+ # Now! Update our fcurve.
+
+ # 'Flatten' our curve segments into a single curve again.
+ curve = []
+ for c, _ in curves:
+ if len(c) >= 2:
+ if curve and curve[-1][2] == c[0][2]:
+ curve[-1][0][2] = c[0][2]
+ curve += c[1:]
+ else:
+ curve += c
+
+ # Update handles of kept, modified keyframes.
+ for bezt, _, pt in c:
+ # Tag 'auto' handles as 'aligned'.
+ if pt.handle_left_type in SIMPLIFIED_TYPES_AUTO:
+ pt.handle_left_type = 'ALIGNED'
+ if pt.handle_right_type in SIMPLIFIED_TYPES_AUTO:
+ pt.handle_right_type = 'ALIGNED'
+ pt.handle_left, pt.co, pt.handle_right = bezt
+
+ # Remove deleted keyframes - WARNING must be the last thing done! Otherwise, other points become invalid...
+ for pt in sorted(del_keyframes, key=lambda pt: pt.co.x, reverse=True):
+ fcurve.keyframe_points.remove(pt, fast=True)
+
+ fcurve.update()
+ return ret
+
+
# XXX visual keying is actually always considered as True in this code...
def bake_action(frame_start,
frame_end,
@@ -37,6 +335,7 @@ def bake_action(frame_start,
do_parents_clear=False,
do_clean=False,
action=None,
+ clean_threshold=0.0,
):
"""
@@ -66,6 +365,8 @@ def bake_action(frame_start,
:arg action: An action to bake the data into, or None for a new action
to be created.
:type action: :class:`bpy.types.Action` or None
+ :arg clean_threshold: How much approximation do we accept while simplifying fcurves.
+ :type clean_threshold: float
:return: an action or None
:rtype: :class:`bpy.types.Action`
@@ -241,6 +542,8 @@ def bake_action(frame_start,
keyframe_points.remove(keyframe_points[i])
else:
i += 1
+ if clean_threshold != 0.0:
+ simplify_fcurve(fcu, keyframe_points[0].co.x, keyframe_points[-1].co.x + 1, clean_threshold)
scene.frame_set(frame_back)
diff --git a/release/scripts/modules/sys_info.py b/release/scripts/modules/sys_info.py
index 1b63d1d9d8d..5d94fd7a6a6 100644
--- a/release/scripts/modules/sys_info.py
+++ b/release/scripts/modules/sys_info.py
@@ -160,6 +160,13 @@ def write_sysinfo(op):
else:
output.write("Blender was built without Cycles support\n")
+ openvdb = bpy.app.openvdb
+ output.write("OpenVDB: ")
+ if openvdb.supported:
+ output.write("%s\n" % openvdb.version_string)
+ else:
+ output.write("Blender was built without OpenVDB support\n")
+
if not bpy.app.build_options.sdl:
output.write("SDL: Blender was built without SDL support\n")
@@ -182,8 +189,11 @@ def write_sysinfo(op):
output.write("\nImplementation Dependent OpenGL Limits:\n")
output.write(lilies)
limit = bgl.Buffer(bgl.GL_INT, 1)
+ limit_fl = bgl.Buffer(bgl.GL_FLOAT, 2)
bgl.glGetIntegerv(bgl.GL_MAX_TEXTURE_UNITS, limit)
output.write("Maximum Fixed Function Texture Units:\t%d\n" % limit[0])
+ bgl.glGetFloatv(bgl.GL_MAX_TEXTURE_UNITS, limit_fl)
+ output.write("Point Sprite Size Range:\t Max: %f Min: %f\n" % (limit_fl[0], limit_fl[1]))
output.write("\nGLSL:\n")
if version[0] > '1':
diff --git a/release/scripts/startup/bl_operators/anim.py b/release/scripts/startup/bl_operators/anim.py
index f3575f26890..a1b10976fe0 100644
--- a/release/scripts/startup/bl_operators/anim.py
+++ b/release/scripts/startup/bl_operators/anim.py
@@ -29,6 +29,7 @@ import bpy
from bpy.types import Operator
from bpy.props import (
IntProperty,
+ FloatProperty,
BoolProperty,
EnumProperty,
StringProperty,
@@ -216,6 +217,13 @@ class BakeAction(Operator):
"(useful for baking only part of bones in an armature)",
default=False,
)
+ clean_threshold = FloatProperty(
+ name="Clean Threshold",
+ description="Allowed error when simplifying baked curves (set to zero to disable)",
+ default=0.1,
+ min=0.0,
+ max=1.0,
+ )
bake_types = EnumProperty(
name="Bake Data",
description="Which data's transformations to bake",
@@ -227,7 +235,6 @@ class BakeAction(Operator):
)
def execute(self, context):
-
from bpy_extras import anim_utils
action = None
@@ -246,6 +253,7 @@ class BakeAction(Operator):
do_constraint_clear=self.clear_constraints,
do_parents_clear=self.clear_parents,
do_clean=True,
+ clean_threshold=self.clean_threshold,
action=action,
)
diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py
index 63c1945d2d2..2d13a8bef0a 100644
--- a/release/scripts/startup/bl_operators/presets.py
+++ b/release/scripts/startup/bl_operators/presets.py
@@ -68,6 +68,39 @@ class AddPresetBase:
trans = maketrans_init()
return name.lower().strip().translate(trans)
+ def write_preset_py(self, file_preset):
+ def rna_recursive_attr_expand(value, rna_path_step, level):
+ if isinstance(value, bpy.types.PropertyGroup):
+ for sub_value_attr in value.bl_rna.properties.keys():
+ if sub_value_attr == "rna_type":
+ continue
+ sub_value = getattr(value, sub_value_attr)
+ rna_recursive_attr_expand(sub_value, "%s.%s" % (rna_path_step, sub_value_attr), level)
+ elif type(value).__name__ == "bpy_prop_collection_idprop": # could use nicer method
+ file_preset.write("%s.clear()\n" % rna_path_step)
+ for sub_value in value:
+ file_preset.write("item_sub_%d = %s.add()\n" % (level, rna_path_step))
+ rna_recursive_attr_expand(sub_value, "item_sub_%d" % level, level + 1)
+ else:
+ # convert thin wrapped sequences
+ # to simple lists to repr()
+ try:
+ value = value[:]
+ except:
+ pass
+
+ file_preset.write("%s = %r\n" % (rna_path_step, value))
+
+ if hasattr(self, "preset_defines"):
+ for rna_path in self.preset_defines:
+ exec(rna_path)
+ file_preset.write("%s\n" % rna_path)
+ file_preset.write("\n")
+
+ for rna_path in self.preset_values:
+ value = eval(rna_path)
+ rna_recursive_attr_expand(value, rna_path, 1)
+
def execute(self, context):
import os
@@ -112,41 +145,10 @@ class AddPresetBase:
filepath,
preset_menu_class.preset_xml_map)
else:
-
- def rna_recursive_attr_expand(value, rna_path_step, level):
- if isinstance(value, bpy.types.PropertyGroup):
- for sub_value_attr in value.bl_rna.properties.keys():
- if sub_value_attr == "rna_type":
- continue
- sub_value = getattr(value, sub_value_attr)
- rna_recursive_attr_expand(sub_value, "%s.%s" % (rna_path_step, sub_value_attr), level)
- elif type(value).__name__ == "bpy_prop_collection_idprop": # could use nicer method
- file_preset.write("%s.clear()\n" % rna_path_step)
- for sub_value in value:
- file_preset.write("item_sub_%d = %s.add()\n" % (level, rna_path_step))
- rna_recursive_attr_expand(sub_value, "item_sub_%d" % level, level + 1)
- else:
- # convert thin wrapped sequences
- # to simple lists to repr()
- try:
- value = value[:]
- except:
- pass
-
- file_preset.write("%s = %r\n" % (rna_path_step, value))
-
file_preset = open(filepath, 'w')
file_preset.write("import bpy\n")
- if hasattr(self, "preset_defines"):
- for rna_path in self.preset_defines:
- exec(rna_path)
- file_preset.write("%s\n" % rna_path)
- file_preset.write("\n")
-
- for rna_path in self.preset_values:
- value = eval(rna_path)
- rna_recursive_attr_expand(value, rna_path, 1)
+ self.write_preset_py(file_preset)
file_preset.close()
@@ -412,6 +414,93 @@ class AddPresetHairDynamics(AddPresetBase, Operator):
]
+class AddPresetCacheLibraryHairSimulation(AddPresetBase, Operator):
+ """Add or remove a Hair Simulation Preset"""
+ bl_idname = "cachelibrary.hair_simulation_preset_add"
+ bl_label = "Add Hair Simulation Preset"
+ preset_menu = "CACHELIBRARY_MT_hair_simulation_presets"
+
+ # XXX cache_modifier should be stored in the context, but using a confirm popup
+ # for the operator causes this to get lost
+ modifier_name = StringProperty(name="Modifier Name")
+
+ '''
+ preset_defines = [
+ "cachelib = bpy.context.cache_library",
+ "md = bpy.context.cache_modifier",
+ ]
+ '''
+
+ @property
+ def preset_defines(self):
+ return [
+ "cachelib = bpy.context.object.cache_library",
+ "md = cachelib.modifiers[%r]" % self.modifier_name,
+ "params = md.parameters",
+ ]
+
+ preset_subdir = "cachelibrary_hair_simulation"
+
+ preset_values = [
+ "params.substeps",
+ "params.timescale",
+ "params.mass",
+ "params.drag",
+ "params.stretch_stiffness",
+ "params.stretch_damping",
+ "params.bend_stiffness",
+ "params.bend_damping",
+ "params.use_bend_stiffness_curve",
+ "params.goal_stiffness",
+ "params.goal_damping",
+ "params.use_goal_stiffness_curve",
+ "params.use_goal_deflect",
+ ]
+
+ def write_curve_map(self, file_preset, cuma, prop):
+ if cuma is None:
+ return
+
+ file_preset.write("from mathutils import *\n")
+
+ file_preset.write("cuma = %s\n" % prop)
+ file_preset.write("if cuma:\n")
+
+ file_preset.write(" cuma.black_level = %r\n" % cuma.black_level)
+ file_preset.write(" cuma.white_level = %r\n" % cuma.white_level)
+
+ file_preset.write(" cuma.use_clip = %r\n" % cuma.use_clip)
+ if cuma.use_clip:
+ file_preset.write(" cuma.clip_min_x = %r\n" % cuma.clip_min_x)
+ file_preset.write(" cuma.clip_max_x = %r\n" % cuma.clip_max_x)
+ file_preset.write(" cuma.clip_min_y = %r\n" % cuma.clip_min_y)
+ file_preset.write(" cuma.clip_max_y = %r\n" % cuma.clip_max_y)
+
+ for i, cu in enumerate(cuma.curves):
+ file_preset.write(" cu = cuma.curves[%d]\n" % i)
+ file_preset.write(" cu.extend = %r\n" % cu.extend)
+ file_preset.write(" for p in range(max(len(cu.points) - %d, 0)):\n" % len(cu.points))
+ file_preset.write(" cu.points.remove(cu.points[0])\n")
+ file_preset.write(" for i in range(max(%d - len(cu.points), 0)):\n" % len(cu.points))
+ file_preset.write(" cu.points.new(0, 0)\n")
+ for j, p in enumerate(cu.points):
+ file_preset.write(" cu.points[%d].handle_type = %r\n" % (j, p.handle_type))
+ file_preset.write(" cu.points[%d].location = %r\n" % (j, p.location))
+ file_preset.write(" cu.points[%d].select = %r\n" % (j, p.select))
+
+ file_preset.write(" cuma.update()\n")
+
+ def write_preset_py(self, file_preset):
+ AddPresetBase.write_preset_py(self, file_preset)
+
+ cachelib = bpy.context.object.cache_library
+ md = cachelib.modifiers[self.modifier_name]
+ params = md.parameters
+
+ self.write_curve_map(file_preset, params.bend_stiffness_curve, "params.bend_stiffness_curve")
+ self.write_curve_map(file_preset, params.goal_stiffness_curve, "params.goal_stiffness_curve")
+
+
class AddPresetSunSky(AddPresetBase, Operator):
"""Add or remove a Sky & Atmosphere Preset"""
bl_idname = "lamp.sunsky_preset_add"
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index edf60aa40e7..8ad68d57560 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -143,6 +143,7 @@ class BRUSH_OT_active_index_set(Operator):
"vertex_paint": "use_paint_vertex",
"weight_paint": "use_paint_weight",
"image_paint": "use_paint_image",
+ "hair_edit": "use_hair_edit",
}
def execute(self, context):
diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py
index 5416735494b..6004c294a23 100644
--- a/release/scripts/startup/bl_ui/properties_data_mesh.py
+++ b/release/scripts/startup/bl_ui/properties_data_mesh.py
@@ -75,6 +75,17 @@ class MESH_UL_vgroups(UIList):
layout.label(text="", icon_value=icon)
+class MESH_UL_fmaps(UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+ # assert(isinstance(item, bpy.types.VertexGroup))
+ fmap = item
+ if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ layout.prop(fmap, "name", text="", emboss=False, icon_value=icon)
+ elif self.layout_type in {'GRID'}:
+ layout.alignment = 'CENTER'
+ layout.label(text="", icon_value=icon)
+
+
class MESH_UL_shape_keys(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
# assert(isinstance(item, bpy.types.ShapeKey))
@@ -226,6 +237,48 @@ class DATA_PT_vertex_groups(MeshButtonsPanel, Panel):
layout.prop(context.tool_settings, "vertex_group_weight", text="Weight")
+class DATA_PT_face_maps(MeshButtonsPanel, Panel):
+ bl_label = "Face Maps"
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
+
+ @classmethod
+ def poll(cls, context):
+ engine = context.scene.render.engine
+ obj = context.object
+ return (obj and obj.type == 'MESH' and (engine in cls.COMPAT_ENGINES))
+
+ def draw(self, context):
+ layout = self.layout
+
+ ob = context.object
+ facemap = ob.face_maps.active
+
+ rows = 2
+ if facemap:
+ rows = 4
+
+ row = layout.row()
+ row.template_list("MESH_UL_fmaps", "", ob, "face_maps", ob.face_maps, "active_index", rows=rows)
+
+ col = row.column(align=True)
+ col.operator("object.face_map_add", icon='ZOOMIN', text="")
+ col.operator("object.face_map_remove", icon='ZOOMOUT', text="")
+ if facemap:
+ col.separator()
+ col.operator("object.face_map_move", icon='TRIA_UP', text="").direction = 'UP'
+ col.operator("object.face_map_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
+
+ if ob.face_maps and (ob.mode == 'EDIT' and ob.type == 'MESH'):
+ row = layout.row()
+
+ sub = row.row(align=True)
+ sub.operator("object.face_map_assign", text="Assign")
+ sub.operator("object.face_map_remove_from", text="Remove")
+
+ sub = row.row(align=True)
+ sub.operator("object.face_map_select", text="Select")
+ sub.operator("object.face_map_deselect", text="Deselect")
+
class DATA_PT_shape_keys(MeshButtonsPanel, Panel):
bl_label = "Shape Keys"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
@@ -297,7 +350,7 @@ class DATA_PT_shape_keys(MeshButtonsPanel, Panel):
row = layout.row()
row.active = enable_edit_value
row.prop(kb, "value")
-
+
split = layout.split()
col = split.column(align=True)
@@ -311,7 +364,7 @@ class DATA_PT_shape_keys(MeshButtonsPanel, Panel):
col.label(text="Blend:")
col.prop_search(kb, "vertex_group", ob, "vertex_groups", text="")
col.prop_search(kb, "relative_key", key, "key_blocks", text="")
-
+ col.prop_search(kb, "face_map", ob, "face_maps", text="")
else:
layout.prop(kb, "interpolation")
row = layout.column()
diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py
index 381c9add34f..e692a2fdf13 100644
--- a/release/scripts/startup/bl_ui/properties_data_modifier.py
+++ b/release/scripts/startup/bl_ui/properties_data_modifier.py
@@ -61,6 +61,7 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
col.label(text="Bind To:")
col.prop(md, "use_vertex_groups", text="Vertex Groups")
col.prop(md, "use_bone_envelopes", text="Bone Envelopes")
+ col.prop(md, "use_face_maps", text="Face Maps")
layout.separator()
@@ -660,11 +661,15 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
def PARTICLE_INSTANCE(self, layout, ob, md):
layout.prop(md, "object")
- layout.prop(md, "particle_system_index", text="Particle System")
+ if md.object:
+ layout.prop_search(md, "particle_system", md.object, "particle_systems", text="Particle System")
+ else:
+ layout.prop(md, "particle_system_index", text="Particle System")
split = layout.split()
col = split.column()
col.label(text="Create From:")
+ layout.prop(md, "space", text="")
col.prop(md, "use_normal")
col.prop(md, "use_children")
col.prop(md, "use_size")
@@ -675,6 +680,10 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
col.prop(md, "show_unborn")
col.prop(md, "show_dead")
+ row = layout.row(align=True)
+ row.prop(md, "particle_amount", text="Amount")
+ row.prop(md, "particle_offset", text="Offset")
+
layout.separator()
layout.prop(md, "use_path", text="Create Along Paths")
@@ -686,8 +695,16 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
col.prop(md, "use_preserve_shape")
col = split.column()
- col.prop(md, "position", slider=True)
- col.prop(md, "random_position", text="Random", slider=True)
+ col2 = col.column(align=True)
+ col2.prop(md, "position", slider=True)
+ col2.prop(md, "random_position", text="Random", slider=True)
+ col2 = col.column(align=True)
+ col2.prop(md, "rotation", slider=True)
+ col2.prop(md, "random_rotation", text="Random", slider=True)
+
+ col = layout.column(align=True)
+ col.prop(md, "index_layer_name", text="Index Layer")
+ col.prop(md, "value_layer_name", text="Value Layer")
def PARTICLE_SYSTEM(self, layout, ob, md):
layout.label(text="Settings can be found inside the Particle context")
diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py
index 3ff7a248c60..cba1454086c 100644
--- a/release/scripts/startup/bl_ui/properties_object.py
+++ b/release/scripts/startup/bl_ui/properties_object.py
@@ -18,8 +18,9 @@
# <pep8 compliant>
import bpy
-from bpy.types import Panel, Menu
+from bpy.types import Panel, Menu, UIList
from rna_prop_ui import PropertyPanel
+from bl_ui.properties_physics_common import effector_weights_ui
class ObjectButtonsPanel:
@@ -253,12 +254,70 @@ class OBJECT_PT_display(ObjectButtonsPanel, Panel):
col.prop(obj, "draw_type", text="")
col = split.column()
- if is_geometry or is_empty_image:
+ if 1:
# Only useful with object having faces/materials...
col.label(text="Object Color:")
- col.prop(obj, "color", text="")
+ row = col.row(align=True)
+ row.prop(obj, "color", text="")
+ row.prop(obj, "show_wire_color", text="", toggle=True, icon='WIRE')
+# XXX temporary solution
+bpy.types.CacheLibrary.filter_string = \
+ bpy.props.StringProperty(
+ name="Filter Object Name",
+ description="Filter cache library objects by name",
+ )
+bpy.types.CacheLibrary.show_metadata = \
+ bpy.props.BoolProperty(
+ name="Show Metadata",
+ description="Show metadata for the input archive",
+ default=False,
+ )
+
+
+def cachelib_objects(cachelib, group):
+ if not cachelib or not group:
+ return []
+
+ filter_string = cachelib.filter_string.lower()
+ if filter_string:
+ return filter(lambda ob: filter_string in ob.name.lower(), group.objects)
+ else:
+ return group.objects
+
+# Yields (type, index, enabled)
+def cachelib_object_items(cachelib, ob):
+ filter_types = cachelib.data_types
+
+ def items_desc():
+ yield 'OBJECT', -1
+
+ if (ob.type == 'MESH'):
+ yield 'DERIVED_MESH', -1
+
+ for index, psys in enumerate(ob.particle_systems):
+ if psys.settings.type == 'EMITTER':
+ yield 'PARTICLES', index
+ if psys.settings.type == 'HAIR':
+ yield 'HAIR', index
+ yield 'HAIR_PATHS', index
+
+ for datatype, index in items_desc():
+ show = False
+ enable = False
+ # always show selected types
+ if datatype in filter_types:
+ show = True
+ enable = True
+ # special case: OBJECT type used as top level, show but disable
+ elif datatype == 'OBJECT':
+ show = True
+ enable = False
+
+ if show:
+ yield datatype, index, enable
+
class OBJECT_PT_duplication(ObjectButtonsPanel, Panel):
bl_label = "Duplication"
@@ -296,6 +355,470 @@ class OBJECT_PT_duplication(ObjectButtonsPanel, Panel):
layout.prop(ob, "dupli_group", text="Group")
+
+class CACHELIB_MT_shape_key_specials(Menu):
+ bl_label = "Shape Key Specials"
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
+
+ def draw(self, context):
+ layout = self.layout
+
+ #layout.operator("object.shape_key_transfer", icon='COPY_ID') # icon is not ideal
+ #layout.operator("object.join_shapes", icon='COPY_ID') # icon is not ideal
+ layout.operator("cachelibrary.shape_key_add", icon='ZOOMIN', text="New Shape From Mix").from_mix = True
+ layout.operator("cachelibrary.shape_key_remove", icon='X', text="Delete All Shapes").all = True
+ layout.operator("cachelibrary.shape_key_move", icon='TRIA_UP_BAR', text="Move To Top").type = 'TOP'
+ layout.operator("cachelibrary.shape_key_move", icon='TRIA_DOWN_BAR', text="Move To Bottom").type = 'BOTTOM'
+
+
+class CACHELIB_UL_shape_keys(UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+ # assert(isinstance(item, bpy.types.ShapeKey))
+ md = active_data
+ # key = data
+ key_block = item
+ if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ split = layout.split(0.66, False)
+ split.prop(key_block, "name", text="", emboss=False, icon_value=icon)
+ row = split.row(align=True)
+ if key_block.mute:
+ row.active = False
+ if not item.id_data.use_relative:
+ row.prop(key_block, "frame", text="", emboss=False)
+ elif index > 0:
+ row.prop(key_block, "value", text="", emboss=False)
+ else:
+ row.label(text="")
+ row.prop(key_block, "mute", text="", emboss=False)
+ elif self.layout_type == 'GRID':
+ layout.alignment = 'CENTER'
+ layout.label(text="", icon_value=icon)
+
+
+class CACHELIBRARY_MT_hair_simulation_presets(Menu):
+ bl_label = "Hair Simulation Presets"
+ preset_subdir = "cachelibrary_hair_simulation"
+ preset_operator = "script.execute_preset"
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
+ draw = Menu.draw_preset
+
+
+class CacheArchiveInfoPanel():
+ def draw_node_structure(self, context, layout, node, indent):
+ row = layout.row()
+ for i in range(indent):
+ row.label(text="", icon='BLANK1')
+
+ if not node.child_nodes:
+ row.label(text="", icon='DOT')
+ elif not node.expand:
+ row.prop(node, "expand", text="", icon='DISCLOSURE_TRI_RIGHT', icon_only=True, emboss=False)
+ else:
+ row.prop(node, "expand", text="", icon='DISCLOSURE_TRI_DOWN', icon_only=True, emboss=False)
+
+ for child in node.child_nodes:
+ self.draw_node_structure(context, layout, child, indent + 1)
+
+
+ info_columns = ['Name', 'Node', 'Samples', 'Size', 'Data', '', 'Array Size']
+
+ def draw_node_info(self, context, layout, node, column):
+ if column == 0:
+ layout.prop(node, "name", text="")
+ if column == 1:
+ layout.prop(node, "type", text="")
+ if column == 2:
+ if node.type in {'SCALAR_PROPERTY', 'ARRAY_PROPERTY'}:
+ layout.prop(node, "samples", text="")
+ else:
+ layout.label(" ")
+ if column == 3:
+ size = int(node.bytes_size)
+ layout.label(sizeof_fmt(size) if size >= 0 else "-")
+ if column == 4:
+ if node.type in {'SCALAR_PROPERTY', 'ARRAY_PROPERTY'}:
+ layout.prop(node, "datatype", text="")
+ else:
+ layout.label(" ")
+ if column == 5:
+ if node.type in {'SCALAR_PROPERTY', 'ARRAY_PROPERTY'}:
+ layout.prop(node, "datatype_extent", text="")
+ else:
+ layout.label(" ")
+ if column == 6:
+ if node.type in {'ARRAY_PROPERTY'}:
+ layout.label(node.array_size if node.array_size >= 0 else "-")
+ else:
+ layout.label(" ")
+
+ if node.expand:
+ for child in node.child_nodes:
+ self.draw_node_info(context, layout, child, column)
+
+ def draw_info(self, context, layout, info, metadata=None):
+ row = layout.row(align=True)
+ row.label("Created by: {} | {}".format(info.app_name if info.app_name else "-", info.date_written if info.date_written else "-"))
+ if info.description:
+ layout.label(info.description)
+
+ if metadata:
+ row = layout.row(align=True)
+ col_key = row.column()
+ col_value = row.column()
+ for key, value in metadata.items():
+ col_key.label(key)
+ col_value.label(str(value))
+
+ if info.root_node:
+ row = layout.row()
+
+ col = row.column()
+ col.label(" ")
+ self.draw_node_structure(context, col, info.root_node, 0)
+
+ for i, column in enumerate(self.info_columns):
+ col = row.column()
+ col.label(column)
+ self.draw_node_info(context, col, info.root_node, i)
+
+
+class OBJECT_PT_cache_library(CacheArchiveInfoPanel, ObjectButtonsPanel, Panel):
+ bl_label = "Cache"
+
+ @classmethod
+ def poll(cls, context):
+ ob = context.object
+ return (ob and ob.dupli_type == 'GROUP' and ob.dupli_group)
+
+ def draw_cache_modifier(self, context, layout, cachelib, md):
+ layout.context_pointer_set("cache_library", cachelib)
+ layout.context_pointer_set("cache_modifier", md)
+
+ row = layout.row(align=True)
+ row.context_pointer_set("cache_modifier", md)
+ row.prop(md, "name", text="")
+ row.operator("cachelibrary.remove_modifier", icon='X', text="", emboss=False)
+
+ # match enum type to our functions, avoids a lookup table.
+ getattr(self, md.type)(context, layout, cachelib, md)
+
+ def draw_cachelib(self, context, layout, ob, cachelib, objects):
+ box = layout.box()
+ row = box.row()
+ row.label("Source:")
+ row.prop(cachelib, "source_mode", text="Source", expand=True)
+ row = box.row(align=True)
+ row.enabled = (cachelib.source_mode == 'CACHE')
+ row.prop(cachelib, "input_filepath", text="")
+ row.prop(cachelib, "show_metadata", text="", icon='QUESTION', toggle=True)
+ if cachelib.show_metadata and cachelib.archive_info:
+ self.draw_info(context, box, cachelib.archive_info, cachelib['input_metadata'])
+
+ layout.separator()
+
+ layout.prop(cachelib, "display_mode", expand=True)
+ row = layout.row()
+ split = row.split()
+ col = split.column()
+ col.label("Display:")
+ col.prop(cachelib, "display_motion", text="Motion")
+ col.prop(cachelib, "display_children", text="Children")
+
+ layout.separator()
+
+ row = layout.row(align=True)
+ row.enabled = (cachelib.display_mode == 'RESULT')
+ row.prop(cachelib, "output_filepath", text="")
+
+ box = layout.box()
+ row = box.row()
+
+ col = row.column()
+ row2 = col.row()
+ row2.alignment = 'LEFT'
+ row2.prop(cachelib, "data_types", icon_only=True, toggle=True)
+ row2.template_ID(cachelib, "filter_group")
+ col.prop(cachelib, "description")
+ col = row.column()
+ props = col.operator("cachelibrary.bake", text="Bake Preview", icon='RESTRICT_VIEW_OFF')
+ props.eval_mode = {'PREVIEW'}
+ if context.scene.use_preview_range:
+ props.start_frame = context.scene.frame_preview_start
+ props.end_frame = context.scene.frame_preview_end
+ else:
+ props.start_frame = context.scene.frame_start
+ props.end_frame = context.scene.frame_end
+ props = col.operator("cachelibrary.bake", text="Bake Render", icon='RESTRICT_RENDER_OFF')
+ props.eval_mode = {'RENDER'}
+ props.start_frame = context.scene.frame_start
+ props.end_frame = context.scene.frame_end
+
+ '''
+ row = layout.row(align=True)
+ row.label("Filter:")
+ row.prop(cachelib, "filter_string", icon='VIEWZOOM', text="")
+
+ first = True
+ for ob in objects:
+ if not any(cachelib_object_items(cachelib, ob)):
+ continue
+
+ if first:
+ layout.separator()
+ first = False
+
+ for datatype, index, enable in cachelib_object_items(cachelib, ob):
+ row = layout.row(align=True)
+ row.alignment = 'LEFT'
+ row.template_cache_library_item(cachelib, ob, datatype, index, enable)
+ '''
+
+ layout.operator_menu_enum("cachelibrary.add_modifier", "type")
+
+ for md in cachelib.modifiers:
+ box = layout.box()
+ self.draw_cache_modifier(context, box, cachelib, md)
+
+ def draw(self, context):
+ ob = context.object
+
+ layout = self.layout
+ row = layout.row(align=True)
+ row.template_ID(ob, "cache_library", new="cachelibrary.new")
+
+ if ob.cache_library:
+ cache_objects = cachelib_objects(ob.cache_library, ob.dupli_group)
+ self.draw_cachelib(context, layout, ob, ob.cache_library, cache_objects)
+
+ def HAIR_SIMULATION(self, context, layout, cachelib, md):
+ params = md.parameters
+
+ col = layout.column(align=True)
+ col.prop_search(md, "object", context.blend_data, "objects", icon='OBJECT_DATA')
+ sub = col.column()
+ if (md.object):
+ sub.prop_search(md, "hair_system", md.object, "particle_systems")
+ else:
+ sub.enabled = False
+ sub.prop(md, "hair_system")
+
+ layout = layout.column()
+ layout.active = md.hair_system is not None
+
+ row = layout.row(align=True)
+ row.menu("CACHELIBRARY_MT_hair_simulation_presets", text=bpy.types.CACHELIBRARY_MT_hair_simulation_presets.bl_label)
+ props = row.operator("cachelibrary.hair_simulation_preset_add", text="", icon='ZOOMIN')
+ props.modifier_name = md.name
+ props = row.operator("cachelibrary.hair_simulation_preset_add", text="", icon='ZOOMOUT')
+ props.modifier_name = md.name
+ props.remove_active = True
+
+ col = layout.column()
+ col.prop(params, "substeps")
+ col.prop(params, "timescale")
+ col.prop(params, "mass")
+ col.prop(params, "drag")
+
+ row = col.row(align=True)
+ row.prop(params, "stretch_stiffness")
+ row.prop(params, "stretch_damping")
+
+ row = col.row(align=True)
+ row.prop(params, "bend_stiffness")
+ row.prop(params, "bend_damping")
+ row.prop(params, "use_bend_stiffness_curve")
+ if params.use_bend_stiffness_curve:
+ sub = col.column()
+ sub.template_curve_mapping(params, "bend_stiffness_curve")
+
+ row = col.row(align=True)
+ row.prop(params, "goal_stiffness")
+ row.prop(params, "goal_damping")
+ row = col.row(align=True)
+ row.prop(params, "use_goal_stiffness_curve")
+ row.prop(params, "use_goal_deflect")
+ if params.use_goal_stiffness_curve:
+ sub = col.column()
+ sub.template_curve_mapping(params, "goal_stiffness_curve")
+
+ layout.separator()
+
+ effector_weights_ui(self, context, params.effector_weights, 'HAIR')
+
+ def FORCE_FIELD(self, context, layout, cachelib, md):
+ layout.prop_search(md, "object", context.blend_data, "objects", icon='OBJECT_DATA')
+
+ layout.prop(md, "force_type", text="")
+
+ row = layout.row(align=True)
+ row.prop(md, "strength")
+ row.prop(md, "falloff")
+
+ col = layout.column(align=True)
+ row = layout.row(align=True)
+ row.prop(md, "min_distance")
+ row.prop(md, "max_distance")
+ col.prop(md, "use_double_sided")
+
+ def HAIRCUT(self, context, layout, cachelib, md):
+ col = layout.column(align=True)
+ col.prop_search(md, "object", context.blend_data, "objects", icon='OBJECT_DATA')
+ sub = col.column()
+ if (md.object):
+ sub.prop_search(md, "hair_system", md.object, "particle_systems")
+ else:
+ sub.enabled = False
+ sub.prop(md, "hair_system")
+
+ row = layout.row()
+ row.prop_search(md, "target", context.blend_data, "objects", icon='OBJECT_DATA')
+ row.prop(md, "use_internal_target", text="Internal")
+ layout.prop(md, "cut_mode", toggle=True, expand=True)
+
+ layout = layout.column()
+ layout.active = md.hair_system is not None
+
+ def SHRINK_WRAP(self, context, layout, cachelib, md):
+ col = layout.column(align=True)
+ col.prop_search(md, "object", context.blend_data, "objects", icon='OBJECT_DATA')
+ sub = col.column()
+ if (md.object):
+ sub.prop_search(md, "hair_system", md.object, "particle_systems")
+ else:
+ sub.enabled = False
+ sub.prop(md, "hair_system")
+
+ row = layout.row()
+ row.prop_search(md, "target", context.blend_data, "objects", icon='OBJECT_DATA')
+ row.prop(md, "use_internal_target", text="Internal")
+
+ layout = layout.column()
+ layout.active = md.hair_system is not None
+
+ def STRANDS_KEY(self, context, layout, cachelib, md):
+ col = layout.column(align=True)
+ col.prop_search(md, "object", context.blend_data, "objects", icon='OBJECT_DATA')
+ sub = col.column()
+ if (md.object):
+ sub.prop_search(md, "hair_system", md.object, "particle_systems")
+ else:
+ sub.enabled = False
+ sub.prop(md, "hair_system")
+
+ key = md.shape_keys
+ kb = md.active_shape_key
+ kb_index = md.active_shape_key_index
+
+ row = layout.row()
+
+ rows = 2
+ if kb:
+ rows = 4
+ row.template_list("CACHELIB_UL_shape_keys", "", key, "key_blocks", md, "active_shape_key_index", rows=rows)
+
+ col = row.column()
+
+ sub = col.column(align=True)
+ #sub.operator("object.shape_key_add", icon='ZOOMIN', text="").from_mix = False
+ #sub.operator("object.shape_key_remove", icon='ZOOMOUT', text="").all = False
+ sub.menu("CACHELIB_MT_shape_key_specials", icon='DOWNARROW_HLT', text="")
+
+ col.prop(md, "use_motion_state")
+
+ if kb:
+ col.separator()
+
+ sub = col.column(align=True)
+ #sub.operator("object.shape_key_move", icon='TRIA_UP', text="").type = 'UP'
+ #sub.operator("object.shape_key_move", icon='TRIA_DOWN', text="").type = 'DOWN'
+
+ split = layout.split(percentage=0.4)
+ row = split.row()
+ row.prop(key, "use_relative")
+
+ row = split.row()
+ row.alignment = 'RIGHT'
+
+ sub = row.row(align=True)
+ sub.label() # XXX, for alignment only
+ subsub = sub.row(align=True)
+ subsub.prop(md, "show_only_shape_key", text="")
+
+ sub = row.row()
+ #if key.use_relative:
+ # sub.operator("object.shape_key_clear", icon='X', text="")
+ #else:
+ # sub.operator("object.shape_key_retime", icon='RECOVER_LAST', text="")
+
+ if key.use_relative:
+ if kb_index != 0:
+ row = layout.row()
+ row.prop(kb, "value")
+
+ split = layout.split()
+
+ col = split.column(align=True)
+ col.label(text="Range:")
+ col.prop(kb, "slider_min", text="Min")
+ col.prop(kb, "slider_max", text="Max")
+
+ col = split.column(align=True)
+ col.label(text="Blend:")
+ #col.prop_search(kb, "vertex_group", ob, "vertex_groups", text="")
+ col.prop_search(kb, "relative_key", key, "key_blocks", text="")
+
+ else:
+ layout.prop(kb, "interpolation")
+ row = layout.column()
+ row.prop(key, "eval_time")
+
+
+# Simple human-readable size (based on http://stackoverflow.com/a/1094933)
+def sizeof_fmt(num, suffix='B'):
+ for unit in ['','K','M','G','T','P','E','Z']:
+ if abs(num) < 1024.0:
+ return "%3.1f%s%s" % (num, unit, suffix)
+ num /= 1024.0
+ return "%.1f%s%s" % (num, 'Y', suffix)
+
+class OBJECT_PT_cache_archive_info(CacheArchiveInfoPanel, ObjectButtonsPanel, Panel):
+ bl_label = "Cache Archive Info"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ ob = context.object
+ return (ob and ob.dupli_type == 'GROUP' and ob.dupli_group and ob.cache_library)
+
+ def draw(self, context):
+ ob = context.object
+ cachelib = ob.cache_library
+ info = cachelib.archive_info
+
+ layout = self.layout
+
+ row = layout.row()
+ props = row.operator("cachelibrary.archive_info", text="Input", icon='QUESTION')
+ props.filepath = cachelib.input_filepath
+ props.use_cache_info = True
+ props = row.operator("cachelibrary.archive_info", text="Output", icon='QUESTION')
+ props.filepath = cachelib.output_filepath
+ props.use_cache_info = True
+
+ if info:
+ row = layout.row()
+ row.enabled = bool(info.filepath)
+ props = layout.operator("cachelibrary.archive_info", text="Calculate Size", icon='FILE_REFRESH')
+ props.filepath = info.filepath
+ props.use_cache_info = True
+ props.calc_bytes_size = True
+
+ layout.separator()
+
+ layout.prop(info, "filepath", text="File")
+ self.draw_info(context, layout, info)
+
+
class OBJECT_PT_relations_extras(ObjectButtonsPanel, Panel):
bl_label = "Relations Extras"
bl_options = {'DEFAULT_CLOSED'}
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index cca142b645c..d1f7c0f5e40 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -38,10 +38,10 @@ class UnifiedPaintPanel:
elif context.image_paint_object:
if (toolsettings.image_paint and toolsettings.image_paint.detect_data()):
return toolsettings.image_paint
-
- return None
elif context.particle_edit_object:
return toolsettings.particle_edit
+ elif context.hair_edit_object:
+ return toolsettings.hair_edit
return None
diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py
index e294f5487a6..c948658f433 100644
--- a/release/scripts/startup/bl_ui/properties_particle.py
+++ b/release/scripts/startup/bl_ui/properties_particle.py
@@ -18,7 +18,7 @@
# <pep8 compliant>
import bpy
-from bpy.types import Panel, Menu
+from bpy.types import Panel, UIList, Menu
from rna_prop_ui import PropertyPanel
from bpy.app.translations import pgettext_iface as iface_
@@ -53,7 +53,12 @@ def particle_panel_poll(cls, context):
if not settings:
return False
- return settings.is_fluid is False and (engine in cls.COMPAT_ENGINES)
+ if settings.is_fluid:
+ return False
+ if hasattr(cls, 'COMPAT_ENGINES') and not (engine in cls.COMPAT_ENGINES):
+ return False
+
+ return True
def particle_get_settings(context):
@@ -340,6 +345,9 @@ class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel, Panel):
row.operator("particle.hair_dynamics_preset_add", text="", icon='ZOOMIN')
row.operator("particle.hair_dynamics_preset_add", text="", icon='ZOOMOUT').remove_active = True
+ # XXX disabled due to stability issues and limited usefulness
+ #layout.prop(psys, "hair_preview_factor")
+
split = layout.column()
col = split.column()
@@ -372,6 +380,14 @@ class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel, Panel):
split.separator()
+ split.separator()
+
+ col = split.column()
+ col.label(text="Pinning")
+ col.prop(cloth, "pin_stiffness", text="Goal Strength")
+
+ split.separator()
+
col = split.column()
col.label(text="Quality:")
col.prop(cloth, "quality", text="Steps", slider=True)
@@ -399,6 +415,133 @@ class PARTICLE_PT_hair_dynamics(ParticleButtonsPanel, Panel):
box.label("Error: %.5f .. %.5f (avg. %.5f)" % (result.min_error, result.max_error, result.avg_error))
+class PARTICLE_UL_shape_keys(UIList):
+ def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+ # assert(isinstance(item, bpy.types.ShapeKey))
+ psys = active_data
+ ob = psys.id_data
+ # key = data
+ key_block = item
+ if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ split = layout.split(0.66, False)
+ split.prop(key_block, "name", text="", emboss=False, icon_value=icon)
+ row = split.row(align=True)
+ if key_block.mute or (ob.mode == 'PARTICLE_EDIT' and not ob.use_shape_key_edit_mode):
+ row.active = False
+ if not item.id_data.use_relative:
+ row.prop(key_block, "frame", text="", emboss=False)
+ elif index > 0:
+ row.prop(key_block, "value", text="", emboss=False)
+ else:
+ row.label(text="")
+ row.prop(key_block, "mute", text="", emboss=False)
+ elif self.layout_type in {'GRID'}:
+ layout.alignment = 'CENTER'
+ layout.label(text="", icon_value=icon)
+
+
+class PARTICLE_MT_shape_key_specials(Menu):
+ bl_label = "Shape Key Specials"
+
+ def draw(self, context):
+ layout = self.layout
+
+ #layout.operator("particle.shape_key_transfer", icon='COPY_ID') # icon is not ideal
+ #layout.operator("particle.join_shapes", icon='COPY_ID') # icon is not ideal
+ #layout.operator("particle.shape_key_mirror", icon='ARROW_LEFTRIGHT').use_topology = False
+ #layout.operator("particle.shape_key_mirror", text="Mirror Shape Key (Topology)", icon='ARROW_LEFTRIGHT').use_topology = True
+ layout.operator("particle.shape_key_add", icon='ZOOMIN', text="New Shape From Mix").from_mix = True
+ layout.operator("particle.shape_key_remove", icon='X', text="Delete All Shapes").all = True
+
+
+class PARTICLE_PT_shape_keys(ParticleButtonsPanel, Panel):
+ bl_label = "Shape Keys"
+
+ def draw(self, context):
+ layout = self.layout
+
+ ob = context.object
+ psys = context.particle_system
+ key = psys.shape_keys
+ kb = psys.active_shape_key
+
+ enable_edit = ob.mode != 'PARTICLE_EDIT'
+ enable_edit_value = False
+
+ if ob.show_only_shape_key is False:
+ if enable_edit or (ob.type == 'MESH' and ob.use_shape_key_edit_mode):
+ enable_edit_value = True
+
+ row = layout.row()
+
+ rows = 2
+ if kb:
+ rows = 4
+ row.template_list("PARTICLE_UL_shape_keys", "", key, "key_blocks", psys, "active_shape_key_index", rows=rows)
+
+ col = row.column()
+
+ sub = col.column(align=True)
+ sub.operator("particle.shape_key_add", icon='ZOOMIN', text="").from_mix = False
+ sub.operator("particle.shape_key_remove", icon='ZOOMOUT', text="").all = False
+ sub.menu("PARTICLE_MT_shape_key_specials", icon='DOWNARROW_HLT', text="")
+
+ if kb:
+ col.separator()
+
+ sub = col.column(align=True)
+ sub.operator("particle.shape_key_move", icon='TRIA_UP', text="").type = 'UP'
+ sub.operator("particle.shape_key_move", icon='TRIA_DOWN', text="").type = 'DOWN'
+
+ split = layout.split(percentage=0.4)
+ row = split.row()
+ row.enabled = enable_edit
+ row.prop(key, "use_relative")
+
+ row = split.row()
+ row.alignment = 'RIGHT'
+
+ sub = row.row(align=True)
+ sub.label() # XXX, for alignment only
+ subsub = sub.row(align=True)
+ subsub.active = enable_edit_value
+ subsub.prop(ob, "show_only_shape_key", text="")
+ sub.prop(ob, "use_shape_key_edit_mode", text="")
+
+ sub = row.row()
+ if key.use_relative:
+ sub.operator("particle.shape_key_clear", icon='X', text="")
+ else:
+ sub.operator("particle.shape_key_retime", icon='RECOVER_LAST', text="")
+
+ if key.use_relative:
+ if psys.active_shape_key_index != 0:
+ row = layout.row()
+ row.active = enable_edit_value
+ row.prop(kb, "value")
+
+ split = layout.split()
+
+ col = split.column(align=True)
+ col.active = enable_edit_value
+ col.label(text="Range:")
+ col.prop(kb, "slider_min", text="Min")
+ col.prop(kb, "slider_max", text="Max")
+
+ col = split.column(align=True)
+ col.active = enable_edit_value
+ col.label(text="Blend:")
+ #col.prop_search(kb, "vertex_group", psys, "vertex_groups", text="")
+ col.prop_search(kb, "relative_key", key, "key_blocks", text="")
+
+ else:
+ layout.prop(kb, "interpolation")
+ row = layout.column()
+ row.active = enable_edit_value
+ row.prop(key, "eval_time")
+ row.prop(key, "slurph")
+
+
class PARTICLE_PT_cache(ParticleButtonsPanel, Panel):
bl_label = "Cache"
bl_options = {'DEFAULT_CLOSED'}
@@ -1191,6 +1334,9 @@ class PARTICLE_PT_children(ParticleButtonsPanel, Panel):
subsub = sub.column()
subsub.enabled = part.use_clump_noise
subsub.prop(part, "clump_noise_size")
+ subsubsub = subsub.column(align=True)
+ subsubsub.prop(part, "clump_noise_random_size")
+ subsubsub.prop(part, "clump_noise_random")
sub = col.column(align=True)
sub.prop(part, "child_length", slider=True)
diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py
index 82eecf0fb5a..61091f3ec21 100644
--- a/release/scripts/startup/bl_ui/properties_physics_common.py
+++ b/release/scripts/startup/bl_ui/properties_physics_common.py
@@ -315,5 +315,8 @@ def basic_force_field_falloff_ui(self, context, field):
sub.active = field.use_max_distance
sub.prop(field, "distance_max", text="Maximum")
+ if field.shape == 'SURFACE':
+ layout.prop(field, "use_signed_distance")
+
if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
diff --git a/release/scripts/startup/bl_ui/properties_physics_smoke.py b/release/scripts/startup/bl_ui/properties_physics_smoke.py
index 85d3c1d7dc4..1fa5ec8c6c3 100644
--- a/release/scripts/startup/bl_ui/properties_physics_smoke.py
+++ b/release/scripts/startup/bl_ui/properties_physics_smoke.py
@@ -18,7 +18,7 @@
# <pep8 compliant>
import bpy
-from bpy.types import Panel
+from bpy.types import Panel, UIList
from bl_ui.properties_physics_common import (
point_cache_ui,
@@ -82,6 +82,8 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
layout.prop(flow, "smoke_flow_type", expand=False)
if flow.smoke_flow_type != 'OUTFLOW':
+ use_const_color = False
+
split = layout.split()
col = split.column()
col.label(text="Flow Source:")
@@ -90,6 +92,10 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
col.label(text="Particle System:")
col.prop_search(flow, "particle_system", ob, "particle_systems", text="")
col.prop(flow, "use_particle_size", text="Set Size")
+ col.prop(flow, "use_particle_texture_color", text="Set Color")
+ # disable const color button when using particle color
+ use_const_color = not flow.use_particle_texture_color
+
sub = col.column()
sub.active = flow.use_particle_size
sub.prop(flow, "particle_size")
@@ -113,7 +119,9 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
if flow.smoke_flow_type in {'SMOKE', 'BOTH'}:
sub.prop(flow, "density")
sub.prop(flow, "temperature")
- sub.prop(flow, "smoke_color")
+ subsub = sub.column()
+ subsub.active = use_const_color
+ subsub.prop(flow, "smoke_color")
if flow.smoke_flow_type in {'FIRE', 'BOTH'}:
sub.prop(flow, "fuel_amount")
sub.label(text="Sampling:")
@@ -128,6 +136,25 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel):
col.prop(coll, "collision_type")
+class PHYSICS_PT_smoke_display(PhysicButtonsPanel, Panel):
+ bl_label = "Smoke Display"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ md = context.smoke
+ return md and (md.smoke_type == 'DOMAIN')
+
+ def draw(self, context):
+ layout = self.layout
+ domain = context.smoke.domain_settings
+
+ split = layout.split()
+
+ col = split.column(align=True)
+ col.prop(domain, "display_thickness")
+
+
class PHYSICS_PT_smoke_flow_advanced(PhysicButtonsPanel, Panel):
bl_label = "Smoke Flow Advanced"
bl_options = {'DEFAULT_CLOSED'}
@@ -309,10 +336,71 @@ class PHYSICS_PT_smoke_cache(PhysicButtonsPanel, Panel):
layout.label(text="Compression:")
layout.prop(md, "point_cache_compress_type", expand=True)
+ layout.prop(md, "point_cache_offset", text="Start Frame")
point_cache_ui(self, context, cache, (cache.is_baked is False), 'SMOKE')
+class DATA_UL_openvdb_caches(UIList):
+ def draw_items(self, context, layout, item, icon, active_data, active_propname, index):
+ if self.layout_type in {'DEFAULT', 'COMPACT'}:
+ layout.prop(item, "name", text="", emboss=False, icon_value=icon)
+ elif self.layout_type in {'GRID'}:
+ layout.alignement = 'CENTER'
+ layout.label(text="", icon_value=icon)
+
+
+class PHYSICS_PT_smoke_openvdb(PhysicButtonsPanel, Panel):
+ bl_label = "OpenVDB export"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ md = context.smoke
+ rd = context.scene.render
+ return md and (md.smoke_type == 'DOMAIN') and (not rd.use_game_engine)
+
+ def draw_header(self, context):
+ layout = self.layout
+ domain = context.smoke.domain_settings
+ layout.prop(domain, "use_openvdb", text="")
+
+ def draw(self, context):
+ layout = self.layout
+
+ if not bpy.app.build_options.openvdb:
+ layout.label("Build without OpenVDB support.")
+ return
+
+ domain = context.smoke.domain_settings
+ layout.active = domain.use_openvdb
+
+ row = layout.row()
+ row.template_list("DATA_UL_openvdb_caches", "", domain, "cache", domain, "active_openvdb_cache_index", rows=3)
+
+ col = row.column()
+ sub = col.row()
+ subsub = sub.column(align=True)
+ subsub.operator("object.openvdb_cache_add", icon='ZOOMIN', text="")
+ subsub.operator("object.openvdb_cache_remove", icon='ZOOMOUT', text="")
+ sub = col.row()
+ subsub = sub.column(align=True)
+ subsub.operator("object.openvdb_cache_move", icon='MOVE_UP_VEC', text="").direction = 'UP'
+ subsub.operator("object.openvdb_cache_move", icon='MOVE_DOWN_VEC', text="").direction = 'DOWN'
+
+ cache = domain.active_openvdb_cache
+
+ if cache:
+ layout.prop(cache, "filepath")
+ layout.prop(cache, "compression")
+ row = layout.row(align=True)
+ row.prop(cache, "frame_start")
+ row.prop(cache, "frame_end")
+
+ layout.operator("object.smoke_vdb_export")
+ layout.operator("object.smoke_vdb_transform_update")
+
+
class PHYSICS_PT_smoke_field_weights(PhysicButtonsPanel, Panel):
bl_label = "Smoke Field Weights"
bl_options = {'DEFAULT_CLOSED'}
diff --git a/release/scripts/startup/bl_ui/properties_texture.py b/release/scripts/startup/bl_ui/properties_texture.py
index 78ea4fa6c3f..5a4937d2509 100644
--- a/release/scripts/startup/bl_ui/properties_texture.py
+++ b/release/scripts/startup/bl_ui/properties_texture.py
@@ -1147,6 +1147,7 @@ class TEXTURE_PT_influence(TextureSlotPanel, Panel):
factor_but(col, "use_map_life", "life_factor", "Lifetime")
factor_but(col, "use_map_density", "density_factor", "Density")
factor_but(col, "use_map_size", "size_factor", "Size")
+ factor_but(col, "use_map_particle_color", "particle_color_factor", "Color")
col = split.column()
col.label(text="Physics:")
@@ -1168,6 +1169,9 @@ class TEXTURE_PT_influence(TextureSlotPanel, Panel):
factor_but(col, "use_map_kink_freq", "kink_freq_factor", "Kink Frequency")
factor_but(col, "use_map_rough", "rough_factor", "Rough")
+ sub = factor_but(layout, "use_map_shapekey", "shapekey_factor", "Shape Key")
+ sub.prop(tex, "shapekey", text="")
+
elif isinstance(idblock, FreestyleLineStyle):
split = layout.split()
@@ -1178,7 +1182,8 @@ class TEXTURE_PT_influence(TextureSlotPanel, Panel):
layout.separator()
- if not isinstance(idblock, ParticleSettings):
+ show_blend_settings = (not isinstance(idblock, ParticleSettings)) or tex.use_map_particle_color
+ if show_blend_settings:
split = layout.split()
col = split.column()
diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py
index 0b7502b585b..7fd9719a6e3 100644
--- a/release/scripts/startup/bl_ui/space_dopesheet.py
+++ b/release/scripts/startup/bl_ui/space_dopesheet.py
@@ -339,6 +339,7 @@ class DOPESHEET_MT_key(Menu):
layout.separator()
layout.operator("action.clean")
+ layout.operator("action.clean", text="Clean Channels").channels = True
layout.operator("action.sample")
layout.separator()
@@ -421,6 +422,8 @@ class DOPESHEET_MT_delete(Menu):
layout.separator()
layout.operator("action.clean")
+ layout.operator("action.clean", text="Clean Channels").channels = True
+
if __name__ == "__main__": # only for live edit.
bpy.utils.register_module(__name__)
diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py
index 0d900a41f25..12ea0977b58 100644
--- a/release/scripts/startup/bl_ui/space_filebrowser.py
+++ b/release/scripts/startup/bl_ui/space_filebrowser.py
@@ -60,6 +60,10 @@ class FILEBROWSER_HT_header(Header):
layout.prop(params, "sort_method", expand=True, text="")
layout.prop(params, "show_hidden", text="", icon='FILE_HIDDEN')
+ if params.collapse_seq:
+ layout.prop(params, "collapse_seq", text="", icon='FULLSCREEN_EXIT')
+ else:
+ layout.prop(params, "collapse_seq", text="", icon='FULLSCREEN_ENTER')
layout.prop(params, "use_filter", text="", icon='FILTER')
row = layout.row(align=True)
diff --git a/release/scripts/startup/bl_ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py
index 2f5381e58c8..104fd14797e 100644
--- a/release/scripts/startup/bl_ui/space_graph.py
+++ b/release/scripts/startup/bl_ui/space_graph.py
@@ -258,6 +258,7 @@ class GRAPH_MT_key(Menu):
layout.separator()
layout.operator("graph.clean")
+ layout.operator("graph.clean", text="Clean Channels").channels = True
layout.operator("graph.smooth")
layout.operator("graph.sample")
layout.operator("graph.bake")
@@ -293,6 +294,7 @@ class GRAPH_MT_delete(Menu):
layout.separator()
layout.operator("graph.clean")
+ layout.operator("graph.clean", text="Clean Channels").channels = True
if __name__ == "__main__": # only for live edit.
diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py
index d0d1376fe05..d50e1253970 100644
--- a/release/scripts/startup/bl_ui/space_node.py
+++ b/release/scripts/startup/bl_ui/space_node.py
@@ -387,8 +387,7 @@ class NODE_PT_backdrop(Panel):
col = layout.column(align=True)
col.label(text="Offset:")
- col.prop(snode, "backdrop_x", text="X")
- col.prop(snode, "backdrop_y", text="Y")
+ col.prop(snode, "backdrop_offset", text="")
col.operator("node.backimage_move", text="Move")
layout.operator("node.backimage_fit", text="Fit")
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index 43224412009..17086fe73ff 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -84,7 +84,7 @@ class SEQUENCER_HT_header(Header):
layout.separator()
layout.operator("sequencer.refresh_all")
- layout.prop(st, "show_backdrop")
+ layout.prop(st, "show_overdrop")
else:
if st.view_type == 'SEQUENCER_PREVIEW':
layout.separator()
@@ -201,16 +201,17 @@ class SEQUENCER_MT_view(Menu):
layout.prop(st, "show_seconds")
layout.prop(st, "show_frame_indicator")
layout.prop(st, "show_strip_offset")
+ layout.prop(st, "show_info")
layout.prop_menu_enum(st, "waveform_draw_type")
if is_preview:
- if st.display_mode == 'IMAGE':
- layout.prop(st, "show_safe_areas")
- layout.prop(st, "show_metadata")
- elif st.display_mode == 'WAVEFORM':
+ if st.display_mode == 'WAVEFORM':
layout.prop(st, "show_separate_color")
+ layout.prop(st, "show_metadata")
+
+
layout.separator()
if is_sequencer_view:
@@ -308,6 +309,7 @@ class SEQUENCER_MT_add(Menu):
layout.operator_menu_enum("sequencer.mask_strip_add", "mask", text="Mask...")
layout.operator("sequencer.movie_strip_add", text="Movie")
+ layout.operator("sequencer.image_sequence_add", text="Image Sequence")
layout.operator("sequencer.image_strip_add", text="Image")
layout.operator("sequencer.sound_strip_add", text="Sound")
@@ -449,7 +451,7 @@ class SequencerButtonsPanel_Output:
@staticmethod
def has_preview(context):
st = context.space_data
- return (st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}) or st.show_backdrop
+ return (st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}) or st.show_overdrop
@classmethod
def poll(cls, context):
@@ -810,11 +812,13 @@ class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel):
layout.template_ID(strip, "scene")
scene = strip.scene
+ layout.prop(strip, "use_sequence")
- layout.label(text="Camera Override")
- layout.template_ID(strip, "scene_camera")
+ if not strip.use_sequence:
+ layout.label(text="Camera Override")
+ layout.template_ID(strip, "scene_camera")
- layout.prop(strip, "use_grease_pencil", text="Show Grease Pencil")
+ layout.prop(strip, "use_grease_pencil", text="Show Grease Pencil")
if scene:
layout.prop(scene, "audio_volume", text="Audio Volume")
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 58bb383e84c..d77ebf48d7e 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -222,7 +222,12 @@ class USERPREF_PT_interface(Panel):
sub.prop(view, "manipulator_size", text="Size")
sub.prop(view, "manipulator_handle_size", text="Handle Size")
sub.prop(view, "manipulator_hotspot", text="Hotspot")
+ col.separator()
+ sub = col.column()
+ sub.prop(view, "shaded_widgets")
+ sub.prop(view, "widgets_3d")
+
col.separator()
col.separator()
col.separator()
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 22869c0273a..4a55212ffca 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -50,6 +50,8 @@ class VIEW3D_HT_header(Header):
# Particle edit
if mode == 'PARTICLE_EDIT':
row.prop(toolsettings.particle_edit, "select_mode", text="", expand=True)
+ elif mode == 'HAIR_EDIT':
+ row.prop(toolsettings.hair_edit, "select_mode", text="", expand=True)
# Occlude geometry
if ((view.viewport_shade not in {'BOUNDBOX', 'WIREFRAME'} and (mode == 'PARTICLE_EDIT' or (mode == 'EDIT' and obj.type == 'MESH'))) or
@@ -141,7 +143,7 @@ class VIEW3D_MT_editor_menus(Menu):
layout.menu("VIEW3D_MT_select_paint_mask")
elif mesh.use_paint_mask_vertex and mode_string == 'PAINT_WEIGHT':
layout.menu("VIEW3D_MT_select_paint_mask_vertex")
- elif mode_string != 'SCULPT':
+ elif mode_string not in {'SCULPT'}:
layout.menu("VIEW3D_MT_select_%s" % mode_string.lower())
if mode_string == 'OBJECT':
@@ -162,7 +164,7 @@ class VIEW3D_MT_editor_menus(Menu):
elif obj:
if mode_string != 'PAINT_TEXTURE':
layout.menu("VIEW3D_MT_%s" % mode_string.lower())
- if mode_string in {'SCULPT', 'PAINT_VERTEX', 'PAINT_WEIGHT', 'PAINT_TEXTURE'}:
+ if mode_string in {'SCULPT', 'PAINT_VERTEX', 'PAINT_WEIGHT', 'PAINT_TEXTURE', 'HAIR'}:
layout.menu("VIEW3D_MT_brush")
if mode_string == 'SCULPT':
layout.menu("VIEW3D_MT_hide_mask")
@@ -919,6 +921,13 @@ class VIEW3D_MT_select_paint_mask_vertex(Menu):
layout.operator("paint.vert_select_ungrouped", text="Ungrouped Verts")
+class VIEW3D_MT_select_hair(Menu):
+ bl_label = "Select"
+
+ def draw(self, context):
+ layout = self.layout
+
+
class VIEW3D_MT_angle_control(Menu):
bl_label = "Angle Control"
@@ -1145,6 +1154,7 @@ class VIEW3D_MT_object(Menu):
layout.menu("VIEW3D_MT_object_parent")
layout.menu("VIEW3D_MT_object_track")
layout.menu("VIEW3D_MT_object_group")
+ layout.menu("VIEW3D_MT_object_cachelib")
layout.menu("VIEW3D_MT_object_constraints")
layout.separator()
@@ -1411,6 +1421,29 @@ class VIEW3D_MT_object_group(Menu):
layout.operator("group.objects_remove_active")
+class VIEW3D_MT_object_cachelib(Menu):
+ bl_label = "Cache Library"
+
+ @classmethod
+ def poll(cls, context):
+ ob = context.active_object
+ if not (ob and ob.cache_library):
+ return False
+ return True
+
+ def draw(self, context):
+ layout = self.layout
+ scene = context.scene
+ ob = context.active_object
+ cachelib = ob.cache_library
+
+ layout.operator("cachelibrary.bake", text="Bake")
+ props = layout.operator("cachelibrary.archive_slice", text="Slice")
+ props.input_filepath = cachelib.input_filepath
+ props.start_frame = scene.frame_start
+ props.end_frame = scene.frame_end
+
+
class VIEW3D_MT_object_constraints(Menu):
bl_label = "Constraints"
@@ -1529,7 +1562,7 @@ class VIEW3D_MT_brush(Menu):
layout.separator()
# brush paint modes
- layout.menu("VIEW3D_MT_brush_paint_modes")
+ layout.menu("VIEW3D_MT_brush_object_modes")
# brush tool
if context.sculpt_object:
@@ -1539,6 +1572,8 @@ class VIEW3D_MT_brush(Menu):
layout.prop_menu_enum(brush, "image_tool")
elif context.vertex_paint_object or context.weight_paint_object:
layout.prop_menu_enum(brush, "vertex_tool")
+ elif context.hair_edit_object:
+ layout.prop_menu_enum(brush, "hair_tool")
# skip if no active brush
if not brush:
@@ -1566,7 +1601,7 @@ class VIEW3D_MT_brush(Menu):
layout.operator("sculpt.set_persistent_base")
-class VIEW3D_MT_brush_paint_modes(Menu):
+class VIEW3D_MT_brush_object_modes(Menu):
bl_label = "Enabled Modes"
def draw(self, context):
@@ -1579,6 +1614,7 @@ class VIEW3D_MT_brush_paint_modes(Menu):
layout.prop(brush, "use_paint_vertex", text="Vertex Paint")
layout.prop(brush, "use_paint_weight", text="Weight Paint")
layout.prop(brush, "use_paint_image", text="Texture Paint")
+ layout.prop(brush, "use_hair_edit", text="Hair Edit")
# ********** Vertex paint menu **********
@@ -1806,6 +1842,9 @@ class VIEW3D_MT_particle_specials(Menu):
layout.separator()
layout.operator("particle.mirror")
+ layout.separator()
+
+ layout.operator("particle.shape_propagate_to_all")
if particle_edit.select_mode == 'POINT':
layout.separator()
@@ -1831,6 +1870,14 @@ class VIEW3D_MT_particle_specials(Menu):
class VIEW3D_MT_particle_showhide(ShowHideMenu, Menu):
_operator_name = "particle"
+# ********** Hair menu **********
+
+class VIEW3D_MT_hair(Menu):
+ bl_label = "Hair"
+
+ def draw(self, context):
+ layout = self.layout
+
# ********** Pose Menu **********
@@ -2877,7 +2924,8 @@ class VIEW3D_PT_view3d_cursor(Panel):
layout = self.layout
view = context.space_data
- layout.column().prop(view, "cursor_location", text="Location")
+ col = layout.column()
+ col.prop(view, "cursor_location", text="Location")
class VIEW3D_PT_view3d_name(Panel):
@@ -2892,6 +2940,7 @@ class VIEW3D_PT_view3d_name(Panel):
def draw(self, context):
layout = self.layout
+ view = context.space_data
ob = context.active_object
row = layout.row()
row.label(text="", icon='OBJECT_DATA')
@@ -2904,6 +2953,10 @@ class VIEW3D_PT_view3d_name(Panel):
row.label(text="", icon='BONE_DATA')
row.prop(bone, "name", text="")
+ row = layout.row(align=True)
+ row.prop(ob, "color", text="")
+ row.prop(ob, "show_wire_color", text="", toggle=True, icon='WIRE')
+
class VIEW3D_PT_view3d_display(Panel):
bl_space_type = 'VIEW_3D'
@@ -2925,6 +2978,7 @@ class VIEW3D_PT_view3d_display(Panel):
col = layout.column()
col.prop(view, "show_only_render")
col.prop(view, "show_world")
+ col.prop(view, "show_motionpaths")
col = layout.column()
display_all = not view.show_only_render
@@ -3025,6 +3079,9 @@ class VIEW3D_PT_view3d_shading(Panel):
if not scene.render.use_shading_nodes:
col.prop(gs, "material_mode", text="")
+ if view.viewport_shade in {'BOUNDBOX', 'WIREFRAME', 'SOLID'}:
+ col.prop(view, "use_wire_color")
+
if view.viewport_shade == 'SOLID':
col.prop(view, "show_textured_solid")
col.prop(view, "use_matcap")
@@ -3462,3 +3519,4 @@ def unregister():
if __name__ == "__main__":
register()
+
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index e381800ae02..81041b5d821 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -888,7 +888,11 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel):
col.prop(brush, "count")
col = layout.column()
col.prop(settings, "use_default_interpolate")
- col.prop(brush, "steps", slider=True)
+ row = col.row()
+ row.prop(brush, "use_add_stroke", text="Stroke")
+ sub = row.row()
+ sub.active = brush.use_add_stroke
+ sub.prop(brush, "steps", slider=True)
col.prop(settings, "default_key_count", slider=True)
elif tool == 'LENGTH':
layout.prop(brush, "length_mode", expand=True)
@@ -896,6 +900,21 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel):
layout.prop(brush, "puff_mode", expand=True)
layout.prop(brush, "use_puff_volume")
+ # Hair Mode #
+
+ elif context.hair_edit_object and brush:
+ col = layout.column()
+
+ row = col.row(align=True)
+ self.prop_unified_size(row, context, brush, "size", slider=True, text="Radius")
+ self.prop_unified_size(row, context, brush, "use_pressure_size")
+
+ row = col.row(align=True)
+ self.prop_unified_strength(row, context, brush, "strength", text="Strength")
+ self.prop_unified_strength(row, context, brush, "use_pressure_strength")
+
+ col.prop(brush, "hair_tool", text="Tool")
+
# Sculpt Mode #
elif context.sculpt_object and brush:
@@ -1856,6 +1875,23 @@ class VIEW3D_PT_tools_particlemode(View3DPanel, Panel):
sub.prop(pe, "fade_frames", slider=True)
+class VIEW3D_PT_tools_hairmode(View3DPanel, Panel):
+ """Tools for hair mode"""
+ bl_context = "hairmode"
+ bl_label = "Options"
+ bl_category = "Tools"
+
+ def draw(self, context):
+ layout = self.layout
+
+ settings = context.tool_settings.hair_edit
+ ob = context.active_object
+
+ if ob.data:
+ col = layout.column(align=True)
+ col.prop(ob.data, "use_mirror_x")
+
+
# Grease Pencil drawing tools
class VIEW3D_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel):
bl_space_type = 'VIEW_3D'
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index bee48de42f5..1994530fdd2 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -193,6 +193,7 @@ shader_node_categories = [
NodeItem("ShaderNodeParticleInfo"),
NodeItem("ShaderNodeCameraData"),
NodeItem("ShaderNodeUVMap"),
+ NodeItem("ShaderNodeOpenVDB"),
NodeItem("ShaderNodeUVAlongStroke", poll=line_style_shader_nodes_poll),
NodeItem("NodeGroupInput", poll=group_input_output_item_poll),
]),
@@ -236,6 +237,7 @@ shader_node_categories = [
NodeItem("ShaderNodeTexMagic"),
NodeItem("ShaderNodeTexChecker"),
NodeItem("ShaderNodeTexBrick"),
+ NodeItem("ShaderNodeTexPointDensity"),
]),
ShaderNewNodeCategory("SH_NEW_OP_COLOR", "Color", items=[
NodeItem("ShaderNodeMixRGB"),
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt
index 9a73921bbc7..0a197cd3e26 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_cache_library_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
@@ -79,6 +80,7 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_sound_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_space_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_speaker_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_strands_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_text_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_texture_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_userdef_types.h
@@ -87,6 +89,7 @@ set(SRC_DNA_INC
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view2d_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view3d_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_windowmanager_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_widget_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_world_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_movieclip_types.h
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_tracking_types.h
@@ -109,6 +112,7 @@ add_subdirectory(gpu)
add_subdirectory(imbuf)
add_subdirectory(nodes)
add_subdirectory(modifiers)
+add_subdirectory(pointcache)
add_subdirectory(makesdna)
add_subdirectory(makesrna)
@@ -151,4 +155,3 @@ endif()
if(WITH_FREESTYLE)
add_subdirectory(freestyle)
endif()
-
diff --git a/source/blender/SConscript b/source/blender/SConscript
index e987b6be779..9bdba768448 100644
--- a/source/blender/SConscript
+++ b/source/blender/SConscript
@@ -44,7 +44,8 @@ SConscript(['avi/SConscript',
'ikplugin/SConscript',
'physics/SConscript',
'windowmanager/SConscript',
- 'blenfont/SConscript'])
+ 'blenfont/SConscript',
+ 'pointcache/SConscript'])
makesrna = SConscript('makesrna/SConscript')
diff --git a/source/blender/blenfont/BLF_translation.h b/source/blender/blenfont/BLF_translation.h
index c9b0c253ed4..badfe1f4a4c 100644
--- a/source/blender/blenfont/BLF_translation.h
+++ b/source/blender/blenfont/BLF_translation.h
@@ -148,6 +148,7 @@ const char *BLF_translate_do_new_dataname(const char *msgctxt, const char *msgid
#define BLF_I18NCONTEXT_ID_ACTION "Action"
#define BLF_I18NCONTEXT_ID_ARMATURE "Armature"
#define BLF_I18NCONTEXT_ID_BRUSH "Brush"
+#define BLF_I18NCONTEXT_ID_CACHELIBRARY "CacheLibrary"
#define BLF_I18NCONTEXT_ID_CAMERA "Camera"
#define BLF_I18NCONTEXT_ID_CURVE "Curve"
#define BLF_I18NCONTEXT_ID_FREESTYLELINESTYLE "FreestyleLineStyle"
diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h
index 789d86b7816..65e7d47c46c 100644
--- a/source/blender/blenkernel/BKE_DerivedMesh.h
+++ b/source/blender/blenkernel/BKE_DerivedMesh.h
@@ -182,7 +182,8 @@ struct DerivedMesh {
DerivedMeshType type;
float auto_bump_scale;
DMDirtyFlag dirty;
- int totmat; /* total materials. Will be valid only before object drawing. */
+ short totfmaps;
+ short totmat; /* total materials. Will be valid only before object drawing. */
struct Material **mat; /* material array. Will be valid only before object drawing */
/* use for converting to BMesh which doesn't store bevel weight and edge crease by default */
@@ -464,6 +465,10 @@ struct DerivedMesh {
void (*setMaterial)(void *userData, int matnr, void *attribs),
bool (*setFace)(void *userData, int index), void *userData);
+ struct GPUDrawObject *(*gpuObjectNew)(DerivedMesh *dm);
+ void (*copy_gpu_data)(DerivedMesh *dm, int type, float *varray, int *index,
+ int *mat_orig_to_new, void *user_data);
+
/** Release reference to the DerivedMesh. This function decides internally
* if the DerivedMesh will be freed, or cached for later use. */
void (*release)(DerivedMesh *dm);
diff --git a/source/blender/blenkernel/BKE_anim.h b/source/blender/blenkernel/BKE_anim.h
index 584f0da323a..bfba221e488 100644
--- a/source/blender/blenkernel/BKE_anim.h
+++ b/source/blender/blenkernel/BKE_anim.h
@@ -41,6 +41,15 @@ struct bAnimVizSettings;
struct bMotionPath;
struct bPoseChannel;
struct ReportList;
+struct GHash;
+struct DupliCache;
+struct DupliObject;
+struct DupliObjectData;
+struct DerivedMesh;
+struct Strands;
+struct StrandsChildren;
+struct DupliCacheIterator;
+struct CacheLibrary;
/* ---------------------------------------------------- */
/* Animation Visualization */
@@ -65,11 +74,43 @@ int where_on_path(struct Object *ob, float ctime, float vec[4], float dir[3], fl
/* ---------------------------------------------------- */
/* Dupli-Geometry */
-struct ListBase *object_duplilist_ex(struct EvaluationContext *eval_ctx, struct Scene *sce, struct Object *ob, bool update);
-struct ListBase *object_duplilist(struct EvaluationContext *eval_ctx, struct Scene *sce, struct Object *ob);
+struct ListBase *object_duplilist_ex(struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob, bool update);
+struct ListBase *object_duplilist(struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob);
+struct ListBase *group_duplilist_ex(struct EvaluationContext *eval_ctx, struct Scene *scene, struct Group *group, bool update);
+struct ListBase *group_duplilist(struct EvaluationContext *eval_ctx, struct Scene *scene, struct Group *group);
void free_object_duplilist(struct ListBase *lb);
int count_duplilist(struct Object *ob);
+void BKE_object_dupli_cache_update(struct Scene *scene, struct Object *ob, struct EvaluationContext *eval_ctx, float frame);
+void BKE_object_dupli_cache_clear(struct Object *ob);
+void BKE_object_dupli_cache_free(struct Object *ob);
+bool BKE_object_dupli_cache_contains(struct Object *ob, struct Object *other);
+struct DupliObjectData *BKE_dupli_cache_find_data(struct DupliCache *dupcache, struct Object *ob);
+
+void BKE_dupli_object_data_init(struct DupliObjectData *data, struct Object *ob);
+/* does not free data itself */
+void BKE_dupli_object_data_clear(struct DupliObjectData *data);
+void BKE_dupli_object_data_set_mesh(struct DupliObjectData *data, struct DerivedMesh *dm);
+void BKE_dupli_object_data_add_strands(struct DupliObjectData *data, const char *name, struct Strands *strands);
+void BKE_dupli_object_data_add_strands_children(struct DupliObjectData *data, const char *name, struct StrandsChildren *children);
+void BKE_dupli_object_data_find_strands(struct DupliObjectData *data, const char *name, struct Strands **r_strands, struct StrandsChildren **r_children);
+bool BKE_dupli_object_data_acquire_strands(struct DupliObjectData *data, struct Strands *strands);
+bool BKE_dupli_object_data_acquire_strands_children(struct DupliObjectData *data, struct StrandsChildren *children);
+
+struct DupliCache *BKE_dupli_cache_new(void);
+void BKE_dupli_cache_free(struct DupliCache *dupcache);
+void BKE_dupli_cache_clear(struct DupliCache *dupcache);
+void BKE_dupli_cache_clear_instances(struct DupliCache *dupcache);
+struct DupliObjectData *BKE_dupli_cache_add_object(struct DupliCache *dupcache, struct Object *ob);
+struct DupliObject *BKE_dupli_cache_add_instance(struct DupliCache *dupcache, float obmat[4][4], struct DupliObjectData *data);
+void BKE_dupli_cache_from_group(struct Scene *scene, struct Group *group, struct CacheLibrary *cachelib, struct DupliCache *dupcache, struct EvaluationContext *eval_ctx, bool calc_strands_base);
+
+struct DupliCacheIterator *BKE_dupli_cache_iter_new(struct DupliCache *dupcache);
+void BKE_dupli_cache_iter_free(struct DupliCacheIterator *iter);
+bool BKE_dupli_cache_iter_valid(struct DupliCacheIterator *iter);
+void BKE_dupli_cache_iter_next(struct DupliCacheIterator *iter);
+struct DupliObjectData *BKE_dupli_cache_iter_get(struct DupliCacheIterator *iter);
+
typedef struct DupliExtraData {
float obmat[4][4];
unsigned int lay;
diff --git a/source/blender/blenkernel/BKE_cache_library.h b/source/blender/blenkernel/BKE_cache_library.h
new file mode 100644
index 00000000000..62ec261f123
--- /dev/null
+++ b/source/blender/blenkernel/BKE_cache_library.h
@@ -0,0 +1,254 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_CACHE_LIBRARY_H__
+#define __BKE_CACHE_LIBRARY_H__
+
+/** \file BKE_cache_library.h
+ * \ingroup bke
+ */
+
+#include "DNA_cache_library_types.h"
+
+struct ListBase;
+struct Main;
+struct bContext;
+struct DerivedMesh;
+struct Group;
+struct Object;
+struct Scene;
+struct EvaluationContext;
+struct ParticleSystem;
+struct DupliCache;
+struct DupliObjectData;
+struct CacheModifier;
+struct ID;
+struct CacheProcessData;
+struct BVHTreeFromMesh;
+struct Strands;
+struct StrandsChildren;
+struct StrandsKeyCacheModifier;
+struct Key;
+struct KeyBlock;
+
+struct ClothModifierData;
+
+struct CacheLibrary *BKE_cache_library_add(struct Main *bmain, const char *name);
+struct CacheLibrary *BKE_cache_library_copy(struct CacheLibrary *cachelib);
+void BKE_cache_library_free(struct CacheLibrary *cachelib);
+void BKE_cache_library_unlink(struct CacheLibrary *cachelib);
+
+const char *BKE_cache_item_name_prefix(int type);
+void BKE_cache_item_name(struct Object *ob, int type, int index, char *name);
+int BKE_cache_item_name_length(struct Object *ob, int type, int index);
+eCacheReadSampleResult BKE_cache_read_result(int ptc_result);
+
+bool BKE_cache_library_validate_item(struct CacheLibrary *cachelib, struct Object *ob, int type, int index);
+
+struct IDProperty *BKE_cache_library_get_input_metadata(struct CacheLibrary *cachelib, bool create);
+struct IDProperty *BKE_cache_library_get_output_metadata(struct CacheLibrary *cachelib, bool create);
+
+/* ========================================================================= */
+
+void BKE_cache_library_get_read_flags(struct CacheLibrary *cachelib, bool use_render, bool for_display, bool *read_strands_motion, bool *read_strands_children);
+
+bool BKE_cache_archive_path_test(struct CacheLibrary *cachelib, const char *path);
+void BKE_cache_archive_path_ex(const char *path, struct Library *lib, const char *default_filename, char *result, int max);
+void BKE_cache_archive_input_path(struct CacheLibrary *cachelib, char *result, int max);
+void BKE_cache_archive_output_path(struct CacheLibrary *cachelib, char *result, int max);
+
+void BKE_cache_library_dag_recalc_tag(struct EvaluationContext *eval_ctx, struct Main *bmain);
+
+/*void BKE_cache_library_filter_duplilist(struct CacheLibrary *cachelib, struct ListBase *duplilist);*/
+void BKE_cache_library_tag_used_objects(CacheLibrary *cachelib);
+
+bool BKE_cache_read_dupli_cache(struct CacheLibrary *cachelib, struct DupliCache *dupcache,
+ struct Scene *scene, struct Group *dupgroup, float frame, bool use_render, bool for_display);
+bool BKE_cache_read_dupli_object(struct CacheLibrary *cachelib, struct DupliObjectData *data,
+ struct Scene *scene, struct Object *ob, float frame, bool use_render, bool for_display);
+
+void BKE_cache_process_dupli_cache(struct CacheLibrary *cachelib, struct CacheProcessData *data,
+ struct Scene *scene, struct Group *dupgroup, float frame_prev, float frame,
+ bool do_modifiers, bool do_strands_child_deform, bool do_strands_motion);
+
+/* ========================================================================= */
+
+typedef void (*CacheModifier_IDWalkFunc)(void *userdata, struct CacheLibrary *cachelib, struct CacheModifier *md, struct ID **id_ptr);
+
+typedef struct CacheProcessContext {
+ struct Main *bmain;
+ struct Scene *scene;
+ struct CacheLibrary *cachelib;
+ struct Group *group;
+} CacheProcessContext;
+
+typedef struct CacheProcessData {
+ unsigned int lay;
+ float mat[4][4];
+ struct DupliCache *dupcache;
+} CacheProcessData;
+
+typedef enum eCacheProcessFlag {
+ eCacheProcessFlag_DoStrands = (1 << 0),
+ eCacheProcessFlag_DoStrandsChildren = (1 << 1),
+} eCacheProcessFlag;
+
+typedef void (*CacheModifier_InitFunc)(struct CacheModifier *md);
+typedef void (*CacheModifier_FreeFunc)(struct CacheModifier *md);
+typedef void (*CacheModifier_CopyFunc)(struct CacheModifier *md, struct CacheModifier *target);
+typedef void (*CacheModifier_ForeachIDLinkFunc)(struct CacheModifier *md, struct CacheLibrary *cachelib,
+ CacheModifier_IDWalkFunc walk, void *userData);
+typedef void (*CacheModifier_ProcessFunc)(struct CacheModifier *md, struct CacheProcessContext *ctx, struct CacheProcessData *data,
+ int frame, int frame_prev, int process_flag);
+
+typedef struct CacheModifierTypeInfo {
+ /* The user visible name for this modifier */
+ char name[32];
+
+ /* The DNA struct name for the modifier data type,
+ * used to write the DNA data out.
+ */
+ char struct_name[32];
+
+ /* The size of the modifier data type, used by allocation. */
+ int struct_size;
+
+ /********************* Non-optional functions *********************/
+
+ /* Copy instance data for this modifier type. Should copy all user
+ * level settings to the target modifier.
+ */
+ CacheModifier_CopyFunc copy;
+
+ /* Should call the given walk function with a pointer to each ID
+ * pointer (i.e. each datablock pointer) that the modifier data
+ * stores. This is used for linking on file load and for
+ * unlinking datablocks or forwarding datablock references.
+ *
+ * This function is optional.
+ */
+ CacheModifier_ForeachIDLinkFunc foreachIDLink;
+
+ /* Process data and write results to the modifier's output archive */
+ CacheModifier_ProcessFunc process;
+
+ /********************* Optional functions *********************/
+
+ /* Initialize new instance data for this modifier type, this function
+ * should set modifier variables to their default values.
+ *
+ * This function is optional.
+ */
+ CacheModifier_InitFunc init;
+
+ /* Free internal modifier data variables, this function should
+ * not free the md variable itself.
+ *
+ * This function is optional.
+ */
+ CacheModifier_FreeFunc free;
+} CacheModifierTypeInfo;
+
+void BKE_cache_modifier_init(void);
+
+const char *BKE_cache_modifier_type_name(eCacheModifier_Type type);
+const char *BKE_cache_modifier_type_struct_name(eCacheModifier_Type type);
+int BKE_cache_modifier_type_struct_size(eCacheModifier_Type type);
+
+bool BKE_cache_modifier_unique_name(struct ListBase *modifiers, struct CacheModifier *md);
+struct CacheModifier *BKE_cache_modifier_add(struct CacheLibrary *cachelib, const char *name, eCacheModifier_Type type);
+void BKE_cache_modifier_remove(struct CacheLibrary *cachelib, struct CacheModifier *md);
+void BKE_cache_modifier_clear(struct CacheLibrary *cachelib);
+struct CacheModifier *BKE_cache_modifier_copy(struct CacheLibrary *cachelib, struct CacheModifier *md);
+
+void BKE_cache_modifier_foreachIDLink(struct CacheLibrary *cachelib, struct CacheModifier *md, CacheModifier_IDWalkFunc walk, void *userdata);
+
+bool BKE_cache_modifier_find_object(struct DupliCache *dupcache, struct Object *ob, struct DupliObjectData **r_data);
+bool BKE_cache_modifier_find_strands(struct DupliCache *dupcache, struct Object *ob, int hair_system, struct DupliObjectData **r_data, struct Strands **r_strands, struct StrandsChildren **r_children, const char **r_name);
+
+struct KeyBlock *BKE_cache_modifier_strands_key_insert_key(struct StrandsKeyCacheModifier *md, struct Strands *strands, const char *name, const bool from_mix);
+bool BKE_cache_modifier_strands_key_get(struct Object *ob, struct StrandsKeyCacheModifier **r_skmd, struct DerivedMesh **r_dm, struct Strands **r_strands,
+ struct DupliObjectData **r_dobdata, const char **r_name, float r_mat[4][4]);
+bool BKE_cache_library_uses_key(struct CacheLibrary *cachelib, struct Key *key);
+
+/* ========================================================================= */
+
+typedef struct CacheEffectorInstance {
+ struct CacheEffectorInstance *next, *prev;
+
+ float mat[4][4];
+ float imat[4][4];
+ // TODO add linear/angular velocity if necessary
+} CacheEffectorInstance;
+
+typedef struct CacheEffector {
+ int type;
+
+ ListBase instances;
+
+ struct DerivedMesh *dm;
+ struct BVHTreeFromMesh *treedata;
+ struct ForceFieldVertexCache *vertex_cache;
+
+ float strength, falloff;
+ float mindist, maxdist;
+ bool double_sided;
+} CacheEffector;
+
+typedef enum eCacheEffector_Type {
+ eCacheEffector_Type_Deflect = 0,
+ eCacheEffector_Type_Drag = 1,
+} eCacheEffector_Type;
+
+typedef struct CacheEffectorPoint {
+ int index;
+ float x[3], v[3];
+} CacheEffectorPoint;
+
+typedef struct CacheEffectorResult {
+ float f[3];
+} CacheEffectorResult;
+
+int BKE_cache_effectors_get(struct CacheEffector *effectors, int max, struct CacheLibrary *cachelib, struct DupliCache *dupcache, float obmat[4][4]);
+void BKE_cache_effectors_free(struct CacheEffector *effectors, int tot);
+void BKE_cache_effector_velocity_update(struct CacheLibrary *cachelib, struct DupliCache *dupcache, float obmat[4][4], float frame);
+int BKE_cache_effectors_eval(struct CacheEffector *effectors, int tot, struct CacheEffectorPoint *point, struct CacheEffectorResult *result);
+int BKE_cache_effectors_eval_ex(struct CacheEffector *effectors, int tot, struct CacheEffectorPoint *point, struct CacheEffectorResult *result,
+ bool (*filter)(void *, struct CacheEffector *), void *filter_data);
+
+/* ========================================================================= */
+
+struct CacheArchiveInfo *BKE_cache_archive_info_new(void);
+void BKE_cache_archive_info_free(struct CacheArchiveInfo *info);
+void BKE_cache_archive_info_clear(struct CacheArchiveInfo *info);
+
+struct CacheArchiveInfoNode *BKE_cache_archive_info_find_node(struct CacheArchiveInfo *info, struct CacheArchiveInfoNode *parent,
+ eCacheArchiveInfoNode_Type type, const char *name);
+struct CacheArchiveInfoNode *BKE_cache_archive_info_add_node(struct CacheArchiveInfo *info, struct CacheArchiveInfoNode *parent,
+ eCacheArchiveInfoNode_Type type, const char *name);
+
+#endif
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h
index f7af3a7f8ec..74dafcac671 100644
--- a/source/blender/blenkernel/BKE_context.h
+++ b/source/blender/blenkernel/BKE_context.h
@@ -58,6 +58,7 @@ struct bPoseChannel;
struct bGPdata;
struct bGPDlayer;
struct bGPDframe;
+struct wmWidget;
struct wmWindow;
struct wmWindowManager;
struct SpaceText;
@@ -106,6 +107,7 @@ enum {
CTX_MODE_PAINT_VERTEX,
CTX_MODE_PAINT_TEXTURE,
CTX_MODE_PARTICLE,
+ CTX_MODE_HAIR,
CTX_MODE_OBJECT
};
diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h
index ab49270ca64..8cd6311e84c 100644
--- a/source/blender/blenkernel/BKE_customdata.h
+++ b/source/blender/blenkernel/BKE_customdata.h
@@ -57,6 +57,8 @@ extern const CustomDataMask CD_MASK_EDITMESH;
extern const CustomDataMask CD_MASK_DERIVEDMESH;
extern const CustomDataMask CD_MASK_BMESH;
extern const CustomDataMask CD_MASK_FACECORNERS;
+extern const CustomDataMask CD_MASK_STRANDS;
+extern const CustomDataMask CD_MASK_STRANDS_BMESH;
extern const CustomDataMask CD_MASK_EVERYTHING;
/* for ORIGINDEX layer type, indicates no original index for this element */
@@ -259,6 +261,7 @@ void *CustomData_get(const struct CustomData *data, int index, int type);
void *CustomData_get_n(const struct CustomData *data, int type, int index, int n);
void *CustomData_bmesh_get(const struct CustomData *data, void *block, int type);
void *CustomData_bmesh_get_n(const struct CustomData *data, void *block, int type, int n);
+void *CustomData_bmesh_get_named(const struct CustomData *data, void *block, int type, const char *name);
/* gets the layer at physical index n, with no type checking.
*/
diff --git a/source/blender/blenkernel/BKE_depsgraph.h b/source/blender/blenkernel/BKE_depsgraph.h
index 862f91f567e..d1214d39469 100644
--- a/source/blender/blenkernel/BKE_depsgraph.h
+++ b/source/blender/blenkernel/BKE_depsgraph.h
@@ -44,6 +44,7 @@
extern "C" {
#endif
+struct Group;
struct ID;
struct Main;
struct Object;
@@ -113,6 +114,7 @@ void DAG_scene_free(struct Scene *sce);
* example a datablock was removed. */
void DAG_scene_update_flags(struct Main *bmain, struct Scene *sce, unsigned int lay, const bool do_time, const bool do_invisible_flush);
+void DAG_scene_update_group_flags(struct Main *bmain, struct Scene *scene, struct Group *group, unsigned int lay, const bool do_time, const bool do_invisible_flush);
void DAG_on_visible_update(struct Main *bmain, const bool do_time);
void DAG_id_tag_update(struct ID *id, short flag);
diff --git a/source/blender/blenkernel/BKE_editstrands.h b/source/blender/blenkernel/BKE_editstrands.h
new file mode 100644
index 00000000000..ddca51e4736
--- /dev/null
+++ b/source/blender/blenkernel/BKE_editstrands.h
@@ -0,0 +1,104 @@
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_EDITSTRANDS_H__
+#define __BKE_EDITSTRANDS_H__
+
+/** \file blender/blenkernel/BKE_editstrands.h
+ * \ingroup bke
+ */
+
+#include "BLI_utildefines.h"
+
+#include "DNA_customdata_types.h"
+
+#include "BKE_customdata.h"
+#include "bmesh.h"
+
+struct BMesh;
+struct DerivedMesh;
+struct Key;
+struct Object;
+struct Strands;
+
+typedef struct BMEditStrands {
+ struct BMesh *bm;
+
+ /*this is for undoing failed operations*/
+ struct BMEditStrands *emcopy;
+ int emcopyusers;
+
+ /* Object this editmesh came from (if it came from one) */
+ struct Object *ob;
+ struct DerivedMesh *root_dm;
+
+ int flag;
+
+ unsigned int vertex_glbuf;
+ unsigned int elem_glbuf;
+ unsigned int dot_glbuf;
+
+ /*temp variables for x-mirror editing*/
+ int mirror_cdlayer; /* -1 is invalid */
+} BMEditStrands;
+
+/* BMEditStrands->flag */
+typedef enum BMEditStrandsFlag {
+ BM_STRANDS_DIRTY_SEGLEN = 1,
+} BMEditStrandsFlag;
+
+struct BMEditStrands *BKE_editstrands_create(struct BMesh *bm, struct DerivedMesh *root_dm, float mat[4][4]);
+struct BMEditStrands *BKE_editstrands_copy(struct BMEditStrands *es);
+struct BMEditStrands *BKE_editstrands_from_object(struct Object *ob);
+void BKE_editstrands_update_linked_customdata(struct BMEditStrands *es);
+void BKE_editstrands_free(struct BMEditStrands *es);
+
+/* === constraints === */
+
+/* Stores vertex locations for temporary reference:
+ * Vertex locations get modified by tools, but then need to be corrected
+ * by calculating a smooth solution based on the difference to original pre-tool locations.
+ */
+typedef float (*BMEditStrandsLocations)[3];
+BMEditStrandsLocations BKE_editstrands_get_locations(struct BMEditStrands *edit);
+void BKE_editstrands_free_locations(BMEditStrandsLocations locs);
+
+void BKE_editstrands_solve_constraints(struct Object *ob, struct BMEditStrands *es, BMEditStrandsLocations orig);
+void BKE_editstrands_ensure(struct BMEditStrands *es);
+
+
+/* === cache shape key conversion === */
+
+struct BMesh *BKE_cache_strands_to_bmesh(struct Strands *strands, struct Key *key, float mat[4][4], int act_key_nr, struct DerivedMesh *dm);
+struct Strands *BKE_cache_strands_from_bmesh(struct BMEditStrands *edit, struct Key *key, float mat[4][4], struct DerivedMesh *dm);
+
+/* === particle conversion === */
+
+struct BMesh *BKE_particles_to_bmesh(struct Object *ob, struct ParticleSystem *psys);
+void BKE_particles_from_bmesh(struct Object *ob, struct ParticleSystem *psys);
+
+#endif
diff --git a/source/blender/blenkernel/BKE_effect.h b/source/blender/blenkernel/BKE_effect.h
index f8fee444d91..69c3d632c49 100644
--- a/source/blender/blenkernel/BKE_effect.h
+++ b/source/blender/blenkernel/BKE_effect.h
@@ -110,7 +110,8 @@ 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);
+struct ListBase *pdInitEffectors_ex(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, int layers, struct EffectorWeights *weights, bool precalc);
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);
@@ -193,7 +194,10 @@ void BKE_sim_debug_data_free(void);
void BKE_sim_debug_data_add_element(int type, const float v1[3], const float v2[3],
float r, float g, float b, const char *category, unsigned int hash);
+void BKE_sim_debug_data_add_element_ex(struct SimDebugData *debug_data, int type, const float v1[3], const float v2[3],
+ float r, float g, float b, unsigned int category_hash, unsigned int hash);
void BKE_sim_debug_data_remove_element(unsigned int hash);
+void BKE_sim_debug_data_remove_element_ex(struct SimDebugData *debug_data, unsigned int hash);
#define BKE_sim_debug_data_add_dot(p, r, g, b, category, ...) { \
const float v2[3] = { 0.0f, 0.0f, 0.0f }; \
diff --git a/source/blender/blenkernel/BKE_facemap.h b/source/blender/blenkernel/BKE_facemap.h
new file mode 100644
index 00000000000..1367ef52f2f
--- /dev/null
+++ b/source/blender/blenkernel/BKE_facemap.h
@@ -0,0 +1,54 @@
+/*
+ * ***** 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): Antony Riakiotakis
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_FACEMAP_H__
+#define __BKE_FACEMAP_H__
+
+/** \file BKE_facemap.h
+ * \ingroup bke
+ * \brief Functions for dealing with objects and facemaps.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct bFaceMap;
+struct ListBase;
+struct Object;
+
+/* Face map operations */
+struct bFaceMap *BKE_object_facemap_add(struct Object *ob);
+struct bFaceMap *BKE_object_facemap_add_name(struct Object *ob, const char *name);
+void BKE_object_facemap_remove(struct Object *ob, struct bFaceMap *fmap);
+void BKE_object_fmap_remove_all(struct Object *ob);
+
+int fmap_name_index(struct Object *ob, const char *name);
+void fmap_unique_name(struct bFaceMap *fmap, struct Object *ob);
+struct bFaceMap *fmap_find_name(struct Object *ob, const char *name);
+void fmap_copy_list(struct ListBase *outbase, struct ListBase *inbase);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BKE_FACEMAP_H__ */
diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h
index abe12282a1b..1e9e392406b 100644
--- a/source/blender/blenkernel/BKE_key.h
+++ b/source/blender/blenkernel/BKE_key.h
@@ -40,6 +40,8 @@ struct Curve;
struct Object;
struct Lattice;
struct Mesh;
+struct ParticleSystem;
+struct Strands;
struct WeightsArrayCache;
/* Kernel prototypes */
@@ -50,11 +52,16 @@ extern "C" {
void BKE_key_free(struct Key *sc);
void BKE_key_free_nolib(struct Key *key);
struct Key *BKE_key_add(struct ID *id);
+struct Key *BKE_key_add_ex(struct ID *from, int fromtype, int fromindex);
+struct Key *BKE_key_add_particles(struct Object *ob, struct ParticleSystem *psys);
struct Key *BKE_key_copy(struct Key *key);
struct Key *BKE_key_copy_nolib(struct Key *key);
void BKE_key_make_local(struct Key *key);
void BKE_key_sort(struct Key *key);
+void BKE_key_set_from_id(struct Key *key, struct ID *id);
+void BKE_key_set_from_particles(struct Key *key, struct Object *ob, struct ParticleSystem *psys);
+
void key_curve_position_weights(float t, float data[4], int type);
void key_curve_tangent_weights(float t, float data[4], int type);
void key_curve_normal_weights(float t, float data[4], int type);
@@ -64,11 +71,24 @@ float *BKE_key_evaluate_object_ex(
float *arr, size_t arr_size);
float *BKE_key_evaluate_object(
struct Object *ob, int *r_totelem);
+float *BKE_key_evaluate_strands_ex(
+ struct Strands *strands, struct Key *key, struct KeyBlock *actkb, bool lock_shape,
+ int *r_totelem, float *arr, size_t arr_size);
+float *BKE_key_evaluate_strands(
+ struct Strands *strand, struct Key *key, struct KeyBlock *actkbs, bool lock_shape,
+ int *r_totelem, bool use_motion);
+float *BKE_key_evaluate_particles_ex(
+ struct Object *ob, struct ParticleSystem *psys, float cfra, int *r_totelem,
+ float *arr, size_t arr_size);
+float *BKE_key_evaluate_particles(
+ struct Object *ob, struct ParticleSystem *psys, float cfra, int *r_totelem);
struct Key **BKE_key_from_object_p(struct Object *ob);
struct Key *BKE_key_from_object(struct Object *ob);
struct KeyBlock *BKE_keyblock_from_object(struct Object *ob);
struct KeyBlock *BKE_keyblock_from_object_reference(struct Object *ob);
+struct KeyBlock *BKE_keyblock_from_particles(struct ParticleSystem *psys);
+struct KeyBlock *BKE_keyblock_from_particles_reference(struct ParticleSystem *psys);
struct KeyBlock *BKE_keyblock_add(struct Key *key, const char *name);
struct KeyBlock *BKE_keyblock_add_ctime(struct Key *key, const char *name, const bool do_force);
@@ -83,10 +103,14 @@ typedef struct WeightsArrayCache {
float **defgroup_weights;
} WeightsArrayCache;
-float **BKE_keyblock_get_per_block_weights(struct Object *ob, struct Key *key, struct WeightsArrayCache *cache);
+float **BKE_keyblock_get_per_block_object_weights(struct Object *ob, struct Key *key, struct WeightsArrayCache *cache);
+float **BKE_keyblock_strands_get_per_block_weights(struct Strands *strands, struct Key *key, struct WeightsArrayCache *cache);
+float **BKE_keyblock_get_per_block_particle_weights(struct Object *ob, struct ParticleSystem *psys, float cfra, struct Key *key, struct WeightsArrayCache *cache);
void BKE_keyblock_free_per_block_weights(struct Key *key, float **per_keyblock_weights, struct WeightsArrayCache *cache);
void BKE_key_evaluate_relative(const int start, int end, const int tot, char *basispoin, struct Key *key, struct KeyBlock *actkb,
float **per_keyblock_weights, const int mode);
+void BKE_key_evaluate_strands_relative(const int start, int end, const int tot, char *basispoin, struct Key *key, struct KeyBlock *actkb,
+ float **per_keyblock_weights, const int mode);
/* conversion functions */
/* Note: 'update_from' versions do not (re)allocate mem in kb, while 'convert_from' do. */
@@ -102,14 +126,22 @@ void BKE_keyblock_update_from_mesh(struct Mesh *me, struct KeyBlock *kb);
void BKE_keyblock_convert_from_mesh(struct Mesh *me, struct KeyBlock *kb);
void BKE_keyblock_convert_to_mesh(struct KeyBlock *kb, struct Mesh *me);
+void BKE_keyblock_update_from_strands(struct Strands *strands, struct KeyBlock *kb, bool use_motion);
+void BKE_keyblock_convert_from_strands(struct Strands *strands, struct Key *key, struct KeyBlock *kb, bool use_motion);
+void BKE_keyblock_convert_to_strands(struct KeyBlock *kb, struct Strands *strands, bool use_motion);
+
void BKE_keyblock_update_from_vertcos(struct Object *ob, struct KeyBlock *kb, float (*vertCos)[3]);
void BKE_keyblock_convert_from_vertcos(struct Object *ob, struct KeyBlock *kb, float (*vertCos)[3]);
float (*BKE_keyblock_convert_to_vertcos(struct Object *ob, struct KeyBlock *kb))[3];
void BKE_keyblock_update_from_offset(struct Object *ob, struct KeyBlock *kb, float (*ofs)[3]);
+void BKE_keyblock_convert_to_hair_keys(struct KeyBlock *kb, struct Object *ob, struct ParticleSystem *psys);
+void BKE_keyblock_convert_from_hair_keys(struct Object *ob, struct ParticleSystem *psys, struct KeyBlock *kb);
+
/* other management */
bool BKE_keyblock_move(struct Object *ob, int org_index, int new_index);
+bool BKE_keyblock_move_ex(struct Key *key, int *shapenr, int org_index, int new_index);
bool BKE_keyblock_is_basis(struct Key *key, const int index);
diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h
index 6ecc955e26c..63192cad11d 100644
--- a/source/blender/blenkernel/BKE_library.h
+++ b/source/blender/blenkernel/BKE_library.h
@@ -71,7 +71,7 @@ void id_clear_lib_data(struct Main *bmain, struct ID *id);
struct ListBase *which_libbase(struct Main *mainlib, short type);
-#define MAX_LIBARRAY 35
+#define MAX_LIBARRAY 36
int set_listbasepointers(struct Main *main, struct ListBase *lb[MAX_LIBARRAY]);
void BKE_libblock_free(struct Main *bmain, void *idv);
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index ec654ea4b71..a0c67e055ae 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -94,6 +94,7 @@ typedef struct Main {
ListBase movieclip;
ListBase mask;
ListBase linestyle;
+ ListBase cache_library;
char id_tag_update[256];
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index 287152baf0a..7cb121bcf5e 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -39,6 +39,7 @@ struct LinkNode;
struct BLI_Stack;
struct MemArena;
struct BMesh;
+struct DupliObjectData;
struct Main;
struct Mesh;
struct MPoly;
@@ -128,6 +129,7 @@ void BKE_mesh_split_faces(struct Mesh *mesh);
struct Mesh *BKE_mesh_new_from_object(struct Main *bmain, struct Scene *sce, struct Object *ob,
int apply_modifiers, int settings, int calc_tessface, int calc_undeformed);
+struct Mesh *BKE_mesh_new_from_dupli_data(struct Main *bmain, struct DupliObjectData *data, bool calc_tessface, bool calc_undeformed);
/* vertex level transformations & checks (no derived mesh) */
diff --git a/source/blender/blenkernel/BKE_mesh_sample.h b/source/blender/blenkernel/BKE_mesh_sample.h
new file mode 100644
index 00000000000..6b489550847
--- /dev/null
+++ b/source/blender/blenkernel/BKE_mesh_sample.h
@@ -0,0 +1,68 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_MESH_SAMPLE_H__
+#define __BKE_MESH_SAMPLE_H__
+
+/** \file BKE_mesh_sample.h
+ * \ingroup bke
+ */
+
+struct DerivedMesh;
+struct Key;
+struct KeyBlock;
+
+struct MSurfaceSample;
+
+/* ==== Evaluate ==== */
+
+bool BKE_mesh_sample_eval(struct DerivedMesh *dm, const struct MSurfaceSample *sample, float loc[3], float nor[3], float tang[3]);
+bool BKE_mesh_sample_shapekey(struct Key *key, struct KeyBlock *kb, const struct MSurfaceSample *sample, float loc[3]);
+
+
+/* ==== Sampling ==== */
+
+/* Storage descriptor to allow generic data storage by arbitrary algorithms */
+typedef struct MSurfaceSampleStorage {
+ bool (*store_sample)(void *data, int capacity, int index, const struct MSurfaceSample *sample);
+ void *data;
+ int capacity;
+ int free_data;
+} MSurfaceSampleStorage;
+
+void BKE_mesh_sample_storage_single(struct MSurfaceSampleStorage *storage, struct MSurfaceSample *sample);
+void BKE_mesh_sample_storage_array(struct MSurfaceSampleStorage *storage, struct MSurfaceSample *samples, int capacity);
+void BKE_mesh_sample_storage_release(struct MSurfaceSampleStorage *storage);
+
+int BKE_mesh_sample_generate_random(struct MSurfaceSampleStorage *dst, struct DerivedMesh *dm, unsigned int seed, int totsample);
+
+typedef bool (*MeshSampleRayCallback)(void *userdata, float ray_start[3], float ray_end[3]);
+int BKE_mesh_sample_generate_raycast(struct MSurfaceSampleStorage *dst, struct DerivedMesh *dm, MeshSampleRayCallback ray_cb, void *userdata, int totsample);
+
+/* ==== Utilities ==== */
+
+struct ParticleSystem;
+struct ParticleData;
+struct BVHTreeFromMesh;
+
+bool BKE_mesh_sample_from_particle(struct MSurfaceSample *sample, struct ParticleSystem *psys, struct DerivedMesh *dm, struct ParticleData *pa);
+bool BKE_mesh_sample_to_particle(struct MSurfaceSample *sample, struct ParticleSystem *psys, struct DerivedMesh *dm, struct BVHTreeFromMesh *bvhtree, struct ParticleData *pa);
+
+#endif /* __BKE_MESH_SAMPLE_H__ */
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 4deaf26e2f5..dc157f67b06 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -453,6 +453,14 @@ struct bNodeSocket *nodeInsertStaticSocket(struct bNodeTree *ntree, struct bNode
void nodeRemoveSocket(struct bNodeTree *ntree, struct bNode *node, struct bNodeSocket *sock);
void nodeRemoveAllSockets(struct bNodeTree *ntree, struct bNode *node);
+typedef void (*CreateSocketsCB)(void *userdata, struct bNodeTree *ntree, struct bNode *node);
+typedef void (*SyncSocketCB)(void *userdata, struct bNode *node, struct bNodeSocket *newsock, struct bNodeSocket *oldsock);
+/* create_outputs_cb must create new sockets using nodeAddSocket/nodeAddStaticSocket
+ * sync_output_cb is optional, for copying settings from old to new socket if needed
+ */
+void nodeSyncInputs(struct bNodeTree *ntree, struct bNode *node, CreateSocketsCB create_inputs_cb, SyncSocketCB sync_input_cb, void *userdata);
+void nodeSyncOutputs(struct bNodeTree *ntree, struct bNode *node, CreateSocketsCB create_outputs_cb, SyncSocketCB sync_output_cb, void *userdata);
+
struct bNode *nodeAddNode(const struct bContext *C, struct bNodeTree *ntree, const char *idname);
struct bNode *nodeAddStaticNode(const struct bContext *C, struct bNodeTree *ntree, int type);
void nodeUnlinkNode(struct bNodeTree *ntree, struct bNode *node);
@@ -782,6 +790,8 @@ struct ShadeResult;
#define SH_NODE_COMBXYZ 189
#define SH_NODE_OUTPUT_LINESTYLE 190
#define SH_NODE_UVALONGSTROKE 191
+#define SH_NODE_TEX_POINTDENSITY 192
+#define SH_NODE_OPENVDB 193
/* custom defines options for Material node */
#define SH_NODE_MAT_DIFF 1
@@ -810,6 +820,8 @@ void set_node_shader_lamp_loop(void (*lamp_loop_func)(struct ShadeInp
void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMaterial *mat, short compatibility);
+void ntreeUpdateOpenVDBNode(struct Main *bmain, struct bNodeTree *ntree, struct bNode *node);
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index 9482ec778d3..8d1fe7640ee 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -67,7 +67,7 @@ void BKE_object_update_base_layer(struct Scene *scene, struct Object *ob);
void BKE_object_free(struct Object *ob);
void BKE_object_free_ex(struct Object *ob, bool do_id_user);
void BKE_object_free_derived_caches(struct Object *ob);
-void BKE_object_free_caches(struct Object *object);
+void BKE_object_free_caches(struct Object *object, bool free_smoke_sim);
void BKE_object_modifier_hook_reset(struct Object *ob, struct HookModifierData *hmd);
diff --git a/source/blender/blenkernel/BKE_object_deform.h b/source/blender/blenkernel/BKE_object_deform.h
index e956815d6f7..fdcaf0c4ede 100644
--- a/source/blender/blenkernel/BKE_object_deform.h
+++ b/source/blender/blenkernel/BKE_object_deform.h
@@ -33,10 +33,12 @@
extern "C" {
#endif
-struct Object;
+struct bDeformGroup;
+struct bFaceMap;
struct ID;
+struct ListBase;
struct MDeformVert;
-struct bDeformGroup;
+struct Object;
/* General vgroup operations */
void BKE_object_defgroup_remap_update_users(struct Object *ob, int *map);
@@ -53,7 +55,6 @@ bool BKE_object_defgroup_clear_all(struct Object *ob, const bool use_selection);
void BKE_object_defgroup_remove(struct Object *ob, struct bDeformGroup *defgroup);
void BKE_object_defgroup_remove_all(struct Object *ob);
-
/* Select helpers */
enum eVGroupSelect;
bool *BKE_object_defgroup_subset_from_select_type(
diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h
index e03789f502b..f8ddb33b0ba 100644
--- a/source/blender/blenkernel/BKE_particle.h
+++ b/source/blender/blenkernel/BKE_particle.h
@@ -63,6 +63,14 @@ struct BVHTreeRay;
struct BVHTreeRayHit;
struct EdgeHash;
+/* XXX disabled for now due to stability issues and limited usefulness */
+//#define USE_PARTICLE_PREVIEW
+/* XXX disabled because this requires sorting of children,
+ * but during render children are constantly recreated based on the deformed mesh,
+ * which leads to different indices every time and therefore different randomization values.
+ */
+//#define USE_PARTICLE_HULL_DRAWING
+
#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))
@@ -110,6 +118,7 @@ typedef struct ParticleTexture {
float damp, gravity, field; /* used in physics */
float length, clump, kink_freq, kink_amp, effector; /* used in path caching */
float rough1, rough2, roughe; /* used in path caching */
+ float color[3];
} ParticleTexture;
typedef struct ParticleSeam {
@@ -124,7 +133,7 @@ typedef struct ParticleCacheKey {
float rot[4];
float col[3];
float time;
- int segments;
+ int segments, hull_parent;
} ParticleCacheKey;
typedef struct ParticleThreadContext {
@@ -242,6 +251,25 @@ typedef struct ParticleDrawData {
int totpoint, totve;
} ParticleDrawData;
+typedef struct ParticleInterpolationData {
+ struct HairKey *hkey[2];
+
+ struct DerivedMesh *dm;
+ struct MVert *mvert[2];
+
+ int keyed;
+ struct ParticleKey *kkey[2];
+
+ struct PointCache *cache;
+ struct PTCacheMem *pm;
+
+ struct PTCacheEditPoint *epoint;
+ struct PTCacheEditKey *ekey[2];
+
+ float birthtime, dietime;
+ int bspline;
+} ParticleInterpolationData;
+
#define PARTICLE_DRAW_DATA_UPDATED 1
#define PSYS_FRAND_COUNT 1024
@@ -296,6 +324,8 @@ bool psys_check_edited(struct ParticleSystem *psys);
void psys_check_group_weights(struct ParticleSettings *part);
int psys_uses_gravity(struct ParticleSimulationData *sim);
+struct KeyBlock *BKE_psys_insert_shape_key(struct Scene *scene, struct Object *ob, struct ParticleSystem *psys, const char *name, const bool from_mix);
+
/* free */
void BKE_particlesettings_free(struct ParticleSettings *part);
void psys_free_path_cache(struct ParticleSystem *psys, struct PTCacheEdit *edit);
@@ -324,6 +354,7 @@ void BKE_particlesettings_make_local(struct ParticleSettings *part);
void psys_reset(struct ParticleSystem *psys, int mode);
+bool psys_hair_update_preview(struct ParticleSimulationData *sim);
void psys_find_parents(struct ParticleSimulationData *sim);
void psys_cache_paths(struct ParticleSimulationData *sim, float cfra);
@@ -337,6 +368,10 @@ float psys_get_child_size(struct ParticleSystem *psys, struct ChildParticle *cpa
void psys_get_particle_on_path(struct ParticleSimulationData *sim, int pa_num, struct ParticleKey *state, const bool vel);
int psys_get_particle_state(struct ParticleSimulationData *sim, int p, struct ParticleKey *state, int always);
+/* interpolation */
+void init_particle_interpolation(struct Object *ob, struct ParticleSystem *psys, struct ParticleData *pa, struct ParticleInterpolationData *pind);
+void do_particle_interpolation(struct ParticleSystem *psys, int p, struct ParticleData *pa, float t, struct ParticleInterpolationData *pind, struct ParticleKey *result);
+
/* child paths */
void BKE_particlesettings_clump_curve_init(struct ParticleSettings *part);
void BKE_particlesettings_rough_curve_init(struct ParticleSettings *part);
@@ -393,18 +428,22 @@ void psys_vec_rot_to_face(struct DerivedMesh *dm, struct ParticleData *pa, float
void psys_mat_hair_to_object(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[4][4]);
void psys_mat_hair_to_global(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[4][4]);
void psys_mat_hair_to_orco(struct Object *ob, struct DerivedMesh *dm, short from, struct ParticleData *pa, float hairmat[4][4]);
+void psys_child_mat_to_object(struct Object *ob, struct ParticleSystem *psys, struct ParticleSystemModifierData *psmd, struct ChildParticle *cpa, float hairmat[4][4]);
float psys_get_dietime_from_cache(struct PointCache *cache, int index);
void psys_free_pdd(struct ParticleSystem *psys);
float *psys_cache_vgroup(struct DerivedMesh *dm, struct ParticleSystem *psys, int vgroup);
+bool particle_mtex_eval(struct ParticleSimulationData *sim, MTex *mtex, ParticleData *pa, float cfra, float *value, float rgba[4]);
void psys_get_texture(struct ParticleSimulationData *sim, struct ParticleData *pa, struct ParticleTexture *ptex, int event, float cfra);
+float psys_get_texture_shapefac(struct ParticleSimulationData *sim, struct ParticleData *pa, float cfra, const char *shapekey);
void psys_interpolate_face(struct MVert *mvert, struct MFace *mface, struct MTFace *tface,
float (*orcodata)[3], float w[4], float vec[3], float nor[3], float utan[3], float vtan[3],
float orco[3], float ornor[3]);
float psys_particle_value_from_verts(struct DerivedMesh *dm, short from, struct ParticleData *pa, float *values);
void psys_get_from_key(struct ParticleKey *key, float loc[3], float vel[3], float rot[4], float *time);
+int psys_get_index_on_dm(struct ParticleSystem *psys, struct DerivedMesh *dm, ParticleData *pa, int *mapindex, float mapfw[4]);
/* BLI_bvhtree_ray_cast callback */
void BKE_psys_collision_neartest_cb(void *userdata, int index, const struct BVHTreeRay *ray, struct BVHTreeRayHit *hit);
diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h
index a3ffad1f66b..3d67b91d767 100644
--- a/source/blender/blenkernel/BKE_pointcache.h
+++ b/source/blender/blenkernel/BKE_pointcache.h
@@ -267,10 +267,8 @@ void BKE_ptcache_id_from_rigidbody(PTCacheID *pid, struct Object *ob, struct Rig
void BKE_ptcache_ids_from_object(struct ListBase *lb, struct Object *ob, struct Scene *scene, int duplis);
-/***************** Global funcs ****************************/
-void BKE_ptcache_remove(void);
-
/************ ID specific functions ************************/
+void BKE_ptcache_id_clear_ex(PTCacheID *pid, int mode, unsigned int cfra, bool allow_file_delete);
void BKE_ptcache_id_clear(PTCacheID *id, int mode, unsigned int cfra);
int BKE_ptcache_id_exist(PTCacheID *id, int cfra);
int BKE_ptcache_id_reset(struct Scene *scene, PTCacheID *id, int mode);
@@ -314,10 +312,10 @@ void BKE_ptcache_quick_cache_all(struct Main *bmain, struct Scene *scene);
void BKE_ptcache_bake(struct PTCacheBaker *baker);
/* Convert disk cache to memory cache. */
-void BKE_ptcache_disk_to_mem(struct PTCacheID *pid);
+void BKE_ptcache_disk_to_mem(struct PTCacheID *pid, bool clear);
/* Convert memory cache to disk cache. */
-void BKE_ptcache_mem_to_disk(struct PTCacheID *pid);
+void BKE_ptcache_mem_to_disk(struct PTCacheID *pid, bool clear);
/* Convert disk cache to memory cache and vice versa. Clears the cache that was converted. */
void BKE_ptcache_toggle_disk_cache(struct PTCacheID *pid);
diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h
index 027bdbbbe58..ff877f88f2c 100644
--- a/source/blender/blenkernel/BKE_scene.h
+++ b/source/blender/blenkernel/BKE_scene.h
@@ -40,6 +40,7 @@ extern "C" {
struct AviCodecData;
struct Base;
struct EvaluationContext;
+struct Group;
struct Main;
struct Object;
struct QuicktimeCodecData;
@@ -119,6 +120,11 @@ void BKE_scene_frame_set(struct Scene *scene, double cfra);
void BKE_scene_update_tagged(struct EvaluationContext *eval_ctx, struct Main *bmain, struct Scene *sce);
void BKE_scene_update_for_newframe(struct EvaluationContext *eval_ctx, struct Main *bmain, struct Scene *sce, unsigned int lay);
void BKE_scene_update_for_newframe_ex(struct EvaluationContext *eval_ctx, struct Main *bmain, struct Scene *sce, unsigned int lay, bool do_invisible_flush);
+void BKE_scene_update_group_for_newframe(struct EvaluationContext *eval_ctx,
+ struct Main *bmain,
+ struct Scene *scene,
+ struct Group *group,
+ unsigned int lay);
struct SceneRenderLayer *BKE_scene_add_render_layer(struct Scene *sce, const char *name);
bool BKE_scene_remove_render_layer(struct Main *main, struct Scene *scene, struct SceneRenderLayer *srl);
diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h
index 48616418e67..60fd402ef4b 100644
--- a/source/blender/blenkernel/BKE_screen.h
+++ b/source/blender/blenkernel/BKE_screen.h
@@ -95,6 +95,9 @@ typedef struct SpaceType {
/* on startup, define dropboxes for spacetype+regions */
void (*dropboxes)(void);
+ /* on startup define areas with widget types */
+ void (*widgets)(void);
+
/* return context data */
int (*context)(const struct bContext *, const char *, struct bContextDataResult *);
diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h
index f73548373ef..5f159b14fb8 100644
--- a/source/blender/blenkernel/BKE_sequencer.h
+++ b/source/blender/blenkernel/BKE_sequencer.h
@@ -227,6 +227,7 @@ void BKE_sequencer_base_clipboard_pointers_restore(struct ListBase *seqbase, str
void BKE_sequence_free(struct Scene *scene, struct Sequence *seq);
void BKE_sequence_free_anim(struct Sequence *seq);
const char *BKE_sequence_give_name(struct Sequence *seq);
+ListBase *BKE_sequence_seqbase_get(struct Sequence *seq, int *r_offset);
void BKE_sequence_calc(struct Scene *scene, struct Sequence *seq);
void BKE_sequence_calc_disp(struct Scene *scene, struct Sequence *seq);
void BKE_sequence_reload_new_file(struct Scene *scene, struct Sequence *seq, const bool lock_range);
diff --git a/source/blender/blenkernel/BKE_smoke.h b/source/blender/blenkernel/BKE_smoke.h
index 07d156cfa02..bc2e3768cb8 100644
--- a/source/blender/blenkernel/BKE_smoke.h
+++ b/source/blender/blenkernel/BKE_smoke.h
@@ -33,6 +33,8 @@
* \author Daniel Genrich
*/
+struct OpenVDBCache;
+
typedef float (*bresenham_callback)(float *result, float *input, int res[3], int *pixel, float *tRay, float correct);
struct DerivedMesh *smokeModifier_do(struct SmokeModifierData *smd, struct Scene *scene, struct Object *ob, struct DerivedMesh *dm, bool for_render);
@@ -48,4 +50,24 @@ void smokeModifier_copy(struct SmokeModifierData *smd, struct SmokeModifierData
float smoke_get_velocity_at(struct Object *ob, float position[3], float velocity[3]);
int smoke_get_data_flags(struct SmokeDomainSettings *sds);
+/* OpenVDB smoke export/import */
+
+typedef void (*update_cb)(void *, float progress, int *cancel);
+
+void smokeModifier_OpenVDB_export(struct SmokeModifierData *smd, struct Scene *scene,
+ struct Object *ob, struct DerivedMesh *dm,
+ update_cb update,
+ void *update_cb_data);
+
+void smokeModifier_OpenVDB_update_transform(struct SmokeModifierData *smd,
+ struct Scene *scene,
+ struct Object *ob,
+ update_cb update,
+ void *update_cb_data);
+
+void smokeModifier_OpenVDB_import(struct SmokeModifierData *smd, struct Scene *scene, struct Object *ob, struct OpenVDBCache *cache);
+
+struct OpenVDBCache *BKE_openvdb_get_current_cache(struct SmokeDomainSettings *sds);
+void BKE_openvdb_cache_filename(char *r_filename, const char *path, const char *fname, const char *relbase, int frame);
+
#endif /* __BKE_SMOKE_H__ */
diff --git a/source/blender/blenkernel/BKE_strands.h b/source/blender/blenkernel/BKE_strands.h
new file mode 100644
index 00000000000..2ec1363727b
--- /dev/null
+++ b/source/blender/blenkernel/BKE_strands.h
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __BKE_STRANDS_H__
+#define __BKE_STRANDS_H__
+
+#include "BLI_utildefines.h"
+
+#include "DNA_strands_types.h"
+
+struct StrandChildIterator;
+
+struct Strands *BKE_strands_new(int strands, int verts);
+struct Strands *BKE_strands_copy(struct Strands *strands);
+void BKE_strands_free(struct Strands *strands);
+
+void BKE_strands_add_motion_state(struct Strands *strands);
+void BKE_strands_remove_motion_state(struct Strands *strands);
+void BKE_strands_state_copy_rest_positions(struct Strands *strands);
+void BKE_strands_state_clear_velocities(struct Strands *strands);
+
+void BKE_strands_ensure_normals(struct Strands *strands);
+
+void BKE_strands_get_minmax(struct Strands *strands, float min[3], float max[3], bool use_motion_state);
+
+
+struct StrandsChildren *BKE_strands_children_new(int strands, int verts);
+struct StrandsChildren *BKE_strands_children_copy(struct StrandsChildren *strands);
+void BKE_strands_children_free(struct StrandsChildren *strands);
+
+void BKE_strands_children_add_uvs(struct StrandsChildren *strands, int num_layers);
+void BKE_strands_children_add_vcols(struct StrandsChildren *strands, int num_layers);
+
+void BKE_strands_children_deform(struct StrandsChildren *strands, struct Strands *parents, bool use_motion);
+
+void BKE_strands_children_ensure_normals(struct StrandsChildren *strands);
+
+void BKE_strands_children_get_minmax(struct StrandsChildren *strands, float min[3], float max[3]);
+
+/* ------------------------------------------------------------------------- */
+/* Strand Curves Iterator */
+
+typedef struct StrandIterator {
+ int index, tot;
+ struct StrandsCurve *curve;
+ struct StrandsVertex *verts;
+ struct StrandsMotionState *state;
+} StrandIterator;
+
+BLI_INLINE void BKE_strand_iter_init(StrandIterator *iter, Strands *strands)
+{
+ iter->tot = strands->totcurves;
+ iter->index = 0;
+ iter->curve = strands->curves;
+ iter->verts = strands->verts;
+ iter->state = strands->state;
+}
+
+BLI_INLINE bool BKE_strand_iter_valid(StrandIterator *iter)
+{
+ return iter->index < iter->tot;
+}
+
+BLI_INLINE void BKE_strand_iter_next(StrandIterator *iter)
+{
+ const int numverts = iter->curve->numverts;
+
+ ++iter->index;
+ ++iter->curve;
+ iter->verts += numverts;
+ if (iter->state)
+ iter->state += numverts;
+}
+
+BLI_INLINE size_t BKE_strand_iter_curve_offset(Strands *strands, StrandIterator *iter)
+{
+ return iter->curve - strands->curves;
+}
+
+BLI_INLINE size_t BKE_strand_iter_vertex_offset(Strands *strands, StrandIterator *iter)
+{
+ return iter->verts - strands->verts;
+}
+
+/* ------------------------------------------------------------------------- */
+/* Strand Vertices Iterator */
+
+typedef struct StrandVertexIterator {
+ int index, tot;
+ StrandsVertex *vertex;
+ StrandsMotionState *state;
+} StrandVertexIterator;
+
+BLI_INLINE void BKE_strand_vertex_iter_init(StrandVertexIterator *iter, StrandIterator *strand_iter)
+{
+ iter->tot = strand_iter->curve->numverts;
+ iter->index = 0;
+ iter->vertex = strand_iter->verts;
+ iter->state = strand_iter->state;
+}
+
+BLI_INLINE bool BKE_strand_vertex_iter_valid(StrandVertexIterator *iter)
+{
+ return iter->index < iter->tot;
+}
+
+BLI_INLINE void BKE_strand_vertex_iter_next(StrandVertexIterator *iter)
+{
+ ++iter->vertex;
+ if (iter->state)
+ ++iter->state;
+ ++iter->index;
+}
+
+BLI_INLINE size_t BKE_strand_vertex_iter_vertex_offset(Strands *strands, StrandVertexIterator *iter)
+{
+ return iter->vertex - strands->verts;
+}
+
+/* ------------------------------------------------------------------------- */
+/* Strand Edges Iterator */
+
+typedef struct StrandEdgeIterator {
+ int index, tot;
+ StrandsVertex *vertex0, *vertex1;
+ StrandsMotionState *state0, *state1;
+} StrandEdgeIterator;
+
+BLI_INLINE void BKE_strand_edge_iter_init(StrandEdgeIterator *iter, StrandIterator *strand_iter)
+{
+ iter->tot = strand_iter->curve->numverts - 1;
+ iter->index = 0;
+ iter->vertex0 = strand_iter->verts;
+ iter->state0 = strand_iter->state;
+ iter->vertex1 = strand_iter->verts + 1;
+ iter->state1 = strand_iter->state + 1;
+}
+
+BLI_INLINE bool BKE_strand_edge_iter_valid(StrandEdgeIterator *iter)
+{
+ return iter->index < iter->tot;
+}
+
+BLI_INLINE void BKE_strand_edge_iter_next(StrandEdgeIterator *iter)
+{
+ ++iter->vertex0;
+ ++iter->vertex1;
+ if (iter->state0) {
+ ++iter->state0;
+ ++iter->state1;
+ }
+ ++iter->index;
+}
+
+BLI_INLINE size_t BKE_strand_edge_iter_vertex0_offset(Strands *strands, StrandEdgeIterator *iter)
+{
+ return iter->vertex0 - strands->verts;
+}
+
+BLI_INLINE size_t BKE_strand_edge_iter_vertex1_offset(Strands *strands, StrandEdgeIterator *iter)
+{
+ return iter->vertex1 - strands->verts;
+}
+
+/* ------------------------------------------------------------------------- */
+/* Strand Bends Iterator */
+
+typedef struct StrandBendIterator {
+ int index, tot;
+ StrandsVertex *vertex0, *vertex1, *vertex2;
+ StrandsMotionState *state0, *state1, *state2;
+} StrandBendIterator;
+
+BLI_INLINE void BKE_strand_bend_iter_init(StrandBendIterator *iter, StrandIterator *strand_iter)
+{
+ iter->tot = strand_iter->curve->numverts - 2;
+ iter->index = 0;
+ iter->vertex0 = strand_iter->verts;
+ iter->state0 = strand_iter->state;
+ iter->vertex1 = strand_iter->verts + 1;
+ iter->state1 = strand_iter->state + 1;
+ iter->vertex2 = strand_iter->verts + 2;
+ iter->state2 = strand_iter->state + 2;
+}
+
+BLI_INLINE bool BKE_strand_bend_iter_valid(StrandBendIterator *iter)
+{
+ return iter->index < iter->tot;
+}
+
+BLI_INLINE void BKE_strand_bend_iter_next(StrandBendIterator *iter)
+{
+ ++iter->vertex0;
+ ++iter->vertex1;
+ ++iter->vertex2;
+ if (iter->state0) {
+ ++iter->state0;
+ ++iter->state1;
+ ++iter->state2;
+ }
+ ++iter->index;
+}
+
+BLI_INLINE size_t BKE_strand_bend_iter_vertex0_offset(Strands *strands, StrandBendIterator *iter)
+{
+ return iter->vertex0 - strands->verts;
+}
+
+BLI_INLINE size_t BKE_strand_bend_iter_vertex1_offset(Strands *strands, StrandBendIterator *iter)
+{
+ return iter->vertex1 - strands->verts;
+}
+
+BLI_INLINE size_t BKE_strand_bend_iter_vertex2_offset(Strands *strands, StrandBendIterator *iter)
+{
+ return iter->vertex2 - strands->verts;
+}
+
+void BKE_strand_bend_iter_transform_rest(StrandBendIterator *iter, float mat[3][3]);
+void BKE_strand_bend_iter_transform_state(StrandBendIterator *iter, float mat[3][3]);
+
+/* ------------------------------------------------------------------------- */
+/* Strand Child Curves Iterator */
+
+typedef struct StrandChildIterator {
+ int index, tot, numuv, numvcol;
+ StrandsChildCurve *curve;
+ StrandsChildCurveUV *curve_uv;
+ StrandsChildCurveVCol *curve_vcol;
+ StrandsChildVertex *verts;
+} StrandChildIterator;
+
+BLI_INLINE void BKE_strand_child_iter_init(StrandChildIterator *iter, StrandsChildren *strands)
+{
+ iter->tot = strands->totcurves;
+ iter->numuv = strands->numuv;
+ iter->numvcol = strands->numvcol;
+ iter->index = 0;
+
+ iter->curve = strands->curves;
+ iter->curve_uv = strands->curve_uvs;
+ iter->curve_vcol = strands->curve_vcols;
+ iter->verts = strands->verts;
+}
+
+BLI_INLINE bool BKE_strand_child_iter_valid(StrandChildIterator *iter)
+{
+ return iter->index < iter->tot;
+}
+
+BLI_INLINE void BKE_strand_child_iter_next(StrandChildIterator *iter)
+{
+ const int numverts = iter->curve->numverts;
+
+ ++iter->index;
+ ++iter->curve;
+ if (iter->curve_uv)
+ iter->curve_uv += iter->numuv;
+ if (iter->curve_vcol)
+ iter->curve_vcol += iter->numvcol;
+ iter->verts += numverts;
+}
+
+BLI_INLINE size_t BKE_strand_child_iter_curve_offset(StrandsChildren *strands, StrandChildIterator *iter)
+{
+ return iter->curve - strands->curves;
+}
+
+BLI_INLINE size_t BKE_strand_child_iter_vertex_offset(StrandsChildren *strands, StrandChildIterator *iter)
+{
+ return iter->verts - strands->verts;
+}
+
+/* ------------------------------------------------------------------------- */
+/* Strand Child Vertices Iterator */
+
+typedef struct StrandChildVertexIterator {
+ int index, tot;
+ StrandsChildVertex *vertex;
+} StrandChildVertexIterator;
+
+BLI_INLINE void BKE_strand_child_vertex_iter_init(StrandChildVertexIterator *iter, StrandChildIterator *strand_iter)
+{
+ iter->tot = strand_iter->curve->numverts;
+ iter->index = 0;
+ iter->vertex = strand_iter->verts;
+}
+
+BLI_INLINE bool BKE_strand_child_vertex_iter_valid(StrandChildVertexIterator *iter)
+{
+ return iter->index < iter->tot;
+}
+
+BLI_INLINE void BKE_strand_child_vertex_iter_next(StrandChildVertexIterator *iter)
+{
+ ++iter->vertex;
+ ++iter->index;
+}
+
+BLI_INLINE size_t BKE_strand_child_vertex_iter_vertex_offset(StrandsChildren *strands, StrandChildVertexIterator *iter)
+{
+ return iter->vertex - strands->verts;
+}
+
+/* ------------------------------------------------------------------------- */
+/* Strand Child Edges Iterator */
+
+typedef struct StrandChildEdgeIterator {
+ int index, tot;
+ StrandsChildVertex *vertex0, *vertex1;
+} StrandChildEdgeIterator;
+
+BLI_INLINE void BKE_strand_child_edge_iter_init(StrandChildEdgeIterator *iter, StrandChildIterator *strand_iter)
+{
+ iter->tot = strand_iter->curve->numverts - 1;
+ iter->index = 0;
+ iter->vertex0 = strand_iter->verts;
+ iter->vertex1 = strand_iter->verts + 1;
+}
+
+BLI_INLINE bool BKE_strand_child_edge_iter_valid(StrandChildEdgeIterator *iter)
+{
+ return iter->index < iter->tot;
+}
+
+BLI_INLINE void BKE_strand_child_edge_iter_next(StrandChildEdgeIterator *iter)
+{
+ ++iter->vertex0;
+ ++iter->vertex1;
+ ++iter->index;
+}
+
+BLI_INLINE size_t BKE_strand_child_edge_iter_vertex0_offset(StrandsChildren *strands, StrandChildEdgeIterator *iter)
+{
+ return iter->vertex0 - strands->verts;
+}
+
+BLI_INLINE size_t BKE_strand_child_edge_iter_vertex1_offset(StrandsChildren *strands, StrandChildEdgeIterator *iter)
+{
+ return iter->vertex1 - strands->verts;
+}
+
+#endif /* __BKE_STRANDS_H__ */
diff --git a/source/blender/blenkernel/BKE_texture.h b/source/blender/blenkernel/BKE_texture.h
index 4e5214bc364..95918b9ca0b 100644
--- a/source/blender/blenkernel/BKE_texture.h
+++ b/source/blender/blenkernel/BKE_texture.h
@@ -115,6 +115,7 @@ void BKE_texture_envmap_free(struct EnvMap *env);
struct EnvMap *BKE_texture_envmap_add(void);
struct EnvMap *BKE_texture_envmap_copy(struct EnvMap *env);
+void BKE_texture_pointdensity_init_data(struct PointDensity *pd);
void BKE_texture_pointdensity_free_data(struct PointDensity *pd);
void BKE_texture_pointdensity_free(struct PointDensity *pd);
struct PointDensity *BKE_texture_pointdensity_add(void);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 37e5b36779f..6ccde9c48c6 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -38,6 +38,7 @@ set(INC
../modifiers
../nodes
../physics
+ ../pointcache
../render/extern/include
../../../intern/ghost
../../../intern/guardedalloc
@@ -77,6 +78,7 @@ set(SRC
intern/brush.c
intern/bullet.c
intern/bvhutils.c
+ intern/cache_library.c
intern/camera.c
intern/cdderivedmesh.c
intern/cloth.c
@@ -96,7 +98,9 @@ set(SRC
intern/editderivedmesh.c
intern/editmesh.c
intern/editmesh_bvh.c
+ intern/editstrands.c
intern/effect.c
+ intern/facemap.c
intern/fcurve.c
intern/fluidsim.c
intern/fmodifier.c
@@ -126,6 +130,7 @@ set(SRC
intern/mesh_evaluate.c
intern/mesh_mapping.c
intern/mesh_remap.c
+ intern/mesh_sample.c
intern/mesh_validate.c
intern/modifier.c
intern/modifiers_bmesh.c
@@ -144,6 +149,7 @@ set(SRC
intern/particle.c
intern/particle_child.c
intern/particle_distribute.c
+ intern/particle_interpolate.c
intern/particle_system.c
intern/pbvh.c
intern/pbvh_bmesh.c
@@ -164,6 +170,7 @@ set(SRC
intern/softbody.c
intern/sound.c
intern/speaker.c
+ intern/strands.c
intern/subsurf_ccg.c
intern/suggestions.c
intern/text.c
@@ -197,6 +204,7 @@ set(SRC
BKE_brush.h
BKE_bullet.h
BKE_bvhutils.h
+ BKE_cache_library.h
BKE_camera.h
BKE_ccg.h
BKE_cdderivedmesh.h
@@ -214,9 +222,11 @@ set(SRC
BKE_depsgraph.h
BKE_displist.h
BKE_dynamicpaint.h
+ BKE_editstrands.h
BKE_editmesh.h
BKE_editmesh_bvh.h
BKE_effect.h
+ BKE_facemap.h
BKE_fcurve.h
BKE_fluidsim.h
BKE_font.h
@@ -243,6 +253,7 @@ set(SRC
BKE_mesh.h
BKE_mesh_mapping.h
BKE_mesh_remap.h
+ BKE_mesh_sample.h
BKE_modifier.h
BKE_movieclip.h
BKE_multires.h
@@ -270,6 +281,7 @@ set(SRC
BKE_softbody.h
BKE_sound.h
BKE_speaker.h
+ BKE_strands.h
BKE_subsurf.h
BKE_suggestions.h
BKE_text.h
@@ -478,6 +490,14 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_OPENVDB)
+ add_definitions(-DWITH_OPENVDB)
+ add_definitions(-DOPENVDB_USE_BLOSC)
+ list(APPEND INC
+ ../../../../intern/openvdb
+ )
+endif()
+
## Warnings as errors, this is too strict!
#if(MSVC)
# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /WX")
diff --git a/source/blender/blenkernel/SConscript b/source/blender/blenkernel/SConscript
index 9d19e1c29b4..40719648a23 100644
--- a/source/blender/blenkernel/SConscript
+++ b/source/blender/blenkernel/SConscript
@@ -67,6 +67,7 @@ incs = [
'../modifiers',
'../nodes',
'../physics',
+ '../pointcache',
'../render/extern/include',
'../windowmanager',
env['BF_ZLIB_INC'],
@@ -168,6 +169,10 @@ if env['WITH_BF_INTERNATIONAL']:
if env['WITH_BF_FREESTYLE']:
defs.append('WITH_FREESTYLE')
+if env['WITH_BF_OPENVDB']:
+ defs.append('WITH_OPENVDB')
+ incs += ' #/intern/openvdb'
+
if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'):
incs += ' ' + env['BF_PTHREADS_INC']
incs += ' ../../../intern/utfconv'
diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c
index 76544e5ed42..8248ea2fa37 100644
--- a/source/blender/blenkernel/intern/bpath.c
+++ b/source/blender/blenkernel/intern/bpath.c
@@ -124,6 +124,7 @@ static bool bpath_relative_convert_visit_cb(void *userdata, char *path_dst, cons
strcpy(path_dst, path_src);
BLI_path_rel(path_dst, data->basedir);
if (BLI_path_is_rel(path_dst)) {
+ BKE_reportf(data->reports, RPT_INFO, "Path '%s' made relative to '%s'", path_src, path_dst);
data->count_changed++;
}
else {
@@ -167,6 +168,7 @@ static bool bpath_absolute_convert_visit_cb(void *userdata, char *path_dst, cons
strcpy(path_dst, path_src);
BLI_path_abs(path_dst, data->basedir);
if (BLI_path_is_rel(path_dst) == false) {
+ BKE_reportf(data->reports, RPT_INFO, "Path '%s' made absolute to '%s'", path_src, path_dst);
data->count_changed++;
}
else {
@@ -497,10 +499,6 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int
BPATH_TRAVERSE_POINTCACHE(smd->domain->ptcaches[0]);
}
}
- else if (md->type == eModifierType_Cloth) {
- ClothModifierData *clmd = (ClothModifierData *) md;
- BPATH_TRAVERSE_POINTCACHE(clmd->ptcaches);
- }
else if (md->type == eModifierType_Ocean) {
OceanModifierData *omd = (OceanModifierData *) md;
rewrite_path_fixed(omd->cachepath, visit_cb, absbase, bpath_user_data);
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index 2464b3b2668..4c75abf2ce9 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -68,7 +68,7 @@ static void brush_defaults(Brush *brush)
brush->blend = 0;
brush->flag = 0;
- brush->ob_mode = OB_MODE_ALL_PAINT;
+ brush->ob_mode = OB_MODE_ALL_BRUSH;
/* BRUSH SCULPT TOOL SETTINGS */
brush->weight = 1.0f; /* weight of brush 0 - 1.0 */
diff --git a/source/blender/blenkernel/intern/cache_library.c b/source/blender/blenkernel/intern/cache_library.c
new file mode 100644
index 00000000000..370a2fc1887
--- /dev/null
+++ b/source/blender/blenkernel/intern/cache_library.c
@@ -0,0 +1,2256 @@
+/*
+ * ***** 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) 2015 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/cache_library.c
+ * \ingroup bke
+ */
+
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#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 "BLI_utildefines.h"
+
+#include "DNA_cache_library_types.h"
+#include "DNA_group_types.h"
+#include "DNA_key_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_force.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_anim.h"
+#include "BKE_bvhutils.h"
+#include "BKE_cache_library.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_editstrands.h"
+#include "BKE_effect.h"
+#include "BKE_global.h"
+#include "BKE_group.h"
+#include "BKE_idprop.h"
+#include "BKE_key.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_modifier.h"
+#include "BKE_scene.h"
+#include "BKE_screen.h"
+#include "BKE_strands.h"
+
+#include "BLF_translation.h"
+
+#include "PTC_api.h"
+
+#include "BPH_mass_spring.h"
+
+CacheLibrary *BKE_cache_library_add(Main *bmain, const char *name)
+{
+ CacheLibrary *cachelib;
+ char basename[MAX_NAME];
+
+ cachelib = BKE_libblock_alloc(bmain, ID_CL, name);
+
+ BLI_strncpy(basename, cachelib->id.name+2, sizeof(basename));
+ BLI_filename_make_safe(basename);
+ BLI_snprintf(cachelib->output_filepath, sizeof(cachelib->output_filepath), "//cache/%s.%s", basename, PTC_get_default_archive_extension());
+
+ cachelib->source_mode = CACHE_LIBRARY_SOURCE_SCENE;
+ cachelib->display_mode = CACHE_LIBRARY_DISPLAY_MODIFIERS;
+ cachelib->display_flag = CACHE_LIBRARY_DISPLAY_MOTION | CACHE_LIBRARY_DISPLAY_CHILDREN;
+
+ /* cache everything by default */
+ cachelib->data_types = CACHE_TYPE_ALL;
+
+ return cachelib;
+}
+
+CacheLibrary *BKE_cache_library_copy(CacheLibrary *cachelib)
+{
+ CacheLibrary *cachelibn;
+
+ cachelibn = BKE_libblock_copy(&cachelib->id);
+
+ if (cachelibn->filter_group)
+ id_us_plus(&cachelibn->filter_group->id);
+
+ {
+ CacheModifier *md;
+ BLI_listbase_clear(&cachelibn->modifiers);
+ for (md = cachelib->modifiers.first; md; md = md->next) {
+ BKE_cache_modifier_copy(cachelibn, md);
+ }
+ }
+
+ cachelibn->archive_info = NULL;
+
+ if (cachelib->id.lib) {
+ BKE_id_lib_local_paths(G.main, cachelib->id.lib, &cachelibn->id);
+ }
+
+ return cachelibn;
+}
+
+void BKE_cache_library_free(CacheLibrary *cachelib)
+{
+ BKE_cache_modifier_clear(cachelib);
+
+ if (cachelib->filter_group)
+ id_us_min(&cachelib->filter_group->id);
+
+ if (cachelib->archive_info)
+ BKE_cache_archive_info_free(cachelib->archive_info);
+}
+
+void BKE_cache_library_unlink(CacheLibrary *UNUSED(cachelib))
+{
+}
+
+/* ========================================================================= */
+
+const char *BKE_cache_item_name_prefix(int type)
+{
+ /* note: avoid underscores and the like here,
+ * the prefixes must be unique and safe when combined with arbitrary strings!
+ */
+ switch (type) {
+ case CACHE_TYPE_OBJECT: return "OBJECT";
+ case CACHE_TYPE_DERIVED_MESH: return "MESH";
+ case CACHE_TYPE_HAIR: return "HAIR";
+ case CACHE_TYPE_HAIR_PATHS: return "HAIRPATHS";
+ case CACHE_TYPE_PARTICLES: return "PARTICLES";
+ default: BLI_assert(false); return NULL; break;
+ }
+}
+
+void BKE_cache_item_name(Object *ob, int type, int index, char *name)
+{
+ if (index >= 0)
+ sprintf(name, "%s_%s_%d", BKE_cache_item_name_prefix(type), ob->id.name+2, index);
+ else
+ sprintf(name, "%s_%s", BKE_cache_item_name_prefix(type), ob->id.name+2);
+}
+
+int BKE_cache_item_name_length(Object *ob, int type, int index)
+{
+ char *str_dummy = (char *)"";
+ if (index >= 0)
+ return BLI_snprintf(str_dummy, 0, "%s_%s_%d", BKE_cache_item_name_prefix(type), ob->id.name + 2, index);
+ else
+ return BLI_snprintf(str_dummy, 0, "%s_%s", BKE_cache_item_name_prefix(type), ob->id.name + 2);
+}
+
+eCacheReadSampleResult BKE_cache_read_result(int ptc_result)
+{
+ switch (ptc_result) {
+ case PTC_READ_SAMPLE_INVALID: return CACHE_READ_SAMPLE_INVALID;
+ case PTC_READ_SAMPLE_EARLY: return CACHE_READ_SAMPLE_EARLY;
+ case PTC_READ_SAMPLE_LATE: return CACHE_READ_SAMPLE_LATE;
+ case PTC_READ_SAMPLE_EXACT: return CACHE_READ_SAMPLE_EXACT;
+ case PTC_READ_SAMPLE_INTERPOLATED: return CACHE_READ_SAMPLE_INTERPOLATED;
+ default: BLI_assert(false); break; /* should never happen, enums out of sync? */
+ }
+ return CACHE_READ_SAMPLE_INVALID;
+}
+
+bool BKE_cache_library_validate_item(CacheLibrary *cachelib, Object *ob, int type, int index)
+{
+ if (!cachelib)
+ return false;
+
+ if (ELEM(type, CACHE_TYPE_DERIVED_MESH)) {
+ if (ob->type != OB_MESH)
+ return false;
+ }
+ else if (ELEM(type, CACHE_TYPE_PARTICLES, CACHE_TYPE_HAIR, CACHE_TYPE_HAIR_PATHS)) {
+ ParticleSystem *psys = BLI_findlink(&ob->particlesystem, index);
+
+ if (!psys)
+ return false;
+
+ if (ELEM(type, CACHE_TYPE_PARTICLES)) {
+ if (psys->part->type != PART_EMITTER)
+ return false;
+ }
+
+ if (ELEM(type, CACHE_TYPE_HAIR, CACHE_TYPE_HAIR_PATHS)) {
+ if (psys->part->type != PART_HAIR)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* ========================================================================= */
+
+/* unused
+ * filtering now only tags objects to exclude them from storing data,
+ * but does not actually remove them from the duplilist.
+ */
+#if 0
+void BKE_cache_library_filter_duplilist(CacheLibrary *cachelib, ListBase *duplilist)
+{
+ if (cachelib->filter_group) {
+ GroupObject *gob;
+
+ /* tag only filter group objects as valid */
+ BKE_main_id_tag_idcode(G.main, ID_OB, false);
+ for (gob = cachelib->filter_group->gobject.first; gob; gob = gob->next)
+ gob->ob->id.flag |= LIB_DOIT;
+ }
+ else {
+ /* all objects valid */
+ BKE_main_id_tag_idcode(G.main, ID_OB, true);
+ }
+
+ {
+ /* remove invalid duplis */
+ DupliObject *dob, *dob_next;
+ for (dob = duplilist->first; dob; dob = dob_next) {
+ dob_next = dob->next;
+
+ if (!(dob->ob->id.flag & LIB_DOIT)) {
+ BLI_remlink(duplilist, dob);
+ MEM_freeN(dob);
+ }
+ }
+ }
+
+ /* clear LIB_DOIT tags */
+ BKE_main_id_tag_idcode(G.main, ID_OB, false);
+}
+#endif
+
+void BKE_cache_library_tag_used_objects(CacheLibrary *cachelib)
+{
+ if (cachelib->filter_group) {
+ GroupObject *gob;
+
+ /* tag only filter group objects as valid */
+ BKE_main_id_tag_idcode(G.main, ID_OB, false);
+ for (gob = cachelib->filter_group->gobject.first; gob; gob = gob->next)
+ gob->ob->id.flag |= LIB_DOIT;
+ }
+ else {
+ /* all objects valid */
+ BKE_main_id_tag_idcode(G.main, ID_OB, true);
+ }
+}
+
+/* ========================================================================= */
+
+static IDProperty *cache_library_get_metadata(CacheLibrary *cachelib, const char *name, bool create)
+{
+ IDProperty *idprops = IDP_GetProperties((ID *)cachelib, create);
+ IDProperty *metadata = NULL;
+ if (idprops) {
+ metadata = IDP_GetPropertyFromGroup(idprops, name);
+ if (!metadata && create) {
+ IDPropertyTemplate val;
+ val.i = 0;
+ metadata = IDP_New(IDP_GROUP, &val, name);
+ IDP_AddToGroup(idprops, metadata);
+ }
+ }
+ return metadata;
+}
+
+IDProperty *BKE_cache_library_get_input_metadata(CacheLibrary *cachelib, bool create)
+{
+ return cache_library_get_metadata(cachelib, "input_metadata", create);
+}
+
+IDProperty *BKE_cache_library_get_output_metadata(CacheLibrary *cachelib, bool create)
+{
+ return cache_library_get_metadata(cachelib, "output_metadata", create);
+}
+
+BLI_INLINE bool path_is_dirpath(const char *path)
+{
+ /* last char is a slash? */
+ const char *last_slash = BLI_last_slash(path);
+ return last_slash ? (*(last_slash + 1) == '\0') : false;
+}
+
+bool BKE_cache_archive_path_test(CacheLibrary *cachelib, const char *path)
+{
+ if (BLI_path_is_rel(path)) {
+ if (!(G.relbase_valid || cachelib->id.lib))
+ return false;
+ }
+
+ return true;
+
+}
+
+void BKE_cache_archive_path_ex(const char *path, Library *lib, const char *default_filename, char *result, int max)
+{
+ char abspath[FILE_MAX];
+
+ result[0] = '\0';
+
+ if (BLI_path_is_rel(path)) {
+ if (G.relbase_valid || lib) {
+ const char *relbase = lib ? lib->filepath : G.main->name;
+
+ BLI_strncpy(abspath, path, sizeof(abspath));
+ BLI_path_abs(abspath, relbase);
+ }
+ else {
+ /* can't construct a valid path */
+ return;
+ }
+ }
+ else {
+ BLI_strncpy(abspath, path, sizeof(abspath));
+ }
+
+ if (abspath[0] != '\0') {
+ if (path_is_dirpath(abspath) || BLI_is_dir(abspath)) {
+ if (default_filename && default_filename[0] != '\0')
+ BLI_join_dirfile(result, max, abspath, default_filename);
+ }
+ else {
+ BLI_strncpy(result, abspath, max);
+ }
+ }
+}
+
+void BKE_cache_archive_input_path(CacheLibrary *cachelib, char *result, int max)
+{
+ BKE_cache_archive_path_ex(cachelib->input_filepath, cachelib->id.lib, NULL, result, max);
+}
+
+void BKE_cache_archive_output_path(CacheLibrary *cachelib, char *result, int max)
+{
+ BKE_cache_archive_path_ex(cachelib->output_filepath, cachelib->id.lib, cachelib->id.name+2, result, max);
+}
+
+static bool has_active_cache(CacheLibrary *cachelib)
+{
+ const bool is_baking = cachelib->flag & CACHE_LIBRARY_BAKING;
+
+ /* don't read results from output archive when baking */
+ if (!is_baking) {
+ if (cachelib->display_mode == CACHE_LIBRARY_DISPLAY_RESULT) {
+ return true;
+ }
+ }
+
+ if (ELEM(cachelib->source_mode, CACHE_LIBRARY_SOURCE_CACHE, CACHE_LIBRARY_DISPLAY_MODIFIERS)) {
+ return true;
+ }
+
+ return false;
+}
+
+static struct PTCReaderArchive *find_active_cache(Scene *scene, CacheLibrary *cachelib)
+{
+ char filename[FILE_MAX];
+ struct PTCReaderArchive *archive = NULL;
+
+ const bool is_baking = cachelib->flag & CACHE_LIBRARY_BAKING;
+
+ /* don't read results from output archive when baking */
+ if (!is_baking) {
+ if (cachelib->display_mode == CACHE_LIBRARY_DISPLAY_RESULT) {
+ /* try using the output cache */
+ BKE_cache_archive_output_path(cachelib, filename, sizeof(filename));
+ archive = PTC_open_reader_archive(scene, filename);
+ }
+ }
+
+ if (!archive && ELEM(cachelib->source_mode, CACHE_LIBRARY_SOURCE_CACHE, CACHE_LIBRARY_DISPLAY_MODIFIERS)) {
+ BKE_cache_archive_input_path(cachelib, filename, sizeof(filename));
+ archive = PTC_open_reader_archive(scene, filename);
+ }
+
+ /* get basic archive info */
+ if (cachelib->archive_info)
+ BKE_cache_archive_info_clear(cachelib->archive_info);
+ else
+ cachelib->archive_info = BKE_cache_archive_info_new();
+ BLI_strncpy(cachelib->archive_info->filepath, filename, sizeof(cachelib->archive_info->filepath));
+ if (archive) {
+ IDProperty *metadata = BKE_cache_library_get_input_metadata(cachelib, true);
+ PTC_get_archive_info(archive, cachelib->archive_info, metadata);
+ }
+
+ return archive;
+}
+
+void BKE_cache_library_get_read_flags(CacheLibrary *cachelib, bool use_render, bool for_display, bool *read_strands_motion, bool *read_strands_children)
+{
+ if (!use_render && for_display) {
+ *read_strands_motion = cachelib->display_flag & CACHE_LIBRARY_DISPLAY_MOTION;
+ *read_strands_children = cachelib->display_flag & CACHE_LIBRARY_DISPLAY_CHILDREN;
+ }
+ else {
+ *read_strands_motion = true;
+ *read_strands_children = true;
+ }
+}
+
+bool BKE_cache_read_dupli_cache(CacheLibrary *cachelib, DupliCache *dupcache,
+ Scene *scene, Group *dupgroup, float frame, bool use_render, bool for_display)
+{
+ bool read_strands_motion, read_strands_children, read_simdebug = G.debug & G_DEBUG_SIMDATA;
+ struct PTCReaderArchive *archive;
+ struct PTCReader *reader;
+
+ if (!dupcache)
+ return false;
+
+ dupcache->result = CACHE_READ_SAMPLE_INVALID;
+
+ if (!dupgroup || !cachelib)
+ return false;
+
+ archive = find_active_cache(scene, cachelib);
+ if (!archive)
+ return false;
+
+ PTC_reader_archive_use_render(archive, use_render);
+
+ BKE_cache_library_get_read_flags(cachelib, use_render, for_display, &read_strands_motion, &read_strands_children);
+ // TODO duplicache reader should only overwrite data that is not sequentially generated by modifiers (simulations) ...
+ reader = PTC_reader_duplicache(dupgroup->id.name, dupgroup, dupcache,
+ read_strands_motion, read_strands_children, read_simdebug);
+ PTC_reader_init(reader, archive);
+
+ dupcache->result = BKE_cache_read_result(PTC_read_sample(reader, frame));
+
+ PTC_reader_free(reader);
+ PTC_close_reader_archive(archive);
+
+ return (dupcache->result != CACHE_READ_SAMPLE_INVALID);
+}
+
+bool BKE_cache_read_dupli_object(CacheLibrary *cachelib, DupliObjectData *data,
+ Scene *scene, Object *ob, float frame, bool use_render, bool for_display)
+{
+ bool read_strands_motion, read_strands_children;
+ struct PTCReaderArchive *archive;
+ struct PTCReader *reader;
+ /*eCacheReadSampleResult result;*/ /* unused */
+
+ if (!data || !ob || !cachelib)
+ return false;
+
+ archive = find_active_cache(scene, cachelib);
+ if (!archive)
+ return false;
+
+ PTC_reader_archive_use_render(archive, use_render);
+
+ BKE_cache_library_get_read_flags(cachelib, use_render, for_display, &read_strands_motion, &read_strands_children);
+ reader = PTC_reader_duplicache_object(ob->id.name, ob, data, read_strands_motion, read_strands_children);
+ PTC_reader_init(reader, archive);
+
+ /*result = */BKE_cache_read_result(PTC_read_sample(reader, frame));
+
+ PTC_reader_free(reader);
+ PTC_close_reader_archive(archive);
+
+ return true;
+}
+
+
+void BKE_cache_library_dag_recalc_tag(EvaluationContext *UNUSED(eval_ctx), Main *bmain)
+{
+ CacheLibrary *cachelib;
+ for (cachelib = bmain->cache_library.first; cachelib; cachelib = cachelib->id.next) {
+ if (has_active_cache(cachelib))
+ DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA | OB_RECALC_TIME);
+ }
+}
+
+/* ========================================================================= */
+
+CacheModifierTypeInfo cache_modifier_types[NUM_CACHE_MODIFIER_TYPES];
+
+static CacheModifierTypeInfo *cache_modifier_type_get(eCacheModifier_Type type)
+{
+ return &cache_modifier_types[type];
+}
+
+static void cache_modifier_type_set(eCacheModifier_Type type, CacheModifierTypeInfo *mti)
+{
+ memcpy(&cache_modifier_types[type], mti, sizeof(CacheModifierTypeInfo));
+}
+
+const char *BKE_cache_modifier_type_name(eCacheModifier_Type type)
+{
+ return cache_modifier_type_get(type)->name;
+}
+
+const char *BKE_cache_modifier_type_struct_name(eCacheModifier_Type type)
+{
+ return cache_modifier_type_get(type)->struct_name;
+}
+
+int BKE_cache_modifier_type_struct_size(eCacheModifier_Type type)
+{
+ return cache_modifier_type_get(type)->struct_size;
+}
+
+/* ------------------------------------------------------------------------- */
+
+bool BKE_cache_modifier_unique_name(ListBase *modifiers, CacheModifier *md)
+{
+ if (modifiers && md) {
+ CacheModifierTypeInfo *mti = cache_modifier_type_get(md->type);
+
+ return BLI_uniquename(modifiers, md, DATA_(mti->name), '.', offsetof(CacheModifier, name), sizeof(md->name));
+ }
+ return false;
+}
+
+CacheModifier *BKE_cache_modifier_add(CacheLibrary *cachelib, const char *name, eCacheModifier_Type type)
+{
+ CacheModifierTypeInfo *mti = cache_modifier_type_get(type);
+
+ CacheModifier *md = MEM_callocN(mti->struct_size, "cache modifier");
+ md->type = type;
+
+ if (!name)
+ name = mti->name;
+ BLI_strncpy_utf8(md->name, name, sizeof(md->name));
+ /* make sure modifier has unique name */
+ BKE_cache_modifier_unique_name(&cachelib->modifiers, md);
+
+ if (mti->init)
+ mti->init(md);
+
+ BLI_addtail(&cachelib->modifiers, md);
+
+ return md;
+}
+
+void BKE_cache_modifier_remove(CacheLibrary *cachelib, CacheModifier *md)
+{
+ CacheModifierTypeInfo *mti = cache_modifier_type_get(md->type);
+
+ BLI_remlink(&cachelib->modifiers, md);
+
+ if (mti->free)
+ mti->free(md);
+
+ MEM_freeN(md);
+}
+
+void BKE_cache_modifier_clear(CacheLibrary *cachelib)
+{
+ CacheModifier *md, *md_next;
+ for (md = cachelib->modifiers.first; md; md = md_next) {
+ CacheModifierTypeInfo *mti = cache_modifier_type_get(md->type);
+ md_next = md->next;
+
+ if (mti->free)
+ mti->free(md);
+
+ MEM_freeN(md);
+ }
+
+ BLI_listbase_clear(&cachelib->modifiers);
+}
+
+CacheModifier *BKE_cache_modifier_copy(CacheLibrary *cachelib, CacheModifier *md)
+{
+ CacheModifierTypeInfo *mti = cache_modifier_type_get(md->type);
+
+ CacheModifier *tmd = MEM_dupallocN(md);
+
+ if (mti->copy)
+ mti->copy(md, tmd);
+
+ BLI_addtail(&cachelib->modifiers, tmd);
+
+ return tmd;
+}
+
+void BKE_cache_modifier_foreachIDLink(struct CacheLibrary *cachelib, struct CacheModifier *md, CacheModifier_IDWalkFunc walk, void *userdata)
+{
+ CacheModifierTypeInfo *mti = cache_modifier_type_get(md->type);
+
+ if (mti->foreachIDLink)
+ mti->foreachIDLink(md, cachelib, walk, userdata);
+}
+
+void BKE_cache_process_dupli_cache(CacheLibrary *cachelib, CacheProcessData *data,
+ Scene *scene, Group *dupgroup, float frame_prev, float frame,
+ bool do_modifiers, bool do_strands_child_deform, bool do_strands_motion)
+{
+ CacheProcessContext ctx;
+ CacheModifier *md;
+
+ ctx.bmain = G.main;
+ ctx.scene = scene;
+ ctx.cachelib = cachelib;
+ ctx.group = dupgroup;
+
+ if (do_modifiers) {
+ for (md = cachelib->modifiers.first; md; md = md->next) {
+ CacheModifierTypeInfo *mti = cache_modifier_type_get(md->type);
+
+ // TODO parent modifiers only here
+ if (mti->process)
+ mti->process(md, &ctx, data, frame, frame_prev, eCacheProcessFlag_DoStrands);
+ }
+ }
+
+ /* deform child strands to follow parent motion */
+ if (do_modifiers || do_strands_child_deform) {
+ struct DupliCacheIterator *it;
+
+ it = BKE_dupli_cache_iter_new(data->dupcache);
+ for (; BKE_dupli_cache_iter_valid(it); BKE_dupli_cache_iter_next(it)) {
+ DupliObjectData *dobdata = BKE_dupli_cache_iter_get(it);
+ DupliObjectDataStrands *link;
+
+ for (link = dobdata->strands.first; link; link = link->next) {
+ if (link->strands_children)
+ BKE_strands_children_deform(link->strands_children, link->strands, do_strands_motion);
+ }
+ }
+ BKE_dupli_cache_iter_free(it);
+ }
+
+ if (do_modifiers) {
+ for (md = cachelib->modifiers.first; md; md = md->next) {
+ CacheModifierTypeInfo *mti = cache_modifier_type_get(md->type);
+
+ // TODO child modifiers only here
+ if (mti->process)
+ mti->process(md, &ctx, data, frame, frame_prev, eCacheProcessFlag_DoStrandsChildren);
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+static ForceFieldVertexCache *forcefield_vertex_cache_new(void);
+static void forcefield_vertex_cache_free(ForceFieldVertexCache *cache);
+static void forcefield_vertex_cache_clear(ForceFieldVertexCache *cache);
+static void forcefield_vertex_cache_init(ForceFieldVertexCache *cache, float frame, DerivedMesh *dm);
+
+static void effector_set_mesh(CacheEffector *eff, Object *ob, DerivedMesh *dm, bool create_dm, bool create_bvhtree, bool world_space)
+{
+ if (create_dm && dm) {
+ unsigned int numverts, i;
+ MVert *mvert, *mv;
+
+ eff->dm = CDDM_copy(dm);
+ if (!eff->dm)
+ return;
+
+ DM_ensure_tessface(eff->dm);
+ CDDM_calc_normals(eff->dm);
+
+ numverts = eff->dm->getNumVerts(eff->dm);
+ mvert = eff->dm->getVertArray(eff->dm);
+
+ if (world_space) {
+ /* convert to world coordinates */
+ for (i = 0, mv = mvert; i < numverts; ++i, ++mv) {
+ mul_m4_v3(ob->obmat, mv->co);
+ }
+ }
+
+ if (create_bvhtree) {
+ if (eff->treedata)
+ free_bvhtree_from_mesh(eff->treedata);
+ else
+ eff->treedata = MEM_callocN(sizeof(BVHTreeFromMesh), "cache effector bvhtree data");
+
+ bvhtree_from_mesh_faces(eff->treedata, eff->dm, 0.0, 2, 6);
+ }
+ }
+}
+
+static void effector_set_instances(CacheEffector *eff, Object *ob, float obmat[4][4], ListBase *duplilist)
+{
+ DupliObject *dob;
+
+ for (dob = duplilist->first; dob; dob = dob->next) {
+ CacheEffectorInstance *inst;
+
+ if (dob->ob != ob)
+ continue;
+
+ inst = MEM_callocN(sizeof(CacheEffectorInstance), "cache effector instance");
+ mul_m4_m4m4(inst->mat, obmat, dob->mat);
+ invert_m4_m4(inst->imat, inst->mat);
+
+ BLI_addtail(&eff->instances, inst);
+ }
+}
+
+static bool forcefield_get_effector(DupliCache *dupcache, float obmat[4][4], ForceFieldCacheModifier *ffmd, CacheEffector *eff)
+{
+ DupliObjectData *dobdata;
+
+ if (!ffmd->object)
+ return false;
+
+ dobdata = BKE_dupli_cache_find_data(dupcache, ffmd->object);
+ if (!dobdata)
+ return false;
+
+ effector_set_mesh(eff, dobdata->ob, dobdata->dm, true, true, false);
+ effector_set_instances(eff, dobdata->ob, obmat, &dupcache->duplilist);
+
+ switch (ffmd->type) {
+ case eForceFieldCacheModifier_Type_Deflect:
+ eff->type = eCacheEffector_Type_Deflect;
+ break;
+ case eForceFieldCacheModifier_Type_Drag:
+ eff->type = eCacheEffector_Type_Drag;
+ break;
+ }
+
+ eff->strength = ffmd->strength;
+ eff->falloff = ffmd->falloff;
+ eff->mindist = ffmd->min_distance;
+ eff->maxdist = ffmd->max_distance;
+ eff->double_sided = ffmd->flag & eForceFieldCacheModifier_Flag_DoubleSided;
+ eff->vertex_cache = ffmd->vertex_cache;
+
+ return true;
+}
+
+int BKE_cache_effectors_get(CacheEffector *effectors, int max, CacheLibrary *cachelib, DupliCache *dupcache, float obmat[4][4])
+{
+ CacheModifier *md;
+ int tot = 0;
+
+ if (tot >= max)
+ return tot;
+
+ memset(effectors, 0, sizeof(CacheEffector) * max);
+
+ for (md = cachelib->modifiers.first; md; md = md->next) {
+ switch (md->type) {
+ case eCacheModifierType_ForceField: {
+ ForceFieldCacheModifier *ffmd = (ForceFieldCacheModifier *)md;
+ if (forcefield_get_effector(dupcache, obmat, ffmd, &effectors[tot]))
+ tot++;
+ break;
+ }
+ }
+
+ BLI_assert(tot <= max);
+ if (tot == max)
+ break;
+ }
+
+ return tot;
+}
+
+void BKE_cache_effectors_free(CacheEffector *effectors, int tot)
+{
+ CacheEffector *eff;
+ int i;
+
+ for (i = 0, eff = effectors; i < tot; ++i, ++eff) {
+ BLI_freelistN(&eff->instances);
+
+ if (eff->treedata) {
+ free_bvhtree_from_mesh(eff->treedata);
+ MEM_freeN(eff->treedata);
+ }
+
+ if (eff->dm) {
+ eff->dm->release(eff->dm);
+ }
+ }
+}
+
+static bool forcefield_velocity_update(DupliCache *dupcache, float obmat[4][4], float frame, ForceFieldCacheModifier *ffmd)
+{
+ DupliObjectData *dobdata;
+ bool use_vertex_cache = false;
+
+ if (!ffmd->object)
+ return false;
+
+ dobdata = BKE_dupli_cache_find_data(dupcache, ffmd->object);
+ if (!dobdata)
+ return false;
+
+ switch (ffmd->type) {
+ case eForceFieldCacheModifier_Type_Drag:
+ use_vertex_cache = true;
+ break;
+ }
+
+ if (use_vertex_cache) {
+ if (!ffmd->vertex_cache) {
+ ffmd->vertex_cache = forcefield_vertex_cache_new();
+ }
+
+ forcefield_vertex_cache_init(ffmd->vertex_cache, frame, dobdata->dm);
+ {
+ int i;
+ for (i = 0; i < ffmd->vertex_cache->totvert; ++i) {
+ float x[3], v[3];
+ mul_v3_m4v3(x, obmat, ffmd->vertex_cache->co_prev[i]);
+ copy_v3_v3(v, ffmd->vertex_cache->vel[i]);
+ mul_mat3_m4_v3(obmat, v);
+ BKE_sim_debug_data_add_vector(x, v, 1,1,0, "hairsim", 45232, i);
+ }
+ }
+ }
+
+ return true;
+}
+
+void BKE_cache_effector_velocity_update(CacheLibrary *cachelib, DupliCache *dupcache, float obmat[4][4], float frame)
+{
+ CacheModifier *md;
+
+ for (md = cachelib->modifiers.first; md; md = md->next) {
+ switch (md->type) {
+ case eCacheModifierType_ForceField:
+ forcefield_velocity_update(dupcache, obmat, frame, (ForceFieldCacheModifier *)md);
+ break;
+ }
+ }
+}
+
+static bool cache_effector_falloff(const CacheEffector *eff, float distance, float *r_factor)
+{
+ float mindist = eff->mindist;
+ float maxdist = eff->maxdist;
+ float falloff = eff->falloff;
+ float range = maxdist - mindist;
+
+ if (r_factor) *r_factor = 0.0f;
+
+ if (range <= 0.0f)
+ return false;
+
+ if (distance > eff->maxdist)
+ return false;
+ CLAMP_MIN(distance, eff->mindist);
+ CLAMP_MIN(falloff, 0.0f);
+
+ if (r_factor) *r_factor = powf(1.0f - (distance - mindist) / range, falloff);
+ return true;
+}
+
+typedef struct CacheEffectorTessfaceData {
+ int face_index;
+ MFace *mface;
+ MVert *mvert[4];
+ float weight[4];
+} CacheEffectorTessfaceData;
+
+static void cache_effector_velocity(const CacheEffector *eff, CacheEffectorInstance *inst, CacheEffectorTessfaceData *tessface, float vel[3])
+{
+ zero_v3(vel);
+
+ if (!eff->vertex_cache)
+ return;
+
+ BLI_assert(eff->vertex_cache->totvert == eff->dm->getNumVerts(eff->dm));
+
+ madd_v3_v3fl(vel, eff->vertex_cache->vel[tessface->mface->v1], tessface->weight[0]);
+ madd_v3_v3fl(vel, eff->vertex_cache->vel[tessface->mface->v2], tessface->weight[1]);
+ madd_v3_v3fl(vel, eff->vertex_cache->vel[tessface->mface->v3], tessface->weight[2]);
+ if (tessface->mface->v4)
+ madd_v3_v3fl(vel, eff->vertex_cache->vel[tessface->mface->v4], tessface->weight[3]);
+
+ /* vertex cache velocities are in local space, effector results are all expected in world space */
+ mul_mat3_m4_v3(inst->mat, vel);
+}
+
+static bool cache_effector_find_nearest(CacheEffector *eff, CacheEffectorInstance *inst, CacheEffectorPoint *point,
+ float r_vec[3], float r_nor[3], float *r_dist, bool *r_inside,
+ CacheEffectorTessfaceData *r_tessface)
+{
+ const bool need_inside = r_dist || r_inside;
+
+ BVHTreeNearest nearest = {0, };
+ float world_near_co[3], world_near_no[3];
+ float co[3], vec[3], dist;
+ bool inside;
+
+ if (!eff->treedata)
+ return false;
+
+ nearest.dist_sq = FLT_MAX;
+
+ /* lookup in object space */
+ mul_v3_m4v3(co, inst->imat, point->x);
+
+ BLI_bvhtree_find_nearest(eff->treedata->tree, co, &nearest, eff->treedata->nearest_callback, eff->treedata);
+ if (nearest.index < 0)
+ return false;
+
+ /* convert back to world space */
+ mul_v3_m4v3(world_near_co, inst->mat, nearest.co);
+ copy_v3_v3(world_near_no, nearest.no);
+ mul_mat3_m4_v3(inst->mat, world_near_no);
+
+ sub_v3_v3v3(vec, point->x, world_near_co);
+ dist = normalize_v3(vec);
+
+ if (need_inside) {
+ inside = false;
+ if (!eff->double_sided) {
+ if (dot_v3v3(vec, world_near_no) < 0.0f) {
+ dist = -dist;
+ inside = true;
+ }
+ }
+ }
+
+ if (r_vec) copy_v3_v3(r_vec, vec);
+ if (r_nor) copy_v3_v3(r_nor, world_near_no);
+ if (r_dist) *r_dist = dist;
+ if (r_inside) *r_inside = inside;
+
+ if (r_tessface && eff->dm) {
+ CacheEffectorTessfaceData *t = r_tessface;
+ DerivedMesh *dm = eff->dm;
+ MFace *mf = dm->getTessFaceArray(dm) + nearest.index;
+ MVert *mverts = dm->getVertArray(dm);
+
+ t->face_index = nearest.index;
+ t->mface = mf;
+ t->mvert[0] = &mverts[mf->v1];
+ t->mvert[1] = &mverts[mf->v2];
+ t->mvert[2] = &mverts[mf->v3];
+
+ if (mf->v4) {
+ t->mvert[3] = &mverts[mf->v4];
+ interp_weights_face_v3(t->weight, t->mvert[0]->co, t->mvert[1]->co, t->mvert[2]->co, t->mvert[3]->co, nearest.co);
+ }
+ else {
+ t->mvert[3] = NULL;
+ interp_weights_face_v3(t->weight, t->mvert[0]->co, t->mvert[1]->co, t->mvert[2]->co, NULL, nearest.co);
+ }
+ }
+
+ return true;
+}
+
+static bool cache_effector_deflect(CacheEffector *eff, CacheEffectorInstance *inst, CacheEffectorPoint *point, CacheEffectorResult *result)
+{
+ float vec[3], dist, falloff;
+ bool inside;
+
+ if (!cache_effector_find_nearest(eff, inst, point, vec, NULL, &dist, &inside, NULL))
+ return false;
+ if (!cache_effector_falloff(eff, dist, &falloff))
+ return false;
+
+ mul_v3_v3fl(result->f, vec, eff->strength * falloff);
+ if (inside)
+ negate_v3(result->f);
+ return true;
+}
+
+static bool cache_effector_drag(CacheEffector *eff, CacheEffectorInstance *inst, CacheEffectorPoint *point, CacheEffectorResult *result)
+{
+ float vec[3], dist, vel[3];
+ float falloff;
+ CacheEffectorTessfaceData facedata;
+
+ if (!cache_effector_find_nearest(eff, inst, point, vec, NULL, &dist, NULL, &facedata))
+ return false;
+ if (!cache_effector_falloff(eff, dist, &falloff))
+ return false;
+
+ cache_effector_velocity(eff, inst, &facedata, vel);
+
+ /* relative velocity */
+ sub_v3_v3v3(vel, point->v, vel);
+
+ mul_v3_v3fl(result->f, vel, -eff->strength * falloff);
+
+ return true;
+}
+
+static void cache_effector_result_init(CacheEffectorResult *result)
+{
+ zero_v3(result->f);
+}
+
+static void cache_effector_result_add(CacheEffectorResult *result, const CacheEffectorResult *other)
+{
+ add_v3_v3(result->f, other->f);
+}
+
+int BKE_cache_effectors_eval_ex(CacheEffector *effectors, int tot, CacheEffectorPoint *point, CacheEffectorResult *result,
+ bool (*filter)(void *, CacheEffector *), void *filter_data)
+{
+ CacheEffector *eff;
+ int i, applied = 0;
+
+ cache_effector_result_init(result);
+
+ for (i = 0, eff = effectors; i < tot; ++i, ++eff) {
+ const eCacheEffector_Type type = eff->type;
+ CacheEffectorInstance *inst;
+
+ for (inst = eff->instances.first; inst; inst = inst->next) {
+ CacheEffectorResult inst_result;
+ cache_effector_result_init(&inst_result);
+
+ if (filter && !filter(filter_data, eff))
+ continue;
+
+ switch (type) {
+ case eCacheEffector_Type_Deflect:
+ if (cache_effector_deflect(eff, inst, point, &inst_result)) {
+ cache_effector_result_add(result, &inst_result);
+ ++applied;
+ }
+ break;
+ case eCacheEffector_Type_Drag:
+ if (cache_effector_drag(eff, inst, point, &inst_result)) {
+ cache_effector_result_add(result, &inst_result);
+ ++applied;
+ }
+ break;
+ }
+ }
+ }
+
+ return applied;
+}
+
+int BKE_cache_effectors_eval(CacheEffector *effectors, int tot, CacheEffectorPoint *point, CacheEffectorResult *result)
+{
+ return BKE_cache_effectors_eval_ex(effectors, tot, point, result, NULL, NULL);
+}
+
+/* ========================================================================= */
+
+bool BKE_cache_modifier_find_object(DupliCache *dupcache, Object *ob, DupliObjectData **r_data)
+{
+ DupliObjectData *dobdata;
+
+ if (!ob)
+ return false;
+ dobdata = BKE_dupli_cache_find_data(dupcache, ob);
+ if (!dobdata)
+ return false;
+
+ if (r_data) *r_data = dobdata;
+ return true;
+}
+
+bool BKE_cache_modifier_find_strands(DupliCache *dupcache, Object *ob, int hair_system, DupliObjectData **r_data, Strands **r_strands, StrandsChildren **r_children, const char **r_name)
+{
+ DupliObjectData *dobdata;
+ ParticleSystem *psys;
+ DupliObjectDataStrands *link;
+ Strands *strands;
+ StrandsChildren *children;
+
+ if (!ob)
+ return false;
+ dobdata = BKE_dupli_cache_find_data(dupcache, ob);
+ if (!dobdata)
+ return false;
+
+ psys = BLI_findlink(&ob->particlesystem, hair_system);
+ if (!psys || (psys->part->type != PART_HAIR))
+ return false;
+
+ strands = NULL;
+ children = NULL;
+ for (link = dobdata->strands.first; link; link = link->next) {
+ if (link->strands && STREQ(link->name, psys->name)) {
+ strands = link->strands;
+ children = link->strands_children;
+ break;
+ }
+ }
+ if ((r_strands && !strands) || (r_children && !children))
+ return false;
+
+ if (r_data) *r_data = dobdata;
+ if (r_strands) *r_strands = strands;
+ if (r_children) *r_children = children;
+ if (r_name) *r_name = psys->name;
+ return true;
+}
+
+static void hairsim_params_init(HairSimParams *params)
+{
+ params->timescale = 1.0f;
+ params->substeps = 5;
+
+ params->mass = 0.3f;
+ params->drag = 0.1f;
+
+ params->stretch_stiffness = 10000.0f;
+ params->stretch_damping = 0.1f;
+ params->bend_stiffness = 100.0f;
+ params->bend_damping = 1.0f;
+ params->goal_stiffness = 0.0f;
+ params->goal_damping = 1.0f;
+ {
+ CurveMapping *cm = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ cm->cm[0].curve[0].x = 0.0f;
+ cm->cm[0].curve[0].y = 1.0f;
+ cm->cm[0].curve[1].x = 1.0f;
+ cm->cm[0].curve[1].y = 0.0f;
+ curvemapping_changed_all(cm);
+ params->goal_stiffness_mapping = cm;
+ }
+ {
+ CurveMapping *cm = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ cm->cm[0].curve[0].x = 0.0f;
+ cm->cm[0].curve[0].y = 1.0f;
+ cm->cm[0].curve[1].x = 1.0f;
+ cm->cm[0].curve[1].y = 1.0f;
+ curvemapping_changed_all(cm);
+ params->bend_stiffness_mapping = cm;
+ }
+
+ params->effector_weights = BKE_add_effector_weights(NULL);
+}
+
+static void hairsim_init(HairSimCacheModifier *hsmd)
+{
+ hsmd->object = NULL;
+ hsmd->hair_system = -1;
+
+ hairsim_params_init(&hsmd->sim_params);
+}
+
+static void hairsim_copy(HairSimCacheModifier *hsmd, HairSimCacheModifier *thsmd)
+{
+ if (hsmd->sim_params.effector_weights)
+ thsmd->sim_params.effector_weights = MEM_dupallocN(hsmd->sim_params.effector_weights);
+ if (hsmd->sim_params.goal_stiffness_mapping)
+ thsmd->sim_params.goal_stiffness_mapping = curvemapping_copy(hsmd->sim_params.goal_stiffness_mapping);
+ if (hsmd->sim_params.bend_stiffness_mapping)
+ thsmd->sim_params.bend_stiffness_mapping = curvemapping_copy(hsmd->sim_params.bend_stiffness_mapping);
+}
+
+static void hairsim_free(HairSimCacheModifier *hsmd)
+{
+ if (hsmd->sim_params.effector_weights)
+ MEM_freeN(hsmd->sim_params.effector_weights);
+ if (hsmd->sim_params.goal_stiffness_mapping)
+ curvemapping_free(hsmd->sim_params.goal_stiffness_mapping);
+ if (hsmd->sim_params.bend_stiffness_mapping)
+ curvemapping_free(hsmd->sim_params.bend_stiffness_mapping);
+}
+
+static void hairsim_foreach_id_link(HairSimCacheModifier *hsmd, CacheLibrary *cachelib, CacheModifier_IDWalkFunc walk, void *userdata)
+{
+ walk(userdata, cachelib, &hsmd->modifier, (ID **)(&hsmd->object));
+ if (hsmd->sim_params.effector_weights)
+ walk(userdata, cachelib, &hsmd->modifier, (ID **)(&hsmd->sim_params.effector_weights->group));
+}
+
+static void hairsim_process(HairSimCacheModifier *hsmd, CacheProcessContext *ctx, CacheProcessData *data, int frame, int frame_prev, int process_flag)
+{
+#define MAX_CACHE_EFFECTORS 64
+
+ Object *ob = hsmd->object;
+ Strands *strands;
+ float mat[4][4];
+ ListBase *effectors;
+ CacheEffector cache_effectors[MAX_CACHE_EFFECTORS];
+ int tot_cache_effectors;
+ struct Implicit_Data *solver_data;
+
+ /* only applies to parent strands */
+ if (!(process_flag & eCacheProcessFlag_DoStrands))
+ return;
+
+ if (!BKE_cache_modifier_find_strands(data->dupcache, ob, hsmd->hair_system, NULL, &strands, NULL, NULL))
+ return;
+
+ /* Note: motion state data should always be created regardless of actual sim.
+ * This is necessary so the cache writer actually writes the first (empty) sample
+ * and the samples get mapped correctly to frames when reading.
+ */
+ BKE_strands_add_motion_state(strands);
+
+ /* skip first step and potential backward steps */
+ if (frame > frame_prev) {
+ if (hsmd->sim_params.flag & eHairSimParams_Flag_UseGoalStiffnessCurve && hsmd->sim_params.goal_stiffness_mapping)
+ curvemapping_changed_all(hsmd->sim_params.goal_stiffness_mapping);
+ if (hsmd->sim_params.flag & eHairSimParams_Flag_UseBendStiffnessCurve && hsmd->sim_params.bend_stiffness_mapping)
+ curvemapping_changed_all(hsmd->sim_params.bend_stiffness_mapping);
+
+ if (ob)
+ mul_m4_m4m4(mat, data->mat, ob->obmat);
+ else
+ copy_m4_m4(mat, data->mat);
+
+ BKE_cache_effector_velocity_update(ctx->cachelib, data->dupcache, data->mat, (float)frame);
+
+ solver_data = BPH_strands_solver_create(strands, &hsmd->sim_params);
+ effectors = pdInitEffectors_ex(ctx->scene, ob, NULL, data->lay, hsmd->sim_params.effector_weights, true);
+ tot_cache_effectors = BKE_cache_effectors_get(cache_effectors, MAX_CACHE_EFFECTORS, ctx->cachelib, data->dupcache, data->mat);
+
+ BPH_strands_solve(strands, mat, solver_data, &hsmd->sim_params, (float)frame, (float)frame_prev, ctx->scene, effectors, cache_effectors, tot_cache_effectors);
+
+ pdEndEffectors(&effectors);
+ BKE_cache_effectors_free(cache_effectors, tot_cache_effectors);
+ BPH_mass_spring_solver_free(solver_data);
+ }
+
+#undef MAX_CACHE_EFFECTORS
+}
+
+CacheModifierTypeInfo cacheModifierType_HairSimulation = {
+ /* name */ "HairSimulation",
+ /* structName */ "HairSimCacheModifier",
+ /* structSize */ sizeof(HairSimCacheModifier),
+
+ /* copy */ (CacheModifier_CopyFunc)hairsim_copy,
+ /* foreachIDLink */ (CacheModifier_ForeachIDLinkFunc)hairsim_foreach_id_link,
+ /* process */ (CacheModifier_ProcessFunc)hairsim_process,
+ /* init */ (CacheModifier_InitFunc)hairsim_init,
+ /* free */ (CacheModifier_FreeFunc)hairsim_free,
+};
+
+/* ------------------------------------------------------------------------- */
+
+static ForceFieldVertexCache *forcefield_vertex_cache_new(void)
+{
+ ForceFieldVertexCache *cache = MEM_callocN(sizeof(ForceFieldVertexCache), "force field vertex cache");
+ return cache;
+}
+
+static void forcefield_vertex_cache_free(ForceFieldVertexCache *cache)
+{
+ if (cache->co_prev)
+ MEM_freeN(cache->co_prev);
+ if (cache->vel)
+ MEM_freeN(cache->vel);
+ MEM_freeN(cache);
+}
+
+static void forcefield_vertex_cache_clear(ForceFieldVertexCache *cache)
+{
+ if (cache->co_prev)
+ MEM_freeN(cache->co_prev);
+ if (cache->vel)
+ MEM_freeN(cache->vel);
+ memset(cache, 0, sizeof(ForceFieldVertexCache));
+}
+
+static void forcefield_vertex_cache_init(ForceFieldVertexCache *cache, float frame, DerivedMesh *dm)
+{
+ MVert *mvert = dm->getVertArray(dm);
+ float dframe = frame - cache->frame_prev;
+ float inv_dframe = dframe > 0.0f ? 1.0f / dframe : 0.0f;
+ bool has_co_prev = (cache->co_prev != NULL);
+ int totvert = dm->getNumVerts(dm);
+ int i;
+
+ if (cache->totvert != totvert) {
+ forcefield_vertex_cache_clear(cache);
+ dframe = 0.0f;
+ }
+
+ if (!cache->co_prev)
+ cache->co_prev = MEM_mallocN(sizeof(float) * 3 * totvert, "force field vertex cache vertices");
+ if (!cache->vel)
+ cache->vel = MEM_mallocN(sizeof(float) * 3 * totvert, "force field vertex cache vertices");
+
+ for (i = 0; i < totvert; ++i) {
+ if (has_co_prev) {
+ sub_v3_v3v3(cache->vel[i], mvert[i].co, cache->co_prev[i]);
+ mul_v3_fl(cache->vel[i], inv_dframe);
+ }
+ else {
+ zero_v3(cache->vel[i]);
+ }
+
+ copy_v3_v3(cache->co_prev[i], mvert[i].co);
+ }
+ cache->frame_prev = frame;
+ cache->totvert = totvert;
+}
+
+static void forcefield_init(ForceFieldCacheModifier *ffmd)
+{
+ ffmd->object = NULL;
+
+ ffmd->vertex_cache = NULL;
+
+ ffmd->strength = 0.0f;
+ ffmd->falloff = 1.0f;
+ ffmd->min_distance = 0.0f;
+ ffmd->max_distance = 1.0f;
+}
+
+static void forcefield_copy(ForceFieldCacheModifier *UNUSED(ffmd), ForceFieldCacheModifier *tffmd)
+{
+ tffmd->vertex_cache = NULL;
+}
+
+static void forcefield_free(ForceFieldCacheModifier *ffmd)
+{
+ if (ffmd->vertex_cache) {
+ forcefield_vertex_cache_free(ffmd->vertex_cache);
+ ffmd->vertex_cache = NULL;
+ }
+}
+
+static void forcefield_foreach_id_link(ForceFieldCacheModifier *ffmd, CacheLibrary *cachelib, CacheModifier_IDWalkFunc walk, void *userdata)
+{
+ walk(userdata, cachelib, &ffmd->modifier, (ID **)(&ffmd->object));
+}
+
+CacheModifierTypeInfo cacheModifierType_ForceField = {
+ /* name */ "ForceField",
+ /* structName */ "ForceFieldCacheModifier",
+ /* structSize */ sizeof(ForceFieldCacheModifier),
+
+ /* copy */ (CacheModifier_CopyFunc)forcefield_copy,
+ /* foreachIDLink */ (CacheModifier_ForeachIDLinkFunc)forcefield_foreach_id_link,
+ /* process */ (CacheModifier_ProcessFunc)NULL,
+ /* init */ (CacheModifier_InitFunc)forcefield_init,
+ /* free */ (CacheModifier_FreeFunc)forcefield_free,
+};
+
+/* ------------------------------------------------------------------------- */
+
+static void shrinkwrap_init(ShrinkWrapCacheModifier *smd)
+{
+ smd->object = NULL;
+ smd->hair_system = -1;
+}
+
+static void shrinkwrap_copy(ShrinkWrapCacheModifier *UNUSED(smd), ShrinkWrapCacheModifier *UNUSED(tsmd))
+{
+}
+
+static void shrinkwrap_free(ShrinkWrapCacheModifier *UNUSED(smd))
+{
+}
+
+static void shrinkwrap_foreach_id_link(ShrinkWrapCacheModifier *smd, CacheLibrary *cachelib, CacheModifier_IDWalkFunc walk, void *userdata)
+{
+ walk(userdata, cachelib, &smd->modifier, (ID **)(&smd->object));
+ walk(userdata, cachelib, &smd->modifier, (ID **)(&smd->target));
+}
+
+typedef struct ShrinkWrapCacheData {
+ DerivedMesh *dm;
+ BVHTreeFromMesh treedata;
+
+ ListBase instances;
+} ShrinkWrapCacheData;
+
+typedef struct ShrinkWrapCacheInstance {
+ struct ShrinkWrapCacheInstance *next, *prev;
+
+ float mat[4][4];
+ float imat[4][4];
+} ShrinkWrapCacheInstance;
+
+static void shrinkwrap_data_get_bvhtree(ShrinkWrapCacheData *data, DerivedMesh *dm, bool create_bvhtree)
+{
+ data->dm = CDDM_copy(dm);
+ if (!data->dm)
+ return;
+
+ DM_ensure_tessface(data->dm);
+ CDDM_calc_normals(data->dm);
+
+ if (create_bvhtree) {
+ bvhtree_from_mesh_faces(&data->treedata, data->dm, 0.0, 2, 6);
+ }
+}
+
+static void shrinkwrap_data_get_instances(ShrinkWrapCacheData *data, Object *ob, float obmat[4][4], ListBase *duplilist)
+{
+ if (duplilist) {
+ DupliObject *dob;
+
+ for (dob = duplilist->first; dob; dob = dob->next) {
+ ShrinkWrapCacheInstance *inst;
+
+ if (dob->ob != ob)
+ continue;
+
+ inst = MEM_callocN(sizeof(ShrinkWrapCacheInstance), "shrink wrap instance");
+ mul_m4_m4m4(inst->mat, obmat, dob->mat);
+ invert_m4_m4(inst->imat, inst->mat);
+
+ BLI_addtail(&data->instances, inst);
+ }
+ }
+ else {
+ ShrinkWrapCacheInstance *inst = MEM_callocN(sizeof(ShrinkWrapCacheInstance), "shrink wrap instance");
+ mul_m4_m4m4(inst->mat, obmat, ob->obmat);
+ invert_m4_m4(inst->imat, inst->mat);
+
+ BLI_addtail(&data->instances, inst);
+ }
+}
+
+static void shrinkwrap_data_free(ShrinkWrapCacheData *data)
+{
+ BLI_freelistN(&data->instances);
+
+ free_bvhtree_from_mesh(&data->treedata);
+
+ if (data->dm) {
+ data->dm->release(data->dm);
+ }
+}
+
+static void shrinkwrap_apply_vertex(ShrinkWrapCacheModifier *UNUSED(smd), ShrinkWrapCacheData *data, ShrinkWrapCacheInstance *inst, const float *point, float *out)
+{
+ BVHTreeNearest nearest = {0, };
+ float co[3], near_co[3], near_no[3];
+
+ if (!data->treedata.tree)
+ return;
+
+ nearest.index = -1;
+ nearest.dist_sq = FLT_MAX;
+
+ /* lookup in target space */
+ mul_v3_m4v3(co, inst->imat, point);
+
+ BLI_bvhtree_find_nearest(data->treedata.tree, co, &nearest, data->treedata.nearest_callback, &data->treedata);
+ if (nearest.index < 0)
+ return;
+
+ /* convert back to world space */
+ mul_v3_m4v3(near_co, inst->mat, nearest.co);
+ copy_v3_v3(near_no, nearest.no);
+ mul_mat3_m4_v3(inst->mat, near_no);
+
+ {
+ float vec[3];
+
+ sub_v3_v3v3(vec, point, near_co);
+
+ /* project along the distance vector */
+ if (dot_v3v3(vec, near_no) < 0.0f) {
+ copy_v3_v3(out, near_co);
+ }
+ }
+}
+
+static void shrinkwrap_apply(ShrinkWrapCacheModifier *smd, ShrinkWrapCacheData *data, Strands *strands, StrandsChildren *children, bool do_motion)
+{
+ /* XXX this is not great, the result depends on order of instances in the duplilist ...
+ * but good enough for single instance use case.
+ */
+ ShrinkWrapCacheInstance *inst;
+ for (inst = data->instances.first; inst; inst = inst->next) {
+
+ if (strands) {
+ StrandIterator it_strand;
+ for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ StrandVertexIterator it_vert;
+ for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) {
+ if (do_motion && strands->state)
+ shrinkwrap_apply_vertex(smd, data, inst, it_vert.state->co, it_vert.state->co);
+ else
+ shrinkwrap_apply_vertex(smd, data, inst, it_vert.vertex->co, it_vert.vertex->co);
+ }
+ }
+ }
+
+ if (children) {
+ StrandChildIterator it_strand;
+ for (BKE_strand_child_iter_init(&it_strand, children); BKE_strand_child_iter_valid(&it_strand); BKE_strand_child_iter_next(&it_strand)) {
+ StrandChildVertexIterator it_vert;
+ for (BKE_strand_child_vertex_iter_init(&it_vert, &it_strand); BKE_strand_child_vertex_iter_valid(&it_vert); BKE_strand_child_vertex_iter_next(&it_vert)) {
+ shrinkwrap_apply_vertex(smd, data, inst, it_vert.vertex->co, it_vert.vertex->co);
+ }
+ }
+ }
+ }
+}
+
+static void shrinkwrap_process(ShrinkWrapCacheModifier *smd, CacheProcessContext *ctx, CacheProcessData *data, int UNUSED(frame), int UNUSED(frame_prev), int process_flag)
+{
+ bool do_strands_motion = true;
+
+ const bool dupli_target = smd->flag & eShrinkWrapCacheModifier_Flag_InternalTarget;
+ Object *ob = smd->object;
+ DupliObject *dob;
+ Strands *strands = NULL;
+ DerivedMesh *target_dm;
+ float mat[4][4];
+
+ ShrinkWrapCacheData shrinkwrap;
+
+ /* only applies to parent strands */
+ if (!(process_flag & eCacheProcessFlag_DoStrands))
+ return;
+
+ if (!BKE_cache_modifier_find_strands(data->dupcache, ob, smd->hair_system, NULL, &strands, NULL, NULL))
+ return;
+
+ if (dupli_target) {
+ DupliObjectData *target_data;
+ if (!BKE_cache_modifier_find_object(data->dupcache, smd->target, &target_data))
+ return;
+ target_dm = target_data->dm;
+ }
+ else {
+ if (!smd->target)
+ return;
+ target_dm = mesh_get_derived_final(ctx->scene, smd->target, CD_MASK_BAREMESH);
+ }
+
+ for (dob = data->dupcache->duplilist.first; dob; dob = dob->next) {
+ if (dob->ob != ob)
+ continue;
+
+ memset(&shrinkwrap, 0, sizeof(shrinkwrap));
+ shrinkwrap_data_get_bvhtree(&shrinkwrap, target_dm, true);
+
+ if (dupli_target) {
+ /* instances are calculated relative to the strands object */
+ invert_m4_m4(mat, dob->mat);
+ shrinkwrap_data_get_instances(&shrinkwrap, smd->target, mat, &data->dupcache->duplilist);
+ }
+ else {
+ /* instances are calculated relative to the strands object */
+ mul_m4_m4m4(mat, data->mat, dob->mat);
+ invert_m4(mat);
+ shrinkwrap_data_get_instances(&shrinkwrap, smd->target, mat, NULL);
+ }
+
+ shrinkwrap_apply(smd, &shrinkwrap, strands, NULL, do_strands_motion);
+
+ shrinkwrap_data_free(&shrinkwrap);
+
+ /* XXX assume a single instance ... otherwise would just overwrite previous strands data */
+ break;
+ }
+}
+
+CacheModifierTypeInfo cacheModifierType_ShrinkWrap = {
+ /* name */ "ShrinkWrap",
+ /* structName */ "ShrinkWrapCacheModifier",
+ /* structSize */ sizeof(ShrinkWrapCacheModifier),
+
+ /* copy */ (CacheModifier_CopyFunc)shrinkwrap_copy,
+ /* foreachIDLink */ (CacheModifier_ForeachIDLinkFunc)shrinkwrap_foreach_id_link,
+ /* process */ (CacheModifier_ProcessFunc)shrinkwrap_process,
+ /* init */ (CacheModifier_InitFunc)shrinkwrap_init,
+ /* free */ (CacheModifier_FreeFunc)shrinkwrap_free,
+};
+
+/* ------------------------------------------------------------------------- */
+
+static void strandskey_init(StrandsKeyCacheModifier *skmd)
+{
+ skmd->object = NULL;
+ skmd->hair_system = -1;
+
+ skmd->key = BKE_key_add_ex(NULL, KEY_OWNER_CACHELIB, -1);
+ skmd->key->type = KEY_RELATIVE;
+}
+
+static void strandskey_copy(StrandsKeyCacheModifier *skmd, StrandsKeyCacheModifier *tskmd)
+{
+ tskmd->key = BKE_key_copy(skmd->key);
+
+ tskmd->edit = NULL;
+}
+
+static void strandskey_free(StrandsKeyCacheModifier *skmd)
+{
+ BKE_key_free(skmd->key);
+
+ if (skmd->edit) {
+ BKE_editstrands_free(skmd->edit);
+ MEM_freeN(skmd->edit);
+ skmd->edit = NULL;
+ }
+}
+
+static void strandskey_foreach_id_link(StrandsKeyCacheModifier *skmd, CacheLibrary *cachelib, CacheModifier_IDWalkFunc walk, void *userdata)
+{
+ walk(userdata, cachelib, &skmd->modifier, (ID **)(&skmd->object));
+}
+
+static void strandskey_process(StrandsKeyCacheModifier *skmd, CacheProcessContext *UNUSED(ctx), CacheProcessData *data, int UNUSED(frame), int UNUSED(frame_prev), int process_flag)
+{
+ const bool use_motion = skmd->flag & eStrandsKeyCacheModifier_Flag_UseMotionState;
+ Object *ob = skmd->object;
+ Strands *strands;
+ KeyBlock *actkb;
+ float *shape;
+
+ /* only applies to parents */
+ if (!(process_flag & eCacheProcessFlag_DoStrands))
+ return;
+ if (!BKE_cache_modifier_find_strands(data->dupcache, ob, skmd->hair_system, NULL, &strands, NULL, NULL))
+ return;
+ if (use_motion && !strands->state)
+ return;
+
+ actkb = BLI_findlink(&skmd->key->block, skmd->shapenr);
+ shape = BKE_key_evaluate_strands(strands, skmd->key, actkb, skmd->flag & eStrandsKeyCacheModifier_Flag_ShapeLock, NULL, use_motion);
+ if (shape) {
+ StrandsVertex *vert = strands->verts;
+ StrandsMotionState *state = use_motion ? strands->state : NULL;
+ int totvert = strands->totverts;
+ int i;
+
+ float *fp = shape;
+ for (i = 0; i < totvert; ++i) {
+ if (state) {
+ copy_v3_v3(state->co, fp);
+ ++state;
+ }
+ else {
+ copy_v3_v3(vert->co, fp);
+ ++vert;
+ }
+ fp += 3;
+ }
+
+ MEM_freeN(shape);
+ }
+}
+
+CacheModifierTypeInfo cacheModifierType_StrandsKey = {
+ /* name */ "StrandsKey",
+ /* structName */ "StrandsKeyCacheModifier",
+ /* structSize */ sizeof(StrandsKeyCacheModifier),
+
+ /* copy */ (CacheModifier_CopyFunc)strandskey_copy,
+ /* foreachIDLink */ (CacheModifier_ForeachIDLinkFunc)strandskey_foreach_id_link,
+ /* process */ (CacheModifier_ProcessFunc)strandskey_process,
+ /* init */ (CacheModifier_InitFunc)strandskey_init,
+ /* free */ (CacheModifier_FreeFunc)strandskey_free,
+};
+
+KeyBlock *BKE_cache_modifier_strands_key_insert_key(StrandsKeyCacheModifier *skmd, Strands *strands, const char *name, const bool from_mix)
+{
+ const bool use_motion = skmd->flag & eStrandsKeyCacheModifier_Flag_UseMotionState;
+ Key *key = skmd->key;
+ KeyBlock *kb;
+ bool newkey = false;
+
+ if (key == NULL) {
+ key = skmd->key = BKE_key_add_ex(NULL, KEY_OWNER_CACHELIB, -1);
+ key->type = KEY_RELATIVE;
+ newkey = true;
+ }
+ else if (BLI_listbase_is_empty(&key->block)) {
+ newkey = true;
+ }
+
+ if (newkey || from_mix == false) {
+ /* create from mesh */
+ kb = BKE_keyblock_add_ctime(key, name, false);
+ BKE_keyblock_convert_from_strands(strands, key, kb, use_motion);
+ }
+ else {
+ /* copy from current values */
+ KeyBlock *actkb = BLI_findlink(&skmd->key->block, skmd->shapenr);
+ bool shape_lock = skmd->flag & eStrandsKeyCacheModifier_Flag_ShapeLock;
+ int totelem;
+ float *data = BKE_key_evaluate_strands(strands, key, actkb, shape_lock, &totelem, use_motion);
+
+ /* create new block with prepared data */
+ kb = BKE_keyblock_add_ctime(key, name, false);
+ kb->data = data;
+ kb->totelem = totelem;
+ }
+
+ return kb;
+}
+
+bool BKE_cache_modifier_strands_key_get(Object *ob, StrandsKeyCacheModifier **r_skmd, DerivedMesh **r_dm, Strands **r_strands, DupliObjectData **r_dobdata, const char **r_name, float r_mat[4][4])
+{
+ CacheLibrary *cachelib = ob->cache_library;
+ CacheModifier *md;
+
+ if (!cachelib)
+ return false;
+
+ /* ignore when the object is not actually using the cachelib */
+ if (!((ob->transflag & OB_DUPLIGROUP) && ob->dup_group && ob->dup_cache))
+ return false;
+
+ for (md = cachelib->modifiers.first; md; md = md->next) {
+ if (md->type == eCacheModifierType_StrandsKey) {
+ StrandsKeyCacheModifier *skmd = (StrandsKeyCacheModifier *)md;
+ DupliObjectData *dobdata;
+
+ if (BKE_cache_modifier_find_strands(ob->dup_cache, skmd->object, skmd->hair_system, &dobdata, r_strands, NULL, r_name)) {
+ if (r_skmd) *r_skmd = skmd;
+ if (r_dm) *r_dm = dobdata->dm;
+ if (r_dobdata) *r_dobdata = dobdata;
+
+ /* relative transform from the original hair object to the duplicator local space */
+ /* XXX bad hack, common problem: we want to display strand edit data in the place of "the" instance,
+ * but in fact there can be multiple instances of the same dupli object data, so this is ambiguous ...
+ * For our basic use case, just pick the first dupli instance, assuming that it's the only one.
+ * ugh ...
+ */
+ if (r_mat) {
+ DupliObject *dob;
+ for (dob = ob->dup_cache->duplilist.first; dob; dob = dob->next) {
+ if (dob->ob == skmd->object)
+ break;
+ }
+ if (dob) {
+ /* note: plain duplis from the dupli cache list are relative
+ * to the duplicator already! (not in world space like final duplis)
+ */
+ copy_m4_m4(r_mat, dob->mat);
+ }
+ else
+ unit_m4(r_mat);
+ }
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void haircut_init(HaircutCacheModifier *hmd)
+{
+ hmd->object = NULL;
+ hmd->hair_system = -1;
+}
+
+static void haircut_copy(HaircutCacheModifier *UNUSED(hmd), HaircutCacheModifier *UNUSED(thmd))
+{
+}
+
+static void haircut_free(HaircutCacheModifier *UNUSED(hmd))
+{
+}
+
+static void haircut_foreach_id_link(HaircutCacheModifier *smd, CacheLibrary *cachelib, CacheModifier_IDWalkFunc walk, void *userdata)
+{
+ walk(userdata, cachelib, &smd->modifier, (ID **)(&smd->object));
+ walk(userdata, cachelib, &smd->modifier, (ID **)(&smd->target));
+}
+
+typedef struct HaircutCacheData {
+ DerivedMesh *dm;
+ BVHTreeFromMesh treedata;
+
+ ListBase instances;
+} HaircutCacheData;
+
+typedef struct HaircutCacheInstance {
+ struct HaircutCacheInstance *next, *prev;
+
+ float mat[4][4];
+ float imat[4][4];
+} HaircutCacheInstance;
+
+static void haircut_data_get_bvhtree(HaircutCacheData *data, DerivedMesh *dm, bool create_bvhtree)
+{
+ data->dm = CDDM_copy(dm);
+ if (!data->dm)
+ return;
+
+ DM_ensure_tessface(data->dm);
+ CDDM_calc_normals(data->dm);
+
+ if (create_bvhtree) {
+ bvhtree_from_mesh_faces(&data->treedata, data->dm, 0.0, 2, 6);
+ }
+}
+
+static void haircut_data_get_instances(HaircutCacheData *data, Object *ob, float obmat[4][4], ListBase *duplilist)
+{
+ if (duplilist) {
+ DupliObject *dob;
+
+ for (dob = duplilist->first; dob; dob = dob->next) {
+ HaircutCacheInstance *inst;
+
+ if (dob->ob != ob)
+ continue;
+
+ inst = MEM_callocN(sizeof(HaircutCacheInstance), "haircut instance");
+ mul_m4_m4m4(inst->mat, obmat, dob->mat);
+ invert_m4_m4(inst->imat, inst->mat);
+
+ BLI_addtail(&data->instances, inst);
+ }
+ }
+ else {
+ HaircutCacheInstance *inst = MEM_callocN(sizeof(HaircutCacheInstance), "haircut instance");
+ mul_m4_m4m4(inst->mat, obmat, ob->obmat);
+ invert_m4_m4(inst->imat, inst->mat);
+
+ BLI_addtail(&data->instances, inst);
+ }
+}
+
+static void haircut_data_free(HaircutCacheData *data)
+{
+ BLI_freelistN(&data->instances);
+
+ free_bvhtree_from_mesh(&data->treedata);
+
+ if (data->dm) {
+ data->dm->release(data->dm);
+ }
+}
+
+/* XXX intersection counting does not work reliably */
+#if 0
+typedef struct PointInsideBVH {
+ BVHTreeFromMesh bvhdata;
+ int num_hits;
+} PointInsideBVH;
+
+static void point_inside_bvh_cb(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
+{
+ PointInsideBVH *data = userdata;
+
+ data->bvhdata.raycast_callback(&data->bvhdata, index, ray, hit);
+
+ if (hit->index != -1)
+ ++data->num_hits;
+}
+
+/* true if the point is inside the target mesh */
+static bool haircut_test_point(HaircutCacheModifier *hmd, HaircutCacheData *data, HaircutCacheInstance *inst, const float *v)
+{
+ const float dir[3] = {1.0f, 0.0f, 0.0f};
+ float start[3];
+ PointInsideBVH userdata;
+
+ if (!(hmd->cut_mode & eHaircutCacheModifier_CutMode_Enter))
+ return false;
+
+ userdata.bvhdata = data->treedata;
+ userdata.num_hits = 0;
+
+ /* lookup in target space */
+ mul_v3_m4v3(start, inst->imat, v);
+
+ BLI_bvhtree_ray_cast_all(data->treedata.tree, start, dir, 0.0f, point_inside_bvh_cb, &userdata);
+
+ /* for any point inside a watertight mesh the number of hits is uneven */
+ return (userdata.num_hits % 2) == 1;
+}
+#else
+/* true if the point is inside the target mesh */
+static bool haircut_test_point(HaircutCacheModifier *hmd, HaircutCacheData *data, HaircutCacheInstance *inst, const float *v)
+{
+ BVHTreeRayHit hit = {0, };
+ float start[3], dir[3] = {0.0f, 0.0f, 1.0f};
+ bool is_entering;
+
+ if (!(hmd->cut_mode & eHaircutCacheModifier_CutMode_Enter))
+ return false;
+ if (!data->treedata.tree)
+ return false;
+
+ /* lookup in target space */
+ mul_v3_m4v3(start, inst->imat, v);
+
+ hit.index = -1;
+ hit.dist = FLT_MAX;
+
+ BLI_bvhtree_ray_cast(data->treedata.tree, start, dir, 0.0f, &hit, data->treedata.raycast_callback, &data->treedata);
+ if (hit.index < 0) {
+ return false;
+ }
+
+ mul_mat3_m4_v3(inst->mat, hit.no);
+
+ is_entering = (dot_v3v3(dir, hit.no) < 0.0f);
+
+ return !is_entering;
+}
+#endif
+
+static bool haircut_find_segment_cut(HaircutCacheModifier *hmd, HaircutCacheData *data, HaircutCacheInstance *inst,
+ const float *v1, const float *v2, float *r_lambda)
+{
+ BVHTreeRayHit hit = {0, };
+ float start[3], dir[3], length;
+ bool is_entering;
+
+ if (!data->treedata.tree)
+ return false;
+
+ /* lookup in target space */
+ mul_v3_m4v3(start, inst->imat, v1);
+ sub_v3_v3v3(dir, v2, v1);
+ mul_mat3_m4_v3(inst->imat, dir);
+ length = normalize_v3(dir);
+
+ if (length == 0.0f)
+ return false;
+
+ hit.index = -1;
+ hit.dist = length;
+
+ BLI_bvhtree_ray_cast(data->treedata.tree, start, dir, 0.0f, &hit, data->treedata.raycast_callback, &data->treedata);
+ if (hit.index < 0)
+ return false;
+
+ is_entering = (dot_v3v3(dir, hit.no) < 0.0f);
+ if ((hmd->cut_mode & eHaircutCacheModifier_CutMode_Enter && is_entering) ||
+ (hmd->cut_mode & eHaircutCacheModifier_CutMode_Exit && !is_entering))
+ {
+ if (r_lambda) *r_lambda = len_v3v3(hit.co, start) / length;
+ return true;
+ }
+
+ return false;
+}
+
+static bool haircut_find_first_strand_cut(HaircutCacheModifier *hmd, HaircutCacheData *data, StrandChildIterator *it_strand, float *r_cutoff)
+{
+ StrandChildVertexIterator it_vert;
+ int vprev = -1;
+ float cutoff = 0.0f;
+
+ for (BKE_strand_child_vertex_iter_init(&it_vert, it_strand); BKE_strand_child_vertex_iter_valid(&it_vert); BKE_strand_child_vertex_iter_next(&it_vert)) {
+ StrandsChildVertex *verts = it_strand->verts;
+ bool found_cut = false;
+ float lambda_min = 1.0f;
+ HaircutCacheInstance *inst;
+
+ if (it_vert.index == 0) {
+ for (inst = data->instances.first; inst; inst = inst->next) {
+ /* test root vertex */
+ if (haircut_test_point(hmd, data, inst, verts[it_vert.index].co)) {
+ if (r_cutoff) *r_cutoff = 0.0f;
+ return true;
+ }
+ }
+ }
+ else {
+ for (inst = data->instances.first; inst; inst = inst->next) {
+ float lambda;
+ if (haircut_find_segment_cut(hmd, data, inst, verts[vprev].co, verts[it_vert.index].co, &lambda)) {
+ found_cut = true;
+ if (lambda < lambda_min)
+ lambda_min = lambda;
+ }
+ }
+
+ if (found_cut) {
+ cutoff += lambda_min;
+ if (r_cutoff) *r_cutoff = cutoff;
+ return true;
+ }
+ else
+ cutoff += 1.0f;
+ }
+
+ vprev = it_vert.index;
+ }
+
+ if (r_cutoff) *r_cutoff = -1.0f; /* indicates "no cutoff" */
+ return false;
+}
+
+/* shortens the last visible segment to have exact cutoff length */
+static void haircut_strand_adjust_tip(StrandChildIterator *it_strand, float cutoff)
+{
+ StrandsChildCurve *curve = it_strand->curve;
+
+ int last, end;
+ float *a, *b;
+ float t;
+
+ if (cutoff < 0 || cutoff >= (float)(curve->numverts-1))
+ return;
+
+ last = (int)cutoff;
+ end = last + 1;
+ BLI_assert(last < curve->numverts);
+ BLI_assert(end < curve->numverts);
+
+ a = it_strand->verts[last].co;
+ b = it_strand->verts[end].co;
+ t = cutoff - floorf(cutoff);
+ interp_v3_v3v3(b, a, b, t);
+}
+
+static void haircut_apply(HaircutCacheModifier *hmd, CacheProcessContext *UNUSED(ctx), HaircutCacheData *data, StrandsChildren *strands)
+{
+ StrandChildIterator it_strand;
+ for (BKE_strand_child_iter_init(&it_strand, strands); BKE_strand_child_iter_valid(&it_strand); BKE_strand_child_iter_next(&it_strand)) {
+ float cutoff = -1.0f;
+ if (haircut_find_first_strand_cut(hmd, data, &it_strand, &cutoff)) {
+ it_strand.curve->cutoff = cutoff;
+ haircut_strand_adjust_tip(&it_strand, cutoff);
+ }
+ }
+}
+
+static void haircut_process(HaircutCacheModifier *hmd, CacheProcessContext *ctx, CacheProcessData *data, int UNUSED(frame), int UNUSED(frame_prev), int process_flag)
+{
+ bool do_strands_children = (process_flag & eCacheProcessFlag_DoStrandsChildren);
+ const bool dupli_target = hmd->flag & eHaircutCacheModifier_Flag_InternalTarget;
+ Object *ob = hmd->object;
+ DupliObject *dob;
+ StrandsChildren *strands;
+ DerivedMesh *target_dm;
+ float mat[4][4];
+
+ HaircutCacheData haircut;
+
+ /* only applies to children */
+ if (!do_strands_children)
+ return;
+ if (!BKE_cache_modifier_find_strands(data->dupcache, ob, hmd->hair_system, NULL, NULL, do_strands_children ? &strands : NULL, NULL))
+ return;
+
+ if (dupli_target) {
+ DupliObjectData *target_data;
+ if (!BKE_cache_modifier_find_object(data->dupcache, hmd->target, &target_data))
+ return;
+ target_dm = target_data->dm;
+ }
+ else {
+ if (!hmd->target)
+ return;
+ target_dm = mesh_get_derived_final(ctx->scene, hmd->target, CD_MASK_BAREMESH);
+ }
+
+ for (dob = data->dupcache->duplilist.first; dob; dob = dob->next) {
+ if (dob->ob != ob)
+ continue;
+
+ memset(&haircut, 0, sizeof(haircut));
+ haircut_data_get_bvhtree(&haircut, target_dm, true);
+ if (dupli_target) {
+ /* instances are calculated relative to the strands object */
+ invert_m4_m4(mat, dob->mat);
+ haircut_data_get_instances(&haircut, hmd->target, mat, &data->dupcache->duplilist);
+ }
+ else {
+ /* instances are calculated relative to the strands object */
+ mul_m4_m4m4(mat, data->mat, dob->mat);
+ invert_m4(mat);
+ haircut_data_get_instances(&haircut, hmd->target, mat, NULL);
+ }
+
+ haircut_apply(hmd, ctx, &haircut, strands);
+
+ haircut_data_free(&haircut);
+
+ /* XXX assume a single instance ... otherwise would just overwrite previous strands data */
+ break;
+ }
+}
+
+CacheModifierTypeInfo cacheModifierType_Haircut = {
+ /* name */ "Haircut",
+ /* structName */ "HaircutCacheModifier",
+ /* structSize */ sizeof(HaircutCacheModifier),
+
+ /* copy */ (CacheModifier_CopyFunc)haircut_copy,
+ /* foreachIDLink */ (CacheModifier_ForeachIDLinkFunc)haircut_foreach_id_link,
+ /* process */ (CacheModifier_ProcessFunc)haircut_process,
+ /* init */ (CacheModifier_InitFunc)haircut_init,
+ /* free */ (CacheModifier_FreeFunc)haircut_free,
+};
+
+/* ------------------------------------------------------------------------- */
+
+bool BKE_cache_library_uses_key(CacheLibrary *cachelib, Key *key)
+{
+ CacheModifier *md;
+ for (md = cachelib->modifiers.first; md; md = md->next) {
+ if (md->type == eCacheModifierType_StrandsKey) {
+ StrandsKeyCacheModifier *skmd = (StrandsKeyCacheModifier *)md;
+ if (skmd->key == key)
+ return true;
+ }
+ }
+ return false;
+}
+
+void BKE_cache_modifier_init(void)
+{
+ cache_modifier_type_set(eCacheModifierType_HairSimulation, &cacheModifierType_HairSimulation);
+ cache_modifier_type_set(eCacheModifierType_ForceField, &cacheModifierType_ForceField);
+ cache_modifier_type_set(eCacheModifierType_ShrinkWrap, &cacheModifierType_ShrinkWrap);
+ cache_modifier_type_set(eCacheModifierType_StrandsKey, &cacheModifierType_StrandsKey);
+ cache_modifier_type_set(eCacheModifierType_Haircut, &cacheModifierType_Haircut);
+}
+
+/* ========================================================================= */
+
+#if 0
+static unsigned int hash_combine(unsigned int kx, unsigned int ky)
+{
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+ unsigned int a, b, c;
+
+ a = b = c = 0xdeadbeef + (2 << 2) + 13;
+ a += kx;
+ b += ky;
+
+ c ^= b; c -= rot(b,14);
+ a ^= c; a -= rot(c,11);
+ b ^= a; b -= rot(a,25);
+ c ^= b; c -= rot(b,16);
+ a ^= c; a -= rot(c,4);
+ b ^= a; b -= rot(a,14);
+ c ^= b; c -= rot(b,24);
+
+ return c;
+
+#undef rot
+}
+
+static unsigned int cache_archive_info_node_hash(const void *key)
+{
+ const CacheArchiveInfoNode *node = key;
+
+ unsigned int hash = hash_combine(BLI_ghashutil_strhash(node->name), BLI_ghashutil_inthash(node->type));
+ if (node->parent_hash != 0)
+ hash = hash_combine(hash, node->parent_hash);
+ return hash;
+}
+
+static bool cache_archive_info_node_cmp(const CacheArchiveInfoNode *a, const CacheArchiveInfoNode *b)
+{
+ if (a->parent_hash != b->parent_hash)
+ return true;
+ else if (a->type != b->type)
+ return true;
+ else if (!STREQ(a->name, b->name))
+ return true;
+ else
+ return false;
+}
+#endif
+
+static void cache_archive_info_node_free(CacheArchiveInfoNode *node)
+{
+ CacheArchiveInfoNode *child, *child_next;
+ for (child = node->child_nodes.first; child; child = child_next) {
+ child_next = child->next;
+ cache_archive_info_node_free(child);
+ }
+
+ MEM_freeN(node);
+}
+
+CacheArchiveInfo *BKE_cache_archive_info_new(void)
+{
+ CacheArchiveInfo *info = MEM_callocN(sizeof(CacheArchiveInfo), "cache archive info");
+
+ return info;
+}
+
+void BKE_cache_archive_info_free(CacheArchiveInfo *info)
+{
+ if (info) {
+ if (info->root_node)
+ cache_archive_info_node_free(info->root_node);
+
+ MEM_freeN(info);
+ }
+}
+
+void BKE_cache_archive_info_clear(CacheArchiveInfo *info)
+{
+ info->filepath[0] = '\0';
+ info->app_name[0] = '\0';
+ info->date_written[0] = '\0';
+ info->description[0] = '\0';
+
+ if (info->root_node) {
+ cache_archive_info_node_free(info->root_node);
+ info->root_node = NULL;
+ }
+}
+
+CacheArchiveInfoNode *BKE_cache_archive_info_find_node(CacheArchiveInfo *info, CacheArchiveInfoNode *parent,
+ eCacheArchiveInfoNode_Type type, const char *name)
+{
+ if (parent) {
+ CacheArchiveInfoNode *child;
+ for (child = parent->child_nodes.first; child; child = child->next) {
+ if (STREQ(child->name, name) && child->type == type)
+ return child;
+ }
+ }
+ else if (info->root_node) {
+ if (STREQ(info->root_node->name, name) && info->root_node->type == type)
+ return info->root_node;
+ }
+ return NULL;
+}
+
+CacheArchiveInfoNode *BKE_cache_archive_info_add_node(CacheArchiveInfo *info, CacheArchiveInfoNode *parent,
+ eCacheArchiveInfoNode_Type type, const char *name)
+{
+ CacheArchiveInfoNode *node;
+
+ BLI_assert(parent || !info->root_node);
+
+ node = MEM_callocN(sizeof(CacheArchiveInfoNode), "cache archive info node");
+ node->type = type;
+ BLI_strncpy(node->name, name, sizeof(node->name));
+
+ /* these values are only optionally calculated, -1 indicates unknown */
+ node->bytes_size = -1;
+ node->array_size = -1;
+
+ if (parent)
+ BLI_addtail(&parent->child_nodes, node);
+ else
+ info->root_node = node;
+
+ return node;
+}
diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c
index 649778c71f5..e29f95f8c6e 100644
--- a/source/blender/blenkernel/intern/cdderivedmesh.c
+++ b/source/blender/blenkernel/intern/cdderivedmesh.c
@@ -393,6 +393,7 @@ static void cdDM_drawEdges(DerivedMesh *dm, bool drawLooseEdges, bool drawAllEdg
{
CDDerivedMesh *cddm = (CDDerivedMesh *) dm;
GPUDrawObject *gdo;
+
if (cddm->pbvh && cddm->pbvh_draw &&
BKE_pbvh_type(cddm->pbvh) == PBVH_BMESH)
{
diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c
index e3ff96853b3..b4bcfc90e56 100644
--- a/source/blender/blenkernel/intern/cloth.c
+++ b/source/blender/blenkernel/intern/cloth.c
@@ -337,7 +337,7 @@ static int do_init_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *resul
return 0;
}
- BKE_cloth_solver_set_positions(clmd);
+ BPH_cloth_solver_set_positions(clmd);
clmd->clothObject->last_frame= MINFRAME-1;
}
@@ -370,7 +370,7 @@ static int do_step_cloth(Object *ob, ClothModifierData *clmd, DerivedMesh *resul
mul_m4_v3(ob->obmat, verts->xconst);
}
- effectors = pdInitEffectors(clmd->scene, ob, NULL, clmd->sim_parms->effector_weights, true);
+ effectors = pdInitEffectors(clmd->scene, ob, NULL, clmd->sim_parms->effector_weights);
/* Support for dynamic vertex groups, changing from frame to frame */
cloth_apply_vgroup ( clmd, result );
@@ -500,7 +500,6 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived
cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe);
if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED) {
- BKE_cloth_solver_set_positions(clmd);
cloth_to_object (ob, clmd, vertexCos);
BKE_ptcache_validate(cache, framenr);
@@ -513,7 +512,7 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived
return;
}
else if (cache_result==PTCACHE_READ_OLD) {
- BKE_cloth_solver_set_positions(clmd);
+ BPH_cloth_solver_set_positions(clmd);
}
else if ( /*ob->id.lib ||*/ (cache->flag & PTCACHE_BAKED)) { /* 2.4x disabled lib, but this can be used in some cases, testing further - campbell */
/* if baked and nothing in cache, do nothing */
@@ -729,11 +728,10 @@ static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm )
clothObj = clmd->clothObject;
- numverts = dm->getNumVerts (dm);
+ numverts = dm->getNumVerts(dm);
- verts = clothObj->verts;
-
if (cloth_uses_vgroup(clmd)) {
+ verts = clothObj->verts;
for ( i = 0; i < numverts; i++, verts++ ) {
/* Reset Goal values to standard */
@@ -794,6 +792,17 @@ static void cloth_apply_vgroup ( ClothModifierData *clmd, DerivedMesh *dm )
}
}
}
+
+#ifdef USE_PARTICLE_PREVIEW
+ {
+ MVert *mvert = dm->getVertArray(dm);
+ verts = clothObj->verts;
+ for ( i = 0; i < numverts; i++, verts++ ) {
+ if (mvert[i].flag & ME_VERT_TMP_TAG)
+ verts->flags |= CLOTH_VERT_FLAG_EXCLUDE;
+ }
+ }
+#endif
}
@@ -900,7 +909,7 @@ static int cloth_from_object(Object *ob, ClothModifierData *clmd, DerivedMesh *d
BPH_cloth_solver_init(ob, clmd);
if (!first)
- BKE_cloth_solver_set_positions(clmd);
+ BPH_cloth_solver_set_positions(clmd);
clmd->clothObject->bvhtree = bvhtree_build_from_cloth ( clmd, MAX2(clmd->coll_parms->epsilon, clmd->coll_parms->distance_repel) );
diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c
index 763024ec565..9ff131157ac 100644
--- a/source/blender/blenkernel/intern/collision.c
+++ b/source/blender/blenkernel/intern/collision.c
@@ -1362,7 +1362,7 @@ void cloth_find_point_contacts(Object *ob, ClothModifierData *clmd, float step,
// create temporary cloth points bvh
cloth_bvh = BLI_bvhtree_new(numverts, MAX2(clmd->coll_parms->epsilon, clmd->coll_parms->distance_repel), 4, 6);
/* fill tree */
- for (i = 0; i < numverts; i++) {
+ for (i = 0; i < cloth->numverts; i++) {
float co[6];
copy_v3_v3(&co[0*3], verts[i].x);
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index 7142c092583..b463a1650b7 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -58,6 +58,8 @@
/* struct */
+struct wmWidget;
+
struct bContext {
int thread;
@@ -926,6 +928,7 @@ int CTX_data_mode_enum(const bContext *C)
else if (ob->mode & OB_MODE_VERTEX_PAINT) return CTX_MODE_PAINT_VERTEX;
else if (ob->mode & OB_MODE_TEXTURE_PAINT) return CTX_MODE_PAINT_TEXTURE;
else if (ob->mode & OB_MODE_PARTICLE_EDIT) return CTX_MODE_PARTICLE;
+ else if (ob->mode & OB_MODE_HAIR_EDIT) return CTX_MODE_HAIR;
}
}
@@ -949,6 +952,7 @@ static const char *data_mode_strings[] = {
"vertexpaint",
"imagepaint",
"particlemode",
+ "hairmode",
"objectmode",
NULL
};
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c
index 1a67ac76937..2c55896e598 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -222,7 +222,7 @@ Curve *BKE_curve_copy(Curve *cu)
cun->bb = MEM_dupallocN(cu->bb);
cun->key = BKE_key_copy(cu->key);
- if (cun->key) cun->key->from = (ID *)cun;
+ BKE_key_set_from_id(cun->key, (ID *)cun);
cun->editnurb = NULL;
cun->editfont = NULL;
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index eced263d493..403e815ce5c 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -55,6 +55,7 @@
#include "BKE_customdata.h"
#include "BKE_customdata_file.h"
+#include "BKE_editstrands.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_mesh_mapping.h"
@@ -1195,6 +1196,16 @@ static void layerSwap_flnor(void *data, const int *corner_indices)
memcpy(flnors, nors, sizeof(nors));
}
+static void layerDefault_fmap(void *data, int count)
+{
+ int *fmap_num = (int *)data;
+ int i;
+ for (i = 0; i < count; i++)
+ *fmap_num = -1;
+
+}
+
+
static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
/* 0: CD_MVERT */
{sizeof(MVert), "MVert", 1, NULL, NULL, NULL, NULL, NULL, NULL},
@@ -1312,6 +1323,10 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
{sizeof(short[4][3]), "", 0, NULL, NULL, NULL, NULL, layerSwap_flnor, NULL},
/* 41: CD_CUSTOMLOOPNORMAL */
{sizeof(short[2]), "vec2s", 1, NULL, NULL, NULL, NULL, NULL, NULL},
+ /* 42: CD_FACEMAP */
+ {sizeof(int), "", 0, NULL, NULL, NULL, NULL, NULL, layerDefault_fmap, NULL},
+ /* 43: CD_MSURFACE_SAMPLE */
+ {sizeof(MSurfaceSample), "MSurfaceSample", 1, NULL, NULL, NULL, NULL, NULL, NULL},
};
/* note, numbers are from trunk and need updating for bmesh */
@@ -1328,12 +1343,13 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
/* 30-34 */ "CDSubSurfCrease", "CDOrigSpaceLoop", "CDPreviewLoopCol", "CDBMElemPyPtr", "CDPaintMask",
/* 35-36 */ "CDGridPaintMask", "CDMVertSkin",
/* 37-38 */ "CDFreestyleEdge", "CDFreestyleFace",
- /* 39-41 */ "CDMLoopTangent", "CDTessLoopNormal", "CDCustomLoopNormal",
+ /* 39-42 */ "CDMLoopTangent", "CDTessLoopNormal", "CDCustomLoopNormal", "CDFaceMap",
+ /* 43 */ "CDMSurfaceSample",
};
const CustomDataMask CD_MASK_BAREMESH =
- CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MFACE | CD_MASK_MLOOP | CD_MASK_MPOLY | CD_MASK_BWEIGHT;
+ CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MFACE | CD_MASK_MLOOP | CD_MASK_MPOLY | CD_MASK_BWEIGHT | CD_MASK_FACEMAP;
const CustomDataMask CD_MASK_MESH =
CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MFACE |
CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE | CD_MASK_MCOL |
@@ -1341,13 +1357,13 @@ const CustomDataMask CD_MASK_MESH =
CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MPOLY | CD_MASK_MLOOP |
CD_MASK_MTEXPOLY | CD_MASK_RECAST | CD_MASK_PAINT_MASK |
CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE |
- CD_MASK_CUSTOMLOOPNORMAL;
+ CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_FACEMAP;
const CustomDataMask CD_MASK_EDITMESH =
CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE | CD_MASK_MLOOPUV |
CD_MASK_MLOOPCOL | CD_MASK_MTEXPOLY | CD_MASK_SHAPE_KEYINDEX |
CD_MASK_MCOL | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR |
CD_MASK_MDISPS | CD_MASK_SHAPEKEY | CD_MASK_RECAST | CD_MASK_PAINT_MASK |
- CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_CUSTOMLOOPNORMAL;
+ CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_FACEMAP;
const CustomDataMask CD_MASK_DERIVEDMESH =
CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MTFACE |
CD_MASK_MCOL | CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_CLOTH_ORCO |
@@ -1355,17 +1371,28 @@ const CustomDataMask CD_MASK_DERIVEDMESH =
CD_MASK_PROP_STR | CD_MASK_ORIGSPACE | CD_MASK_ORIGSPACE_MLOOP | CD_MASK_ORCO | CD_MASK_TANGENT |
CD_MASK_PREVIEW_MCOL | CD_MASK_SHAPEKEY | CD_MASK_RECAST |
CD_MASK_ORIGINDEX | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE |
- CD_MASK_CUSTOMLOOPNORMAL;
+ CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_FACEMAP;
const CustomDataMask CD_MASK_BMESH =
CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_MTEXPOLY |
CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_PROP_FLT | CD_MASK_PROP_INT |
CD_MASK_PROP_STR | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_MDISPS |
CD_MASK_CREASE | CD_MASK_BWEIGHT | CD_MASK_RECAST | CD_MASK_PAINT_MASK |
CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE |
- CD_MASK_CUSTOMLOOPNORMAL;
+ CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_FACEMAP;
const CustomDataMask CD_MASK_FACECORNERS = /* XXX Not used anywhere! */
CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_MTEXPOLY | CD_MASK_MLOOPUV |
CD_MASK_MLOOPCOL | CD_MASK_NORMAL | CD_MASK_MLOOPTANGENT;
+const CustomDataMask CD_MASK_STRANDS =
+ CD_MASK_MVERT | CD_MASK_MEDGE |
+ CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_MCOL |
+ CD_MASK_PROP_FLT | CD_MASK_PROP_INT | CD_MASK_PROP_STR | CD_MASK_MDISPS |
+ CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE |
+ CD_MASK_MSURFACE_SAMPLE;
+const CustomDataMask CD_MASK_STRANDS_BMESH =
+ CD_MASK_MSTICKY | CD_MASK_MDEFORMVERT | CD_MASK_PROP_FLT | CD_MASK_PROP_INT |
+ CD_MASK_PROP_STR | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_MDISPS |
+ CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE |
+ CD_MASK_MSURFACE_SAMPLE;
const CustomDataMask CD_MASK_EVERYTHING =
CD_MASK_MVERT | CD_MASK_MSTICKY /* DEPRECATED */ | CD_MASK_MDEFORMVERT | CD_MASK_MEDGE | CD_MASK_MFACE |
CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_ORIGINDEX | CD_MASK_NORMAL /* | CD_MASK_POLYINDEX */ | CD_MASK_PROP_FLT |
@@ -1377,7 +1404,7 @@ const CustomDataMask CD_MASK_EVERYTHING =
/* BMESH ONLY END */
CD_MASK_PAINT_MASK | CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN |
CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE |
- CD_MASK_MLOOPTANGENT | CD_MASK_TESSLOOPNORMAL | CD_MASK_CUSTOMLOOPNORMAL;
+ CD_MASK_MLOOPTANGENT | CD_MASK_TESSLOOPNORMAL | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_FACEMAP | CD_MASK_MSURFACE_SAMPLE;
static const LayerTypeInfo *layerType_getInfo(int type)
{
@@ -2826,6 +2853,18 @@ void *CustomData_bmesh_get_layer_n(const CustomData *data, void *block, int n)
return POINTER_OFFSET(block, data->layers[n].offset);
}
+/*Bmesh Custom Data Functions. Should replace editmesh ones with these as well, due to more effecient memory alloc*/
+void *CustomData_bmesh_get_named(const CustomData *data, void *block, int type, const char *name)
+{
+ int layer_index;
+
+ /* get the layer index of the named layer of type */
+ layer_index = CustomData_get_named_layer_index(data, type, name);
+ if (layer_index == -1) return NULL;
+
+ return (char *)block + data->layers[layer_index].offset;
+}
+
bool CustomData_layer_has_math(const struct CustomData *data, int layer_n)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[layer_n].type);
diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c
index 2fd53045e29..ffaafe94b96 100644
--- a/source/blender/blenkernel/intern/depsgraph.c
+++ b/source/blender/blenkernel/intern/depsgraph.c
@@ -45,6 +45,7 @@
#include "BLI_threads.h"
#include "DNA_anim_types.h"
+#include "DNA_cache_library_types.h"
#include "DNA_camera_types.h"
#include "DNA_group_types.h"
#include "DNA_lamp_types.h"
@@ -629,8 +630,22 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc
/* inverted relation, so addtoroot shouldn't be set to zero */
}
+ /* XXX Fake dependency: duplicator object becomes a child of group objects.
+ * This exploits the layer visibility mechanism, making the group objects update
+ * when the duplicator is visible (even if group objects are not visible themselves).
+ * It is not a true dependency, the duplicator does not in any way depend on group objects or data!
+ */
if (ob->transflag & OB_DUPLI) {
+ /* XXX In theory it would be possible to disable the visibility dependency when dupli groups are cached,
+ * since we use the results from the cache instead of the generated object data anyway.
+ * However, the caching system depends a lot on DNA objects currently and behaves unpredictably without this ...
+ */
+#if 0
+ bool is_cached = ob->cache_library && ob->cache_library->source_mode == CACHE_LIBRARY_SOURCE_CACHE;
+ if (!is_cached && (ob->transflag & OB_DUPLIGROUP) && ob->dup_group) {
+#else
if ((ob->transflag & OB_DUPLIGROUP) && ob->dup_group) {
+#endif
GroupObject *go;
for (go = ob->dup_group->gobject.first; go; go = go->next) {
if (go->ob) {
@@ -770,6 +785,10 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc
if (!psys_check_enabled(ob, psys))
continue;
+ key = psys->key;
+ if (key && key->adt)
+ dag_add_driver_relation(key->adt, dag, node, 1);
+
if (ELEM(part->phystype, PART_PHYS_KEYED, PART_PHYS_BOIDS)) {
ParticleTarget *pt = psys->targets.first;
@@ -798,7 +817,7 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc
}
}
- effectors = pdInitEffectors(scene, ob, psys, part->effector_weights, false);
+ effectors = pdInitEffectors_ex(scene, ob, psys, ob->lay, part->effector_weights, false);
if (effectors) {
for (eff = effectors->first; eff; eff = eff->next) {
@@ -2193,6 +2212,10 @@ static void dag_object_time_update_flags(Main *bmain, Scene *scene, Object *ob)
}
}
+ /* invalidate dupli cache */
+ if (ob->dup_cache)
+ ob->dup_cache->flag |= DUPCACHE_FLAG_DIRTY;
+
if (ob->recalc & OB_RECALC_OB)
lib_id_recalc_tag(bmain, &ob->id);
if (ob->recalc & OB_RECALC_DATA)
@@ -2281,6 +2304,90 @@ void DAG_scene_update_flags(Main *bmain, Scene *scene, unsigned int lay, const b
}
}
+void DAG_scene_update_group_flags(Main *bmain,
+ Scene *scene,
+ Group *group,
+ unsigned int lay,
+ const bool do_time,
+ const bool do_invisible_flush)
+{
+ DagNode *root_node = scene->theDag->DagNode.first, *node;
+ GroupObject *go;
+ DagNodeQueue *queue;
+
+ /* Tag all possible objects for update. */
+ DAG_scene_update_flags(bmain, scene, lay, do_time, do_invisible_flush);
+
+ /* Initialize colors of nodes. */
+ for (node = root_node; node != NULL; node = node->next) {
+ node->color = DAG_WHITE;
+ node->scheduled = false;
+ }
+
+ /* Tag nodes which corresponds to objects which are to be updated. */
+ for (go = group->gobject.first; go != NULL; go = go->next) {
+ if (go->ob != NULL) {
+ node = dag_find_node(scene->theDag, go->ob);
+ if (node != NULL) {
+ node->scheduled = true;
+ }
+ }
+ }
+
+ /* Flush schedule flags to parent. */
+ queue = queue_create(DAGQUEUEALLOC);
+ for (node = root_node; node != NULL; node = node->next) {
+ if (node->color == DAG_WHITE) {
+ push_stack(queue, node);
+ node->color = DAG_GRAY;
+ while (queue->count) {
+ DagNode *current_node = get_top_node_queue(queue);
+ DagAdjList *itA;
+ bool skip = false;
+ /* Check if all child nodes were scheduled. */
+ for (itA = current_node->child; itA; itA = itA->next) {
+ if (itA->node->color == DAG_WHITE) {
+ itA->node->color = DAG_GRAY;
+ push_stack(queue, itA->node);
+ skip = true;
+ break;
+ }
+ }
+ /* Check if there are scheduled children and if so schedule
+ * current node as well since it's needed for chidlren.
+ */
+ if (!skip) {
+ current_node = pop_queue(queue);
+ if (current_node->type == ID_OB) {
+ for (itA = current_node->child; itA; itA = itA->next) {
+ if (itA->node->scheduled) {
+ current_node->scheduled = true;
+ break;
+ }
+ }
+ }
+ node->color = DAG_BLACK;
+ }
+ }
+ }
+ }
+ queue_delete(queue);
+
+ /* Clear recalc flags from objects which corresponds to nodes which are
+ * not needed for the interesting group update.
+ */
+ for (node = root_node; node != NULL; node = node->next) {
+ if (node->type == ID_OB) {
+ Object *object = node->ob;
+ if (!node->scheduled) {
+ object->recalc &= ~OB_RECALC_ALL;
+ }
+ }
+ node->color = DAG_WHITE;
+ node->scheduled = false;
+ }
+}
+
/* struct returned by DagSceneLayer */
typedef struct DagSceneLayer {
struct DagSceneLayer *next, *prev;
@@ -2559,12 +2666,26 @@ static void dag_id_flush_update(Main *bmain, Scene *sce, ID *id)
/* set flags based on ShapeKey */
if (idtype == ID_KE) {
for (obt = bmain->object.first; obt; obt = obt->id.next) {
- Key *key = BKE_key_from_object(obt);
- if (!(ob && obt == ob) && ((ID *)key == id)) {
- obt->flag |= (OB_RECALC_OB | OB_RECALC_DATA);
- lib_id_recalc_tag(bmain, &obt->id);
- lib_id_recalc_data_tag(bmain, &obt->id);
- BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH);
+ if (!(ob && obt == ob)) {
+ Key *key = BKE_key_from_object(obt);
+ ParticleSystem *psys;
+
+ if ((ID *)key == id) {
+ obt->flag |= (OB_RECALC_OB | OB_RECALC_DATA);
+ lib_id_recalc_tag(bmain, &obt->id);
+ lib_id_recalc_data_tag(bmain, &obt->id);
+ BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH);
+ }
+
+ for (psys = obt->particlesystem.first; psys; psys = psys->next) {
+ key = psys->key;
+ if ((ID *)key == id) {
+ obt->flag |= (OB_RECALC_OB | OB_RECALC_DATA);
+ lib_id_recalc_tag(bmain, &obt->id);
+ lib_id_recalc_data_tag(bmain, &obt->id);
+ BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH);
+ }
+ }
}
}
}
@@ -2645,6 +2766,21 @@ static void dag_id_flush_update(Main *bmain, Scene *sce, ID *id)
}
}
}
+
+ /* set flags based on CacheLibrary */
+ if (idtype == ID_CL) {
+ for (obt = bmain->object.first; obt; obt = obt->id.next) {
+ if (!(ob && obt == ob) && ((ID *)obt->cache_library == id)) {
+ obt->flag |= (OB_RECALC_OB | OB_RECALC_DATA);
+ lib_id_recalc_tag(bmain, &obt->id);
+ lib_id_recalc_data_tag(bmain, &obt->id);
+
+ /* invalidate dupli cache */
+ if (obt->dup_cache)
+ obt->dup_cache->flag |= DUPCACHE_FLAG_DIRTY;
+ }
+ }
+ }
/* camera's matrix is used to orient reconstructed stuff,
* so it should happen tracking-related constraints recalculation
diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c
index 8dac578ac0a..ce9e85c6813 100644
--- a/source/blender/blenkernel/intern/dynamicpaint.c
+++ b/source/blender/blenkernel/intern/dynamicpaint.c
@@ -4221,7 +4221,7 @@ static int dynamicPaint_prepareEffectStep(DynamicPaintSurface *surface, Scene *s
/* Init force data if required */
if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) {
float vel[3] = {0};
- ListBase *effectors = pdInitEffectors(scene, ob, NULL, surface->effector_weights, true);
+ ListBase *effectors = pdInitEffectors(scene, ob, NULL, surface->effector_weights);
/* allocate memory for force data (dir vector + strength) */
*force = MEM_mallocN(sData->total_points * 4 * sizeof(float), "PaintEffectForces");
diff --git a/source/blender/blenkernel/intern/editstrands.c b/source/blender/blenkernel/intern/editstrands.c
new file mode 100644
index 00000000000..fcf98dfc2ee
--- /dev/null
+++ b/source/blender/blenkernel/intern/editstrands.c
@@ -0,0 +1,272 @@
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/editstrands.c
+ * \ingroup bke
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_mempool.h"
+
+#include "DNA_cache_library_types.h"
+#include "DNA_customdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_strands_types.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_cache_library.h"
+#include "BKE_customdata.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_editstrands.h"
+#include "BKE_effect.h"
+#include "BKE_mesh_sample.h"
+#include "BKE_particle.h"
+
+#include "BPH_strands.h"
+
+#include "intern/bmesh_strands_conv.h"
+
+/* mat can be used to transform the dm into another space,
+ * in case the edited object is not the active object:
+ * mat = inv(M_act) * M_edit
+ */
+BMEditStrands *BKE_editstrands_create(BMesh *bm, DerivedMesh *root_dm, float mat[4][4])
+{
+ BMEditStrands *es = MEM_callocN(sizeof(BMEditStrands), __func__);
+
+ es->bm = bm;
+ es->root_dm = CDDM_copy(root_dm);
+
+ if (mat) {
+ DerivedMesh *dm = es->root_dm;
+ MVert *mv = dm->getVertArray(dm);
+ int totvert = dm->getNumVerts(dm), i;
+ for (i = 0; i < totvert; ++i, ++mv) {
+ mul_m4_v3(mat, mv->co);
+ }
+ }
+
+ return es;
+}
+
+BMEditStrands *BKE_editstrands_copy(BMEditStrands *es)
+{
+ BMEditStrands *es_copy = MEM_callocN(sizeof(BMEditStrands), __func__);
+ *es_copy = *es;
+
+ es_copy->bm = BM_mesh_copy(es->bm);
+ es_copy->root_dm = CDDM_copy(es->root_dm);
+
+ return es_copy;
+}
+
+/**
+ * \brief Return the BMEditStrands for a given object
+ */
+BMEditStrands *BKE_editstrands_from_object(Object *ob)
+{
+ {
+ ParticleSystem *psys = psys_get_current(ob);
+ if (psys && psys->hairedit)
+ return psys->hairedit;
+ }
+
+ {
+ StrandsKeyCacheModifier *skmd;
+ if (BKE_cache_modifier_strands_key_get(ob, &skmd, NULL, NULL, NULL, NULL, NULL)) {
+ if (skmd->edit)
+ return skmd->edit;
+ }
+ }
+
+ return NULL;
+}
+
+void BKE_editstrands_update_linked_customdata(BMEditStrands *es)
+{
+ BMesh *bm = es->bm;
+
+ /* this is done for BMEditMesh, but should never exist for strands */
+ BLI_assert(!CustomData_has_layer(&bm->pdata, CD_MTEXPOLY));
+}
+
+/*does not free the BMEditStrands struct itself*/
+void BKE_editstrands_free(BMEditStrands *es)
+{
+ if (es->bm)
+ BM_mesh_free(es->bm);
+ if (es->root_dm)
+ es->root_dm->release(es->root_dm);
+}
+
+/* === constraints === */
+
+BMEditStrandsLocations BKE_editstrands_get_locations(BMEditStrands *edit)
+{
+ BMesh *bm = edit->bm;
+ BMEditStrandsLocations locs = MEM_mallocN(3*sizeof(float) * bm->totvert, "editstrands locations");
+
+ BMVert *v;
+ BMIter iter;
+ int i;
+
+ BM_ITER_MESH_INDEX(v, &iter, bm, BM_VERTS_OF_MESH, i) {
+ copy_v3_v3(locs[i], v->co);
+ }
+
+ return locs;
+}
+
+void BKE_editstrands_free_locations(BMEditStrandsLocations locs)
+{
+ MEM_freeN(locs);
+}
+
+void BKE_editstrands_solve_constraints(Object *ob, BMEditStrands *es, BMEditStrandsLocations orig)
+{
+ BKE_editstrands_ensure(es);
+
+ BPH_strands_solve_constraints(ob, es, orig);
+}
+
+static void editstrands_calc_segment_lengths(BMesh *bm)
+{
+ BMVert *root;
+ BMIter iter;
+
+ BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) {
+ BMVert *v, *vprev = NULL;
+ BMIter iter_strand;
+ BM_ITER_STRANDS_ELEM(v, &iter_strand, root, BM_VERTS_OF_STRAND) {
+ if (vprev) {
+ float length = len_v3v3(v->co, vprev->co);
+ BM_elem_float_data_named_set(&bm->vdata, vprev, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH, length);
+ }
+ vprev = v;
+ }
+ if (vprev) {
+ /* set last to 0 */
+ BM_elem_float_data_named_set(&bm->vdata, vprev, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH, 0.0f);
+ }
+ }
+}
+
+void BKE_editstrands_ensure(BMEditStrands *es)
+{
+ BM_strands_cd_flag_ensure(es->bm, 0);
+
+ if (es->flag & BM_STRANDS_DIRTY_SEGLEN) {
+ editstrands_calc_segment_lengths(es->bm);
+
+ es->flag &= ~BM_STRANDS_DIRTY_SEGLEN;
+ }
+}
+
+
+/* === cache shape key conversion === */
+
+BMesh *BKE_cache_strands_to_bmesh(struct Strands *strands, struct Key *key, float mat[4][4], int act_key_nr, DerivedMesh *dm)
+{
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_STRANDS(strands);
+ BMesh *bm;
+
+ DM_ensure_tessface(dm);
+
+ bm = BM_mesh_create(&allocsize);
+ BM_strands_bm_from_strands(bm, strands, mat, key, dm, true, act_key_nr);
+ editstrands_calc_segment_lengths(bm);
+
+ return bm;
+}
+
+struct Strands *BKE_cache_strands_from_bmesh(BMEditStrands *edit, struct Key *key, float mat[4][4], DerivedMesh *dm)
+{
+ BMesh *bm = edit ? edit->bm : NULL;
+ Strands *strands = NULL;
+
+ if (bm && dm) {
+ BVHTreeFromMesh bvhtree = {NULL};
+
+ DM_ensure_tessface(dm);
+
+ bvhtree_from_mesh_faces(&bvhtree, dm, 0.0, 2, 6);
+
+ strands = BM_strands_bm_to_strands(bm, strands, mat, key, dm, &bvhtree);
+
+ free_bvhtree_from_mesh(&bvhtree);
+ }
+
+ return strands;
+}
+
+
+/* === particle conversion === */
+
+BMesh *BKE_particles_to_bmesh(Object *ob, ParticleSystem *psys)
+{
+ ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
+
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_PSYS(psys);
+ BMesh *bm;
+
+ bm = BM_mesh_create(&allocsize);
+
+ if (psmd && psmd->dm) {
+ DM_ensure_tessface(psmd->dm);
+
+ BM_strands_bm_from_psys(bm, ob, psys, psmd->dm, true, /*psys->shapenr*/ -1);
+
+ editstrands_calc_segment_lengths(bm);
+ }
+
+ return bm;
+}
+
+void BKE_particles_from_bmesh(Object *ob, ParticleSystem *psys)
+{
+ ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
+ BMesh *bm = psys->hairedit ? psys->hairedit->bm : NULL;
+
+ if (bm) {
+ if (psmd && psmd->dm) {
+ BVHTreeFromMesh bvhtree = {NULL};
+
+ DM_ensure_tessface(psmd->dm);
+
+ bvhtree_from_mesh_faces(&bvhtree, psmd->dm, 0.0, 2, 6);
+
+ BM_strands_bm_to_psys(bm, ob, psys, psmd->dm, &bvhtree);
+
+ free_bvhtree_from_mesh(&bvhtree);
+ }
+ }
+}
diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c
index 785561d8239..4842b40bf84 100644
--- a/source/blender/blenkernel/intern/effect.c
+++ b/source/blender/blenkernel/intern/effect.c
@@ -203,18 +203,17 @@ 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)
+ListBase *pdInitEffectors_ex(Scene *scene, Object *ob_src, ParticleSystem *psys_src, int layers,
+ EffectorWeights *weights, bool precalc)
{
Base *base;
- unsigned int layer= ob_src->lay;
ListBase *effectors = NULL;
if (weights->group) {
GroupObject *go;
for (go= weights->group->gobject.first; go; go= go->next) {
- if ( (go->ob->lay & layer) ) {
+ if ( (go->ob->lay & layers) ) {
if ( go->ob->pd && go->ob->pd->forcefield )
add_object_to_effectors(&effectors, scene, weights, go->ob, ob_src);
@@ -229,7 +228,7 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src
}
else {
for (base = scene->base.first; base; base= base->next) {
- if ( (base->lay & layer) ) {
+ if ( (base->lay & layers) ) {
if ( base->object->pd && base->object->pd->forcefield )
add_object_to_effectors(&effectors, scene, weights, base->object, ob_src);
@@ -249,6 +248,12 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src
return effectors;
}
+ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src,
+ EffectorWeights *weights)
+{
+ return pdInitEffectors_ex(scene, ob_src, psys_src, ob_src->lay, weights, true);
+}
+
void pdEndEffectors(ListBase **effectors)
{
if (*effectors) {
@@ -831,6 +836,10 @@ static void do_physical_effector(EffectorCache *eff, EffectorData *efd, Effected
}
copy_v3_v3(force, efd->vec_to_point);
+ if (pd->shape == PFIELD_SHAPE_SURFACE && (pd->flag & PFIELD_USE_SIGNED_DISTANCE)) {
+ if (dot_v3v3(efd->vec_to_point, efd->nor) < 0.0f)
+ mul_v3_fl(force, -1.0f);
+ }
switch (pd->forcefield) {
case PFIELD_WIND:
@@ -1121,10 +1130,10 @@ static void debug_data_insert(SimDebugData *debug_data, SimDebugElement *elem)
BLI_ghash_insert(debug_data->gh, elem, elem);
}
-void BKE_sim_debug_data_add_element(int type, const float v1[3], const float v2[3], float r, float g, float b, const char *category, unsigned int hash)
+void BKE_sim_debug_data_add_element(int type, const float v1[3], const float v2[3],
+ float r, float g, float b, const char *category, unsigned int hash)
{
unsigned int category_hash = BLI_ghashutil_strhash_p(category);
- SimDebugElement *elem;
if (!_sim_debug_data) {
if (G.debug & G_DEBUG_SIMDATA)
@@ -1133,6 +1142,16 @@ void BKE_sim_debug_data_add_element(int type, const float v1[3], const float v2[
return;
}
+ BKE_sim_debug_data_add_element_ex(_sim_debug_data, type, v1, v2, r, g, b, category_hash, hash);
+}
+
+void BKE_sim_debug_data_add_element_ex(SimDebugData *debug_data, int type, const float v1[3], const float v2[3],
+ float r, float g, float b, unsigned int category_hash, unsigned int hash)
+{
+ SimDebugElement *elem;
+ if (!debug_data)
+ return;
+
elem = MEM_callocN(sizeof(SimDebugElement), "sim debug data element");
elem->type = type;
elem->category_hash = category_hash;
@@ -1143,17 +1162,22 @@ void BKE_sim_debug_data_add_element(int type, const float v1[3], const float v2[
copy_v3_v3(elem->v1, v1);
copy_v3_v3(elem->v2, v2);
- debug_data_insert(_sim_debug_data, elem);
+ debug_data_insert(debug_data, elem);
}
void BKE_sim_debug_data_remove_element(unsigned int hash)
{
+ BKE_sim_debug_data_remove_element_ex(_sim_debug_data, hash);
+}
+
+void BKE_sim_debug_data_remove_element_ex(SimDebugData *debug_data, unsigned int hash)
+{
SimDebugElement dummy;
- if (!_sim_debug_data)
+ if (!debug_data)
return;
dummy.hash = hash;
- BLI_ghash_remove(_sim_debug_data->gh, &dummy, NULL, debug_element_free);
+ BLI_ghash_remove(debug_data->gh, &dummy, NULL, debug_element_free);
}
void BKE_sim_debug_data_clear(void)
diff --git a/source/blender/blenkernel/intern/facemap.c b/source/blender/blenkernel/intern/facemap.c
new file mode 100644
index 00000000000..2dca535bea3
--- /dev/null
+++ b/source/blender/blenkernel/intern/facemap.c
@@ -0,0 +1,259 @@
+
+/*
+ * ***** 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) 2008 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+
+#include "DNA_object_types.h"
+#include "DNA_mesh_types.h"
+
+#include "WM_types.h"
+#include "WM_api.h"
+
+#include "BKE_context.h"
+#include "BKE_customdata.h"
+#include "BKE_facemap.h"
+#include "BKE_editmesh.h"
+#include "BKE_object.h"
+#include "BKE_object_deform.h"
+
+#include "BKE_depsgraph.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BLI_listbase.h"
+
+#include "BLF_translation.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_define.h"
+#include "RNA_access.h"
+
+#include <string.h>
+
+
+static bool fmap_unique_check(void *arg, const char *name)
+{
+ struct {Object *ob; void *fm; } *data = arg;
+
+ bFaceMap *fmap;
+
+ for (fmap = data->ob->fmaps.first; fmap; fmap = fmap->next) {
+ if (data->fm != fmap) {
+ if (!strcmp(fmap->name, name)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static bFaceMap *fmap_duplicate(bFaceMap *infmap)
+{
+ bFaceMap *outfmap;
+
+ if (!infmap)
+ return NULL;
+
+ outfmap = MEM_callocN(sizeof(bFaceMap), "copy facemap");
+
+ /* For now, just copy everything over. */
+ memcpy(outfmap, infmap, sizeof(bFaceMap));
+
+ outfmap->next = outfmap->prev = NULL;
+
+ return outfmap;
+}
+
+void fmap_copy_list(ListBase *outbase, ListBase *inbase)
+{
+ bFaceMap *fmap, *fmapn;
+
+ BLI_listbase_clear(outbase);
+
+ for (fmap = inbase->first; fmap; fmap = fmap->next) {
+ fmapn = fmap_duplicate(fmap);
+ BLI_addtail(outbase, fmapn);
+ }
+}
+
+void fmap_unique_name(bFaceMap *fmap, Object *ob)
+{
+ struct {Object *ob; void *fmap; } data;
+ data.ob = ob;
+ data.fmap = fmap;
+
+ BLI_uniquename_cb(fmap_unique_check, &data, DATA_("Group"), '.', fmap->name, sizeof(fmap->name));
+}
+
+bFaceMap *BKE_object_facemap_add_name(Object *ob, const char *name)
+{
+ bFaceMap *fmap;
+
+ if (!ob || ob->type != OB_MESH)
+ return NULL;
+
+ fmap = MEM_callocN(sizeof(bFaceMap), __func__);
+
+ BLI_strncpy(fmap->name, name, sizeof(fmap->name));
+
+ BLI_addtail(&ob->fmaps, fmap);
+
+ ob->actfmap = BLI_listbase_count(&ob->fmaps);
+
+ fmap_unique_name(fmap, ob);
+
+ return fmap;
+}
+
+bFaceMap *BKE_object_facemap_add(Object *ob)
+{
+ return BKE_object_facemap_add_name(ob, DATA_("FaceMap"));
+}
+
+
+static void object_fmap_remove_edit_mode(Object *ob, bFaceMap *fmap, bool do_selected, bool purge)
+{
+ const int fmap_nr = BLI_findindex(&ob->fmaps, fmap);
+
+ if (ob->type == OB_MESH) {
+ Mesh *me = ob->data;
+
+ if (me->edit_btmesh) {
+ BMEditMesh *em = me->edit_btmesh;
+ const int cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP);
+
+ if (cd_fmap_offset != -1) {
+ BMFace *efa;
+ BMIter iter;
+ int *map;
+
+ if (purge) {
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
+
+ if (map) {
+ if (*map == fmap_nr)
+ *map = -1;
+ else if (*map > fmap_nr)
+ *map -= 1;
+ }
+ }
+ }
+ else {
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
+
+ if (map && *map == fmap_nr && (!do_selected || BM_elem_flag_test(efa, BM_ELEM_SELECT))) {
+ *map = -1;
+ }
+ }
+ }
+ }
+
+ if (ob->actfmap == BLI_listbase_count(&ob->fmaps))
+ ob->actfmap--;
+
+ BLI_remlink(&ob->fmaps, fmap);
+ MEM_freeN(fmap);
+ }
+ }
+}
+
+static void object_fmap_remove_object_mode(Object *ob, bFaceMap *fmap, bool purge)
+{
+ const int fmap_nr = BLI_findindex(&ob->fmaps, fmap);
+
+ if (ob->type == OB_MESH) {
+ Mesh *me = ob->data;
+
+ if (CustomData_has_layer(&me->pdata, CD_FACEMAP)) {
+ int *map = CustomData_get_layer(&me->pdata, CD_FACEMAP);
+ int i;
+
+ if (map) {
+ for (i = 0; i < me->totpoly; i++) {
+ if (map[i] == fmap_nr)
+ map[i] = -1;
+ else if (purge && map[i] > fmap_nr)
+ map[i]--;
+ }
+ }
+ }
+
+ if (ob->actfmap == BLI_listbase_count(&ob->fmaps))
+ ob->actfmap--;
+
+ BLI_remlink(&ob->fmaps, fmap);
+ MEM_freeN(fmap);
+ }
+}
+
+void BKE_object_facemap_remove(Object *ob, bFaceMap *fmap)
+{
+ if (BKE_object_is_in_editmode(ob))
+ object_fmap_remove_edit_mode(ob, fmap, false, true);
+ else
+ object_fmap_remove_object_mode(ob, fmap, true);
+}
+
+void BKE_object_fmap_remove_all(Object *ob)
+{
+ bFaceMap *fmap = (bFaceMap *)ob->fmaps.first;
+
+ if (fmap) {
+ const bool edit_mode = BKE_object_is_in_editmode_vgroup(ob);
+
+ while (fmap) {
+ bFaceMap *next_fmap = fmap->next;
+
+ if (edit_mode)
+ object_fmap_remove_edit_mode(ob, fmap, false, false);
+ else
+ object_fmap_remove_object_mode(ob, fmap, false);
+
+ fmap = next_fmap;
+ }
+ }
+ /* remove all dverts */
+ if (ob->type == OB_MESH) {
+ Mesh *me = ob->data;
+ CustomData_free_layer(&me->pdata, CD_FACEMAP, me->totpoly, 0);
+ }
+ ob->actfmap = 0;
+}
+
+int fmap_name_index(Object *ob, const char *name)
+{
+ return (name) ? BLI_findstringindex(&ob->fmaps, name, offsetof(bFaceMap, name)) : -1;
+}
+
+bFaceMap *fmap_find_name(Object *ob, const char *name)
+{
+ return BLI_findstring(&ob->fmaps, name, offsetof(bFaceMap, name));
+}
diff --git a/source/blender/blenkernel/intern/fmodifier.c b/source/blender/blenkernel/intern/fmodifier.c
index 396a260c3b8..6e78d08b508 100644
--- a/source/blender/blenkernel/intern/fmodifier.c
+++ b/source/blender/blenkernel/intern/fmodifier.c
@@ -1398,7 +1398,10 @@ float evaluate_time_fmodifiers(FModifierStackStorage *storage, ListBase *modifie
/* sanity checks */
if (ELEM(NULL, modifiers, modifiers->last))
return evaltime;
-
+
+ if (fcu->flag & FCURVE_MOD_OFF)
+ return evaltime;
+
/* Starting from the end of the stack, calculate the time effects of various stacked modifiers
* on the time the F-Curve should be evaluated at.
*
@@ -1455,6 +1458,9 @@ void evaluate_value_fmodifiers(FModifierStackStorage *storage, ListBase *modifie
/* sanity checks */
if (ELEM(NULL, modifiers, modifiers->first))
return;
+
+ if (fcu->flag & FCURVE_MOD_OFF)
+ return;
/* evaluate modifiers */
for (fcm = modifiers->first; fcm; fcm = fcm->next) {
diff --git a/source/blender/blenkernel/intern/group.c b/source/blender/blenkernel/intern/group.c
index ae3ab833a87..78f8a42ffe6 100644
--- a/source/blender/blenkernel/intern/group.c
+++ b/source/blender/blenkernel/intern/group.c
@@ -36,6 +36,7 @@
#include "MEM_guardedalloc.h"
+#include "DNA_cache_library_types.h"
#include "DNA_group_types.h"
#include "DNA_material_types.h"
#include "DNA_object_types.h"
@@ -45,7 +46,7 @@
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
-
+#include "BKE_cache_library.h"
#include "BKE_depsgraph.h"
#include "BKE_global.h"
#include "BKE_group.h"
@@ -318,7 +319,7 @@ bool BKE_group_is_animated(Group *group, Object *UNUSED(parent))
GroupObject *go;
#if 0 /* XXX OLD ANIMSYS, NLASTRIPS ARE NO LONGER USED */
- if (parent->nlastrips.first)
+ if (parent && parent->nlastrips.first)
return 1;
#endif
@@ -385,7 +386,7 @@ void BKE_group_handle_recalc_and_update(EvaluationContext *eval_ctx, Scene *scen
* but when its enabled at some point it will need to be changed so as not to update so much - campbell */
/* if animated group... */
- if (parent->nlastrips.first) {
+ if (parent && parent->nlastrips.first) {
int cfrao;
/* switch to local time */
diff --git a/source/blender/blenkernel/intern/idcode.c b/source/blender/blenkernel/intern/idcode.c
index 091d8a6ea17..82612558966 100644
--- a/source/blender/blenkernel/intern/idcode.c
+++ b/source/blender/blenkernel/intern/idcode.c
@@ -57,6 +57,7 @@ static IDType idtypes[] = {
{ ID_AC, "Action", "actions", BLF_I18NCONTEXT_ID_ACTION, IDTYPE_FLAGS_ISLINKABLE },
{ ID_AR, "Armature", "armatures", BLF_I18NCONTEXT_ID_ARMATURE, IDTYPE_FLAGS_ISLINKABLE },
{ ID_BR, "Brush", "brushes", BLF_I18NCONTEXT_ID_BRUSH, IDTYPE_FLAGS_ISLINKABLE },
+ { ID_CL, "CacheLibrary", "cache_libraries", BLF_I18NCONTEXT_ID_CACHELIBRARY, IDTYPE_FLAGS_ISLINKABLE },
{ ID_CA, "Camera", "cameras", BLF_I18NCONTEXT_ID_CAMERA, IDTYPE_FLAGS_ISLINKABLE },
{ ID_CU, "Curve", "curves", BLF_I18NCONTEXT_ID_CURVE, IDTYPE_FLAGS_ISLINKABLE },
{ ID_GD, "GPencil", "grease_pencil", BLF_I18NCONTEXT_ID_GPENCIL, IDTYPE_FLAGS_ISLINKABLE }, /* rename gpencil */
diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c
index 8427b17c01a..092dd90041f 100644
--- a/source/blender/blenkernel/intern/key.c
+++ b/source/blender/blenkernel/intern/key.c
@@ -45,34 +45,43 @@
#include "DNA_anim_types.h"
#include "DNA_key_types.h"
#include "DNA_lattice_types.h"
+#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
#include "DNA_scene_types.h"
+#include "DNA_strands_types.h"
+#include "DNA_texture_types.h"
#include "BKE_animsys.h"
#include "BKE_curve.h"
#include "BKE_customdata.h"
#include "BKE_deform.h"
+#include "BKE_pointcache.h"
#include "BKE_global.h"
#include "BKE_key.h"
#include "BKE_lattice.h"
#include "BKE_library.h"
+#include "BKE_particle.h"
#include "BKE_editmesh.h"
#include "BKE_scene.h"
-
+#include "BKE_strands.h"
#include "RNA_access.h"
-#define KEY_MODE_DUMMY 0 /* use where mode isn't checked for */
-#define KEY_MODE_BPOINT 1
-#define KEY_MODE_BEZTRIPLE 2
+#include "RE_render_ext.h"
/* old defines from DNA_ipo_types.h for data-type, stored in DNA - don't modify! */
#define IPO_FLOAT 4
#define IPO_BEZTRIPLE 100
#define IPO_BPOINT 101
+#define KEY_MODE_DUMMY 0 /* use where mode isn't checked for */
+#define KEY_MODE_BPOINT 1
+#define KEY_MODE_BEZTRIPLE 2
+
void BKE_key_free(Key *key)
{
KeyBlock *kb;
@@ -97,55 +106,82 @@ void BKE_key_free_nolib(Key *key)
}
}
-Key *BKE_key_add(ID *id) /* common function */
+static void key_set_elemstr(short fromtype, char *r_elemstr, int *r_elemsize)
+{
+ /* XXX the code here uses some defines which will soon be deprecated... */
+ char elemtype = IPO_FLOAT;
+ char numelem = 0;
+ int elemsize = 0;
+
+ switch (fromtype) {
+ case KEY_OWNER_MESH:
+ numelem = 3;
+ elemtype = IPO_FLOAT;
+ elemsize = 12;
+ break;
+ case KEY_OWNER_LATTICE:
+ numelem = 3;
+ elemtype = IPO_FLOAT;
+ elemsize = 12;
+ break;
+ case KEY_OWNER_CURVE:
+ numelem = 4;
+ elemtype = IPO_BPOINT;
+ elemsize = 16;
+ break;
+ case KEY_OWNER_PARTICLES:
+ numelem = 3;
+ elemtype = IPO_FLOAT;
+ elemsize = 12;
+ break;
+ case KEY_OWNER_CACHELIB:
+ numelem = 3;
+ elemtype = IPO_FLOAT;
+ elemsize = 12;
+ break;
+ }
+
+ r_elemstr[0] = numelem;
+ r_elemstr[1] = elemtype;
+ r_elemstr[2] = 0;
+ *r_elemsize = elemsize;
+}
+
+Key *BKE_key_add_ex(ID *from, int fromtype, int fromindex) /* common function */
{
Key *key;
- char *el;
key = BKE_libblock_alloc(G.main, ID_KE, "Key");
key->type = KEY_NORMAL;
- key->from = id;
+ key->from = from;
+ key->fromtype = fromtype;
+ key->fromindex = fromindex;
key->uidgen = 1;
- /* XXX the code here uses some defines which will soon be deprecated... */
- switch (GS(id->name)) {
- case ID_ME:
- el = key->elemstr;
-
- el[0] = 3;
- el[1] = IPO_FLOAT;
- el[2] = 0;
-
- key->elemsize = 12;
-
- break;
- case ID_LT:
- el = key->elemstr;
-
- el[0] = 3;
- el[1] = IPO_FLOAT;
- el[2] = 0;
-
- key->elemsize = 12;
-
- break;
- case ID_CU:
- el = key->elemstr;
-
- el[0] = 4;
- el[1] = IPO_BPOINT;
- el[2] = 0;
-
- key->elemsize = 16;
-
- break;
- }
+ key_set_elemstr(fromtype, key->elemstr, &key->elemsize);
return key;
}
+Key *BKE_key_add(ID *id)
+{
+ int fromtype = 0;
+ switch (GS(id->name)) {
+ case ID_ME: fromtype = KEY_OWNER_MESH; break;
+ case ID_CU: fromtype = KEY_OWNER_CURVE; break;
+ case ID_LT: fromtype = KEY_OWNER_LATTICE; break;
+ default: BLI_assert(false); break; /* other fromtypes should use the _ex version for specifying the type */
+ }
+ return BKE_key_add_ex(id, fromtype, -1);
+}
+
+Key *BKE_key_add_particles(Object *ob, ParticleSystem *psys) /* particles are "special" */
+{
+ return BKE_key_add_ex((ID *)ob, KEY_OWNER_PARTICLES, BLI_findindex(&ob->particlesystem, psys));
+}
+
Key *BKE_key_copy(Key *key)
{
Key *keyn;
@@ -250,6 +286,28 @@ void BKE_key_sort(Key *key)
key->refkey = key->block.first;
}
+void BKE_key_set_from_id(Key *key, ID *id)
+{
+ if (key) {
+ key->from = id;
+ switch (GS(id->name)) {
+ case ID_ME: key->fromtype = KEY_OWNER_MESH; break;
+ case ID_CU: key->fromtype = KEY_OWNER_CURVE; break;
+ case ID_LT: key->fromtype = KEY_OWNER_LATTICE; break;
+ }
+ key->fromindex = -1;
+ }
+}
+
+void BKE_key_set_from_particles(Key *key, Object *ob, ParticleSystem *psys)
+{
+ if (key) {
+ key->from = (ID *)ob;
+ key->fromtype = KEY_OWNER_PARTICLES;
+ key->fromindex = BLI_findindex(&ob->particlesystem, psys);
+ }
+}
+
/**************** do the key ****************/
void key_curve_position_weights(float t, float data[4], int type)
@@ -514,7 +572,7 @@ static char *key_block_get_data(Key *key, KeyBlock *actkb, KeyBlock *kb, char **
if (kb == actkb) {
/* this hack makes it possible to edit shape keys in
* edit mode with shape keys blending applied */
- if (GS(key->from->name) == ID_ME) {
+ if (key->from && key->fromtype == KEY_OWNER_MESH) {
Mesh *me;
BMVert *eve;
BMIter iter;
@@ -546,20 +604,20 @@ static char *key_block_get_data(Key *key, KeyBlock *actkb, KeyBlock *kb, char **
/* currently only the first value of 'ofs' may be set. */
static bool key_pointer_size(const Key *key, const int mode, int *poinsize, int *ofs)
{
- if (key->from == NULL) {
+ /* some types allow NULL for key->from */
+ if (!key->from && !ELEM(key->fromtype, KEY_OWNER_CACHELIB))
return false;
- }
-
- switch (GS(key->from->name)) {
- case ID_ME:
+
+ switch (key->fromtype) {
+ case KEY_OWNER_MESH:
*ofs = sizeof(float) * 3;
*poinsize = *ofs;
break;
- case ID_LT:
+ case KEY_OWNER_LATTICE:
*ofs = sizeof(float) * 3;
*poinsize = *ofs;
break;
- case ID_CU:
+ case KEY_OWNER_CURVE:
if (mode == KEY_MODE_BPOINT) {
*ofs = sizeof(float) * 4;
*poinsize = *ofs;
@@ -568,13 +626,20 @@ static bool key_pointer_size(const Key *key, const int mode, int *poinsize, int
ofs[0] = sizeof(float) * 12;
*poinsize = (*ofs) / 3;
}
-
break;
+ case KEY_OWNER_PARTICLES:
+ *ofs = sizeof(float) * 3;
+ *poinsize = *ofs;
+ break;
+ case KEY_OWNER_CACHELIB:
+ *ofs = sizeof(float) * 3;
+ *poinsize = *ofs;
+ break;
+
default:
BLI_assert(!"invalid 'key->from' ID type");
return false;
}
-
return true;
}
@@ -696,6 +761,87 @@ static void cp_key(const int start, int end, const int tot, char *poin, Key *key
if (freekref) MEM_freeN(freekref);
}
+static void cp_key_strands(const int start, int end, const int tot, char *poin, Key *key, KeyBlock *actkb, KeyBlock *kb, float *weights, const int mode)
+{
+ float ktot = 0.0, kd = 0.0;
+ int elemsize, poinsize = 0, a, ofs, flagflo = 0;
+ char *k1, *freek1;
+
+ /* currently always 0, in future key_pointer_size may assign */
+ ofs = 0;
+
+ if (!key_pointer_size(key, mode, &poinsize, &ofs))
+ return;
+
+ if (end > tot) end = tot;
+
+ if (tot != kb->totelem) {
+ ktot = 0.0;
+ flagflo = 1;
+ if (kb->totelem) {
+ kd = kb->totelem / (float)tot;
+ }
+ else {
+ return;
+ }
+ }
+
+ k1 = key_block_get_data(key, actkb, kb, &freek1);
+
+ /* this exception is needed curves with multiple splines */
+ if (start != 0) {
+
+ poin += poinsize * start;
+
+ if (flagflo) {
+ ktot += start * kd;
+ a = (int)floor(ktot);
+ if (a) {
+ ktot -= a;
+ k1 += a * key->elemsize;
+ }
+ }
+ else {
+ k1 += start * key->elemsize;
+ }
+ }
+
+ /* just do it here, not above! */
+ elemsize = key->elemsize;
+ if (mode == KEY_MODE_BEZTRIPLE) elemsize *= 3;
+
+ for (a = start; a < end; a++) {
+ if (weights) {
+ if (*weights != 0.0f)
+ madd_v3_v3fl((float *)poin, (float *)k1, *weights);
+ weights++;
+ }
+ else {
+ add_v3_v3((float *)poin, (float *)k1);
+ }
+
+ poin += ofs;
+
+ /* are we going to be nasty? */
+ if (flagflo) {
+ ktot += kd;
+ while (ktot >= 1.0f) {
+ ktot -= 1.0f;
+ k1 += elemsize;
+ }
+ }
+ else {
+ k1 += elemsize;
+ }
+
+ if (mode == KEY_MODE_BEZTRIPLE) {
+ a += 2;
+ }
+ }
+
+ if (freek1) MEM_freeN(freek1);
+}
+
static void cp_cu_key(Curve *cu, Key *key, KeyBlock *actkb, KeyBlock *kb, const int start, int end, char *out, const int tot)
{
Nurb *nu;
@@ -730,7 +876,8 @@ void BKE_key_evaluate_relative(const int start, int end, const int tot, char *ba
{
KeyBlock *kb;
int *ofsp, ofs[3], elemsize, b;
- char *cp, *poin, *reffrom, *from, elemstr[8];
+ char *poin, *reffrom, *from;
+ char elemstr[8];
int poinsize, keyblock_index;
/* currently always 0, in future key_pointer_size may assign */
@@ -761,23 +908,25 @@ void BKE_key_evaluate_relative(const int start, int end, const int tot, char *ba
/* only with value, and no difference allowed */
if (!(kb->flag & KEYBLOCK_MUTE) && icuval != 0.0f && kb->totelem == tot) {
- KeyBlock *refb;
float weight, *weights = per_keyblock_weights ? per_keyblock_weights[keyblock_index] : NULL;
char *freefrom = NULL, *freereffrom = NULL;
- /* reference now can be any block */
- refb = BLI_findlink(&key->block, kb->relative);
- if (refb == NULL) continue;
-
poin = basispoin;
from = key_block_get_data(key, actkb, kb, &freefrom);
- reffrom = key_block_get_data(key, actkb, refb, &freereffrom);
+ {
+ /* reference now can be any block */
+ KeyBlock *refb = BLI_findlink(&key->block, kb->relative);
+ if (refb == NULL) continue;
+
+ reffrom = key_block_get_data(key, actkb, refb, &freereffrom);
+ }
poin += start * poinsize;
reffrom += key->elemsize * start; // key elemsize yes!
from += key->elemsize * start;
for (b = start; b < end; b++) {
+ char *cp;
weight = weights ? (*weights * icuval) : icuval;
@@ -826,6 +975,57 @@ void BKE_key_evaluate_relative(const int start, int end, const int tot, char *ba
}
}
+void BKE_key_evaluate_strands_relative(const int start, int end, const int tot, char *basispoin, Key *key, KeyBlock *actkb,
+ float **per_keyblock_weights, const int mode)
+{
+ KeyBlock *kb;
+ int ofs, elemsize, b;
+ char *poin, *from;
+ int poinsize, keyblock_index;
+
+ if (!key_pointer_size(key, mode, &poinsize, &ofs))
+ return;
+
+ if (end > tot) end = tot;
+
+ /* just here, not above! */
+ elemsize = key->elemsize;
+ if (mode == KEY_MODE_BEZTRIPLE) elemsize *= 3;
+
+ for (kb = key->block.first, keyblock_index = 0; kb; kb = kb->next, keyblock_index++) {
+ if (kb != key->refkey) {
+ float icuval = kb->curval;
+
+ /* only with value, and no difference allowed */
+ if (!(kb->flag & KEYBLOCK_MUTE) && icuval != 0.0f && kb->totelem == tot) {
+ float weight, *weights = per_keyblock_weights ? per_keyblock_weights[keyblock_index] : NULL;
+ char *freefrom = NULL;
+
+ poin = basispoin;
+ from = key_block_get_data(key, actkb, kb, &freefrom);
+
+ poin += start * poinsize;
+ from += key->elemsize * start;
+
+ for (b = start; b < end; b++) {
+ float delta[3];
+
+ weight = weights ? (*weights * icuval) : icuval;
+
+ sub_v3_v3v3(delta, (float *)from, (float *)poin);
+ madd_v3_v3fl((float *)poin, delta, weight);
+
+ poin += ofs;
+ from += elemsize;
+ if (mode == KEY_MODE_BEZTRIPLE) b += 2;
+ if (weights) weights++;
+ }
+
+ if (freefrom) MEM_freeN(freefrom);
+ }
+ }
+ }
+}
static void do_key(const int start, int end, const int tot, char *poin, Key *key, KeyBlock *actkb, KeyBlock **k, float *t, const int mode)
{
@@ -1052,7 +1252,199 @@ static void do_key(const int start, int end, const int tot, char *poin, Key *key
if (freek4) MEM_freeN(freek4);
}
-static float *get_weights_array(Object *ob, char *vgroup, WeightsArrayCache *cache)
+static void do_key_strands(const int start, int end, const int tot, char *poin, Key *key, KeyBlock *actkb, KeyBlock **k, float *t, const int mode)
+{
+ float k1tot = 0.0, k2tot = 0.0, k3tot = 0.0, k4tot = 0.0;
+ float k1d = 0.0, k2d = 0.0, k3d = 0.0, k4d = 0.0;
+ int a, ofs;
+ int flagdo = 15, flagflo = 0, elemsize, poinsize = 0;
+ char *k1, *k2, *k3, *k4, *freek1, *freek2, *freek3, *freek4;
+
+ /* currently always 0, in future key_pointer_size may assign */
+ if (!key_pointer_size(key, mode, &poinsize, &ofs))
+ return;
+
+ if (end > tot) end = tot;
+
+ k1 = key_block_get_data(key, actkb, k[0], &freek1);
+ k2 = key_block_get_data(key, actkb, k[1], &freek2);
+ k3 = key_block_get_data(key, actkb, k[2], &freek3);
+ k4 = key_block_get_data(key, actkb, k[3], &freek4);
+
+ /* test for more or less points (per key!) */
+ if (tot != k[0]->totelem) {
+ k1tot = 0.0;
+ flagflo |= 1;
+ if (k[0]->totelem) {
+ k1d = k[0]->totelem / (float)tot;
+ }
+ else {
+ flagdo -= 1;
+ }
+ }
+ if (tot != k[1]->totelem) {
+ k2tot = 0.0;
+ flagflo |= 2;
+ if (k[0]->totelem) {
+ k2d = k[1]->totelem / (float)tot;
+ }
+ else {
+ flagdo -= 2;
+ }
+ }
+ if (tot != k[2]->totelem) {
+ k3tot = 0.0;
+ flagflo |= 4;
+ if (k[0]->totelem) {
+ k3d = k[2]->totelem / (float)tot;
+ }
+ else {
+ flagdo -= 4;
+ }
+ }
+ if (tot != k[3]->totelem) {
+ k4tot = 0.0;
+ flagflo |= 8;
+ if (k[0]->totelem) {
+ k4d = k[3]->totelem / (float)tot;
+ }
+ else {
+ flagdo -= 8;
+ }
+ }
+
+ /* this exception is needed for curves with multiple splines */
+ if (start != 0) {
+
+ poin += poinsize * start;
+
+ if (flagdo & 1) {
+ if (flagflo & 1) {
+ k1tot += start * k1d;
+ a = (int)floor(k1tot);
+ if (a) {
+ k1tot -= a;
+ k1 += a * key->elemsize;
+ }
+ }
+ else {
+ k1 += start * key->elemsize;
+ }
+ }
+ if (flagdo & 2) {
+ if (flagflo & 2) {
+ k2tot += start * k2d;
+ a = (int)floor(k2tot);
+ if (a) {
+ k2tot -= a;
+ k2 += a * key->elemsize;
+ }
+ }
+ else {
+ k2 += start * key->elemsize;
+ }
+ }
+ if (flagdo & 4) {
+ if (flagflo & 4) {
+ k3tot += start * k3d;
+ a = (int)floor(k3tot);
+ if (a) {
+ k3tot -= a;
+ k3 += a * key->elemsize;
+ }
+ }
+ else {
+ k3 += start * key->elemsize;
+ }
+ }
+ if (flagdo & 8) {
+ if (flagflo & 8) {
+ k4tot += start * k4d;
+ a = (int)floor(k4tot);
+ if (a) {
+ k4tot -= a;
+ k4 += a * key->elemsize;
+ }
+ }
+ else {
+ k4 += start * key->elemsize;
+ }
+ }
+
+ }
+
+ /* only here, not above! */
+ elemsize = key->elemsize;
+ if (mode == KEY_MODE_BEZTRIPLE) elemsize *= 3;
+
+ for (a = start; a < end; a++) {
+
+ zero_v3((float *)poin);
+ madd_v3_v3fl((float *)poin, (float *)k1, t[0]);
+ madd_v3_v3fl((float *)poin, (float *)k2, t[1]);
+ madd_v3_v3fl((float *)poin, (float *)k3, t[2]);
+ madd_v3_v3fl((float *)poin, (float *)k4, t[3]);
+
+ poin += ofs;
+
+ /* lets do it the difficult way: when keys have a different size */
+ if (flagdo & 1) {
+ if (flagflo & 1) {
+ k1tot += k1d;
+ while (k1tot >= 1.0f) {
+ k1tot -= 1.0f;
+ k1 += elemsize;
+ }
+ }
+ else k1 += elemsize;
+ }
+ if (flagdo & 2) {
+ if (flagflo & 2) {
+ k2tot += k2d;
+ while (k2tot >= 1.0f) {
+ k2tot -= 1.0f;
+ k2 += elemsize;
+ }
+ }
+ else {
+ k2 += elemsize;
+ }
+ }
+ if (flagdo & 4) {
+ if (flagflo & 4) {
+ k3tot += k3d;
+ while (k3tot >= 1.0f) {
+ k3tot -= 1.0f;
+ k3 += elemsize;
+ }
+ }
+ else {
+ k3 += elemsize;
+ }
+ }
+ if (flagdo & 8) {
+ if (flagflo & 8) {
+ k4tot += k4d;
+ while (k4tot >= 1.0f) {
+ k4tot -= 1.0f;
+ k4 += elemsize;
+ }
+ }
+ else {
+ k4 += elemsize;
+ }
+ }
+
+ if (mode == KEY_MODE_BEZTRIPLE) a += 2;
+ }
+
+ if (freek1) MEM_freeN(freek1);
+ if (freek2) MEM_freeN(freek2);
+ if (freek3) MEM_freeN(freek3);
+ if (freek4) MEM_freeN(freek4);
+}
+
+static float *get_object_weights_array(Object *ob, char *vgroup, WeightsArrayCache *cache)
{
MDeformVert *dvert = NULL;
BMEditMesh *em = NULL;
@@ -1124,7 +1516,109 @@ static float *get_weights_array(Object *ob, char *vgroup, WeightsArrayCache *cac
return NULL;
}
-float **BKE_keyblock_get_per_block_weights(Object *ob, Key *key, WeightsArrayCache *cache)
+static float *get_weights_array_strands(Strands *strands, const char *UNUSED(vgroup), bool is_refkey, WeightsArrayCache *UNUSED(cache))
+{
+ int totvert = strands->totverts;
+
+ if (is_refkey) {
+ /* for the refkey, return zero weights, so the refkey actually uses the unmodified data */
+ float *weights = MEM_callocN(totvert * sizeof(float), "weights");
+ return weights;
+ }
+ else {
+ /* TODO no vgroup support for strands yet */
+ return NULL;
+ }
+}
+
+float **BKE_keyblock_get_per_block_object_weights(Object *ob, Key *key, WeightsArrayCache *cache)
+{
+ KeyBlock *keyblock;
+ float **per_keyblock_weights;
+ int keyblock_index;
+
+ per_keyblock_weights =
+ MEM_mallocN(sizeof(*per_keyblock_weights) * key->totkey,
+ "per keyblock weights");
+
+ for (keyblock = key->block.first, keyblock_index = 0;
+ keyblock;
+ keyblock = keyblock->next, keyblock_index++)
+ {
+ per_keyblock_weights[keyblock_index] = get_object_weights_array(ob, keyblock->vgroup, cache);
+ }
+
+ return per_keyblock_weights;
+}
+
+static int particle_count_keys(ParticleSystem *psys)
+{
+ ParticleData *pa;
+ int p;
+ int totkey = 0;
+ for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa) {
+ totkey += pa->totkey;
+ }
+ return totkey;
+}
+
+static float *get_particle_weights_array(Object *ob, ParticleSystem *psys, const char *name, float cfra)
+{
+ ParticleSettings *part = psys->part;
+ ParticleSimulationData sim;
+ MTex **mtexp;
+ int m, i, p, k;
+ int totvert = 0;
+ float *weights = NULL;
+
+ sim.scene = NULL;
+ sim.ob = ob;
+ sim.psys = psys;
+ sim.psmd = psys_get_modifier(ob, psys);
+
+ for (m = 0, mtexp = part->mtex; m < MAX_MTEX; ++m, ++mtexp) {
+ MTex *mtex = *mtexp;
+ if (mtex && (mtex->mapto & PAMAP_SHAPEKEY) && STREQ(mtex->shapekey, name)) {
+ const float def = mtex->def_var;
+ const short blend = mtex->blendtype;
+
+ ParticleData *pa;
+ float value, rgba[4];
+
+ /* lazy init */
+ if (!weights) {
+ totvert = particle_count_keys(psys);
+ weights = MEM_mallocN(totvert * sizeof(float), "weights");
+ for (i = 0; i < totvert; ++i)
+ weights[i] = 1.0f;
+ }
+
+ i = 0;
+ for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa) {
+ if (particle_mtex_eval(&sim, mtex, pa, cfra, &value, rgba)) {
+ /* weight is the same for all hair keys,
+ * only need to evaluate the texture once!
+ */
+ float result = texture_value_blend(def, weights[i], value, mtex->shapefac, blend);
+ for (k = 0; k < pa->totkey; ++k) {
+ weights[i + k] = result;
+ }
+ }
+
+ i += pa->totkey;
+ }
+ }
+ }
+
+ if (weights) {
+ for (i = 0; i < totvert; ++i)
+ CLAMP(weights[i], 0.0f, 1.0f);
+ }
+
+ return weights;
+}
+
+float **BKE_keyblock_get_per_block_particle_weights(Object *ob, ParticleSystem *psys, float cfra, Key *key, WeightsArrayCache *UNUSED(cache))
{
KeyBlock *keyblock;
float **per_keyblock_weights;
@@ -1138,7 +1632,27 @@ float **BKE_keyblock_get_per_block_weights(Object *ob, Key *key, WeightsArrayCac
keyblock;
keyblock = keyblock->next, keyblock_index++)
{
- per_keyblock_weights[keyblock_index] = get_weights_array(ob, keyblock->vgroup, cache);
+ per_keyblock_weights[keyblock_index] = get_particle_weights_array(ob, psys, keyblock->name, cfra);
+ }
+
+ return per_keyblock_weights;
+}
+
+float **BKE_keyblock_strands_get_per_block_weights(Strands *strands, Key *key, WeightsArrayCache *cache)
+{
+ KeyBlock *keyblock;
+ float **per_keyblock_weights;
+ int keyblock_index;
+
+ per_keyblock_weights =
+ MEM_mallocN(sizeof(*per_keyblock_weights) * key->totkey,
+ "per keyblock weights");
+
+ for (keyblock = key->block.first, keyblock_index = 0;
+ keyblock;
+ keyblock = keyblock->next, keyblock_index++)
+ {
+ per_keyblock_weights[keyblock_index] = get_weights_array_strands(strands, keyblock->vgroup, keyblock == key->refkey, cache);
}
return per_keyblock_weights;
@@ -1179,7 +1693,7 @@ static void do_mesh_key(Object *ob, Key *key, char *out, const int tot)
if (key->type == KEY_RELATIVE) {
WeightsArrayCache cache = {0, NULL};
float **per_keyblock_weights;
- per_keyblock_weights = BKE_keyblock_get_per_block_weights(ob, key, &cache);
+ per_keyblock_weights = BKE_keyblock_get_per_block_object_weights(ob, key, &cache);
BKE_key_evaluate_relative(0, tot, tot, (char *)out, key, actkb, per_keyblock_weights, KEY_MODE_DUMMY);
BKE_keyblock_free_per_block_weights(key, per_keyblock_weights, &cache);
}
@@ -1270,7 +1784,7 @@ static void do_latt_key(Object *ob, Key *key, char *out, const int tot)
if (key->type == KEY_RELATIVE) {
float **per_keyblock_weights;
- per_keyblock_weights = BKE_keyblock_get_per_block_weights(ob, key, NULL);
+ per_keyblock_weights = BKE_keyblock_get_per_block_object_weights(ob, key, NULL);
BKE_key_evaluate_relative(0, tot, tot, (char *)out, key, actkb, per_keyblock_weights, KEY_MODE_DUMMY);
BKE_keyblock_free_per_block_weights(key, per_keyblock_weights, NULL);
}
@@ -1290,6 +1804,32 @@ static void do_latt_key(Object *ob, Key *key, char *out, const int tot)
if (lt->flag & LT_OUTSIDE) outside_lattice(lt);
}
+static void do_psys_key(Object *ob, ParticleSystem *psys, float cfra, Key *key, char *out, const int tot)
+{
+ KeyBlock *k[4], *actkb = BKE_keyblock_from_particles(psys);
+ float t[4];
+ int flag = 0;
+
+ if (key->type == KEY_RELATIVE) {
+ WeightsArrayCache cache = {0, NULL};
+ float **per_keyblock_weights;
+
+ per_keyblock_weights = BKE_keyblock_get_per_block_particle_weights(ob, psys, cfra, key, &cache);
+ BKE_key_evaluate_relative(0, tot, tot, (char *)out, key, actkb, per_keyblock_weights, KEY_MODE_DUMMY);
+ BKE_keyblock_free_per_block_weights(key, per_keyblock_weights, &cache);
+ }
+ else {
+ const float ctime_scaled = key->ctime / 100.0f;
+
+ flag = setkeys(ctime_scaled, &key->block, k, t, 0);
+
+ if (flag == 0)
+ do_key(0, tot, tot, (char *)out, key, actkb, k, t, KEY_MODE_DUMMY);
+ else
+ cp_key(0, tot, tot, (char *)out, key, actkb, k[2], NULL, KEY_MODE_DUMMY);
+ }
+}
+
/* returns key coordinates (+ tilt) when key applied, NULL otherwise */
float *BKE_key_evaluate_object_ex(
Object *ob, int *r_totelem,
@@ -1349,7 +1889,7 @@ float *BKE_key_evaluate_object_ex(
}
/* prevent python from screwing this up? anyhoo, the from pointer could be dropped */
- key->from = (ID *)ob->data;
+ BKE_key_set_from_id(key, (ID *)ob->data);
if (ob->shapeflag & OB_SHAPE_LOCK) {
/* shape locked, copy the locked shape instead of blending */
@@ -1364,7 +1904,7 @@ float *BKE_key_evaluate_object_ex(
}
if (OB_TYPE_SUPPORT_VGROUP(ob->type)) {
- float *weights = get_weights_array(ob, kb->vgroup, NULL);
+ float *weights = get_object_weights_array(ob, kb->vgroup, NULL);
cp_key(0, tot, tot, out, key, actkb, kb, weights, 0);
@@ -1392,6 +1932,196 @@ float *BKE_key_evaluate_object(Object *ob, int *r_totelem)
return BKE_key_evaluate_object_ex(ob, r_totelem, NULL, 0);
}
+static void do_strands_key(Strands *strands, Key *key, KeyBlock *actkb, char *out, const int tot)
+{
+ KeyBlock *k[4];
+ float t[4];
+ int flag = 0;
+
+ if (key->type == KEY_RELATIVE) {
+ WeightsArrayCache cache = {0, NULL};
+ float **per_keyblock_weights ;
+ per_keyblock_weights = BKE_keyblock_strands_get_per_block_weights(strands, key, &cache);
+ BKE_key_evaluate_strands_relative(0, tot, tot, (char *)out, key, actkb, per_keyblock_weights, KEY_MODE_DUMMY);
+ BKE_keyblock_free_per_block_weights(key, per_keyblock_weights, &cache);
+ }
+ else {
+ const float ctime_scaled = key->ctime / 100.0f;
+
+ flag = setkeys(ctime_scaled, &key->block, k, t, 0);
+
+ if (flag == 0) {
+ do_key_strands(0, tot, tot, (char *)out, key, actkb, k, t, KEY_MODE_DUMMY);
+ }
+ else {
+ cp_key_strands(0, tot, tot, (char *)out, key, actkb, k[2], NULL, KEY_MODE_DUMMY);
+ }
+ }
+}
+
+float *BKE_key_evaluate_strands_ex(Strands *strands, Key *key, KeyBlock *actkb, bool lock_shape, int *r_totelem, float *arr, size_t arr_size)
+{
+ char *out;
+ int tot = 0, size = 0;
+
+ if (key == NULL || BLI_listbase_is_empty(&key->block))
+ return NULL;
+
+ /* compute size of output array */
+ tot = strands->totverts;
+ size = tot * 3 * sizeof(float);
+ /* if nothing to interpolate, cancel */
+ if (tot == 0 || size == 0)
+ return NULL;
+
+ /* allocate array */
+ if (arr == NULL) {
+ out = MEM_callocN(size, "BKE_key_evaluate_strands out");
+ }
+ else {
+ if (arr_size != size) {
+ return NULL;
+ }
+
+ out = (char *)arr;
+ }
+
+ if (lock_shape && actkb) {
+ /* shape locked, copy the locked shape instead of blending */
+ float *weights;
+
+ if (actkb->flag & KEYBLOCK_MUTE)
+ actkb = key->refkey;
+
+ /* XXX weights not supported for strands yet */
+ weights = get_weights_array_strands(strands, actkb->vgroup, actkb == key->refkey, NULL);
+
+ cp_key_strands(0, tot, tot, out, key, actkb, actkb, weights, 0);
+
+ if (weights)
+ MEM_freeN(weights);
+ }
+ else {
+ do_strands_key(strands, key, actkb, out, tot);
+ }
+
+ if (r_totelem) {
+ *r_totelem = tot;
+ }
+ return (float *)out;
+}
+
+float *BKE_key_evaluate_strands(Strands *strands, Key *key, KeyBlock *actkb, bool lock_shape, int *r_totelem, bool use_motion)
+{
+ size_t size = sizeof(float) * 3 * strands->totverts;
+ float *data = MEM_mallocN(size, "strands shape key data");
+ float *result;
+ float *fp;
+ int i;
+
+ if (use_motion && strands->state) {
+ for (i = 0, fp = data; i < strands->totverts; ++i, fp += 3)
+ copy_v3_v3(fp, strands->state[i].co);
+ }
+ else {
+ for (i = 0, fp = data; i < strands->totverts; ++i, fp += 3)
+ copy_v3_v3(fp, strands->verts[i].co);
+ }
+
+ result = BKE_key_evaluate_strands_ex(strands, key, actkb, lock_shape, r_totelem, data, size);
+ if (result != data)
+ MEM_freeN(data);
+
+ return result;
+}
+
+/* returns key coordinates when key applied, NULL otherwise */
+float *BKE_key_evaluate_particles_ex(Object *ob, ParticleSystem *psys, float cfra, int *r_totelem,
+ float *arr, size_t arr_size)
+{
+ const bool use_editmode = (ob->mode & OB_MODE_PARTICLE_EDIT) && psys == psys_get_current(ob) && (psys->edit || psys->pointcache->edit) && !psys->renderdata;
+ Key *key = psys->key;
+ KeyBlock *actkb = BKE_keyblock_from_particles(psys);
+ char *out;
+ int tot = 0, size = 0;
+ int i;
+
+ if (key == NULL || BLI_listbase_is_empty(&key->block))
+ return NULL;
+
+ /* compute size of output array */
+ tot = 0;
+ for (i = 0; i < psys->totpart; ++i)
+ tot += psys->particles[i].totkey;
+ size = tot * 3 * sizeof(float);
+
+ /* if nothing to interpolate, cancel */
+ if (tot == 0 || size == 0)
+ return NULL;
+
+ /* allocate array */
+ if (arr == NULL) {
+ out = MEM_callocN(size, "BKE_key_evaluate_object out");
+ }
+ else {
+ if (arr_size != size) {
+ return NULL;
+ }
+
+ out = (char *)arr;
+ }
+
+ /* prevent python from screwing this up? anyhoo, the from pointer could be dropped */
+ BKE_key_set_from_particles(key, ob, psys);
+
+ if (use_editmode) {
+ /* in edit mode, only evaluate the active shape */
+ KeyBlock *kb = BLI_findlink(&key->block, psys->shapenr - 1);
+
+ if (kb && (kb->flag & KEYBLOCK_MUTE))
+ kb = key->refkey;
+
+ if (kb == NULL) {
+ kb = key->block.first;
+ psys->shapenr = 1;
+ }
+
+ cp_key(0, tot, tot, out, key, actkb, kb, NULL, 0);
+ }
+ else if (ob->shapeflag & OB_SHAPE_LOCK) {
+ /* shape locked, copy the locked shape instead of blending */
+ KeyBlock *kb = BLI_findlink(&key->block, psys->shapenr - 1);
+ float *weights;
+
+ if (kb && (kb->flag & KEYBLOCK_MUTE))
+ kb = key->refkey;
+
+ if (kb == NULL) {
+ kb = key->block.first;
+ psys->shapenr = 1;
+ }
+
+ weights = get_particle_weights_array(ob, psys, kb->name, cfra);
+
+ cp_key(0, tot, tot, out, key, actkb, kb, weights, 0);
+
+ if (weights) MEM_freeN(weights);
+ }
+ else {
+ do_psys_key(ob, psys, cfra, key, out, tot);
+ }
+
+ if (r_totelem) {
+ *r_totelem = tot;
+ }
+ return (float *)out;
+}
+
+float *BKE_key_evaluate_particles(Object *ob, ParticleSystem *psys, float cfra, int *r_totelem)
+{
+ return BKE_key_evaluate_particles_ex(ob, psys, cfra, r_totelem, NULL, 0);
+}
+
Key **BKE_key_from_object_p(Object *ob)
{
if (ob == NULL)
@@ -1522,6 +2252,29 @@ KeyBlock *BKE_keyblock_from_object_reference(Object *ob)
return NULL;
}
+/* only the active keyblock */
+KeyBlock *BKE_keyblock_from_particles(ParticleSystem *psys)
+{
+ Key *key = psys->key;
+
+ if (key) {
+ KeyBlock *kb = BLI_findlink(&key->block, psys->shapenr - 1);
+ return kb;
+ }
+
+ return NULL;
+}
+
+KeyBlock *BKE_keyblock_from_particles_reference(ParticleSystem *psys)
+{
+ Key *key = psys->key;
+
+ if (key)
+ return key->refkey;
+
+ return NULL;
+}
+
/* get the appropriate KeyBlock given an index */
KeyBlock *BKE_keyblock_from_key(Key *key, int index)
{
@@ -1774,6 +2527,72 @@ void BKE_keyblock_convert_to_mesh(KeyBlock *kb, Mesh *me)
}
}
+/************************* Strands ************************/
+void BKE_keyblock_update_from_strands(Strands *strands, KeyBlock *kb, bool use_motion)
+{
+ float (*fp)[3];
+ int a, tot;
+
+ BLI_assert(strands->totverts == kb->totelem);
+
+ tot = strands->totverts;
+ if (tot == 0) return;
+
+ fp = kb->data;
+ /* use vertex locations as fallback, so we always get a valid shape */
+ if (use_motion && strands->state) {
+ StrandsMotionState *state = strands->state;
+ for (a = 0; a < tot; a++, fp++, state++) {
+ copy_v3_v3(*fp, state->co);
+ }
+ }
+ else {
+ StrandsVertex *vert = strands->verts;
+ for (a = 0; a < tot; a++, fp++, vert++) {
+ copy_v3_v3(*fp, vert->co);
+ }
+ }
+}
+
+void BKE_keyblock_convert_from_strands(Strands *strands, Key *key, KeyBlock *kb, bool use_motion)
+{
+ int tot = strands->totverts;
+
+ if (strands->totverts == 0) return;
+
+ MEM_SAFE_FREE(kb->data);
+
+ kb->data = MEM_mallocN(key->elemsize * tot, __func__);
+ kb->totelem = tot;
+
+ BKE_keyblock_update_from_strands(strands, kb, use_motion);
+}
+
+void BKE_keyblock_convert_to_strands(KeyBlock *kb, Strands *strands, bool use_motion)
+{
+ const float (*fp)[3];
+ int a, tot;
+
+ fp = kb->data;
+
+ tot = min_ii(kb->totelem, strands->totverts);
+
+ if (use_motion) {
+ if (strands->state) {
+ StrandsMotionState *state = strands->state;
+ for (a = 0; a < tot; a++, fp++, state++) {
+ copy_v3_v3(state->co, *fp);
+ }
+ }
+ }
+ else {
+ StrandsVertex *vert = strands->verts;
+ for (a = 0; a < tot; a++, fp++, vert++) {
+ copy_v3_v3(vert->co, *fp);
+ }
+ }
+}
+
/************************* raw coords ************************/
void BKE_keyblock_update_from_vertcos(Object *ob, KeyBlock *kb, float (*vertCos)[3])
{
@@ -1960,6 +2779,48 @@ void BKE_keyblock_update_from_offset(Object *ob, KeyBlock *kb, float (*ofs)[3])
}
}
+/************************* Mesh ************************/
+
+void BKE_keyblock_convert_to_hair_keys(struct KeyBlock *kb, struct Object *UNUSED(ob), struct ParticleSystem *psys)
+{
+ ParticleData *pa;
+ HairKey *hkey;
+ float *fp;
+ int i, k;
+
+ fp = kb->data;
+ for (i = 0, pa = psys->particles; i < psys->totpart; ++i, ++pa) {
+ for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) {
+ copy_v3_v3(hkey->co, fp);
+ fp += 3;
+ }
+ }
+}
+
+void BKE_keyblock_convert_from_hair_keys(struct Object *UNUSED(ob), struct ParticleSystem *psys, struct KeyBlock *kb)
+{
+ ParticleData *pa;
+ HairKey *hkey;
+ float *fp;
+ int i, k;
+
+ if (kb->data) MEM_freeN(kb->data);
+
+ kb->totelem = 0;
+ for (i = 0, pa = psys->particles; i < psys->totpart; ++i, ++pa) {
+ kb->totelem += pa->totkey;
+ }
+ kb->data = MEM_mallocN(psys->key->elemsize * kb->totelem, "kb->data");
+
+ fp = kb->data;
+ for (i = 0, pa = psys->particles; i < psys->totpart; ++i, ++pa) {
+ for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) {
+ copy_v3_v3(fp, hkey->co);
+ fp += 3;
+ }
+ }
+}
+
/* ==========================================================*/
/** Move shape key from org_index to new_index. Safe, clamps index to valid range, updates reference keys,
@@ -1969,11 +2830,10 @@ void BKE_keyblock_update_from_offset(Object *ob, KeyBlock *kb, float (*ofs)[3])
* \param org_index if < 0, current object's active shape will be used as skey to move.
* \return true if something was done, else false.
*/
-bool BKE_keyblock_move(Object *ob, int org_index, int new_index)
+bool BKE_keyblock_move_ex(Key *key, int *shapenr, int org_index, int new_index)
{
- Key *key = BKE_key_from_object(ob);
KeyBlock *kb;
- const int act_index = ob->shapenr - 1;
+ const int act_index = *shapenr - 1;
const int totkey = key->totkey;
int i;
bool rev, in_range = false;
@@ -2033,13 +2893,13 @@ bool BKE_keyblock_move(Object *ob, int org_index, int new_index)
/* Need to update active shape number if it's affected, same principle as for relative indices above. */
if (org_index == act_index) {
- ob->shapenr = new_index + 1;
+ *shapenr = new_index + 1;
}
else if (act_index < org_index && act_index >= new_index) {
- ob->shapenr++;
+ (*shapenr)++;
}
else if (act_index > org_index && act_index <= new_index) {
- ob->shapenr--;
+ (*shapenr)--;
}
/* First key is always refkey, matches interface and BKE_key_sort */
@@ -2048,6 +2908,14 @@ bool BKE_keyblock_move(Object *ob, int org_index, int new_index)
return true;
}
+bool BKE_keyblock_move(Object *ob, int org_index, int new_index)
+{
+ int shapenr;
+ bool result = BKE_keyblock_move_ex(BKE_key_from_object(ob), &shapenr, org_index, new_index);
+ ob->shapenr = shapenr;
+ return result;
+}
+
/**
* Check if given keyblock (as index) is used as basis by others in given key.
*/
diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c
index 86927609ff6..582c164a776 100644
--- a/source/blender/blenkernel/intern/lattice.c
+++ b/source/blender/blenkernel/intern/lattice.c
@@ -275,7 +275,7 @@ Lattice *BKE_lattice_copy(Lattice *lt)
ltn->def = MEM_dupallocN(lt->def);
ltn->key = BKE_key_copy(ltn->key);
- if (ltn->key) ltn->key->from = (ID *)ltn;
+ BKE_key_set_from_id(ltn->key, (ID *)ltn);
if (lt->dvert) {
int tot = lt->pntsu * lt->pntsv * lt->pntsw;
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index b4bb07f0d3a..79b3c0b49d0 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_cache_library_types.h"
#include "DNA_camera_types.h"
#include "DNA_group_types.h"
#include "DNA_gpencil_types.h"
@@ -79,6 +80,7 @@
#include "BKE_armature.h"
#include "BKE_bpath.h"
#include "BKE_brush.h"
+#include "BKE_cache_library.h"
#include "BKE_camera.h"
#include "BKE_context.h"
#include "BKE_curve.h"
@@ -107,6 +109,7 @@
#include "BKE_paint.h"
#include "BKE_particle.h"
#include "BKE_packedFile.h"
+#include "BKE_pointcache.h"
#include "BKE_speaker.h"
#include "BKE_sound.h"
#include "BKE_screen.h"
@@ -411,6 +414,10 @@ bool id_unlink(ID *id, int test)
if (test) return true;
BKE_object_unlink((Object *)id);
break;
+ case ID_CL:
+ if (test) return true;
+ BKE_cache_library_unlink((CacheLibrary *)id);
+ break;
}
if (id->us == 0) {
@@ -523,6 +530,8 @@ ListBase *which_libbase(Main *mainlib, short type)
return &(mainlib->palettes);
case ID_PC:
return &(mainlib->paintcurves);
+ case ID_CL:
+ return &(mainlib->cache_library);
}
return NULL;
}
@@ -620,6 +629,7 @@ int set_listbasepointers(Main *main, ListBase **lb)
lb[a++] = &(main->linestyle); /* referenced by scenes */
lb[a++] = &(main->scene);
lb[a++] = &(main->library);
+ lb[a++] = &(main->cache_library);
lb[a++] = &(main->wm);
lb[a++] = &(main->mask);
@@ -751,6 +761,9 @@ static ID *alloc_libblock_notest(short type)
case ID_PC:
id = MEM_callocN(sizeof(PaintCurve), "Paint Curve");
break;
+ case ID_CL:
+ id = MEM_callocN(sizeof(CacheLibrary), "Cache Library");
+ break;
}
return id;
}
@@ -1039,6 +1052,9 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, bool do_id_user)
case ID_PC:
BKE_paint_curve_free((PaintCurve *)id);
break;
+ case ID_CL:
+ BKE_cache_library_free((CacheLibrary *)id);
+ break;
}
/* avoid notifying on removed data */
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index 37a98eae58b..c8223657a05 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -571,7 +571,7 @@ Mesh *BKE_mesh_copy_ex(Main *bmain, Mesh *me)
men->bb = MEM_dupallocN(men->bb);
men->key = BKE_key_copy(me->key);
- if (men->key) men->key->from = (ID *)men;
+ BKE_key_set_from_id(men->key, (ID *)men);
if (me->id.lib) {
BKE_id_lib_local_paths(bmain, me->id.lib, &men->id);
@@ -2524,6 +2524,66 @@ Mesh *BKE_mesh_new_from_object(
return tmpmesh;
}
+/* settings: 1 - preview, 2 - render */
+Mesh *BKE_mesh_new_from_dupli_data(
+ Main *bmain, DupliObjectData *data,
+ bool calc_tessface, bool calc_undeformed)
+{
+ Object *ob = data->ob;
+ DerivedMesh *dm = data->dm;
+ CustomDataMask mask;
+
+ Mesh *tmpmesh;
+
+ if (!ob || !dm)
+ return NULL;
+
+ mask = CD_MASK_MESH; /* this seems more suitable, exporter,
+ * for example, needs CD_MASK_MDEFORMVERT */
+ if (calc_undeformed)
+ mask |= CD_MASK_ORCO;
+
+ tmpmesh = BKE_mesh_add(bmain, "Mesh");
+ DM_to_mesh(dm, tmpmesh, ob, mask, true);
+
+ /* BKE_mesh_add/copy gives us a user count we don't need */
+ tmpmesh->id.us--;
+
+ /* Copy materials to new mesh */
+ switch (ob->type) {
+ case OB_MESH: {
+ Mesh *origmesh = ob->data;
+ int i;
+
+ tmpmesh->flag = origmesh->flag;
+ tmpmesh->mat = MEM_dupallocN(origmesh->mat);
+ tmpmesh->totcol = origmesh->totcol;
+ tmpmesh->smoothresh = origmesh->smoothresh;
+ if (origmesh->mat) {
+ for (i = origmesh->totcol; i-- > 0; ) {
+ /* are we an object material or data based? */
+ tmpmesh->mat[i] = ob->matbits[i] ? ob->mat[i] : origmesh->mat[i];
+
+ if (tmpmesh->mat[i]) {
+ tmpmesh->mat[i]->id.us++;
+ }
+ }
+ }
+ break;
+ }
+ } /* end copy materials */
+
+ if (calc_tessface) {
+ /* cycles and exporters rely on this still */
+ BKE_mesh_tessface_ensure(tmpmesh);
+ }
+
+ /* make sure materials get updated in objects */
+ test_object_materials(bmain, &tmpmesh->id);
+
+ return tmpmesh;
+}
+
/* **** Depsgraph evaluation **** */
void BKE_mesh_eval_geometry(EvaluationContext *UNUSED(eval_ctx),
diff --git a/source/blender/blenkernel/intern/mesh_sample.c b/source/blender/blenkernel/intern/mesh_sample.c
new file mode 100644
index 00000000000..b1a89ec64fc
--- /dev/null
+++ b/source/blender/blenkernel/intern/mesh_sample.c
@@ -0,0 +1,381 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/mesh_sample.c
+ * \ingroup bke
+ *
+ * Sample a mesh surface or volume and evaluate samples on deformed meshes.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_key_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+#include "BLI_rand.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_mesh_sample.h"
+#include "BKE_customdata.h"
+#include "BKE_DerivedMesh.h"
+
+#include "BLI_strict_flags.h"
+
+/* ==== Evaluate ==== */
+
+bool BKE_mesh_sample_eval(DerivedMesh *dm, const MSurfaceSample *sample, float loc[3], float nor[3], float tang[3])
+{
+ MVert *mverts = dm->getVertArray(dm);
+ unsigned int totverts = (unsigned int)dm->getNumVerts(dm);
+ MVert *v1, *v2, *v3;
+
+ zero_v3(loc);
+ zero_v3(nor);
+ zero_v3(tang);
+
+ if (sample->orig_verts[0] >= totverts ||
+ sample->orig_verts[1] >= totverts ||
+ sample->orig_verts[2] >= totverts)
+ return false;
+
+ v1 = &mverts[sample->orig_verts[0]];
+ v2 = &mverts[sample->orig_verts[1]];
+ v3 = &mverts[sample->orig_verts[2]];
+
+ { /* location */
+ madd_v3_v3fl(loc, v1->co, sample->orig_weights[0]);
+ madd_v3_v3fl(loc, v2->co, sample->orig_weights[1]);
+ madd_v3_v3fl(loc, v3->co, sample->orig_weights[2]);
+ }
+
+ { /* normal */
+ float vnor[3];
+
+ normal_short_to_float_v3(vnor, v1->no);
+ madd_v3_v3fl(nor, vnor, sample->orig_weights[0]);
+ normal_short_to_float_v3(vnor, v2->no);
+ madd_v3_v3fl(nor, vnor, sample->orig_weights[1]);
+ normal_short_to_float_v3(vnor, v3->no);
+ madd_v3_v3fl(nor, vnor, sample->orig_weights[2]);
+
+ normalize_v3(nor);
+ }
+
+ { /* tangent */
+ float edge[3];
+
+ /* XXX simply using the v1-v2 edge as a tangent vector for now ...
+ * Eventually mikktspace generated tangents (CD_TANGENT tessface layer)
+ * should be used for consistency, but requires well-defined tessface
+ * indices for the mesh surface samples.
+ */
+
+ sub_v3_v3v3(edge, v2->co, v1->co);
+ /* make edge orthogonal to nor */
+ madd_v3_v3fl(edge, nor, -dot_v3v3(edge, nor));
+ normalize_v3_v3(tang, edge);
+ }
+
+ return true;
+}
+
+bool BKE_mesh_sample_shapekey(Key *key, KeyBlock *kb, const MSurfaceSample *sample, float loc[3])
+{
+ float *v1, *v2, *v3;
+
+ (void)key; /* Unused in release builds. */
+
+ BLI_assert(key->elemsize == 3 * sizeof(float));
+ BLI_assert(sample->orig_verts[0] < (unsigned int)kb->totelem);
+ BLI_assert(sample->orig_verts[1] < (unsigned int)kb->totelem);
+ BLI_assert(sample->orig_verts[2] < (unsigned int)kb->totelem);
+
+ v1 = (float *)kb->data + sample->orig_verts[0] * 3;
+ v2 = (float *)kb->data + sample->orig_verts[1] * 3;
+ v3 = (float *)kb->data + sample->orig_verts[2] * 3;
+
+ zero_v3(loc);
+ madd_v3_v3fl(loc, v1, sample->orig_weights[0]);
+ madd_v3_v3fl(loc, v2, sample->orig_weights[1]);
+ madd_v3_v3fl(loc, v3, sample->orig_weights[2]);
+
+ /* TODO use optional vgroup weights to determine if a shapeky actually affects the sample */
+ return true;
+}
+
+
+/* ==== Sampling Utilities ==== */
+
+BLI_INLINE void mesh_sample_weights_from_loc(MSurfaceSample *sample, DerivedMesh *dm, int face_index, const float loc[3])
+{
+ MFace *face = &dm->getTessFaceArray(dm)[face_index];
+ unsigned int index[4] = { face->v1, face->v2, face->v3, face->v4 };
+ MVert *mverts = dm->getVertArray(dm);
+
+ float *v1 = mverts[face->v1].co;
+ float *v2 = mverts[face->v2].co;
+ float *v3 = mverts[face->v3].co;
+ float *v4 = face->v4 ? mverts[face->v4].co : NULL;
+ float w[4];
+ int tri[3];
+
+ interp_weights_face_v3_index(tri, w, v1, v2, v3, v4, loc);
+
+ sample->orig_verts[0] = index[tri[0]];
+ sample->orig_verts[1] = index[tri[1]];
+ sample->orig_verts[2] = index[tri[2]];
+ sample->orig_weights[0] = w[tri[0]];
+ sample->orig_weights[1] = w[tri[1]];
+ sample->orig_weights[2] = w[tri[2]];
+}
+
+/* ==== Sampling ==== */
+
+static bool mesh_sample_store_array_sample(void *vdata, int capacity, int index, const MSurfaceSample *sample)
+{
+ MSurfaceSample *data = vdata;
+ if (index >= capacity)
+ return false;
+
+ data[index] = *sample;
+ return true;
+}
+
+void BKE_mesh_sample_storage_single(MSurfaceSampleStorage *storage, MSurfaceSample *sample)
+{
+ /* handled as just a special array case with capacity = 1 */
+ storage->store_sample = mesh_sample_store_array_sample;
+ storage->capacity = 1;
+ storage->data = sample;
+ storage->free_data = false;
+}
+
+void BKE_mesh_sample_storage_array(MSurfaceSampleStorage *storage, MSurfaceSample *samples, int capacity)
+{
+ storage->store_sample = mesh_sample_store_array_sample;
+ storage->capacity = capacity;
+ storage->data = samples;
+ storage->free_data = false;
+}
+
+void BKE_mesh_sample_storage_release(MSurfaceSampleStorage *storage)
+{
+ if (storage->free_data)
+ MEM_freeN(storage->data);
+}
+
+
+int BKE_mesh_sample_generate_random(MSurfaceSampleStorage *dst, DerivedMesh *dm, unsigned int seed, int totsample)
+{
+ MFace *mfaces;
+ int totfaces;
+ RNG *rng;
+ MFace *mface;
+ float a, b;
+ int i, stored = 0;
+
+ rng = BLI_rng_new(seed);
+
+ DM_ensure_tessface(dm);
+ mfaces = dm->getTessFaceArray(dm);
+ totfaces = dm->getNumTessFaces(dm);
+
+ for (i = 0; i < totsample; ++i) {
+ MSurfaceSample sample = {{0}};
+
+ mface = &mfaces[BLI_rng_get_int(rng) % totfaces];
+
+ if (mface->v4 && BLI_rng_get_int(rng) % 2 == 0) {
+ sample.orig_verts[0] = mface->v3;
+ sample.orig_verts[1] = mface->v4;
+ sample.orig_verts[2] = mface->v1;
+ }
+ else {
+ sample.orig_verts[0] = mface->v1;
+ sample.orig_verts[1] = mface->v2;
+ sample.orig_verts[2] = mface->v3;
+ }
+
+ a = BLI_rng_get_float(rng);
+ b = BLI_rng_get_float(rng);
+ if (a + b > 1.0f) {
+ a = 1.0f - a;
+ b = 1.0f - b;
+ }
+ sample.orig_weights[0] = 1.0f - (a + b);
+ sample.orig_weights[1] = a;
+ sample.orig_weights[2] = b;
+
+ if (dst->store_sample(dst->data, dst->capacity, i, &sample))
+ ++stored;
+ else
+ break;
+ }
+
+ BLI_rng_free(rng);
+
+ return stored;
+}
+
+
+static bool sample_bvh_raycast(MSurfaceSample *sample, DerivedMesh *dm, BVHTreeFromMesh *bvhdata, const float ray_start[3], const float ray_end[3])
+{
+ BVHTreeRayHit hit;
+ float ray_normal[3], dist;
+
+ sub_v3_v3v3(ray_normal, ray_end, ray_start);
+ dist = normalize_v3(ray_normal);
+
+ hit.index = -1;
+ hit.dist = dist;
+
+ if (BLI_bvhtree_ray_cast(bvhdata->tree, ray_start, ray_normal, 0.0f,
+ &hit, bvhdata->raycast_callback, bvhdata) >= 0) {
+
+ mesh_sample_weights_from_loc(sample, dm, hit.index, hit.co);
+
+ return true;
+ }
+ else
+ return false;
+}
+
+int BKE_mesh_sample_generate_raycast(MSurfaceSampleStorage *dst, DerivedMesh *dm, MeshSampleRayCallback ray_cb, void *userdata, int totsample)
+{
+ BVHTreeFromMesh bvhdata;
+ float ray_start[3], ray_end[3];
+ int i, stored = 0;
+
+ DM_ensure_tessface(dm);
+
+ memset(&bvhdata, 0, sizeof(BVHTreeFromMesh));
+ bvhtree_from_mesh_faces(&bvhdata, dm, 0.0f, 4, 6);
+
+ if (bvhdata.tree) {
+ for (i = 0; i < totsample; ++i) {
+ if (ray_cb(userdata, ray_start, ray_end)) {
+ MSurfaceSample sample;
+ if (sample_bvh_raycast(&sample, dm, &bvhdata, ray_start, ray_end)) {
+ if (dst->store_sample(dst->data, dst->capacity, i, &sample))
+ ++stored;
+ else
+ break;
+ }
+ }
+ }
+ }
+
+ free_bvhtree_from_mesh(&bvhdata);
+
+ return stored;
+}
+
+/* ==== Utilities ==== */
+
+#include "DNA_particle_types.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_particle.h"
+
+bool BKE_mesh_sample_from_particle(MSurfaceSample *sample, ParticleSystem *psys, DerivedMesh *dm, ParticleData *pa)
+{
+ MVert *mverts;
+ MFace *mface;
+ float mapfw[4];
+ int mapindex;
+ float *co1 = NULL, *co2 = NULL, *co3 = NULL, *co4 = NULL;
+ float vec[3];
+ float w[4];
+
+ if (!psys_get_index_on_dm(psys, dm, pa, &mapindex, mapfw))
+ return false;
+
+ mface = dm->getTessFaceData(dm, mapindex, CD_MFACE);
+ mverts = dm->getVertDataArray(dm, CD_MVERT);
+
+ co1 = mverts[mface->v1].co;
+ co2 = mverts[mface->v2].co;
+ co3 = mverts[mface->v3].co;
+
+ if (mface->v4) {
+ co4 = mverts[mface->v4].co;
+
+ interp_v3_v3v3v3v3(vec, co1, co2, co3, co4, mapfw);
+ }
+ else {
+ interp_v3_v3v3v3(vec, co1, co2, co3, mapfw);
+ }
+
+ /* test both triangles of the face */
+ interp_weights_face_v3(w, co1, co2, co3, NULL, vec);
+ if (w[0] <= 1.0f && w[1] <= 1.0f && w[2] <= 1.0f) {
+ sample->orig_verts[0] = mface->v1;
+ sample->orig_verts[1] = mface->v2;
+ sample->orig_verts[2] = mface->v3;
+
+ copy_v3_v3(sample->orig_weights, w);
+ return true;
+ }
+ else if (mface->v4) {
+ interp_weights_face_v3(w, co3, co4, co1, NULL, vec);
+ sample->orig_verts[0] = mface->v3;
+ sample->orig_verts[1] = mface->v4;
+ sample->orig_verts[2] = mface->v1;
+
+ copy_v3_v3(sample->orig_weights, w);
+ return true;
+ }
+ else
+ return false;
+}
+
+bool BKE_mesh_sample_to_particle(MSurfaceSample *sample, ParticleSystem *UNUSED(psys), DerivedMesh *dm, BVHTreeFromMesh *bvhtree, ParticleData *pa)
+{
+ BVHTreeNearest nearest;
+ float vec[3], nor[3], tang[3];
+
+ BKE_mesh_sample_eval(dm, sample, vec, nor, tang);
+
+ nearest.index = -1;
+ nearest.dist_sq = FLT_MAX;
+ BLI_bvhtree_find_nearest(bvhtree->tree, vec, &nearest, bvhtree->nearest_callback, bvhtree);
+ if (nearest.index >= 0) {
+ MFace *mface = dm->getTessFaceData(dm, nearest.index, CD_MFACE);
+ MVert *mverts = dm->getVertDataArray(dm, CD_MVERT);
+
+ float *co1 = mverts[mface->v1].co;
+ float *co2 = mverts[mface->v2].co;
+ float *co3 = mverts[mface->v3].co;
+ float *co4 = mface->v4 ? mverts[mface->v4].co : NULL;
+
+ pa->num = nearest.index;
+ pa->num_dmcache = DMCACHE_NOTFOUND;
+
+ interp_weights_face_v3(pa->fuv, co1, co2, co3, co4, vec);
+ pa->foffset = 0.0f; /* XXX any sensible way to reconstruct this? */
+
+ return true;
+ }
+ else
+ return false;
+}
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index 1f6d40dafa3..4d007e12ef5 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -57,6 +57,7 @@
#include "BLF_translation.h"
#include "BKE_appdir.h"
+#include "BKE_cache_library.h"
#include "BKE_key.h"
#include "BKE_multires.h"
#include "BKE_DerivedMesh.h"
@@ -68,7 +69,7 @@
#include "MOD_modifiertypes.h"
-static ModifierTypeInfo *modifier_types[NUM_MODIFIER_TYPES] = {NULL};
+static ModifierTypeInfo *cache_modifier_types[NUM_MODIFIER_TYPES] = {NULL};
static VirtualModifierData virtualModifierCommonData;
void BKE_modifier_init(void)
@@ -76,7 +77,7 @@ void BKE_modifier_init(void)
ModifierData *md;
/* Initialize modifier types */
- modifier_type_init(modifier_types); /* MOD_utils.c */
+ modifier_type_init(cache_modifier_types); /* MOD_utils.c */
/* Initialize global cmmon storage used for virtual modifier list */
md = modifier_new(eModifierType_Armature);
@@ -99,13 +100,15 @@ void BKE_modifier_init(void)
virtualModifierCommonData.cmd.modifier.mode |= eModifierMode_Virtual;
virtualModifierCommonData.lmd.modifier.mode |= eModifierMode_Virtual;
virtualModifierCommonData.smd.modifier.mode |= eModifierMode_Virtual;
+
+ BKE_cache_modifier_init();
}
const ModifierTypeInfo *modifierType_getInfo(ModifierType type)
{
/* type unsigned, no need to check < 0 */
- if (type < NUM_MODIFIER_TYPES && modifier_types[type]->name[0] != '\0') {
- return modifier_types[type];
+ if (type < NUM_MODIFIER_TYPES && cache_modifier_types[type]->name[0] != '\0') {
+ return cache_modifier_types[type];
}
else {
return NULL;
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index 8ac0a8a31f6..e305e5f94be 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -741,6 +741,92 @@ void nodeRemoveAllSockets(bNodeTree *ntree, bNode *node)
node->update |= NODE_UPDATE;
}
+static bNodeSocket *node_socket_find_match(bNodeSocket *newsock, ListBase *oldsocklist)
+{
+ bNodeSocket *sock;
+
+ for (sock = oldsocklist->first; sock; sock = sock->next)
+ if (STREQ(sock->name, newsock->name))
+ return sock;
+ return NULL;
+}
+
+static bNodeSocket *node_socket_relink(ListBase *newsocklist, bNodeSocket *oldsock, int oldindex, bool use_index_fallback)
+{
+ bNodeSocket *sock;
+
+ /* first try to find matching socket name */
+ for (sock = newsocklist->first; sock; sock = sock->next)
+ if (STREQ(sock->name, oldsock->name))
+ return sock;
+
+ if (use_index_fallback)
+ /* no matching name, simply link to same index */
+ return BLI_findlink(newsocklist, oldindex);
+ else
+ return NULL;
+}
+
+static void node_sync_sockets(bNodeTree *ntree, bNode *node, ListBase *socklist, CreateSocketsCB create_sockets_cb, SyncSocketCB sync_socket_cb, void *userdata)
+{
+ bNodeSocket *newsock, *oldsock, *oldsock_next;
+ ListBase oldsocklist;
+ int oldindex;
+ bNodeLink *link;
+
+ /* store current nodes in oldsocklist, then clear socket list */
+ oldsocklist = *socklist;
+ BLI_listbase_clear(socklist);
+
+ /* create new sockets for the node */
+ create_sockets_cb(userdata, ntree, node);
+
+ /* copy settings from the old to the new socket, if necessary */
+ if (sync_socket_cb) {
+ for (newsock = socklist->first; newsock; newsock = newsock->next) {
+ oldsock = node_socket_find_match(newsock, &oldsocklist);
+ if (oldsock)
+ sync_socket_cb(userdata, node, newsock, oldsock);
+ }
+ }
+
+ /* move links to new socket */
+ for (oldsock = oldsocklist.first, oldindex = 0; oldsock; oldsock = oldsock->next, ++oldindex) {
+ /* XXX falling back on index mapping does not appear to be very useful
+ * for dynamically changing sockets, so it's disabled for now
+ */
+ newsock = node_socket_relink(socklist, oldsock, oldindex, false);
+ if (newsock) {
+ for (link = ntree->links.first; link; link = link->next) {
+ if (link->fromsock == oldsock)
+ link->fromsock = newsock;
+ }
+ }
+ }
+
+ /* delete old sockets
+ * XXX oldsock is not actually in the socklist any more,
+ * but the nodeRemoveSocket function works anyway. In future this
+ * should become part of the core code, so can take care of this behavior.
+ */
+ for (oldsock = oldsocklist.first; oldsock; oldsock = oldsock_next) {
+ oldsock_next = oldsock->next;
+ if (oldsock->storage)
+ MEM_freeN(oldsock->storage);
+ nodeRemoveSocket(ntree, node, oldsock);
+ }
+}
+
+void nodeSyncInputs(bNodeTree *ntree, bNode *node, CreateSocketsCB create_inputs_cb, SyncSocketCB sync_input_cb, void *userdata)
+{
+ node_sync_sockets(ntree, node, &node->inputs, create_inputs_cb, sync_input_cb, userdata);
+}
+
+void nodeSyncOutputs(bNodeTree *ntree, bNode *node, CreateSocketsCB create_outputs_cb, SyncSocketCB sync_output_cb, void *userdata)
+{
+ node_sync_sockets(ntree, node, &node->outputs, create_outputs_cb, sync_output_cb, userdata);
+}
+
/* finds a node based on its name */
bNode *nodeFindNodebyName(bNodeTree *ntree, const char *name)
{
@@ -3574,6 +3660,7 @@ static void registerShaderNodes(void)
register_node_type_sh_add_shader();
register_node_type_sh_uvmap();
register_node_type_sh_uvalongstroke();
+ register_node_type_sh_openvdb();
register_node_type_sh_output_lamp();
register_node_type_sh_output_material();
@@ -3591,6 +3678,7 @@ static void registerShaderNodes(void)
register_node_type_sh_tex_magic();
register_node_type_sh_tex_checker();
register_node_type_sh_tex_brick();
+ register_node_type_sh_tex_pointdensity();
}
static void registerTextureNodes(void)
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 647aadf237c..fbe28f80535 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -38,6 +38,7 @@
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
+#include "DNA_cache_library_types.h"
#include "DNA_camera_types.h"
#include "DNA_constraint_types.h"
#include "DNA_group_types.h"
@@ -76,6 +77,7 @@
#include "BKE_armature.h"
#include "BKE_action.h"
#include "BKE_bullet.h"
+#include "BKE_cache_library.h"
#include "BKE_deform.h"
#include "BKE_depsgraph.h"
#include "BKE_DerivedMesh.h"
@@ -85,6 +87,7 @@
#include "BKE_curve.h"
#include "BKE_displist.h"
#include "BKE_effect.h"
+#include "BKE_facemap.h"
#include "BKE_fcurve.h"
#include "BKE_group.h"
#include "BKE_key.h"
@@ -99,6 +102,7 @@
#include "BKE_multires.h"
#include "BKE_node.h"
#include "BKE_object.h"
+#include "BKE_object_deform.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
@@ -107,6 +111,7 @@
#include "BKE_sca.h"
#include "BKE_scene.h"
#include "BKE_sequencer.h"
+#include "BKE_smoke.h"
#include "BKE_speaker.h"
#include "BKE_softbody.h"
#include "BKE_subsurf.h"
@@ -325,9 +330,12 @@ void BKE_object_free_derived_caches(Object *ob)
}
if (ob->derivedFinal) {
- ob->derivedFinal->needsFree = 1;
- ob->derivedFinal->release(ob->derivedFinal);
- ob->derivedFinal = NULL;
+ /* dupli cache owns the derivedFinal, is freed by duplicator object */
+ if (!(ob->transflag & OB_IS_DUPLI_CACHE)) {
+ ob->derivedFinal->needsFree = 1;
+ ob->derivedFinal->release(ob->derivedFinal);
+ ob->derivedFinal = NULL;
+ }
}
if (ob->derivedDeform) {
ob->derivedDeform->needsFree = 1;
@@ -336,9 +344,11 @@ void BKE_object_free_derived_caches(Object *ob)
}
BKE_object_free_curve_cache(ob);
+
+ BKE_object_dupli_cache_clear(ob);
}
-void BKE_object_free_caches(Object *object)
+void BKE_object_free_caches(Object *object, bool free_smoke_sim)
{
ModifierData *md;
short update_flag = 0;
@@ -367,6 +377,28 @@ void BKE_object_free_caches(Object *object)
update_flag |= OB_RECALC_DATA;
}
}
+ else if (md->type == eModifierType_Smoke) {
+ if (free_smoke_sim) {
+ SmokeModifierData *smd = (SmokeModifierData *) md;
+ SmokeDomainSettings *sds = smd->domain;
+ if (sds != NULL) {
+ bool use_sim = sds->point_cache[0] == NULL;
+ PointCache *cache;
+ /* We only reset cache if all the point caches are baked to file. */
+ for (cache = sds->ptcaches[0].first;
+ cache != NULL && use_sim == false;
+ cache = cache->next)
+ {
+ use_sim |= ((cache->flag & (PTCACHE_BAKED|PTCACHE_DISK_CACHE)) != (PTCACHE_BAKED|PTCACHE_DISK_CACHE));
+ }
+ if (!use_sim) {
+ smokeModifier_reset(smd);
+ smokeModifier_reset_turbulence(smd);
+ update_flag |= OB_RECALC_DATA;
+ }
+ }
+ }
+ }
}
/* Tag object for update, so once memory critical operation is over and
@@ -423,6 +455,8 @@ void BKE_object_free_ex(Object *ob, bool do_id_user)
if (ob->gpd) ((ID *)ob->gpd)->us--;
if (ob->defbase.first)
BLI_freelistN(&ob->defbase);
+ if (ob->fmaps.first)
+ BLI_freelistN(&ob->fmaps);
if (ob->pose)
BKE_pose_free_ex(ob->pose, do_id_user);
if (ob->mpath)
@@ -457,6 +491,8 @@ void BKE_object_free_ex(Object *ob, bool do_id_user)
free_path(ob->curve_cache->path);
MEM_freeN(ob->curve_cache);
}
+
+ BKE_object_dupli_cache_free(ob);
}
void BKE_object_free(Object *ob)
@@ -475,6 +511,15 @@ static void unlink_object__unlinkModifierLinks(void *userData, Object *ob, Objec
}
}
+static void unlink_object__unlinkCacheModifierLinks(void *userData, CacheLibrary *UNUSED(cachelib), CacheModifier *UNUSED(md), ID **idpoin)
+{
+ Object *unlinkOb = userData;
+
+ if (*idpoin == (ID *)unlinkOb) {
+ *idpoin = NULL;
+ }
+}
+
void BKE_object_unlink(Object *ob)
{
Main *bmain = G.main;
@@ -496,6 +541,7 @@ void BKE_object_unlink(Object *ob)
ARegion *ar;
RegionView3D *rv3d;
LodLevel *lod;
+ CacheLibrary *cachelib;
int a, found;
unlink_controllers(&ob->controllers);
@@ -683,6 +729,10 @@ void BKE_object_unlink(Object *ob)
lod->source = NULL;
}
+ /* dupli cache is cleared entirely if the object in question is duplified to keep it simple */
+ if (BKE_object_dupli_cache_contains(obt, ob))
+ BKE_object_dupli_cache_clear(obt);
+
obt = obt->id.next;
}
@@ -845,6 +895,15 @@ void BKE_object_unlink(Object *ob)
}
camera = camera->id.next;
}
+
+ /* cache libraries */
+ for (cachelib = bmain->cache_library.first; cachelib; cachelib = cachelib->id.next) {
+ CacheModifier *md;
+
+ for (md = cachelib->modifiers.first; md; md = md->next) {
+ BKE_cache_modifier_foreachIDLink(cachelib, md, unlink_object__unlinkCacheModifierLinks, ob);
+ }
+ }
}
/* actual check for internal data, not context or flags */
@@ -1312,6 +1371,7 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys)
psysn->pathcache = NULL;
psysn->childcache = NULL;
psysn->edit = NULL;
+ psysn->hairedit = NULL;
psysn->pdd = NULL;
psysn->effectors = NULL;
psysn->tree = NULL;
@@ -1323,13 +1383,8 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys)
psysn->pointcache = BKE_ptcache_copy_list(&psysn->ptcaches, &psys->ptcaches, false);
- /* XXX - from reading existing code this seems correct but intended usage of
- * pointcache should /w cloth should be added in 'ParticleSystem' - campbell */
- if (psysn->clmd) {
- psysn->clmd->point_cache = psysn->pointcache;
- }
-
id_us_plus((ID *)psysn->part);
+ id_us_plus((ID *)psysn->key);
return psysn;
}
@@ -1513,6 +1568,7 @@ Object *BKE_object_copy_ex(Main *bmain, Object *ob, bool copy_caches)
BKE_pose_rebuild(obn, obn->data);
}
defgroup_copy_list(&obn->defbase, &ob->defbase);
+ fmap_copy_list(&obn->fmaps, &ob->fmaps);
BKE_constraints_copy(&obn->constraints, &ob->constraints, true);
obn->mode = OB_MODE_OBJECT;
@@ -1522,6 +1578,7 @@ Object *BKE_object_copy_ex(Main *bmain, Object *ob, bool copy_caches)
id_us_plus((ID *)obn->data);
id_us_plus((ID *)obn->gpd);
id_lib_extern((ID *)obn->dup_group);
+ id_lib_extern((ID *)obn->cache_library);
for (a = 0; a < obn->totcol; a++) id_us_plus((ID *)obn->mat[a]);
@@ -1553,6 +1610,8 @@ Object *BKE_object_copy_ex(Main *bmain, Object *ob, bool copy_caches)
/* Copy runtime surve data. */
obn->curve_cache = NULL;
+ obn->dup_cache = NULL;
+
if (ob->id.lib) {
BKE_id_lib_local_paths(bmain, ob->id.lib, &obn->id);
}
@@ -1584,13 +1643,16 @@ static void extern_local_object(Object *ob)
id_lib_extern((ID *)ob->data);
id_lib_extern((ID *)ob->dup_group);
+ id_lib_extern((ID *)ob->cache_library);
id_lib_extern((ID *)ob->poselib);
id_lib_extern((ID *)ob->gpd);
extern_local_matarar(ob->mat, ob->totcol);
- for (psys = ob->particlesystem.first; psys; psys = psys->next)
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
id_lib_extern((ID *)psys->part);
+ id_lib_extern((ID *)psys->key);
+ }
modifiers_foreachIDLink(ob, extern_local_object__modifiersForeachIDLink, NULL);
}
@@ -2839,7 +2901,15 @@ bool BKE_object_minmax_dupli(Scene *scene, Object *ob, float r_min[3], float r_m
/* pass */
}
else {
- BoundBox *bb = BKE_object_boundbox_get(dob->ob);
+ BoundBox *bb = NULL;
+ if (ob->dup_cache) {
+ DupliObjectData *dob_data = BKE_dupli_cache_find_data(ob->dup_cache, dob->ob);
+ if (dob_data && dob_data->dm) {
+ bb = &dob_data->bb;
+ }
+ }
+ if (!bb)
+ bb = BKE_object_boundbox_get(dob->ob);
if (bb) {
int i;
diff --git a/source/blender/blenkernel/intern/object_dupli.c b/source/blender/blenkernel/intern/object_dupli.c
index 8abe4bdbb97..fd0d0daf7e4 100644
--- a/source/blender/blenkernel/intern/object_dupli.c
+++ b/source/blender/blenkernel/intern/object_dupli.c
@@ -35,11 +35,16 @@
#include "MEM_guardedalloc.h"
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
#include "BLI_listbase.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_math.h"
#include "BLI_rand.h"
+#include "BLI_task.h"
#include "DNA_anim_types.h"
#include "DNA_group_types.h"
@@ -48,6 +53,7 @@
#include "DNA_vfont_types.h"
#include "BKE_animsys.h"
+#include "BKE_cache_library.h"
#include "BKE_DerivedMesh.h"
#include "BKE_depsgraph.h"
#include "BKE_font.h"
@@ -56,15 +62,18 @@
#include "BKE_lattice.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_sample.h"
#include "BKE_object.h"
#include "BKE_particle.h"
#include "BKE_scene.h"
#include "BKE_editmesh.h"
#include "BKE_anim.h"
-
+#include "BKE_strands.h"
#include "BLI_strict_flags.h"
+#include "BLF_translation.h"
+
/* Dupli-Geometry */
typedef struct DupliContext {
@@ -96,7 +105,7 @@ typedef struct DupliGenerator {
static const DupliGenerator *get_dupli_generator(const DupliContext *ctx);
/* create initial context for root object */
-static void init_context(DupliContext *r_ctx, EvaluationContext *eval_ctx, Scene *scene, Object *ob, float space_mat[4][4], bool update)
+static void init_context_ex(DupliContext *r_ctx, EvaluationContext *eval_ctx, Scene *scene, Object *ob, float space_mat[4][4], const DupliGenerator *gen, bool update)
{
r_ctx->eval_ctx = eval_ctx;
r_ctx->scene = scene;
@@ -110,14 +119,19 @@ static void init_context(DupliContext *r_ctx, EvaluationContext *eval_ctx, Scene
copy_m4_m4(r_ctx->space_mat, space_mat);
else
unit_m4(r_ctx->space_mat);
- r_ctx->lay = ob->lay;
+ r_ctx->lay = ob ? ob->lay : 0;
r_ctx->level = 0;
- r_ctx->gen = get_dupli_generator(r_ctx);
+ r_ctx->gen = gen ? gen : get_dupli_generator(r_ctx);
r_ctx->duplilist = NULL;
}
+static void init_context(DupliContext *r_ctx, EvaluationContext *eval_ctx, Scene *scene, Object *ob, bool update)
+{
+ init_context_ex(r_ctx, eval_ctx, scene, ob, NULL, NULL, update);
+}
+
/* create sub-context for recursive duplis */
static void copy_dupli_context(DupliContext *r_ctx, const DupliContext *ctx, Object *ob, float mat[4][4], int index, bool animated)
{
@@ -126,7 +140,7 @@ static void copy_dupli_context(DupliContext *r_ctx, const DupliContext *ctx, Obj
r_ctx->animated |= animated; /* object animation makes all children animated */
/* XXX annoying, previously was done by passing an ID* argument, this at least is more explicit */
- if (ctx->gen->type == OB_DUPLIGROUP)
+ if (ctx->gen->type == OB_DUPLIGROUP && ctx->object)
r_ctx->group = ctx->object->dup_group;
r_ctx->object = ob;
@@ -219,7 +233,10 @@ static void make_child_duplis(const DupliContext *ctx, void *userdata, MakeChild
{
Object *parent = ctx->object;
Object *obedit = ctx->scene->obedit;
-
+
+ if (!parent)
+ return;
+
if (ctx->group) {
unsigned int lay = ctx->group->layer;
GroupObject *go;
@@ -255,69 +272,84 @@ static void make_child_duplis(const DupliContext *ctx, void *userdata, MakeChild
/*---- Implementations ----*/
-/* OB_DUPLIGROUP */
-static void make_duplis_group(const DupliContext *ctx)
+/* Intern function for creating instances of group content
+ * with or without a parent (parent == NULL is allowed!)
+ * Note: some of the group animation update functions use the parent object,
+ * but this is old NLA code that is currently disabled and might be removed entirely.
+ */
+static void make_duplis_group_intern(const DupliContext *ctx, Group *group, Object *parent)
{
- bool for_render = (ctx->eval_ctx->mode == DAG_EVAL_RENDER);
- Object *ob = ctx->object;
- Group *group;
+ const bool for_render = (ctx->eval_ctx->mode == DAG_EVAL_RENDER);
+
GroupObject *go;
float group_mat[4][4];
int id;
- bool animated, hide;
-
- if (ob->dup_group == NULL) return;
- group = ob->dup_group;
-
- /* combine group offset and obmat */
+ bool animated;
+
unit_m4(group_mat);
sub_v3_v3(group_mat[3], group->dupli_ofs);
- mul_m4_m4m4(group_mat, ob->obmat, group_mat);
- /* don't access 'ob->obmat' from now on. */
-
+
+ if (parent) {
+ /* combine group offset and obmat */
+ mul_m4_m4m4(group_mat, parent->obmat, group_mat);
+ /* don't access 'parent->obmat' from now on. */
+ }
+
/* handles animated groups */
-
+
/* we need to check update for objects that are not in scene... */
if (ctx->do_update) {
/* note: update is optional because we don't always need object
* transformations to be correct. Also fixes bug [#29616]. */
- BKE_group_handle_recalc_and_update(ctx->eval_ctx, ctx->scene, ob, group);
+ BKE_group_handle_recalc_and_update(ctx->eval_ctx, ctx->scene, parent, group);
}
-
- animated = BKE_group_is_animated(group, ob);
-
+
+ animated = BKE_group_is_animated(group, parent);
+
for (go = group->gobject.first, id = 0; go; go = go->next, id++) {
+ float mat[4][4];
+ bool hide;
+
/* note, if you check on layer here, render goes wrong... it still deforms verts and uses parent imat */
- if (go->ob != ob) {
- float mat[4][4];
-
- /* Special case for instancing dupli-groups, see: T40051
- * this object may be instanced via dupli-verts/faces, in this case we don't want to render
- * (blender convention), but _do_ show in the viewport.
- *
- * Regular objects work fine but not if we're instancing dupli-groups,
- * because the rules for rendering aren't applied to objects they instance.
- * We could recursively pass down the 'hide' flag instead, but that seems unnecessary.
- */
- if (for_render && go->ob->parent && go->ob->parent->transflag & (OB_DUPLIVERTS | OB_DUPLIFACES)) {
- continue;
- }
-
- /* group dupli offset, should apply after everything else */
- mul_m4_m4m4(mat, group_mat, go->ob->obmat);
-
- /* check the group instance and object layers match, also that the object visible flags are ok. */
- hide = (go->ob->lay & group->layer) == 0 ||
- (for_render ? go->ob->restrictflag & OB_RESTRICT_RENDER : go->ob->restrictflag & OB_RESTRICT_VIEW);
-
- make_dupli(ctx, go->ob, mat, id, animated, hide);
-
- /* recursion */
- make_recursive_duplis(ctx, go->ob, group_mat, id, animated);
+ if (go->ob == parent)
+ continue;
+
+ /* Special case for instancing dupli-groups, see: T40051
+ * this object may be instanced via dupli-verts/faces, in this case we don't want to render
+ * (blender convention), but _do_ show in the viewport.
+ *
+ * Regular objects work fine but not if we're instancing dupli-groups,
+ * because the rules for rendering aren't applied to objects they instance.
+ * We could recursively pass down the 'hide' flag instead, but that seems unnecessary.
+ */
+ if (for_render && go->ob->parent && go->ob->parent->transflag & (OB_DUPLIVERTS | OB_DUPLIFACES)) {
+ continue;
}
+
+ /* group dupli offset, should apply after everything else */
+ mul_m4_m4m4(mat, group_mat, go->ob->obmat);
+
+ /* check the group instance and object layers match, also that the object visible flags are ok. */
+ hide = (go->ob->lay & group->layer) == 0 ||
+ (for_render ? go->ob->restrictflag & OB_RESTRICT_RENDER : go->ob->restrictflag & OB_RESTRICT_VIEW);
+
+ make_dupli(ctx, go->ob, mat, id, animated, hide);
+
+ /* recursion */
+ make_recursive_duplis(ctx, go->ob, group_mat, id, animated);
}
}
+/* OB_DUPLIGROUP */
+static void make_duplis_group(const DupliContext *ctx)
+{
+ Object *ob = ctx->object;
+ if (!ob || !ob->dup_group)
+ return;
+
+ make_duplis_group_intern(ctx, ob->dup_group, ob);
+}
+
const DupliGenerator gen_dupli_group = {
OB_DUPLIGROUP, /* type */
make_duplis_group /* make_duplis */
@@ -331,8 +363,9 @@ static void make_duplis_frames(const DupliContext *ctx)
extern int enable_cu_speed; /* object.c */
Object copyob;
int cfrao = scene->r.cfra;
- int dupend = ob->dupend;
+ if (!ob)
+ return;
/* dupliframes not supported inside groups */
if (ctx->group)
return;
@@ -341,7 +374,7 @@ static void make_duplis_frames(const DupliContext *ctx)
*/
if (ob->parent == NULL && BLI_listbase_is_empty(&ob->constraints) && ob->adt == NULL)
return;
-
+
/* make a copy of the object's original data (before any dupli-data overwrites it)
* as we'll need this to keep track of unkeyed data
* - this doesn't take into account other data that can be reached from the object,
@@ -356,7 +389,7 @@ static void make_duplis_frames(const DupliContext *ctx)
* updates, as this is not a permanent change to the object */
ob->id.flag |= LIB_ANIM_NO_RECALC;
- for (scene->r.cfra = ob->dupsta; scene->r.cfra <= dupend; scene->r.cfra++) {
+ for (scene->r.cfra = ob->dupsta; scene->r.cfra <= ob->dupend; scene->r.cfra++) {
int ok = 1;
/* - dupoff = how often a frames within the range shouldn't be made into duplis
@@ -512,6 +545,9 @@ static void make_duplis_verts(const DupliContext *ctx)
Object *parent = ctx->object;
bool use_texcoords = ELEM(ctx->eval_ctx->mode, DAG_EVAL_RENDER, DAG_EVAL_PREVIEW);
VertexDupliData vdd;
+
+ if (!parent)
+ return;
vdd.ctx = ctx;
vdd.use_rotation = parent->transflag & OB_DUPLIROT;
@@ -592,6 +628,8 @@ static void make_duplis_font(const DupliContext *ctx)
const wchar_t *text = NULL;
bool text_free = false;
+ if (!par)
+ return;
/* font dupliverts not supported inside groups */
if (ctx->group)
return;
@@ -779,6 +817,9 @@ static void make_duplis_faces(const DupliContext *ctx)
bool use_texcoords = ELEM(ctx->eval_ctx->mode, DAG_EVAL_RENDER, DAG_EVAL_PREVIEW);
FaceDupliData fdd;
+ if (!parent)
+ return;
+
fdd.use_scale = ((parent->transflag & OB_DUPLIFACES_SCALE) != 0);
/* gather mesh info */
@@ -842,7 +883,8 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
int no_draw_flag = PARS_UNEXIST;
- if (psys == NULL) return;
+ if (!psys)
+ return;
part = psys->part;
@@ -983,7 +1025,7 @@ static void make_duplis_particle_system(const DupliContext *ctx, ParticleSystem
/* for groups, pick the object based on settings */
if (part->draw & PART_DRAW_RAND_GR)
- b = BLI_rand() % totgroup;
+ b = (int)(psys_frand(psys, (unsigned int)(a + 974)) * (float)totgroup) % totgroup;
else
b = a % totgroup;
@@ -1123,6 +1165,9 @@ static void make_duplis_particles(const DupliContext *ctx)
ParticleSystem *psys;
int psysid;
+ if (!ctx->object)
+ return;
+
/* particle system take up one level in id, the particles another */
for (psys = ctx->object->particlesystem.first, psysid = 0; psys; psys = psys->next, psysid++) {
/* particles create one more level for persistent psys index */
@@ -1142,8 +1187,13 @@ const DupliGenerator gen_dupli_particles = {
/* select dupli generator from given context */
static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
{
- int transflag = ctx->object->transflag;
- int restrictflag = ctx->object->restrictflag;
+ int transflag, restrictflag;
+
+ if (!ctx->object)
+ return NULL;
+
+ transflag = ctx->object->transflag;
+ restrictflag = ctx->object->restrictflag;
if ((transflag & OB_DUPLI) == 0)
return NULL;
@@ -1183,15 +1233,36 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
/* Returns a list of DupliObject */
ListBase *object_duplilist_ex(EvaluationContext *eval_ctx, Scene *scene, Object *ob, bool update)
{
- ListBase *duplilist = MEM_callocN(sizeof(ListBase), "duplilist");
- DupliContext ctx;
- init_context(&ctx, eval_ctx, scene, ob, NULL, update);
- if (ctx.gen) {
- ctx.duplilist = duplilist;
- ctx.gen->make_duplis(&ctx);
+ if (update) {
+ BKE_object_dupli_cache_update(scene, ob, eval_ctx, (float)scene->r.cfra + scene->r.subframe);
+ }
+
+ if (ob->dup_cache && (ob->dup_cache->result != CACHE_READ_SAMPLE_INVALID)) {
+ /* Note: duplis in the cache don't have the main duplicator obmat applied.
+ * duplilist also should return a full copy of duplis, so we copy
+ * the cached list and apply the obmat to each.
+ */
+ ListBase *duplilist = MEM_callocN(sizeof(ListBase), "duplilist");
+ DupliObject *dob;
+
+ BLI_duplicatelist(duplilist, &ob->dup_cache->duplilist);
+
+ for (dob = duplilist->first; dob; dob = dob->next) {
+ mul_m4_m4m4(dob->mat, ob->obmat, dob->mat);
+ }
+
+ return duplilist;
+ }
+ else {
+ ListBase *duplilist = MEM_callocN(sizeof(ListBase), "duplilist");
+ DupliContext ctx;
+ init_context(&ctx, eval_ctx, scene, ob, update);
+ if (ctx.gen) {
+ ctx.duplilist = duplilist;
+ ctx.gen->make_duplis(&ctx);
+ }
+ return duplilist;
}
-
- return duplilist;
}
/* note: previously updating was always done, this is why it defaults to be on
@@ -1201,6 +1272,24 @@ ListBase *object_duplilist(EvaluationContext *eval_ctx, Scene *sce, Object *ob)
return object_duplilist_ex(eval_ctx, sce, ob, true);
}
+ListBase *group_duplilist_ex(EvaluationContext *eval_ctx, Scene *scene, Group *group, bool update)
+{
+ ListBase *duplilist = MEM_callocN(sizeof(ListBase), "duplilist");
+ DupliContext ctx;
+
+ init_context_ex(&ctx, eval_ctx, scene, NULL, NULL, &gen_dupli_group, update);
+ ctx.duplilist = duplilist;
+
+ make_duplis_group_intern(&ctx, group, NULL);
+
+ return duplilist;
+}
+
+ListBase *group_duplilist(EvaluationContext *eval_ctx, Scene *scene, Group *group)
+{
+ return group_duplilist_ex(eval_ctx, scene, group, true);
+}
+
void free_object_duplilist(ListBase *lb)
{
BLI_freelistN(lb);
@@ -1237,6 +1326,558 @@ int count_duplilist(Object *ob)
return 1;
}
+/* ------------------------------------------------------------------------- */
+
+static void dupli_cache_calc_boundbox(DupliObjectData *data)
+{
+ DupliObjectDataStrands *link;
+ float min[3], max[3];
+ bool has_data = false;
+
+ INIT_MINMAX(min, max);
+ if (data->dm) {
+ data->dm->getMinMax(data->dm, min, max);
+ has_data = true;
+ }
+ for (link = data->strands.first; link; link = link->next) {
+ if (link->strands) {
+ BKE_strands_get_minmax(link->strands, min, max, true);
+ has_data = true;
+ }
+ if (link->strands_children) {
+ BKE_strands_children_get_minmax(link->strands_children, min, max);
+ has_data = true;
+ }
+ }
+
+ if (!has_data) {
+ zero_v3(min);
+ zero_v3(max);
+ }
+
+ BKE_boundbox_init_from_minmax(&data->bb, min, max);
+}
+
+static bool UNUSED_FUNCTION(dupli_object_data_strands_unique_name)(ListBase *lb, DupliObjectDataStrands *link)
+{
+ if (lb && link) {
+ return BLI_uniquename(lb, link, DATA_("Strands"), '.', offsetof(DupliObjectDataStrands, name), sizeof(link->name));
+ }
+ return false;
+}
+
+void BKE_dupli_object_data_init(DupliObjectData *data, Object *ob)
+{
+ data->ob = ob;
+
+ data->dm = NULL;
+ BLI_listbase_clear(&data->strands);
+
+ memset(&data->bb, 0, sizeof(data->bb));
+ dupli_cache_calc_boundbox(data);
+}
+
+void BKE_dupli_object_data_clear(DupliObjectData *data)
+{
+ DupliObjectDataStrands *link;
+
+ if (data->dm) {
+ /* we lock DMs in the cache to prevent freeing outside,
+ * now allow releasing again
+ */
+ data->dm->needsFree = true;
+ data->dm->release(data->dm);
+ }
+
+ for (link = data->strands.first; link; link = link->next) {
+ if (link->strands)
+ BKE_strands_free(link->strands);
+ if (link->strands_children)
+ BKE_strands_children_free(link->strands_children);
+ }
+ BLI_freelistN(&data->strands);
+}
+
+void BKE_dupli_object_data_set_mesh(DupliObjectData *data, DerivedMesh *dm)
+{
+ if (data->dm) {
+ /* we lock DMs in the cache to prevent freeing outside,
+ * now allow releasing again
+ */
+ data->dm->needsFree = true;
+ data->dm->release(data->dm);
+ }
+
+ data->dm = dm;
+ /* we own this dm now and need to protect it until we free it ourselves */
+ dm->needsFree = false;
+
+ dupli_cache_calc_boundbox(data);
+}
+
+void BKE_dupli_object_data_add_strands(DupliObjectData *data, const char *name, Strands *strands)
+{
+ DupliObjectDataStrands *link = NULL;
+ for (link = data->strands.first; link; link = link->next) {
+ if (STREQ(link->name, name))
+ break;
+ }
+
+ if (!link) {
+ link = MEM_callocN(sizeof(DupliObjectDataStrands), "strands link");
+ BLI_strncpy(link->name, name, sizeof(link->name));
+ link->strands = strands;
+
+ BLI_addtail(&data->strands, link);
+ }
+ else {
+ if (link->strands && link->strands != strands)
+ BKE_strands_free(link->strands);
+ link->strands = strands;
+ }
+
+ dupli_cache_calc_boundbox(data);
+}
+
+void BKE_dupli_object_data_add_strands_children(DupliObjectData *data, const char *name, StrandsChildren *children)
+{
+ DupliObjectDataStrands *link = NULL;
+ for (link = data->strands.first; link; link = link->next) {
+ if (STREQ(link->name, name))
+ break;
+ }
+
+ if (!link) {
+ link = MEM_callocN(sizeof(DupliObjectDataStrands), "strands link");
+ BLI_strncpy(link->name, name, sizeof(link->name));
+ link->strands_children = children;
+
+ BLI_addtail(&data->strands, link);
+ }
+ else {
+ if (link->strands_children && link->strands_children != children)
+ BKE_strands_children_free(link->strands_children);
+ link->strands_children = children;
+ }
+
+ dupli_cache_calc_boundbox(data);
+}
+
+void BKE_dupli_object_data_find_strands(DupliObjectData *data, const char *name, Strands **r_strands, StrandsChildren **r_children)
+{
+ DupliObjectDataStrands *link;
+ for (link = data->strands.first; link; link = link->next) {
+ if (STREQ(link->name, name)) {
+ if (r_strands) *r_strands = link->strands;
+ if (r_children) *r_children = link->strands_children;
+ return;
+ }
+ }
+
+ if (r_strands) *r_strands = NULL;
+ if (r_children) *r_children = NULL;
+}
+
+bool BKE_dupli_object_data_acquire_strands(DupliObjectData *data, Strands *strands)
+{
+ DupliObjectDataStrands *link, *link_next;
+ bool found = false;
+
+ if (!data || !strands)
+ return false;
+
+ for (link = data->strands.first; link; link = link_next) {
+ link_next = link->next;
+ if (link->strands == strands) {
+ link->strands = NULL;
+ found = true;
+ }
+ }
+ return found;
+}
+
+bool BKE_dupli_object_data_acquire_strands_children(DupliObjectData *data, StrandsChildren *children)
+{
+ DupliObjectDataStrands *link, *link_next;
+ bool found = false;
+
+ if (!data || !children)
+ return false;
+
+ for (link = data->strands.first; link; link = link_next) {
+ link_next = link->next;
+ if (link->strands_children == children) {
+ link->strands_children = NULL;
+ found = true;
+ }
+ }
+ return found;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void dupli_object_data_free(DupliObjectData *data)
+{
+ BKE_dupli_object_data_clear(data);
+ MEM_freeN(data);
+}
+
+static void dupli_object_free(DupliObject *dob)
+{
+ MEM_freeN(dob);
+}
+
+DupliCache *BKE_dupli_cache_new(void)
+{
+ DupliCache *dupcache = MEM_callocN(sizeof(DupliCache), "dupli object cache");
+
+ dupcache->ghash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "dupli object data hash");
+
+ return dupcache;
+}
+
+void BKE_dupli_cache_free(DupliCache *dupcache)
+{
+ BKE_dupli_cache_clear(dupcache);
+
+ BLI_ghash_free(dupcache->ghash, NULL, (GHashValFreeFP)dupli_object_data_free);
+ MEM_freeN(dupcache);
+}
+
+void BKE_dupli_cache_clear(DupliCache *dupcache)
+{
+ DupliObject *dob, *dob_next;
+ for (dob = dupcache->duplilist.first; dob; dob = dob_next) {
+ dob_next = dob->next;
+
+ dupli_object_free(dob);
+ }
+ BLI_listbase_clear(&dupcache->duplilist);
+
+ BLI_ghash_clear(dupcache->ghash, NULL, (GHashValFreeFP)dupli_object_data_free);
+}
+
+void BKE_dupli_cache_clear_instances(DupliCache *dupcache)
+{
+ DupliObject *dob, *dob_next;
+ for (dob = dupcache->duplilist.first; dob; dob = dob_next) {
+ dob_next = dob->next;
+
+ dupli_object_free(dob);
+ }
+ BLI_listbase_clear(&dupcache->duplilist);
+}
+
+static DupliObjectData *dupli_cache_add_object_data(DupliCache *dupcache, Object *ob)
+{
+ DupliObjectData *data = MEM_callocN(sizeof(DupliObjectData), "dupli object data");
+
+ data->ob = ob;
+ BLI_ghash_insert(dupcache->ghash, data->ob, data);
+ return data;
+}
+
+static DupliObject *dupli_cache_add_object(DupliCache *dupcache)
+{
+ DupliObject *dob = MEM_callocN(sizeof(DupliObject), "dupli object");
+
+ unit_m4(dob->mat);
+
+ BLI_addtail(&dupcache->duplilist, dob);
+ return dob;
+}
+
+static int count_hair_verts(ParticleSystem *psys)
+{
+ int numverts = 0;
+ int p;
+ for (p = 0; p < psys->totpart; ++p) {
+ numverts += psys->particles[p].totkey;
+ }
+ return numverts;
+}
+
+static void dupli_strands_data_update(CacheLibrary *cachelib, DupliObjectData *data,
+ DupliObject *dob, bool calc_strands_base) {
+ ParticleSystem *psys;
+ for (psys = dob->ob->particlesystem.first; psys; psys = psys->next) {
+ if (cachelib->data_types & CACHE_TYPE_HAIR) {
+ if (psys->part && psys->part->type == PART_HAIR) {
+ int numstrands = psys->totpart;
+ int numverts = count_hair_verts(psys);
+ ParticleData *pa;
+ HairKey *hkey;
+ int p, k;
+
+ Strands *strands = BKE_strands_new(numstrands, numverts);
+ StrandsCurve *scurve = strands->curves;
+ StrandsVertex *svert = strands->verts;
+
+ for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa) {
+ float hairmat[4][4];
+ psys_mat_hair_to_object(dob->ob, data->dm, psys->part->from, pa, hairmat);
+
+ scurve->numverts = pa->totkey;
+ copy_m3_m4(scurve->root_matrix, hairmat);
+ BKE_mesh_sample_from_particle(&scurve->msurf, psys, data->dm, pa);
+
+ for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) {
+ copy_v3_v3(svert->co, hkey->co);
+ if (calc_strands_base)
+ copy_v3_v3(svert->base, hkey->co);
+ svert->time = hkey->time;
+ svert->weight = hkey->weight;
+ ++svert;
+ }
+ ++scurve;
+ }
+
+ BKE_dupli_object_data_add_strands(data, psys->name, strands);
+ }
+ }
+ }
+}
+
+typedef struct DupliObjectDataFromGroupState {
+ EvaluationContext *eval_ctx;
+ Scene *scene;
+ CacheLibrary *cachelib;
+ bool calc_strands_base;
+} DupliObjectDataFromGroupState;
+
+typedef struct DupliObjectDataFromGroupTask {
+ DupliObject *dob;
+ DupliObjectData *data;
+} DupliObjectDataFromGroupTask;
+
+static void dupli_object_data_from_group_func(TaskPool *pool, void *taskdata, int UNUSED(threadid))
+{
+ DupliObjectDataFromGroupState *state = (DupliObjectDataFromGroupState *)BLI_task_pool_userdata(pool);
+ DupliObjectDataFromGroupTask *task = (DupliObjectDataFromGroupTask *)taskdata;
+ Object *object = task->dob->ob;
+ DerivedMesh *dm;
+
+ if (state->eval_ctx->mode == DAG_EVAL_RENDER) {
+ dm = mesh_create_derived_render(state->scene, object, CD_MASK_BAREMESH);
+ }
+ else {
+ dm = mesh_create_derived_view(state->scene, object, CD_MASK_BAREMESH);
+ }
+
+ if (dm != NULL) {
+ BKE_dupli_object_data_set_mesh(task->data, dm);
+ }
+
+ dupli_strands_data_update(state->cachelib, task->data, task->dob, state->calc_strands_base);
+}
+
+void BKE_dupli_cache_from_group(Scene *scene, Group *group, CacheLibrary *cachelib, DupliCache *dupcache, EvaluationContext *eval_ctx, bool calc_strands_base)
+{
+ DupliObject *dob;
+ TaskScheduler *task_scheduler = BLI_task_scheduler_get();
+ TaskPool *task_pool;
+ DupliObjectDataFromGroupState state;
+
+ BKE_dupli_cache_clear(dupcache);
+
+ if (!(group && cachelib))
+ return;
+
+ {
+ /* copy duplilist to the cache */
+ ListBase *duplilist = group_duplilist(eval_ctx, scene, group);
+ dupcache->duplilist = *duplilist;
+ MEM_freeN(duplilist);
+ }
+
+ state.eval_ctx = eval_ctx;
+ state.scene = scene;
+ state.cachelib = cachelib;
+ state.calc_strands_base = calc_strands_base;
+ task_pool = BLI_task_pool_create(task_scheduler, &state);
+
+ /* tag objects for which to store data */
+ BKE_cache_library_tag_used_objects(cachelib);
+
+ for (dob = dupcache->duplilist.first; dob; dob = dob->next) {
+ DupliObjectData *data = BKE_dupli_cache_find_data(dupcache, dob->ob);
+ if (!data) {
+ bool strands_handled = false;
+ data = dupli_cache_add_object_data(dupcache, dob->ob);
+
+ /* generate data only for filtered objects */
+ if (dob->ob->id.flag & LIB_DOIT) {
+ if (cachelib->data_types & CACHE_TYPE_DERIVED_MESH) {
+ if (dob->ob->type == OB_MESH) {
+ /* TODO(sergey): Consider using memory pool instead. */
+ DupliObjectDataFromGroupTask *task = MEM_mallocN(sizeof(DupliObjectDataFromGroupTask),
+ "dupcache task");
+ task->dob = dob;
+ task->data = data;
+ BLI_task_pool_push(task_pool, dupli_object_data_from_group_func, task, true, TASK_PRIORITY_LOW);
+ /* Task is getting care of strands as well. */
+ strands_handled = true;
+ }
+ }
+ if (!strands_handled) {
+ dupli_strands_data_update(cachelib, data, dob, calc_strands_base);
+ }
+ }
+ }
+ }
+
+ BLI_task_pool_work_and_wait(task_pool);
+ BLI_task_pool_free(task_pool);
+}
+
+/* ------------------------------------------------------------------------- */
+
+void BKE_object_dupli_cache_update(Scene *scene, Object *ob, EvaluationContext *eval_ctx, float frame)
+{
+ bool use_render = (eval_ctx->mode == DAG_EVAL_RENDER);
+ bool is_dupligroup = (ob->transflag & OB_DUPLIGROUP) && ob->dup_group;
+ bool is_cached = ob->cache_library && (ob->cache_library->source_mode == CACHE_LIBRARY_SOURCE_CACHE || ob->cache_library->display_mode == CACHE_LIBRARY_DISPLAY_RESULT);
+ bool do_modifiers = ob->cache_library && ob->cache_library->display_mode == CACHE_LIBRARY_DISPLAY_MODIFIERS;
+
+ /* cache is a group duplicator feature only */
+ if (is_dupligroup && is_cached) {
+
+ if (ob->dup_cache && !(ob->dup_cache->flag & DUPCACHE_FLAG_DIRTY)) {
+ /* skip if cache is valid */
+ }
+ else {
+ if (G.debug & G_DEBUG)
+ printf("Update dupli cache for object '%s'\n", ob->id.name+2);
+
+ if (ob->dup_cache) {
+ BKE_dupli_cache_clear(ob->dup_cache);
+ }
+ else {
+ ob->dup_cache = BKE_dupli_cache_new();
+ }
+
+ /* skip reading when the cachelib is baking, avoids unnecessary memory allocation */
+ if (!(ob->cache_library->flag & CACHE_LIBRARY_BAKING)) {
+ bool do_strands_motion, do_strands_children;
+ CacheLibrary *cachelib = ob->cache_library;
+ CacheProcessData process_data;
+
+ BKE_cache_library_get_read_flags(ob->cache_library, use_render, true, &do_strands_motion, &do_strands_children);
+
+ /* TODO at this point we could apply animation offset */
+ BKE_cache_read_dupli_cache(ob->cache_library, ob->dup_cache, scene, ob->dup_group, frame, use_render, true);
+
+ process_data.lay = ob->lay;
+ copy_m4_m4(process_data.mat, ob->obmat);
+ process_data.dupcache = ob->dup_cache;
+
+ BKE_cache_process_dupli_cache(cachelib, &process_data, scene, ob->dup_group, (float)scene->r.cfra, frame, do_modifiers, do_strands_children, do_strands_motion);
+ }
+
+ ob->dup_cache->flag &= ~DUPCACHE_FLAG_DIRTY;
+ ob->dup_cache->cfra = frame;
+ }
+
+ }
+ else {
+ if (ob->dup_cache) {
+ BKE_dupli_cache_free(ob->dup_cache);
+ ob->dup_cache = NULL;
+ }
+ }
+}
+
+void BKE_object_dupli_cache_clear(Object *ob)
+{
+ if (ob->dup_cache) {
+ BKE_dupli_cache_clear(ob->dup_cache);
+ }
+}
+
+void BKE_object_dupli_cache_free(Object *ob)
+{
+ if (ob->dup_cache) {
+ BKE_dupli_cache_free(ob->dup_cache);
+ ob->dup_cache = NULL;
+ }
+}
+
+bool BKE_object_dupli_cache_contains(Object *ob, Object *other)
+{
+ if (ob->dup_cache) {
+ DupliObject *dob;
+ for (dob = ob->dup_cache->duplilist.first; dob; dob = dob->next) {
+ if (dob->ob == other)
+ return true;
+ }
+ }
+ return false;
+}
+
+
+DupliObjectData *BKE_dupli_cache_find_data(DupliCache *dupcache, Object *ob)
+{
+ DupliObjectData *data = BLI_ghash_lookup(dupcache->ghash, ob);
+ return data;
+}
+
+DupliObjectData *BKE_dupli_cache_add_object(DupliCache *dupcache, Object *ob)
+{
+ DupliObjectData *data = BKE_dupli_cache_find_data(dupcache, ob);
+ if (!data) {
+ data = dupli_cache_add_object_data(dupcache, ob);
+ }
+ return data;
+}
+
+DupliObject *BKE_dupli_cache_add_instance(DupliCache *dupcache, float obmat[4][4], DupliObjectData *data)
+{
+ DupliObject *dob = dupli_cache_add_object(dupcache);
+
+ /* data must have been created correctly */
+ BLI_assert(BLI_ghash_lookup(dupcache->ghash, data->ob) != NULL);
+
+ dob->ob = data->ob;
+ copy_m4_m4(dob->mat, obmat);
+
+ dob->data = data;
+
+ return dob;
+}
+
+/* ------------------------------------------------------------------------- */
+
+typedef struct DupliCacheIterator {
+ int unused;
+} DupliCacheIterator;
+
+DupliCacheIterator *BKE_dupli_cache_iter_new(struct DupliCache *dupcache)
+{
+ return (DupliCacheIterator *)BLI_ghashIterator_new(dupcache->ghash);
+}
+
+void BKE_dupli_cache_iter_free(DupliCacheIterator *iter)
+{
+ BLI_ghashIterator_free((GHashIterator *)iter);
+}
+
+bool BKE_dupli_cache_iter_valid(DupliCacheIterator *iter)
+{
+ return !BLI_ghashIterator_done((GHashIterator *)iter);
+}
+
+void BKE_dupli_cache_iter_next(DupliCacheIterator *iter)
+{
+ BLI_ghashIterator_step((GHashIterator *)iter);
+}
+
+DupliObjectData *BKE_dupli_cache_iter_get(DupliCacheIterator *iter)
+{
+ return BLI_ghashIterator_getValue((GHashIterator *)iter);
+}
+
+/* ------------------------------------------------------------------------- */
+
DupliApplyData *duplilist_apply(Object *ob, Scene *scene, ListBase *duplilist)
{
DupliApplyData *apply_data = NULL;
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index 483968c6bd9..6752beb6d3b 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -163,6 +163,7 @@ void BKE_object_handle_data_update(EvaluationContext *eval_ctx,
ID *data_id = (ID *)ob->data;
AnimData *adt = BKE_animdata_from_id(data_id);
Key *key;
+ ParticleSystem *psys;
float ctime = BKE_scene_frame_get(scene);
if (G.debug & G_DEBUG_DEPSGRAPH)
@@ -181,6 +182,12 @@ void BKE_object_handle_data_update(EvaluationContext *eval_ctx,
if (!(ob->shapeflag & OB_SHAPE_LOCK))
BKE_animsys_evaluate_animdata(scene, &key->id, key->adt, ctime, ADT_RECALC_DRIVERS);
}
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ key = psys->key;
+ if (key && key->block.first) {
+ BKE_animsys_evaluate_animdata(scene, &key->id, key->adt, ctime, ADT_RECALC_DRIVERS);
+ }
+ }
/* includes all keys and modifiers */
switch (ob->type) {
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index 6c354c59d7a..e7534741c89 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -65,6 +65,7 @@
#include "BKE_boids.h"
#include "BKE_cloth.h"
#include "BKE_colortools.h"
+#include "BKE_editstrands.h"
#include "BKE_effect.h"
#include "BKE_global.h"
#include "BKE_group.h"
@@ -362,6 +363,38 @@ int psys_uses_gravity(ParticleSimulationData *sim)
{
return sim->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY && sim->psys->part && sim->psys->part->effector_weights->global_gravity != 0.0f;
}
+
+KeyBlock *BKE_psys_insert_shape_key(Scene *scene, Object *ob, ParticleSystem *psys, const char *name, const bool from_mix)
+{
+ Key *key = psys->key;
+ KeyBlock *kb;
+ int newkey = 0;
+
+ if (key == NULL) {
+ key = psys->key = BKE_key_add_particles(ob, psys);
+ key->type = KEY_RELATIVE;
+ newkey = 1;
+ }
+
+ if (newkey || !from_mix) {
+ /* create from particles */
+ kb = BKE_keyblock_add_ctime(key, name, false);
+ BKE_keyblock_convert_from_hair_keys(ob, psys, kb);
+ }
+ else {
+ /* copy from current values */
+ int totelem;
+ float *data = BKE_key_evaluate_particles(ob, psys, scene ? scene->r.cfra : 0.0f, &totelem);
+
+ /* create new block with prepared data */
+ kb = BKE_keyblock_add_ctime(key, name, false);
+ kb->data = data;
+ kb->totelem = totelem;
+ }
+
+ return kb;
+}
+
/************************************************/
/* Freeing stuff */
/************************************************/
@@ -546,6 +579,11 @@ void psys_free(Object *ob, ParticleSystem *psys)
if (psys->edit && psys->free_edit)
psys->free_edit(psys->edit);
+ if (psys->hairedit) {
+ BKE_editstrands_free(psys->hairedit);
+ MEM_freeN(psys->hairedit);
+ psys->hairedit = NULL;
+ }
if (psys->child) {
MEM_freeN(psys->child);
@@ -570,6 +608,10 @@ void psys_free(Object *ob, ParticleSystem *psys)
psys->part->id.us--;
psys->part = NULL;
}
+ if (psys->key) {
+ id_us_min(&psys->key->id);
+ psys->key = NULL;
+ }
BKE_ptcache_free_list(&psys->ptcaches);
psys->pointcache = NULL;
@@ -769,400 +811,6 @@ bool psys_render_simplify_params(ParticleSystem *psys, ChildParticle *cpa, float
}
/************************************************/
-/* Interpolation */
-/************************************************/
-static float interpolate_particle_value(float v1, float v2, float v3, float v4, const float w[4], int four)
-{
- float value;
-
- value = w[0] * v1 + w[1] * v2 + w[2] * v3;
- if (four)
- value += w[3] * v4;
-
- CLAMP(value, 0.f, 1.f);
-
- return value;
-}
-
-void psys_interpolate_particle(short type, ParticleKey keys[4], float dt, ParticleKey *result, bool velocity)
-{
- float t[4];
-
- if (type < 0) {
- interp_cubic_v3(result->co, result->vel, keys[1].co, keys[1].vel, keys[2].co, keys[2].vel, dt);
- }
- else {
- key_curve_position_weights(dt, t, type);
-
- interp_v3_v3v3v3v3(result->co, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t);
-
- if (velocity) {
- float temp[3];
-
- if (dt > 0.999f) {
- key_curve_position_weights(dt - 0.001f, t, type);
- interp_v3_v3v3v3v3(temp, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t);
- sub_v3_v3v3(result->vel, result->co, temp);
- }
- else {
- key_curve_position_weights(dt + 0.001f, t, type);
- interp_v3_v3v3v3v3(temp, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t);
- sub_v3_v3v3(result->vel, temp, result->co);
- }
- }
- }
-}
-
-
-typedef struct ParticleInterpolationData {
- HairKey *hkey[2];
-
- DerivedMesh *dm;
- MVert *mvert[2];
-
- int keyed;
- ParticleKey *kkey[2];
-
- PointCache *cache;
- PTCacheMem *pm;
-
- PTCacheEditPoint *epoint;
- PTCacheEditKey *ekey[2];
-
- float birthtime, dietime;
- int bspline;
-} ParticleInterpolationData;
-/* Assumes pointcache->mem_cache exists, so for disk cached particles call psys_make_temp_pointcache() before use */
-/* It uses ParticleInterpolationData->pm to store the current memory cache frame so it's thread safe. */
-static void get_pointcache_keys_for_time(Object *UNUSED(ob), PointCache *cache, PTCacheMem **cur, int index, float t, ParticleKey *key1, ParticleKey *key2)
-{
- static PTCacheMem *pm = NULL;
- int index1, index2;
-
- if (index < 0) { /* initialize */
- *cur = cache->mem_cache.first;
-
- if (*cur)
- *cur = (*cur)->next;
- }
- else {
- if (*cur) {
- while (*cur && (*cur)->next && (float)(*cur)->frame < t)
- *cur = (*cur)->next;
-
- pm = *cur;
-
- index2 = BKE_ptcache_mem_index_find(pm, index);
- index1 = BKE_ptcache_mem_index_find(pm->prev, index);
-
- BKE_ptcache_make_particle_key(key2, index2, pm->data, (float)pm->frame);
- if (index1 < 0)
- copy_particle_key(key1, key2, 1);
- else
- BKE_ptcache_make_particle_key(key1, index1, pm->prev->data, (float)pm->prev->frame);
- }
- else if (cache->mem_cache.first) {
- pm = cache->mem_cache.first;
- index2 = BKE_ptcache_mem_index_find(pm, index);
- BKE_ptcache_make_particle_key(key2, index2, pm->data, (float)pm->frame);
- copy_particle_key(key1, key2, 1);
- }
- }
-}
-static int get_pointcache_times_for_particle(PointCache *cache, int index, float *start, float *end)
-{
- PTCacheMem *pm;
- int ret = 0;
-
- for (pm = cache->mem_cache.first; pm; pm = pm->next) {
- if (BKE_ptcache_mem_index_find(pm, index) >= 0) {
- *start = pm->frame;
- ret++;
- break;
- }
- }
-
- for (pm = cache->mem_cache.last; pm; pm = pm->prev) {
- if (BKE_ptcache_mem_index_find(pm, index) >= 0) {
- *end = pm->frame;
- ret++;
- break;
- }
- }
-
- return ret == 2;
-}
-
-float psys_get_dietime_from_cache(PointCache *cache, int index)
-{
- PTCacheMem *pm;
- int dietime = 10000000; /* some max value so that we can default to pa->time+lifetime */
-
- for (pm = cache->mem_cache.last; pm; pm = pm->prev) {
- if (BKE_ptcache_mem_index_find(pm, index) >= 0)
- return (float)pm->frame;
- }
-
- return (float)dietime;
-}
-
-static void init_particle_interpolation(Object *ob, ParticleSystem *psys, ParticleData *pa, ParticleInterpolationData *pind)
-{
-
- if (pind->epoint) {
- PTCacheEditPoint *point = pind->epoint;
-
- pind->ekey[0] = point->keys;
- pind->ekey[1] = point->totkey > 1 ? point->keys + 1 : NULL;
-
- pind->birthtime = *(point->keys->time);
- pind->dietime = *((point->keys + point->totkey - 1)->time);
- }
- else if (pind->keyed) {
- ParticleKey *key = pa->keys;
- pind->kkey[0] = key;
- pind->kkey[1] = pa->totkey > 1 ? key + 1 : NULL;
-
- pind->birthtime = key->time;
- pind->dietime = (key + pa->totkey - 1)->time;
- }
- else if (pind->cache) {
- float start = 0.0f, end = 0.0f;
- get_pointcache_keys_for_time(ob, pind->cache, &pind->pm, -1, 0.0f, NULL, NULL);
- pind->birthtime = pa ? pa->time : pind->cache->startframe;
- pind->dietime = pa ? pa->dietime : pind->cache->endframe;
-
- if (get_pointcache_times_for_particle(pind->cache, pa - psys->particles, &start, &end)) {
- pind->birthtime = MAX2(pind->birthtime, start);
- pind->dietime = MIN2(pind->dietime, end);
- }
- }
- else {
- HairKey *key = pa->hair;
- pind->hkey[0] = key;
- pind->hkey[1] = key + 1;
-
- pind->birthtime = key->time;
- pind->dietime = (key + pa->totkey - 1)->time;
-
- if (pind->dm) {
- pind->mvert[0] = CDDM_get_vert(pind->dm, pa->hair_index);
- pind->mvert[1] = pind->mvert[0] + 1;
- }
- }
-}
-static void edit_to_particle(ParticleKey *key, PTCacheEditKey *ekey)
-{
- copy_v3_v3(key->co, ekey->co);
- if (ekey->vel) {
- copy_v3_v3(key->vel, ekey->vel);
- }
- key->time = *(ekey->time);
-}
-static void hair_to_particle(ParticleKey *key, HairKey *hkey)
-{
- copy_v3_v3(key->co, hkey->co);
- key->time = hkey->time;
-}
-
-static void mvert_to_particle(ParticleKey *key, MVert *mvert, HairKey *hkey)
-{
- copy_v3_v3(key->co, mvert->co);
- key->time = hkey->time;
-}
-
-static void do_particle_interpolation(ParticleSystem *psys, int p, ParticleData *pa, float t, ParticleInterpolationData *pind, ParticleKey *result)
-{
- PTCacheEditPoint *point = pind->epoint;
- ParticleKey keys[4];
- int point_vel = (point && point->keys->vel);
- float real_t, dfra, keytime, invdt = 1.f;
-
- /* billboards wont fill in all of these, so start cleared */
- memset(keys, 0, sizeof(keys));
-
- /* interpret timing and find keys */
- if (point) {
- if (result->time < 0.0f)
- real_t = -result->time;
- else
- real_t = *(pind->ekey[0]->time) + t * (*(pind->ekey[0][point->totkey - 1].time) - *(pind->ekey[0]->time));
-
- while (*(pind->ekey[1]->time) < real_t)
- pind->ekey[1]++;
-
- pind->ekey[0] = pind->ekey[1] - 1;
- }
- else if (pind->keyed) {
- /* we have only one key, so let's use that */
- if (pind->kkey[1] == NULL) {
- copy_particle_key(result, pind->kkey[0], 1);
- return;
- }
-
- if (result->time < 0.0f)
- real_t = -result->time;
- else
- real_t = pind->kkey[0]->time + t * (pind->kkey[0][pa->totkey - 1].time - pind->kkey[0]->time);
-
- if (psys->part->phystype == PART_PHYS_KEYED && psys->flag & PSYS_KEYED_TIMING) {
- ParticleTarget *pt = psys->targets.first;
-
- pt = pt->next;
-
- while (pt && pa->time + pt->time < real_t)
- pt = pt->next;
-
- if (pt) {
- pt = pt->prev;
-
- if (pa->time + pt->time + pt->duration > real_t)
- real_t = pa->time + pt->time;
- }
- else
- real_t = pa->time + ((ParticleTarget *)psys->targets.last)->time;
- }
-
- CLAMP(real_t, pa->time, pa->dietime);
-
- while (pind->kkey[1]->time < real_t)
- pind->kkey[1]++;
-
- pind->kkey[0] = pind->kkey[1] - 1;
- }
- else if (pind->cache) {
- if (result->time < 0.0f) /* flag for time in frames */
- real_t = -result->time;
- else
- real_t = pa->time + t * (pa->dietime - pa->time);
- }
- else {
- if (result->time < 0.0f)
- real_t = -result->time;
- else
- real_t = pind->hkey[0]->time + t * (pind->hkey[0][pa->totkey - 1].time - pind->hkey[0]->time);
-
- while (pind->hkey[1]->time < real_t) {
- pind->hkey[1]++;
- pind->mvert[1]++;
- }
-
- pind->hkey[0] = pind->hkey[1] - 1;
- }
-
- /* set actual interpolation keys */
- if (point) {
- edit_to_particle(keys + 1, pind->ekey[0]);
- edit_to_particle(keys + 2, pind->ekey[1]);
- }
- else if (pind->dm) {
- pind->mvert[0] = pind->mvert[1] - 1;
- mvert_to_particle(keys + 1, pind->mvert[0], pind->hkey[0]);
- mvert_to_particle(keys + 2, pind->mvert[1], pind->hkey[1]);
- }
- else if (pind->keyed) {
- memcpy(keys + 1, pind->kkey[0], sizeof(ParticleKey));
- memcpy(keys + 2, pind->kkey[1], sizeof(ParticleKey));
- }
- else if (pind->cache) {
- get_pointcache_keys_for_time(NULL, pind->cache, &pind->pm, p, real_t, keys + 1, keys + 2);
- }
- else {
- hair_to_particle(keys + 1, pind->hkey[0]);
- hair_to_particle(keys + 2, pind->hkey[1]);
- }
-
- /* set secondary interpolation keys for hair */
- if (!pind->keyed && !pind->cache && !point_vel) {
- if (point) {
- if (pind->ekey[0] != point->keys)
- edit_to_particle(keys, pind->ekey[0] - 1);
- else
- edit_to_particle(keys, pind->ekey[0]);
- }
- else if (pind->dm) {
- if (pind->hkey[0] != pa->hair)
- mvert_to_particle(keys, pind->mvert[0] - 1, pind->hkey[0] - 1);
- else
- mvert_to_particle(keys, pind->mvert[0], pind->hkey[0]);
- }
- else {
- if (pind->hkey[0] != pa->hair)
- hair_to_particle(keys, pind->hkey[0] - 1);
- else
- hair_to_particle(keys, pind->hkey[0]);
- }
-
- if (point) {
- if (pind->ekey[1] != point->keys + point->totkey - 1)
- edit_to_particle(keys + 3, pind->ekey[1] + 1);
- else
- edit_to_particle(keys + 3, pind->ekey[1]);
- }
- else if (pind->dm) {
- if (pind->hkey[1] != pa->hair + pa->totkey - 1)
- mvert_to_particle(keys + 3, pind->mvert[1] + 1, pind->hkey[1] + 1);
- else
- mvert_to_particle(keys + 3, pind->mvert[1], pind->hkey[1]);
- }
- else {
- if (pind->hkey[1] != pa->hair + pa->totkey - 1)
- hair_to_particle(keys + 3, pind->hkey[1] + 1);
- else
- hair_to_particle(keys + 3, pind->hkey[1]);
- }
- }
-
- dfra = keys[2].time - keys[1].time;
- keytime = (real_t - keys[1].time) / dfra;
-
- /* convert velocity to timestep size */
- if (pind->keyed || pind->cache || point_vel) {
- invdt = dfra * 0.04f * (psys ? psys->part->timetweak : 1.f);
- mul_v3_fl(keys[1].vel, invdt);
- mul_v3_fl(keys[2].vel, invdt);
- interp_qt_qtqt(result->rot, keys[1].rot, keys[2].rot, keytime);
- }
-
- /* now we should have in chronologiacl order k1<=k2<=t<=k3<=k4 with keytime between [0, 1]->[k2, k3] (k1 & k4 used for cardinal & bspline interpolation)*/
- psys_interpolate_particle((pind->keyed || pind->cache || point_vel) ? -1 /* signal for cubic interpolation */
- : (pind->bspline ? KEY_BSPLINE : KEY_CARDINAL),
- keys, keytime, result, 1);
-
- /* the velocity needs to be converted back from cubic interpolation */
- if (pind->keyed || pind->cache || point_vel)
- mul_v3_fl(result->vel, 1.f / invdt);
-}
-
-static void interpolate_pathcache(ParticleCacheKey *first, float t, ParticleCacheKey *result)
-{
- int i = 0;
- ParticleCacheKey *cur = first;
-
- /* scale the requested time to fit the entire path even if the path is cut early */
- t *= (first + first->segments)->time;
-
- while (i < first->segments && cur->time < t)
- cur++;
-
- if (cur->time == t)
- *result = *cur;
- else {
- float dt = (t - (cur - 1)->time) / (cur->time - (cur - 1)->time);
- interp_v3_v3v3(result->co, (cur - 1)->co, cur->co, dt);
- interp_v3_v3v3(result->vel, (cur - 1)->vel, cur->vel, dt);
- interp_qt_qtqt(result->rot, (cur - 1)->rot, cur->rot, dt);
- result->time = t;
- }
-
- /* first is actual base rotation, others are incremental from first */
- if (cur == first || cur - 1 == first)
- copy_qt_qt(result->rot, first->rot);
- else
- mul_qt_qtqt(result->rot, first->rot, result->rot);
-}
-
-/************************************************/
/* Particles on a dm */
/************************************************/
/* interpolate a location on a face based on face coordinates */
@@ -1333,6 +981,19 @@ void psys_interpolate_mcol(const MCol *mcol, int quad, const float w[4], MCol *m
}
}
+static float interpolate_particle_value(float v1, float v2, float v3, float v4, const float w[4], int four)
+{
+ float value;
+
+ value = w[0] * v1 + w[1] * v2 + w[2] * v3;
+ if (four)
+ value += w[3] * v4;
+
+ CLAMP(value, 0.f, 1.f);
+
+ return value;
+}
+
static float psys_interpolate_value_from_verts(DerivedMesh *dm, short from, int index, const float fw[4], const float *values)
{
if (values == 0 || index == -1)
@@ -1520,6 +1181,11 @@ static int psys_map_index_on_dm(DerivedMesh *dm, int from, int index, int index_
return 1;
}
+int psys_get_index_on_dm(ParticleSystem *psys, DerivedMesh *dm, ParticleData *pa, int *mapindex, float mapfw[4])
+{
+ return psys_map_index_on_dm(dm, psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, mapindex, mapfw);
+}
+
/* interprets particle data to get a point on a mesh in object space */
void psys_particle_on_dm(DerivedMesh *dm, int from, int index, int index_dmcache,
const float fw[4], float foffset, float vec[3], float nor[3], float utan[3], float vtan[3],
@@ -1709,8 +1375,8 @@ void psys_particle_on_emitter(ParticleSystemModifierData *psmd, int from, int in
extern void do_kink(ParticleKey *state, const float par_co[3], const float par_vel[3], const float par_rot[4], float time, float freq, float shape, float amplitude, float flat,
short type, short axis, float obmat[4][4], int smooth_start);
-extern float do_clump(ParticleKey *state, const float par_co[3], float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump,
- bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve);
+extern float do_clump(ParticleKey *state, const float par_co[3], const float par_orco[3], float time, const float orco[3], float clumpfac, float clumppow, float pa_clump,
+ bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve, float clump_noise_random, float clump_noise_random_size, float mat[4][4]);
void precalc_guides(ParticleSimulationData *sim, ListBase *effectors)
{
@@ -1834,12 +1500,14 @@ int do_guides(ParticleSettings *part, ListBase *effectors, ParticleKey *state, i
float par_co[3] = {0.0f, 0.0f, 0.0f};
float par_vel[3] = {0.0f, 0.0f, 0.0f};
float par_rot[4] = {1.0f, 0.0f, 0.0f, 0.0f};
- float orco_offset[3] = {0.0f, 0.0f, 0.0f};
+ float par_orco[3] = {0.0f, 0.0f, 0.0f};
+ float orco[3] = {0.0f, 0.0f, 0.0f};
copy_v3_v3(key.co, vec_to_point);
do_kink(&key, par_co, par_vel, par_rot, guidetime, pd->kink_freq, pd->kink_shape, pd->kink_amp, 0.f, pd->kink, pd->kink_axis, 0, 0);
- do_clump(&key, par_co, guidetime, orco_offset, pd->clump_fac, pd->clump_pow, 1.0f,
- part->child_flag & PART_CHILD_USE_CLUMP_NOISE, part->clump_noise_size, clumpcurve);
+ do_clump(&key, par_co, par_orco, guidetime, orco, pd->clump_fac, pd->clump_pow, 1.0f,
+ part->child_flag & PART_CHILD_USE_CLUMP_NOISE, part->clump_noise_size, clumpcurve,
+ part->clump_noise_random, part->clump_noise_random_size, NULL);
copy_v3_v3(vec_to_point, key.co);
}
@@ -1868,7 +1536,6 @@ int do_guides(ParticleSettings *part, ListBase *effectors, ParticleKey *state, i
}
return 0;
}
-
static void do_path_effectors(ParticleSimulationData *sim, int i, ParticleCacheKey *ca, int k, int steps, float *UNUSED(rootco), float effector, float UNUSED(dfra), float UNUSED(cfra), float *length, float *vec)
{
float force[3] = {0.0f, 0.0f, 0.0f};
@@ -2068,53 +1735,26 @@ static void psys_task_init_path(ParticleTask *task, ParticleSimulationData *sim)
task->rng_path = BLI_rng_new(seed);
}
-/* note: this function must be thread safe, except for branching! */
-static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cpa, ParticleCacheKey *child_keys, int i)
+static void psys_calc_child_parent_weights(ParticleTask *task, struct ChildParticle *cpa,
+ float orco[3], float ornor[3], float hairmat[4][4], int *cpa_num, float **cpa_fuv, short *cpa_from,
+ ParticleCacheKey *key[4], float weight[4], float offset[4][3])
{
ParticleThreadContext *ctx = task->ctx;
Object *ob = ctx->sim.ob;
ParticleSystem *psys = ctx->sim.psys;
ParticleSettings *part = psys->part;
- ParticleCacheKey **cache = psys->childcache;
ParticleCacheKey **pcache = psys_in_edit_mode(ctx->sim.scene, psys) && psys->edit ? psys->edit->pathcache : psys->pathcache;
- ParticleCacheKey *child, *key[4];
- ParticleTexture ptex;
- float *cpa_fuv = 0, *par_rot = 0, rot[4];
- float orco[3], ornor[3], hairmat[4][4], dvec[3], off1[4][3], off2[4][3];
- float eff_length, eff_vec[3], weight[4];
- int k, cpa_num;
- short cpa_from;
-
- if (!pcache)
- return;
-
+
if (ctx->between) {
ParticleData *pa = psys->particles + cpa->pa[0];
- int w, needupdate;
- float foffset, wsum = 0.f;
+ int w;
+ float wsum = 0.f;
float co[3];
float p_min = part->parting_min;
float p_max = part->parting_max;
/* Virtual parents don't work nicely with parting. */
float p_fac = part->parents > 0.f ? 0.f : part->parting_fac;
- if (ctx->editupdate) {
- needupdate = 0;
- w = 0;
- while (w < 4 && cpa->pa[w] >= 0) {
- if (psys->edit->points[cpa->pa[w]].flag & PEP_EDIT_RECALC) {
- needupdate = 1;
- break;
- }
- w++;
- }
-
- if (!needupdate)
- return;
- else
- memset(child_keys, 0, sizeof(*child_keys) * (ctx->segments + 1));
- }
-
/* get parent paths */
for (w = 0; w < 4; w++) {
if (cpa->pa[w] >= 0) {
@@ -2128,9 +1768,9 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp
}
/* modify weights to create parting */
- if (p_fac > 0.f) {
+ if (p_fac > 0.f && key[0]->segments != -1) {
for (w = 0; w < 4; w++) {
- if (w && weight[w] > 0.f) {
+ if (w && weight[w] > 0.f && key[w]->segments != -1) {
float d;
if (part->flag & PART_CHILD_LONG_HAIR) {
/* For long hair use tip distance/root distance as parting factor instead of root to tip angle. */
@@ -2168,46 +1808,83 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp
}
/* get the original coordinates (orco) for texture usage */
- cpa_num = cpa->num;
-
- foffset = cpa->foffset;
- cpa_fuv = cpa->fuv;
- cpa_from = PART_FROM_FACE;
+ *cpa_from = PART_FROM_FACE;
+ *cpa_num = cpa->num;
+ *cpa_fuv = cpa->fuv;
- psys_particle_on_emitter(ctx->sim.psmd, cpa_from, cpa_num, DMCACHE_ISCHILD, cpa->fuv, foffset, co, ornor, 0, 0, orco, 0);
+ psys_particle_on_emitter(ctx->sim.psmd, *cpa_from, *cpa_num, DMCACHE_ISCHILD, *cpa_fuv, cpa->foffset, co, ornor, 0, 0, orco, 0);
mul_m4_v3(ob->obmat, co);
for (w = 0; w < 4; w++)
- sub_v3_v3v3(off1[w], co, key[w]->co);
+ sub_v3_v3v3(offset[w], co, key[w]->co);
psys_mat_hair_to_global(ob, ctx->sim.psmd->dm, psys->part->from, pa, hairmat);
}
else {
ParticleData *pa = psys->particles + cpa->parent;
float co[3];
- if (ctx->editupdate) {
- if (!(psys->edit->points[cpa->parent].flag & PEP_EDIT_RECALC))
- return;
-
- memset(child_keys, 0, sizeof(*child_keys) * (ctx->segments + 1));
- }
/* get the parent path */
key[0] = pcache[cpa->parent];
/* get the original coordinates (orco) for texture usage */
- cpa_from = part->from;
- cpa_num = pa->num;
+ *cpa_from = part->from;
+ *cpa_num = pa->num;
/* XXX hack to avoid messed up particle num and subsequent crash (#40733) */
- if (cpa_num > ctx->sim.psmd->dm->getNumTessFaces(ctx->sim.psmd->dm))
- cpa_num = 0;
- cpa_fuv = pa->fuv;
+ if (*cpa_num > ctx->sim.psmd->dm->getNumTessFaces(ctx->sim.psmd->dm))
+ *cpa_num = 0;
+ *cpa_fuv = pa->fuv;
- psys_particle_on_emitter(ctx->sim.psmd, cpa_from, cpa_num, DMCACHE_ISCHILD, cpa_fuv, pa->foffset, co, ornor, 0, 0, orco, 0);
+ psys_particle_on_emitter(ctx->sim.psmd, *cpa_from, *cpa_num, DMCACHE_ISCHILD, *cpa_fuv, pa->foffset, co, ornor, 0, 0, orco, 0);
psys_mat_hair_to_global(ob, ctx->sim.psmd->dm, psys->part->from, pa, hairmat);
}
+}
+
+/* note: this function must be thread safe, except for branching! */
+static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cpa, ParticleCacheKey *child_keys, int i)
+{
+ ParticleThreadContext *ctx = task->ctx;
+ ParticleSystem *psys = ctx->sim.psys;
+ ParticleSettings *part = psys->part;
+ ParticleCacheKey **cache = psys->childcache;
+ ParticleCacheKey **pcache = psys_in_edit_mode(ctx->sim.scene, psys) && psys->edit ? psys->edit->pathcache : psys->pathcache;
+ ParticleCacheKey *child, *key[4];
+ ParticleTexture ptex;
+ float *cpa_fuv = 0, *par_rot = 0, rot[4];
+ float orco[3], ornor[3], hairmat[4][4], dvec[3], off1[4][3], off2[4][3];
+ float eff_length, eff_vec[3], weight[4];
+ int k, cpa_num;
+ short cpa_from;
+
+ if (!pcache)
+ return;
+
+ if (ctx->editupdate) {
+ bool need_update;
+
+ if (ctx->between) {
+ int w;
+ need_update = false;
+ for (w = 0; w < 4 && cpa->pa[w] >= 0; ++w) {
+ if (psys->edit->points[cpa->pa[w]].flag & PEP_EDIT_RECALC) {
+ need_update = true;
+ break;
+ }
+ }
+ }
+ else {
+ need_update = psys->edit->points[cpa->parent].flag & PEP_EDIT_RECALC;
+ }
+
+ if (need_update)
+ memset(child_keys, 0, sizeof(*child_keys) * (ctx->segments + 1));
+ else
+ return;
+ }
+
+ psys_calc_child_parent_weights(task, cpa, orco, ornor, hairmat, &cpa_num, &cpa_fuv, &cpa_from, key, weight, off1);
child_keys->segments = ctx->segments;
@@ -2221,6 +1898,8 @@ static void psys_thread_create_path(ParticleTask *task, struct ChildParticle *cp
/* create the child path */
for (k = 0, child = child_keys; k <= ctx->segments; k++, child++) {
+ child->hull_parent = cpa->hull_parent;
+
if (ctx->between) {
int w = 0;
@@ -2453,11 +2132,11 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra)
ParticleSystem *psys = sim->psys;
ParticleSettings *part = psys->part;
ParticleCacheKey *ca, **cache;
+ const bool keyed = psys->flag & PSYS_KEYED;
+ const bool baked = psys->pointcache->mem_cache.first && psys->part->type != PART_HAIR;
DerivedMesh *hair_dm = (psys->part->type == PART_HAIR && psys->flag & PSYS_HAIR_DYNAMICS) ? psys->hair_out_dm : NULL;
- ParticleKey result;
-
Material *ma;
ParticleInterpolationData pind;
ParticleTexture ptex;
@@ -2475,7 +2154,6 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra)
float length, vec[3];
float *vg_effector = NULL;
float *vg_length = NULL, pa_length = 1.0f;
- int keyed, baked;
/* we don't have anything valid to create paths from so let's quit here */
if ((psys->flag & PSYS_HAIR_DONE || psys->flag & PSYS_KEYED || psys->pointcache) == 0)
@@ -2485,9 +2163,6 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra)
if (psys->renderdata == 0 && (psys->edit == NULL || pset->flag & PE_DRAW_PART) == 0)
return;
- keyed = psys->flag & PSYS_KEYED;
- baked = psys->pointcache->mem_cache.first && psys->part->type != PART_HAIR;
-
/* clear out old and create new empty path cache */
psys_free_path_cache(psys, psys->edit);
cache = psys->pathcache = psys_alloc_path_cache_buffers(&psys->pathcachebufs, totpart, segments + 1);
@@ -2557,6 +2232,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra)
/*--interpolate actual path from data points--*/
for (k = 0, ca = cache[p]; k <= segments; k++, ca++) {
+ ParticleKey result;
time = (float)k / (float)segments;
t = birthtime + time * (dietime - birthtime);
result.time = -t;
@@ -2573,14 +2249,6 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra)
copy_v3_v3(ca->col, col);
}
- if (part->type == PART_HAIR) {
- HairKey *hkey;
-
- for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) {
- mul_v3_m4v3(hkey->world_co, hairmat, hkey->co);
- }
- }
-
/*--modify paths and calculate rotation & velocity--*/
if (!(psys->flag & PSYS_GLOBAL_HAIR)) {
@@ -2965,7 +2633,7 @@ static void psys_face_mat(Object *ob, DerivedMesh *dm, ParticleData *pa, float m
triatomat(v[0], v[1], v[2], (osface) ? osface->uv : NULL, mat);
}
-void psys_mat_hair_to_object(Object *UNUSED(ob), DerivedMesh *dm, short from, ParticleData *pa, float hairmat[4][4])
+void psys_mat_hair_to_object(Object *ob, DerivedMesh *dm, short from, ParticleData *pa, float hairmat[4][4])
{
float vec[3];
@@ -2975,7 +2643,7 @@ void psys_mat_hair_to_object(Object *UNUSED(ob), DerivedMesh *dm, short from, Pa
return;
}
- psys_face_mat(0, dm, pa, hairmat, 0);
+ psys_face_mat(ob, dm, pa, hairmat, 0);
psys_particle_on_dm(dm, from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, vec, 0, 0, 0, 0, 0);
copy_v3_v3(hairmat[3], vec);
}
@@ -3011,6 +2679,25 @@ void psys_mat_hair_to_global(Object *ob, DerivedMesh *dm, short from, ParticleDa
mul_m4_m4m4(hairmat, ob->obmat, facemat);
}
+void psys_child_mat_to_object(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, ChildParticle *cpa, float hairmat[4][4])
+{
+ const bool between = (psys->part->childtype == PART_CHILD_FACES);
+ float co[3];
+
+ if (between) {
+ ParticleData *pa = &psys->particles[cpa->pa[0]];
+ psys_particle_on_emitter(psmd, PART_FROM_FACE, cpa->num, DMCACHE_ISCHILD, cpa->fuv, cpa->foffset, co, NULL, NULL, NULL, NULL, NULL);
+ psys_mat_hair_to_object(ob, psmd->dm, psys->part->from, pa, hairmat);
+ }
+ else {
+ ParticleData *pa = &psys->particles[cpa->parent];
+ psys_particle_on_emitter(psmd, psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, NULL, NULL, NULL, NULL, NULL);
+ psys_mat_hair_to_object(ob, psmd->dm, psys->part->from, pa, hairmat);
+ }
+
+ copy_v3_v3(hairmat[3], co);
+}
+
/************************************************/
/* ParticleSettings handling */
/************************************************/
@@ -3032,6 +2719,8 @@ ModifierData *object_add_particle_system(Scene *scene, Object *ob, const char *n
BLI_addtail(&ob->particlesystem, psys);
psys->part = psys_new_settings(DATA_("ParticleSettings"), NULL);
+ psys->key = BKE_key_add_particles(ob, psys);
+ psys->key->type = KEY_RELATIVE;
if (BLI_listbase_count_ex(&ob->particlesystem, 2) > 1)
BLI_snprintf(psys->name, sizeof(psys->name), DATA_("ParticleSystem %i"), BLI_listbase_count(&ob->particlesystem));
@@ -3054,6 +2743,8 @@ ModifierData *object_add_particle_system(Scene *scene, Object *ob, const char *n
psys->flag = PSYS_CURRENT;
psys->cfra = BKE_scene_frame_get_from_ctime(scene, CFRA + 1);
+ psys->hair_preview_factor = 100.0f;
+
DAG_relations_tag_update(G.main);
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
@@ -3095,7 +2786,7 @@ void object_remove_particle_system(Scene *UNUSED(scene), Object *ob)
if (ob->particlesystem.first)
((ParticleSystem *) ob->particlesystem.first)->flag |= PSYS_CURRENT;
else
- ob->mode &= ~OB_MODE_PARTICLE_EDIT;
+ ob->mode &= ~(OB_MODE_PARTICLE_EDIT | OB_MODE_HAIR_EDIT);
DAG_relations_tag_update(G.main);
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
@@ -3372,6 +3063,11 @@ static int get_particle_uv(DerivedMesh *dm, ParticleData *pa, int face_index, co
pvalue = texture_value_blend(def, pvalue, value, texfac, blend); \
} (void)0
+#define SET_PARTICLE_TEXTURE_RGB(type, prgb, texfac) \
+ if ((event & mtex->mapto) & type) { \
+ texture_rgb_blend(prgb, rgba, prgb, value, texfac, blend); \
+ } (void)0
+
#define CLAMP_PARTICLE_TEXTURE_POS(type, pvalue) \
if (event & type) { \
if (pvalue < 0.0f) \
@@ -3384,6 +3080,21 @@ static int get_particle_uv(DerivedMesh *dm, ParticleData *pa, int face_index, co
CLAMP(pvalue, -1.0f, 1.0f); \
} (void)0
+#define CLAMP_PARTICLE_TEXTURE_RGB(type, prgb) \
+ if (event & type) { \
+ CLAMP3(prgb, 0.0f, 1.0f); \
+ } (void)0
+
+/* actual usable texco mode for particles */
+BLI_INLINE int particle_texco(ParticleSettings *part, MTex *mtex)
+{
+ short texco = mtex->texco;
+ if (ELEM(texco, TEXCO_UV, TEXCO_ORCO) &&
+ (!ELEM(part->from, PART_FROM_FACE, PART_FROM_VOLUME) || part->distr == PART_DISTR_GRID))
+ texco = TEXCO_GLOB;
+ return texco;
+}
+
static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSettings *part, ParticleData *par, int child_index, int face_index, const float fw[4], float *orco, ParticleTexture *ptex, int event, float cfra)
{
MTex *mtex, **mtexp = part->mtex;
@@ -3393,6 +3104,7 @@ static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSetti
ptex->ivel = ptex->life = ptex->exist = ptex->size = ptex->damp =
ptex->gravity = ptex->field = ptex->time = ptex->clump = ptex->kink_freq = ptex->kink_amp =
ptex->effector = ptex->rough1 = ptex->rough2 = ptex->roughe = 1.0f;
+ ptex->color[0] = ptex->color[1] = ptex->color[2] = 1.0f;
ptex->length = 1.0f - part->randlength * psys_frand(psys, child_index + 26);
ptex->length *= part->clength_thres < psys_frand(psys, child_index + 27) ? part->clength : 1.0f;
@@ -3402,10 +3114,7 @@ static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSetti
if (mtex && mtex->tex && mtex->mapto) {
float def = mtex->def_var;
short blend = mtex->blendtype;
- short texco = mtex->texco;
-
- if (ELEM(texco, TEXCO_UV, TEXCO_ORCO) && (ELEM(part->from, PART_FROM_FACE, PART_FROM_VOLUME) == 0 || part->distr == PART_DISTR_GRID))
- texco = TEXCO_GLOB;
+ short texco = particle_texco(part, mtex);
switch (texco) {
case TEXCO_GLOB:
@@ -3441,6 +3150,7 @@ static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSetti
SET_PARTICLE_TEXTURE(PAMAP_KINK_AMP, ptex->kink_amp, mtex->kinkampfac);
SET_PARTICLE_TEXTURE(PAMAP_KINK_FREQ, ptex->kink_freq, mtex->kinkfac);
SET_PARTICLE_TEXTURE(PAMAP_DENS, ptex->exist, mtex->padensfac);
+ SET_PARTICLE_TEXTURE_RGB(PAMAP_COLOR, ptex->color, mtex->pacolfac);
}
}
@@ -3450,71 +3160,87 @@ static void get_cpa_texture(DerivedMesh *dm, ParticleSystem *psys, ParticleSetti
CLAMP_PARTICLE_TEXTURE_POS(PAMAP_KINK_FREQ, ptex->kink_freq);
CLAMP_PARTICLE_TEXTURE_POS(PAMAP_ROUGH, ptex->rough1);
CLAMP_PARTICLE_TEXTURE_POS(PAMAP_DENS, ptex->exist);
+ CLAMP_PARTICLE_TEXTURE_RGB(PAMAP_COLOR, ptex->color);
}
-void psys_get_texture(ParticleSimulationData *sim, ParticleData *pa, ParticleTexture *ptex, int event, float cfra)
+
+bool particle_mtex_eval(ParticleSimulationData *sim, MTex *mtex, ParticleData *pa, float cfra, float *value, float rgba[4])
{
Object *ob = sim->ob;
Mesh *me = (Mesh *)ob->data;
ParticleSettings *part = sim->psys->part;
+ short texco;
+
+ float texvec[3];
+
+ if (!(mtex->tex && mtex->mapto))
+ return false;
+
+ texco = particle_texco(part, mtex);
+ switch (texco) {
+ case TEXCO_GLOB:
+ copy_v3_v3(texvec, pa->state.co);
+ break;
+ case TEXCO_OBJECT:
+ copy_v3_v3(texvec, pa->state.co);
+ if (mtex->object)
+ mul_m4_v3(mtex->object->imat, texvec);
+ break;
+ case TEXCO_UV:
+ if (get_particle_uv(sim->psmd->dm, pa, 0, pa->fuv, mtex->uvname, texvec))
+ break;
+ /* no break, failed to get uv's, so let's try orco's */
+ case TEXCO_ORCO: {
+ float co[3];
+
+ psys_particle_on_emitter(sim->psmd, sim->psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, 0, 0, 0, texvec, 0);
+
+ if (me->bb == NULL || (me->bb->flag & BOUNDBOX_DIRTY)) {
+ BKE_mesh_texspace_calc(me);
+ }
+ sub_v3_v3(texvec, me->loc);
+ if (me->size[0] != 0.0f) texvec[0] /= me->size[0];
+ if (me->size[1] != 0.0f) texvec[1] /= me->size[1];
+ if (me->size[2] != 0.0f) texvec[2] /= me->size[2];
+ break;
+ }
+ case TEXCO_PARTICLE:
+ /* texture coordinates in range [-1, 1] */
+ texvec[0] = 2.f * (cfra - pa->time) / (pa->dietime - pa->time) - 1.f;
+ if (sim->psys->totpart > 0)
+ texvec[1] = 2.f * (float)(pa - sim->psys->particles) / (float)sim->psys->totpart - 1.f;
+ else
+ texvec[1] = 0.0f;
+ texvec[2] = 0.f;
+ break;
+ }
+
+ externtex(mtex, texvec, value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false);
+
+ return true;
+}
+
+void psys_get_texture(ParticleSimulationData *sim, ParticleData *pa, ParticleTexture *ptex, int event, float cfra)
+{
+ ParticleSettings *part = sim->psys->part;
MTex **mtexp = part->mtex;
MTex *mtex;
int m;
- float value, rgba[4], co[3], texvec[3];
+ float value, rgba[4];
int setvars = 0;
/* initialize ptex */
ptex->ivel = ptex->life = ptex->exist = ptex->size = ptex->damp =
ptex->gravity = ptex->field = ptex->length = ptex->clump = ptex->kink_freq = ptex->kink_amp =
ptex->effector = ptex->rough1 = ptex->rough2 = ptex->roughe = 1.0f;
+ ptex->color[0] = ptex->color[1] = ptex->color[2] = 1.0f;
ptex->time = (float)(pa - sim->psys->particles) / (float)sim->psys->totpart;
for (m = 0; m < MAX_MTEX; m++, mtexp++) {
mtex = *mtexp;
- if (mtex && mtex->tex && mtex->mapto) {
+ if (mtex && particle_mtex_eval(sim, mtex, pa, cfra, &value, rgba)) {
float def = mtex->def_var;
short blend = mtex->blendtype;
- short texco = mtex->texco;
-
- if (texco == TEXCO_UV && (ELEM(part->from, PART_FROM_FACE, PART_FROM_VOLUME) == 0 || part->distr == PART_DISTR_GRID))
- texco = TEXCO_GLOB;
-
- switch (texco) {
- case TEXCO_GLOB:
- copy_v3_v3(texvec, pa->state.co);
- break;
- case TEXCO_OBJECT:
- copy_v3_v3(texvec, pa->state.co);
- if (mtex->object)
- mul_m4_v3(mtex->object->imat, texvec);
- break;
- case TEXCO_UV:
- if (get_particle_uv(sim->psmd->dm, pa, 0, pa->fuv, mtex->uvname, texvec))
- break;
- /* no break, failed to get uv's, so let's try orco's */
- case TEXCO_ORCO:
- psys_particle_on_emitter(sim->psmd, sim->psys->part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, 0, 0, 0, texvec, 0);
-
- if (me->bb == NULL || (me->bb->flag & BOUNDBOX_DIRTY)) {
- BKE_mesh_texspace_calc(me);
- }
- sub_v3_v3(texvec, me->loc);
- if (me->size[0] != 0.0f) texvec[0] /= me->size[0];
- if (me->size[1] != 0.0f) texvec[1] /= me->size[1];
- if (me->size[2] != 0.0f) texvec[2] /= me->size[2];
- break;
- case TEXCO_PARTICLE:
- /* texture coordinates in range [-1, 1] */
- texvec[0] = 2.f * (cfra - pa->time) / (pa->dietime - pa->time) - 1.f;
- if (sim->psys->totpart > 0)
- texvec[1] = 2.f * (float)(pa - sim->psys->particles) / (float)sim->psys->totpart - 1.f;
- else
- texvec[1] = 0.0f;
- texvec[2] = 0.f;
- break;
- }
-
- externtex(mtex, texvec, &value, rgba, rgba + 1, rgba + 2, rgba + 3, 0, NULL, false);
if ((event & mtex->mapto) & PAMAP_TIME) {
/* the first time has to set the base value for time regardless of blend mode */
@@ -3536,6 +3262,7 @@ void psys_get_texture(ParticleSimulationData *sim, ParticleData *pa, ParticleTex
SET_PARTICLE_TEXTURE(PAMAP_GRAVITY, ptex->gravity, mtex->gravityfac);
SET_PARTICLE_TEXTURE(PAMAP_DAMP, ptex->damp, mtex->dampfac);
SET_PARTICLE_TEXTURE(PAMAP_LENGTH, ptex->length, mtex->lengthfac);
+ SET_PARTICLE_TEXTURE_RGB(PAMAP_COLOR, ptex->color, mtex->pacolfac);
}
}
@@ -3548,7 +3275,37 @@ void psys_get_texture(ParticleSimulationData *sim, ParticleData *pa, ParticleTex
CLAMP_PARTICLE_TEXTURE_POSNEG(PAMAP_GRAVITY, ptex->gravity);
CLAMP_PARTICLE_TEXTURE_POS(PAMAP_DAMP, ptex->damp);
CLAMP_PARTICLE_TEXTURE_POS(PAMAP_LENGTH, ptex->length);
+ CLAMP_PARTICLE_TEXTURE_RGB(PAMAP_COLOR, ptex->color);
}
+
+/* specialized texture eval for shapekey influences */
+float psys_get_texture_shapefac(ParticleSimulationData *sim, ParticleData *pa, float cfra, const char *shapekey)
+{
+ ParticleSettings *part = sim->psys->part;
+ MTex **mtexp = part->mtex;
+ MTex *mtex;
+ int m;
+ float value, rgba[4];
+
+ float result = 1.0f;
+
+ for (m = 0; m < MAX_MTEX; m++, mtexp++) {
+ mtex = *mtexp;
+ if (mtex && (mtex->mapto & PAMAP_SHAPEKEY) && STREQ(mtex->shapekey, shapekey)) {
+ float def = mtex->def_var;
+ short blend = mtex->blendtype;
+
+ if (particle_mtex_eval(sim, mtex, pa, cfra, &value, rgba)) {
+ result = texture_value_blend(def, result, value, mtex->shapefac, blend);
+ }
+ }
+ }
+
+ CLAMP(result, 0.0f, 1.0f);
+
+ return result;
+}
+
/************************************************/
/* Particle State */
/************************************************/
@@ -3628,6 +3385,35 @@ static void get_child_modifier_parameters(ParticleSettings *part, ParticleThread
if (ctx->vg_effector)
ptex->effector *= psys_interpolate_value_from_verts(ctx->dm, cpa_from, cpa_num, cpa_fuv, ctx->vg_effector);
}
+
+static void interpolate_pathcache(ParticleCacheKey *first, float t, ParticleCacheKey *result)
+{
+ int i = 0;
+ ParticleCacheKey *cur = first;
+
+ /* scale the requested time to fit the entire path even if the path is cut early */
+ t *= (first + first->segments)->time;
+
+ while (i < first->segments && cur->time < t)
+ cur++;
+
+ if (cur->time == t)
+ *result = *cur;
+ else {
+ float dt = (t - (cur - 1)->time) / (cur->time - (cur - 1)->time);
+ interp_v3_v3v3(result->co, (cur - 1)->co, cur->co, dt);
+ interp_v3_v3v3(result->vel, (cur - 1)->vel, cur->vel, dt);
+ interp_qt_qtqt(result->rot, (cur - 1)->rot, cur->rot, dt);
+ result->time = t;
+ }
+
+ /* first is actual base rotation, others are incremental from first */
+ if (cur == first || cur - 1 == first)
+ copy_qt_qt(result->rot, first->rot);
+ else
+ mul_qt_qtqt(result->rot, first->rot, result->rot);
+}
+
/* get's hair (or keyed) particles state at the "path time" specified in state->time */
void psys_get_particle_on_path(ParticleSimulationData *sim, int p, ParticleKey *state, const bool vel)
{
diff --git a/source/blender/blenkernel/intern/particle_child.c b/source/blender/blenkernel/intern/particle_child.c
index 7b2e07ea96f..1ccb5574df7 100644
--- a/source/blender/blenkernel/intern/particle_child.c
+++ b/source/blender/blenkernel/intern/particle_child.c
@@ -41,8 +41,8 @@ struct Material;
void do_kink(ParticleKey *state, const float par_co[3], const float par_vel[3], const float par_rot[4], float time, float freq, float shape, float amplitude, float flat,
short type, short axis, float obmat[4][4], int smooth_start);
-float do_clump(ParticleKey *state, const float par_co[3], float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump,
- bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve);
+float do_clump(ParticleKey *state, const float par_co[3], const float par_orco[3], float time, const float orco[3], float clumpfac, float clumppow, float pa_clump,
+ bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve, float clump_noise_random, float clump_noise_random_size, float mat[4][4]);
void do_child_modifiers(ParticleSimulationData *sim,
ParticleTexture *ptex, const float par_co[3], const float par_vel[3], const float par_rot[4], const float par_orco[3],
ChildParticle *cpa, const float orco[3], float mat[4][4], ParticleKey *state, float t);
@@ -584,48 +584,64 @@ static float do_clump_level(float result[3], const float co[3], const float par_
return clump;
}
-float do_clump(ParticleKey *state, const float par_co[3], float time, const float orco_offset[3], float clumpfac, float clumppow, float pa_clump,
- bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve)
+BLI_INLINE void simple_roughness(float mat[4][4], float size, float factor, const float loc[3], float time, float result[3])
+{
+ float turbloc[3], rough[3];
+
+ mul_v3_v3fl(turbloc, loc, time);
+ rough[0] = -1.0f + 2.0f * BLI_gTurbulence(size, turbloc[0], turbloc[1], turbloc[2], 2, 0, 2);
+ rough[1] = -1.0f + 2.0f * BLI_gTurbulence(size, turbloc[1], turbloc[2], turbloc[0], 2, 0, 2);
+ rough[2] = -1.0f + 2.0f * BLI_gTurbulence(size, turbloc[2], turbloc[0], turbloc[1], 2, 0, 2);
+
+ madd_v3_v3fl(result, mat[0], factor * rough[0]);
+ madd_v3_v3fl(result, mat[1], factor * rough[1]);
+ madd_v3_v3fl(result, mat[2], factor * rough[2]);
+}
+
+float do_clump(ParticleKey *state, const float par_co[3], const float par_orco[3], float time, const float orco[3], float clumpfac, float clumppow, float pa_clump,
+ bool use_clump_noise, float clump_noise_size, CurveMapping *clumpcurve, float clump_noise_random, float clump_noise_random_size, float mat[4][4])
{
float clump;
+ float rough_offset[3];
+
+ zero_v3(rough_offset);
if (use_clump_noise && clump_noise_size != 0.0f) {
- float center[3], noisevec[3];
+ float center_orco[3], center[3], noisevec[3], orco_offset[3];
float da[4], pa[12];
+ sub_v3_v3v3(orco_offset, orco, par_orco);
mul_v3_v3fl(noisevec, orco_offset, 1.0f / clump_noise_size);
voronoi(noisevec[0], noisevec[1], noisevec[2], da, pa, 1.0f, 0);
mul_v3_fl(&pa[0], clump_noise_size);
- add_v3_v3v3(center, par_co, &pa[0]);
+ if (clump_noise_random != 0.0f && mat) {
+ add_v3_v3v3(center_orco, par_orco, &pa[0]);
+ simple_roughness(mat, clump_noise_random_size, clump_noise_random, center_orco, time, rough_offset);
+ }
+
+ add_v3_v3v3(center, par_co, &pa[0]);
do_clump_level(state->co, state->co, center, time, clumpfac, clumppow, pa_clump, clumpcurve);
}
clump = do_clump_level(state->co, state->co, par_co, time, clumpfac, clumppow, pa_clump, clumpcurve);
+ if (use_clump_noise && clump_noise_size != 0.0f && clump_noise_random != 0.0f && mat) {
+ add_v3_v3(state->co, rough_offset);
+ }
+
return clump;
}
static void do_rough(const float loc[3], float mat[4][4], float t, float fac, float size, float thres, ParticleKey *state)
{
- float rough[3];
- float rco[3];
-
if (thres != 0.0f) {
if (fabsf((float)(-1.5f + loc[0] + loc[1] + loc[2])) < 1.5f * thres) {
return;
}
}
- copy_v3_v3(rco, loc);
- mul_v3_fl(rco, t);
- rough[0] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[0], rco[1], rco[2], 2, 0, 2);
- rough[1] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[1], rco[2], rco[0], 2, 0, 2);
- rough[2] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[2], rco[0], rco[1], 2, 0, 2);
-
- madd_v3_v3fl(state->co, mat[0], fac * rough[0]);
- madd_v3_v3fl(state->co, mat[1], fac * rough[1]);
- madd_v3_v3fl(state->co, mat[2], fac * rough[2]);
+ simple_roughness(mat, size, fac, loc, t, state->co);
}
static void do_rough_end(const float loc[3], float mat[4][4], float t, float fac, float shape, ParticleKey *state)
@@ -645,23 +661,12 @@ static void do_rough_end(const float loc[3], float mat[4][4], float t, float fac
static void do_rough_curve(const float loc[3], float mat[4][4], float time, float fac, float size, CurveMapping *roughcurve, ParticleKey *state)
{
- float rough[3];
- float rco[3];
-
if (!roughcurve)
return;
fac *= CLAMPIS(curvemapping_evaluateF(roughcurve, 0, time), 0.0f, 1.0f);
- copy_v3_v3(rco, loc);
- mul_v3_fl(rco, time);
- rough[0] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[0], rco[1], rco[2], 2, 0, 2);
- rough[1] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[1], rco[2], rco[0], 2, 0, 2);
- rough[2] = -1.0f + 2.0f * BLI_gTurbulence(size, rco[2], rco[0], rco[1], 2, 0, 2);
-
- madd_v3_v3fl(state->co, mat[0], fac * rough[0]);
- madd_v3_v3fl(state->co, mat[1], fac * rough[1]);
- madd_v3_v3fl(state->co, mat[2], fac * rough[2]);
+ simple_roughness(mat, size, fac, loc, time, state->co);
}
void do_child_modifiers(ParticleSimulationData *sim, ParticleTexture *ptex, const float par_co[3], const float par_vel[3], const float par_rot[4], const float par_orco[3],
@@ -694,12 +699,11 @@ void do_child_modifiers(ParticleSimulationData *sim, ParticleTexture *ptex, cons
guided = do_guides(sim->psys->part, sim->psys->effectors, (ParticleKey *)state, cpa->parent, t);
if (guided == 0) {
- float orco_offset[3];
float clump;
- sub_v3_v3v3(orco_offset, orco, par_orco);
- clump = do_clump(state, par_co, t, orco_offset, part->clumpfac, part->clumppow, ptex ? ptex->clump : 1.f,
- part->child_flag & PART_CHILD_USE_CLUMP_NOISE, part->clump_noise_size, clumpcurve);
+ clump = do_clump(state, par_co, par_orco, t, orco, part->clumpfac, part->clumppow, ptex ? ptex->clump : 1.f,
+ part->child_flag & PART_CHILD_USE_CLUMP_NOISE, part->clump_noise_size, clumpcurve,
+ part->clump_noise_random, part->clump_noise_random_size, mat);
if (kink_freq != 0.f) {
kink_amp *= (1.f - kink_amp_clump * clump);
diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c
index 50634460028..13bb7c65025 100644
--- a/source/blender/blenkernel/intern/particle_distribute.c
+++ b/source/blender/blenkernel/intern/particle_distribute.c
@@ -32,6 +32,7 @@
*/
#include <string.h>
+#include <stdlib.h>
#include "MEM_guardedalloc.h"
@@ -1153,6 +1154,183 @@ static void distribute_particles_on_shape(ParticleSimulationData *sim, int UNUSE
fprintf(stderr,"Shape emission not yet possible!\n");
}
+#ifdef USE_PARTICLE_HULL_DRAWING
+/* placeholder for child particle sorting, storing emitter hair-space offset */
+typedef struct ChildParticleSort {
+ int index;
+ float x, y;
+ int parent;
+ bool is_hull;
+} ChildParticleSort;
+
+/* comparison function for sorting children.
+ * primary criterion is the childrens' primary parent
+ * secondary criterion is the combined offset from the parent in the tangential plane,
+ * which is needed for further constructing the convex hull of children around each parent
+ */
+static int psys_child_cmp(const void *a, const void *b)
+{
+ const ChildParticleSort *cpa_a = a, *cpa_b = b;
+
+ if (UNLIKELY(cpa_a->parent == cpa_b->parent)) {
+ if (UNLIKELY(cpa_a->x == cpa_b->x))
+ return cpa_a->y < cpa_b->y ? -1 : 1;
+ else
+ return cpa_a->x < cpa_b->x ? -1 : 1;
+ }
+ else
+ return cpa_a->parent < cpa_b->parent ? -1 : 1;
+}
+
+BLI_INLINE bool ccw(const ChildParticleSort *p1, const ChildParticleSort *p2, const ChildParticleSort *p3)
+{
+ return (p2->x - p1->x) * (p3->y - p1->y) - (p2->y - p1->y) * (p3->x - p1->x) > 0.0f;
+}
+
+static int make_convex_child_hull(ChildParticleSort *childdata, int totchild, ChildParticleSort **hull)
+{
+ const int parent = childdata->parent;
+
+ int tothull, upper_start;
+ int i;
+
+ tothull = 0;
+ /* lower hull */
+ for (i = 0; i < totchild; ++i) {
+ ChildParticleSort *cdata = &childdata[i];
+ /* limit to the parent group */
+ if (cdata->parent != parent)
+ break;
+
+ while (tothull >= 2 && !ccw(hull[tothull-2], hull[tothull-1], cdata))
+ --tothull;
+ hull[tothull++] = cdata;
+ }
+ /* this is the actual size of the child group sharing the same parent */
+ totchild = i;
+
+ /* upper hull */
+ upper_start = tothull + 1;
+ for (i = totchild-2; i >= 0; --i) {
+ ChildParticleSort *cdata = &childdata[i];
+
+ while (tothull >= upper_start && !ccw(hull[tothull-2], hull[tothull-1], cdata))
+ --tothull;
+ hull[tothull++] = cdata;
+ }
+
+ return tothull - 1;
+}
+
+BLI_INLINE int count_child_group(ChildParticleSort *data, int start, int totchild)
+{
+ const int parent = data[start].parent;
+ int i;
+
+ i = start;
+ do {
+ ++i;
+ } while (i < totchild && data[i].parent == parent);
+
+ return i - start;
+}
+
+/* sort children by primary parent and relative emitter location, then calculate convex hulls */
+static void psys_sort_children(ParticleSimulationData *sim)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+ const int totchild = psys->totchild;
+ const bool between = (part->childtype == PART_CHILD_FACES);
+ const int cpa_from = between ? PART_FROM_FACE : part->from;
+
+ ChildParticleSort *sort;
+ int i;
+
+ if (totchild == 0)
+ return;
+ if (!sim->psmd->dm)
+ return;
+
+ sort = MEM_mallocN(sizeof(ChildParticleSort) * totchild, "child sorting data");
+
+ for (i = 0; i < totchild; ++i) {
+ ChildParticle *cpa = &psys->child[i];
+ ChildParticleSort *cdata = &sort[i];
+
+ ParticleData *parent;
+ float co[3], hairmat[4][4], hairimat[4][4];
+
+ cdata->index = i;
+ cdata->parent = between ? cpa->pa[0] : cpa->parent;
+
+ psys_particle_on_emitter(sim->psmd, cpa_from, cpa->num, DMCACHE_ISCHILD, cpa->fuv, cpa->foffset, co, NULL, NULL, NULL, NULL, NULL);
+
+ parent = &psys->particles[cdata->parent];
+ psys_mat_hair_to_global(sim->ob, sim->psmd->dm, psys->part->from, parent, hairmat);
+ invert_m4_m4(hairimat, hairmat);
+
+ mul_m4_v3(hairimat, co);
+ cdata->x = co[0];
+ cdata->y = co[1];
+
+ cdata->is_hull = false;
+ }
+
+ qsort(sort, totchild, sizeof(ChildParticleSort), psys_child_cmp);
+
+ /* Calculate the convex hull of children for each parent using Andrew's Monotone Chain algorithm
+ * http://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain
+ */
+ {
+ ChildParticle *nchild = MEM_mallocN(sizeof(ChildParticle) * totchild, "sorted child particles");
+ ChildParticle *ncpa = nchild;
+
+ /* note: the algorithm requires one additional element in the buffer in worst case */
+ ChildParticleSort **hull = MEM_mallocN(sizeof(ChildParticleSort*) * (totchild+1), "child convex hull points");
+
+ int groupstart = 0;
+ while (groupstart < totchild) {
+ int groupsize, tothull;
+
+ groupsize = count_child_group(sort, groupstart, totchild);
+ BLI_assert(groupstart + groupsize <= totchild);
+
+ tothull = make_convex_child_hull(sort + groupstart, groupsize, hull);
+
+ for (i = 0; i < tothull; ++i) {
+ ChildParticleSort *cdata = hull[i];
+ memcpy(ncpa, psys->child + cdata->index, sizeof(ChildParticle));
+ ncpa->hull_parent = cdata->parent;
+ ++ncpa;
+
+ /* tag actual hull children */
+ cdata->is_hull = true;
+ }
+
+ /* insert remaining child particles after the hull children */
+ for (i = groupstart; i < groupstart + groupsize; ++i) {
+ ChildParticleSort *cdata = &sort[i];
+ if (!cdata->is_hull) {
+ memcpy(ncpa, psys->child + cdata->index, sizeof(ChildParticle));
+ ncpa->hull_parent = -1;
+ ++ncpa;
+ }
+ }
+
+ groupstart += groupsize;
+ }
+
+ MEM_freeN(hull);
+
+ MEM_freeN(psys->child);
+ psys->child = nchild;
+ }
+
+ MEM_freeN(sort);
+}
+#endif
+
void distribute_particles(ParticleSimulationData *sim, int from)
{
PARTICLE_PSMD;
@@ -1167,6 +1345,11 @@ void distribute_particles(ParticleSimulationData *sim, int from)
else
distribute_particles_on_shape(sim, from);
+#ifdef USE_PARTICLE_HULL_DRAWING
+ if (from == PART_FROM_CHILD)
+ psys_sort_children(sim);
+#endif
+
if (distr_error) {
distribute_invalid(sim->scene, sim->psys, from);
diff --git a/source/blender/blenkernel/intern/particle_interpolate.c b/source/blender/blenkernel/intern/particle_interpolate.c
new file mode 100644
index 00000000000..88b0adad665
--- /dev/null
+++ b/source/blender/blenkernel/intern/particle_interpolate.c
@@ -0,0 +1,382 @@
+/*
+ * ***** 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) 2007 by Janne Karhu.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/particle_interpolate.c
+ * \ingroup bke
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_key_types.h"
+#include "DNA_particle_types.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+
+#include "BKE_cdderivedmesh.h"
+#include "BKE_key.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
+#include "BKE_scene.h"
+
+void psys_interpolate_particle(short type, ParticleKey keys[4], float dt, ParticleKey *result, bool velocity)
+{
+ float t[4];
+
+ if (type < 0) {
+ interp_cubic_v3(result->co, result->vel, keys[1].co, keys[1].vel, keys[2].co, keys[2].vel, dt);
+ }
+ else {
+ key_curve_position_weights(dt, t, type);
+
+ interp_v3_v3v3v3v3(result->co, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t);
+
+ if (velocity) {
+ float temp[3];
+
+ if (dt > 0.999f) {
+ key_curve_position_weights(dt - 0.001f, t, type);
+ interp_v3_v3v3v3v3(temp, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t);
+ sub_v3_v3v3(result->vel, result->co, temp);
+ }
+ else {
+ key_curve_position_weights(dt + 0.001f, t, type);
+ interp_v3_v3v3v3v3(temp, keys[0].co, keys[1].co, keys[2].co, keys[3].co, t);
+ sub_v3_v3v3(result->vel, temp, result->co);
+ }
+ }
+ }
+}
+
+float psys_get_dietime_from_cache(PointCache *cache, int index)
+{
+ PTCacheMem *pm;
+ int dietime = 10000000; /* some max value so that we can default to pa->time+lifetime */
+
+ for (pm = cache->mem_cache.last; pm; pm = pm->prev) {
+ if (BKE_ptcache_mem_index_find(pm, index) >= 0)
+ return (float)pm->frame;
+ }
+
+ return (float)dietime;
+}
+
+/* Assumes pointcache->mem_cache exists, so for disk cached particles call psys_make_temp_pointcache() before use */
+/* It uses ParticleInterpolationData->pm to store the current memory cache frame so it's thread safe. */
+static void get_pointcache_keys_for_time(Object *UNUSED(ob), PointCache *cache, PTCacheMem **cur, int index, float t, ParticleKey *key1, ParticleKey *key2)
+{
+ static PTCacheMem *pm = NULL;
+ int index1, index2;
+
+ if (index < 0) { /* initialize */
+ *cur = cache->mem_cache.first;
+
+ if (*cur)
+ *cur = (*cur)->next;
+ }
+ else {
+ if (*cur) {
+ while (*cur && (*cur)->next && (float)(*cur)->frame < t)
+ *cur = (*cur)->next;
+
+ pm = *cur;
+
+ index2 = BKE_ptcache_mem_index_find(pm, index);
+ index1 = BKE_ptcache_mem_index_find(pm->prev, index);
+
+ BKE_ptcache_make_particle_key(key2, index2, pm->data, (float)pm->frame);
+ if (index1 < 0)
+ copy_particle_key(key1, key2, 1);
+ else
+ BKE_ptcache_make_particle_key(key1, index1, pm->prev->data, (float)pm->prev->frame);
+ }
+ else if (cache->mem_cache.first) {
+ pm = cache->mem_cache.first;
+ index2 = BKE_ptcache_mem_index_find(pm, index);
+ BKE_ptcache_make_particle_key(key2, index2, pm->data, (float)pm->frame);
+ copy_particle_key(key1, key2, 1);
+ }
+ }
+}
+
+static int get_pointcache_times_for_particle(PointCache *cache, int index, float *start, float *end)
+{
+ PTCacheMem *pm;
+ int ret = 0;
+
+ for (pm = cache->mem_cache.first; pm; pm = pm->next) {
+ if (BKE_ptcache_mem_index_find(pm, index) >= 0) {
+ *start = pm->frame;
+ ret++;
+ break;
+ }
+ }
+
+ for (pm = cache->mem_cache.last; pm; pm = pm->prev) {
+ if (BKE_ptcache_mem_index_find(pm, index) >= 0) {
+ *end = pm->frame;
+ ret++;
+ break;
+ }
+ }
+
+ return ret == 2;
+}
+
+static void edit_to_particle(ParticleKey *key, PTCacheEditKey *ekey)
+{
+ copy_v3_v3(key->co, ekey->co);
+ if (ekey->vel) {
+ copy_v3_v3(key->vel, ekey->vel);
+ }
+ key->time = *(ekey->time);
+}
+
+static void hair_to_particle(ParticleKey *key, HairKey *hkey)
+{
+ copy_v3_v3(key->co, hkey->co);
+ key->time = hkey->time;
+}
+
+static void mvert_to_particle(ParticleKey *key, MVert *mvert, HairKey *hkey)
+{
+ copy_v3_v3(key->co, mvert->co);
+ key->time = hkey->time;
+}
+
+void init_particle_interpolation(Object *ob, ParticleSystem *psys, ParticleData *pa, ParticleInterpolationData *pind)
+{
+
+ if (pind->epoint) {
+ PTCacheEditPoint *point = pind->epoint;
+
+ pind->ekey[0] = point->keys;
+ pind->ekey[1] = point->totkey > 1 ? point->keys + 1 : NULL;
+
+ pind->birthtime = *(point->keys->time);
+ pind->dietime = *((point->keys + point->totkey - 1)->time);
+ }
+ else if (pind->keyed) {
+ ParticleKey *key = pa->keys;
+ pind->kkey[0] = key;
+ pind->kkey[1] = pa->totkey > 1 ? key + 1 : NULL;
+
+ pind->birthtime = key->time;
+ pind->dietime = (key + pa->totkey - 1)->time;
+ }
+ else if (pind->cache) {
+ float start = 0.0f, end = 0.0f;
+ get_pointcache_keys_for_time(ob, pind->cache, &pind->pm, -1, 0.0f, NULL, NULL);
+ pind->birthtime = pa ? pa->time : pind->cache->startframe;
+ pind->dietime = pa ? pa->dietime : pind->cache->endframe;
+
+ if (get_pointcache_times_for_particle(pind->cache, pa - psys->particles, &start, &end)) {
+ pind->birthtime = MAX2(pind->birthtime, start);
+ pind->dietime = MIN2(pind->dietime, end);
+ }
+ }
+ else {
+ HairKey *key = pa->hair;
+ pind->hkey[0] = key;
+ pind->hkey[1] = key + 1;
+
+ pind->birthtime = key->time;
+ pind->dietime = (key + pa->totkey - 1)->time;
+
+ if (pind->dm) {
+ pind->mvert[0] = CDDM_get_vert(pind->dm, pa->hair_index);
+ pind->mvert[1] = pind->mvert[0] + 1;
+ }
+ }
+}
+
+void do_particle_interpolation(ParticleSystem *psys, int p, ParticleData *pa, float t, ParticleInterpolationData *pind, ParticleKey *result)
+{
+ PTCacheEditPoint *point = pind->epoint;
+ ParticleKey keys[4];
+ int point_vel = (point && point->keys->vel);
+ float real_t, dfra, keytime, invdt = 1.f;
+
+ /* billboards wont fill in all of these, so start cleared */
+ memset(keys, 0, sizeof(keys));
+
+ /* interpret timing and find keys */
+ if (point) {
+ if (result->time < 0.0f)
+ real_t = -result->time;
+ else
+ real_t = *(pind->ekey[0]->time) + t * (*(pind->ekey[0][point->totkey - 1].time) - *(pind->ekey[0]->time));
+
+ while (*(pind->ekey[1]->time) < real_t)
+ pind->ekey[1]++;
+
+ pind->ekey[0] = pind->ekey[1] - 1;
+ }
+ else if (pind->keyed) {
+ /* we have only one key, so let's use that */
+ if (pind->kkey[1] == NULL) {
+ copy_particle_key(result, pind->kkey[0], 1);
+ return;
+ }
+
+ if (result->time < 0.0f)
+ real_t = -result->time;
+ else
+ real_t = pind->kkey[0]->time + t * (pind->kkey[0][pa->totkey - 1].time - pind->kkey[0]->time);
+
+ if (psys->part->phystype == PART_PHYS_KEYED && psys->flag & PSYS_KEYED_TIMING) {
+ ParticleTarget *pt = psys->targets.first;
+
+ pt = pt->next;
+
+ while (pt && pa->time + pt->time < real_t)
+ pt = pt->next;
+
+ if (pt) {
+ pt = pt->prev;
+
+ if (pa->time + pt->time + pt->duration > real_t)
+ real_t = pa->time + pt->time;
+ }
+ else
+ real_t = pa->time + ((ParticleTarget *)psys->targets.last)->time;
+ }
+
+ CLAMP(real_t, pa->time, pa->dietime);
+
+ while (pind->kkey[1]->time < real_t)
+ pind->kkey[1]++;
+
+ pind->kkey[0] = pind->kkey[1] - 1;
+ }
+ else if (pind->cache) {
+ if (result->time < 0.0f) /* flag for time in frames */
+ real_t = -result->time;
+ else
+ real_t = pa->time + t * (pa->dietime - pa->time);
+ }
+ else {
+ if (result->time < 0.0f)
+ real_t = -result->time;
+ else
+ real_t = pind->hkey[0]->time + t * (pind->hkey[0][pa->totkey - 1].time - pind->hkey[0]->time);
+
+ while (pind->hkey[1]->time < real_t) {
+ pind->hkey[1]++;
+ pind->mvert[1]++;
+ }
+
+ pind->hkey[0] = pind->hkey[1] - 1;
+ }
+
+ /* set actual interpolation keys */
+ if (point) {
+ edit_to_particle(keys + 1, pind->ekey[0]);
+ edit_to_particle(keys + 2, pind->ekey[1]);
+ }
+ else if (pind->dm) {
+ pind->mvert[0] = pind->mvert[1] - 1;
+ mvert_to_particle(keys + 1, pind->mvert[0], pind->hkey[0]);
+ mvert_to_particle(keys + 2, pind->mvert[1], pind->hkey[1]);
+ }
+ else if (pind->keyed) {
+ memcpy(keys + 1, pind->kkey[0], sizeof(ParticleKey));
+ memcpy(keys + 2, pind->kkey[1], sizeof(ParticleKey));
+ }
+ else if (pind->cache) {
+ get_pointcache_keys_for_time(NULL, pind->cache, &pind->pm, p, real_t, keys + 1, keys + 2);
+ }
+ else {
+ hair_to_particle(keys + 1, pind->hkey[0]);
+ hair_to_particle(keys + 2, pind->hkey[1]);
+ }
+
+ /* set secondary interpolation keys for hair */
+ if (!pind->keyed && !pind->cache && !point_vel) {
+ if (point) {
+ if (pind->ekey[0] != point->keys)
+ edit_to_particle(keys, pind->ekey[0] - 1);
+ else
+ edit_to_particle(keys, pind->ekey[0]);
+ }
+ else if (pind->dm) {
+ if (pind->hkey[0] != pa->hair)
+ mvert_to_particle(keys, pind->mvert[0] - 1, pind->hkey[0] - 1);
+ else
+ mvert_to_particle(keys, pind->mvert[0], pind->hkey[0]);
+ }
+ else {
+ if (pind->hkey[0] != pa->hair)
+ hair_to_particle(keys, pind->hkey[0] - 1);
+ else
+ hair_to_particle(keys, pind->hkey[0]);
+ }
+
+ if (point) {
+ if (pind->ekey[1] != point->keys + point->totkey - 1)
+ edit_to_particle(keys + 3, pind->ekey[1] + 1);
+ else
+ edit_to_particle(keys + 3, pind->ekey[1]);
+ }
+ else if (pind->dm) {
+ if (pind->hkey[1] != pa->hair + pa->totkey - 1)
+ mvert_to_particle(keys + 3, pind->mvert[1] + 1, pind->hkey[1] + 1);
+ else
+ mvert_to_particle(keys + 3, pind->mvert[1], pind->hkey[1]);
+ }
+ else {
+ if (pind->hkey[1] != pa->hair + pa->totkey - 1)
+ hair_to_particle(keys + 3, pind->hkey[1] + 1);
+ else
+ hair_to_particle(keys + 3, pind->hkey[1]);
+ }
+ }
+
+ dfra = keys[2].time - keys[1].time;
+ keytime = (real_t - keys[1].time) / dfra;
+
+ /* convert velocity to timestep size */
+ if (pind->keyed || pind->cache || point_vel) {
+ invdt = dfra * 0.04f * (psys ? psys->part->timetweak : 1.f);
+ mul_v3_fl(keys[1].vel, invdt);
+ mul_v3_fl(keys[2].vel, invdt);
+ interp_qt_qtqt(result->rot, keys[1].rot, keys[2].rot, keytime);
+ }
+
+ /* now we should have in chronologiacl order k1<=k2<=t<=k3<=k4 with keytime between [0, 1]->[k2, k3] (k1 & k4 used for cardinal & bspline interpolation)*/
+ psys_interpolate_particle((pind->keyed || pind->cache || point_vel) ? -1 /* signal for cubic interpolation */
+ : (pind->bspline ? KEY_BSPLINE : KEY_CARDINAL),
+ keys, keytime, result, 1);
+
+ /* the velocity needs to be converted back from cubic interpolation */
+ if (pind->keyed || pind->cache || point_vel)
+ mul_v3_fl(result->vel, 1.f / invdt);
+}
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
index 89db2dd45a9..cf4bf32fd77 100644
--- a/source/blender/blenkernel/intern/particle_system.c
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -84,6 +84,7 @@
#include "BKE_object.h"
#include "BKE_material.h"
#include "BKE_cloth.h"
+#include "BKE_key.h"
#include "BKE_lattice.h"
#include "BKE_pointcache.h"
#include "BKE_mesh.h"
@@ -1201,7 +1202,7 @@ void psys_make_temp_pointcache(Object *ob, ParticleSystem *psys)
PTCacheID pid;
BKE_ptcache_id_from_particles(&pid, ob, psys);
cache->flag &= ~PTCACHE_DISK_CACHE;
- BKE_ptcache_disk_to_mem(&pid);
+ BKE_ptcache_disk_to_mem(&pid, false);
cache->flag |= PTCACHE_DISK_CACHE;
}
}
@@ -1291,7 +1292,7 @@ static void psys_update_effectors(ParticleSimulationData *sim)
{
pdEndEffectors(&sim->psys->effectors);
sim->psys->effectors = pdInitEffectors(sim->scene, sim->ob, sim->psys,
- sim->psys->part->effector_weights, true);
+ sim->psys->part->effector_weights);
precalc_guides(sim, sim->psys->effectors);
}
@@ -2896,79 +2897,93 @@ static void collision_check(ParticleSimulationData *sim, int p, float dfra, floa
/************************************************/
/* Hair */
/************************************************/
-/* check if path cache or children need updating and do it if needed */
-static void psys_update_path_cache(ParticleSimulationData *sim, float cfra)
+
+static bool psys_needs_path_cache(ParticleSimulationData *sim)
{
ParticleSystem *psys = sim->psys;
ParticleSettings *part = psys->part;
ParticleEditSettings *pset = &sim->scene->toolsettings->particle;
Base *base;
- int distr=0, alloc=0, skip=0;
-
- if ((psys->part->childtype && psys->totchild != psys_get_tot_child(sim->scene, psys)) || psys->recalc&PSYS_RECALC_RESET)
- alloc=1;
-
- if (alloc || psys->recalc&PSYS_RECALC_CHILD || (psys->vgroup[PSYS_VG_DENSITY] && (sim->ob && sim->ob->mode & OB_MODE_WEIGHT_PAINT)))
- distr=1;
-
- if (distr) {
- if (alloc)
- realloc_particles(sim, sim->psys->totpart);
-
- if (psys_get_tot_child(sim->scene, psys)) {
- /* don't generate children while computing the hair keys */
- if (!(psys->part->type == PART_HAIR) || (psys->flag & PSYS_HAIR_DONE)) {
- distribute_particles(sim, PART_FROM_CHILD);
-
- if (part->childtype==PART_CHILD_FACES && part->parents != 0.0f)
- psys_find_parents(sim);
+
+ /* particle instance modifier with "path" option need cached paths even if particle system doesn't */
+ for (base = sim->scene->base.first; base; base= base->next) {
+ ModifierData *md = modifiers_findByType(base->object, eModifierType_ParticleInstance);
+ if (md) {
+ ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *)md;
+ if (pimd->flag & eParticleInstanceFlag_Path && pimd->ob == sim->ob && pimd->psys == (psys - (ParticleSystem*)sim->ob->particlesystem.first)) {
+ return true;
}
}
- else
- psys_free_children(psys);
}
-
+
if ((part->type==PART_HAIR || psys->flag&PSYS_KEYED || psys->pointcache->flag & PTCACHE_BAKED)==0)
- skip = 1; /* only hair, keyed and baked stuff can have paths */
+ return false; /* only hair, keyed and baked stuff can have paths */
else if (part->ren_as != PART_DRAW_PATH && !(part->type==PART_HAIR && ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR)))
- skip = 1; /* particle visualization must be set as path */
+ return false; /* particle visualization must be set as path */
else if (!psys->renderdata) {
- if (part->draw_as != PART_DRAW_REND)
- skip = 1; /* draw visualization */
+ if (!ELEM(part->draw_as, PART_DRAW_REND, PART_DRAW_HULL))
+ return false; /* not a mode that requires paths */
else if (psys->pointcache->flag & PTCACHE_BAKING)
- skip = 1; /* no need to cache paths while baking dynamics */
+ return false; /* no need to cache paths while baking dynamics */
else if (psys_in_edit_mode(sim->scene, psys)) {
if ((pset->flag & PE_DRAW_PART)==0)
- skip = 1;
+ return false;
else if (part->childtype==0 && (psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED)==0)
- skip = 1; /* in edit mode paths are needed for child particles and dynamic hair */
+ return false; /* in edit mode paths are needed for child particles and dynamic hair */
}
}
+
+ return true;
+}
+/* check if path cache or children need updating and do it if needed */
+static void psys_update_path_cache(ParticleSimulationData *sim, float cfra)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
- /* particle instance modifier with "path" option need cached paths even if particle system doesn't */
- for (base = sim->scene->base.first; base; base= base->next) {
- ModifierData *md = modifiers_findByType(base->object, eModifierType_ParticleInstance);
- if (md) {
- ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *)md;
- if (pimd->flag & eParticleInstanceFlag_Path && pimd->ob == sim->ob && pimd->psys == (psys - (ParticleSystem*)sim->ob->particlesystem.first)) {
- skip = 0;
- break;
+ /* check if particles need to be reallocated or redistributed */
+ {
+ bool alloc = false, distr = false;
+
+ if ((psys->part->childtype && psys->totchild != psys_get_tot_child(sim->scene, psys)) || psys->recalc&PSYS_RECALC_RESET)
+ alloc = true;
+
+ if (alloc || psys->recalc&PSYS_RECALC_CHILD || (psys->vgroup[PSYS_VG_DENSITY] && (sim->ob && sim->ob->mode & OB_MODE_WEIGHT_PAINT)))
+ distr = true;
+
+ if (distr) {
+ if (alloc)
+ realloc_particles(sim, sim->psys->totpart);
+
+ if (psys_get_tot_child(sim->scene, psys)) {
+ /* don't generate children while computing the hair keys */
+ if (!(psys->part->type == PART_HAIR) || (psys->flag & PSYS_HAIR_DONE)) {
+ distribute_particles(sim, PART_FROM_CHILD);
+
+ if (part->childtype==PART_CHILD_FACES && part->parents != 0.0f)
+ psys_find_parents(sim);
+ }
}
+ else
+ psys_free_children(psys);
}
}
- if (!skip) {
+ if (psys_needs_path_cache(sim)) {
+
psys_cache_paths(sim, cfra);
/* for render, child particle paths are computed on the fly */
if (part->childtype) {
+ bool do_child_paths = true;
+
if (!psys->totchild)
- skip = 1;
+ do_child_paths = false;
else if (psys->part->type == PART_HAIR && (psys->flag & PSYS_HAIR_DONE)==0)
- skip = 1;
+ do_child_paths = false;
- if (!skip)
+ if (do_child_paths)
psys_cache_child_paths(sim, cfra, 0);
}
}
@@ -3015,6 +3030,120 @@ static MDeformVert *hair_set_pinning(MDeformVert *dvert, float weight)
return dvert;
}
+bool psys_hair_update_preview(ParticleSimulationData *sim)
+{
+#ifdef USE_PARTICLE_PREVIEW
+ ParticleSystem *psys = sim->psys;
+ ParticleSettings *part = psys->part;
+ DerivedMesh *dm = sim->psmd->dm;
+ const float ratio = psys->hair_preview_factor * 0.01f;
+ /* target number of simulated hairs
+ * NOTE: this has to be reached exactly, in order to allow
+ * comparison with the psys->hair_num_simulated value!
+ */
+ const int num_simulated = psys->totpart * ratio;
+
+ if (!(part->type == PART_HAIR))
+ return false;
+ if (num_simulated == psys->hair_num_simulated)
+ return false;
+
+ { /* Random hair selection method */
+ KDTree *tree = BLI_kdtree_new(num_simulated); /* kdtree for finding nearest simulated hairs for blending */
+ RNG *rng = BLI_rng_new(98250 + psys->seed);
+ ParticleData *pa;
+ int cur_simulated = 0;
+ int i;
+
+ /* construct a kd-tree of all simulated hairs */
+ pa = psys->particles;
+ for (i = 0; i < psys->totpart; ++i, ++pa) {
+ bool simulate = true;
+ if (cur_simulated == num_simulated) {
+ /* don't simulate more than the total number */
+ simulate = false;
+ }
+ else if (num_simulated - cur_simulated <= psys->totpart - i) {
+ /* only allow disabling if the target sim number
+ * can be reached with the remaining hairs
+ */
+ simulate = BLI_rng_get_float(rng) < ratio;
+ }
+
+ if (simulate) {
+ float co[3];
+
+ pa->flag &= ~PARS_HAIR_BLEND;
+
+ if (pa->totkey >= 2) {
+ psys_particle_on_dm(dm, part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, 0, 0, 0, 0, 0);
+ BLI_kdtree_insert(tree, i, co);
+ }
+
+ ++cur_simulated;
+ }
+ else {
+ pa->flag |= PARS_HAIR_BLEND;
+ }
+ }
+
+ BLI_kdtree_balance(tree);
+
+ /* look up nearest simulated hairs for preview hairs and calculate blending weights */
+ pa = psys->particles;
+ for (i = 0; i < psys->totpart; ++i, ++pa) {
+ if (pa->flag & PARS_HAIR_BLEND) {
+ float co[3];
+ int maxw, w;
+ KDTreeNearest nearest[4];
+
+ psys_particle_on_dm(dm, part->from, pa->num, pa->num_dmcache, pa->fuv, pa->foffset, co, 0, 0, 0, 0, 0);
+ maxw = BLI_kdtree_find_nearest_n(tree, co, nearest, 4);
+ if (maxw == 1) {
+ pa->blend_index[0] = nearest[0].index;
+ pa->blend_weight[0] = 1.0f;
+ }
+ else if (maxw > 1) {
+ float norm, totdist = 0.0f;
+ for (w = 0; w < maxw; ++w)
+ totdist += nearest[w].dist;
+ norm = totdist > 0.0f ? 1.0f / (totdist * (float)(maxw - 1)) : 0.0f;
+
+ for (w = 0; w < maxw; ++w) {
+ pa->blend_index[w] = nearest[w].index;
+ pa->blend_weight[w] = (totdist - nearest[w].dist) * norm;
+ }
+ }
+ /* clear unused weights */
+ for (w = maxw; w < 4; ++w) {
+ pa->blend_index[w] = -1;
+ pa->blend_weight[w] = 0.0f;
+ }
+ }
+ else {
+ pa->blend_index[0] = -1;
+ pa->blend_index[1] = -1;
+ pa->blend_index[2] = -1;
+ pa->blend_index[3] = -1;
+ pa->blend_weight[0] = 0.0f;
+ pa->blend_weight[1] = 0.0f;
+ pa->blend_weight[2] = 0.0f;
+ pa->blend_weight[3] = 0.0f;
+ }
+ }
+
+ BLI_kdtree_free(tree);
+ BLI_rng_free(rng);
+ }
+
+ psys->hair_num_simulated = num_simulated;
+ return true;
+#else
+ (void)sim;
+ return false;
+#endif
+}
+
static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int totedge, DerivedMesh **r_dm, ClothHairData **r_hairdata)
{
ParticleSystem *psys = sim->psys;
@@ -3030,6 +3159,8 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int
float hairmat[4][4];
float max_length;
float hair_radius;
+ float *shapekey_data, *shapekey;
+ int totshapekey;
dm = *r_dm;
if (!dm) {
@@ -3060,6 +3191,8 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int
/* XXX placeholder for more flexible future hair settings */
hair_radius = part->size;
+ shapekey = shapekey_data = BKE_key_evaluate_particles(sim->ob, psys, sim->scene ? sim->scene->r.cfra : 0.0f, &totshapekey);
+
/* make vgroup for pin roots etc.. */
hair_index = 1;
LOOP_PARTICLES {
@@ -3080,8 +3213,15 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int
ClothHairData *hair;
float *co, *co_next;
- co = key->co;
- co_next = (key+1)->co;
+ if (shapekey) {
+ co = shapekey;
+ co_next = shapekey + 3;
+ shapekey += 3;
+ }
+ else {
+ co = key->co;
+ co_next = (key+1)->co;
+ }
/* create fake root before actual root to resist bending */
if (k==0) {
@@ -3101,6 +3241,14 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int
dvert = hair_set_pinning(dvert, 1.0f);
+#ifdef USE_PARTICLE_PREVIEW
+ /* tag vert if hair is not simulated */
+ if (pa->flag & PARS_HAIR_BLEND)
+ mvert->flag |= ME_VERT_TMP_TAG;
+ else
+ mvert->flag &= ~ME_VERT_TMP_TAG;
+#endif
+
mvert++;
medge++;
}
@@ -3127,6 +3275,14 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int
else
dvert = hair_set_pinning(dvert, 1.0f);
+#ifdef USE_PARTICLE_PREVIEW
+ /* tag vert if hair is not simulated */
+ if (pa->flag & PARS_HAIR_BLEND)
+ mvert->flag |= ME_VERT_TMP_TAG;
+ else
+ mvert->flag &= ~ME_VERT_TMP_TAG;
+#endif
+
mvert++;
if (k)
medge++;
@@ -3136,6 +3292,105 @@ static void hair_create_input_dm(ParticleSimulationData *sim, int totpoint, int
}
}
+#ifdef USE_PARTICLE_PREVIEW
+static void hair_deform_preview_curve(ParticleSystem *psys, ParticleData *pa, float (*deformedVerts)[3], ClothHairData *hairdata)
+{
+ ParticleData *particles = psys->particles;
+ HairKey *hkey;
+ float (*vert)[3];
+ ClothHairData *root;
+ int k;
+ float totlen, norm;
+
+ /* first key is root, no blending for them */
+ if (pa->totkey < 2)
+ return;
+
+ /* calculate normalization factor to equally parameterize hairs */
+ totlen = 0.0f;
+ hkey = pa->hair;
+ for (k = 0; k < pa->totkey - 1; ++k, ++hkey)
+ totlen += len_v3v3((hkey+1)->co, hkey->co);
+ norm = totlen > 0.0f ? 1.0f / totlen : 0.0f;
+
+ totlen = 0.0f;
+ hkey = pa->hair;
+ vert = deformedVerts + pa->hair_index;
+ root = hairdata + pa->hair_index;
+ for (k = 0; k < pa->totkey; ++k, ++hkey, ++vert, ++root) {
+ float param;
+ int w;
+
+ if (k == 0) /* skip root vertex */
+ continue;
+ param = totlen * norm;
+ totlen += len_v3v3(hkey->co, (hkey-1)->co);
+
+ zero_v3(vert[0]);
+ for (w = 0; w < 4; ++w) {
+ ParticleData *blend_pa;
+ float (*blend_vert)[3];
+ ClothHairData *blend_hair;
+ float blend_key, blend_factor;
+ int blend_totkey, blend_index;
+ float co[3];
+
+ if (pa->blend_index[w] < 0)
+ continue;
+
+ blend_pa = particles + pa->blend_index[w];
+ blend_totkey = blend_pa->totkey;
+
+ blend_key = param * (float)blend_totkey;
+ blend_index = (int)blend_key;
+ if (blend_index >= blend_totkey - 1) {
+ blend_index = blend_totkey - 2;
+ blend_factor = 1.0f;
+ }
+ else {
+ blend_factor = blend_key - floorf(blend_key);
+ }
+
+ /* pa->hair_index is set when creating input_dm,
+ * use it here to map to output mvert index
+ */
+ blend_vert = deformedVerts + blend_pa->hair_index + blend_index;
+ blend_hair = hairdata + blend_pa->hair_index + blend_index;
+
+ interp_v3_v3v3(co, blend_vert[0], blend_vert[1], blend_factor);
+
+ /* transform parent vector from world to root space, then back into root space of the blended hair */
+ sub_v3_v3(co, blend_hair->loc);
+ /* note: rotation transform disabled, this does not work nicely with global force directions (gravity, wind etc.)
+ * these forces would also get rotated, giving movement in a different direction than the force would actually incur.
+ * would have to split internal (stretch, bend) and external forces somehow to make this plausible
+ */
+#if 0
+ mul_transposed_m3_v3(blend_hair->rot, co);
+ mul_m3_v3(root->rot, co);
+#endif
+ add_v3_v3(co, root->loc);
+
+ madd_v3_v3fl(vert[0], co, pa->blend_weight[w]);
+ }
+ }
+}
+
+static void hair_deform_preview_hairs(ParticleSimulationData *sim, float (*deformedVerts)[3], ClothHairData *roots)
+{
+ ParticleSystem *psys = sim->psys;
+ ParticleData *pa;
+ int p;
+
+ pa = psys->particles;
+ for (p = 0; p < psys->totpart; ++p, ++pa) {
+ if (pa->flag & PARS_HAIR_BLEND) {
+ hair_deform_preview_curve(psys, pa, deformedVerts, roots);
+ }
+ }
+}
+#endif
+
static void do_hair_dynamics(ParticleSimulationData *sim)
{
ParticleSystem *psys = sim->psys;
@@ -3146,6 +3401,11 @@ static void do_hair_dynamics(ParticleSimulationData *sim)
float (*deformedVerts)[3];
bool realloc_roots;
+ if (psys_hair_update_preview(sim)) {
+ if (psys->clmd)
+ cloth_free_modifier(psys->clmd);
+ }
+
if (!psys->clmd) {
psys->clmd = (ClothModifierData*)modifier_new(eModifierType_Cloth);
psys->clmd->sim_parms->goalspring = 0.0f;
@@ -3197,6 +3457,9 @@ static void do_hair_dynamics(ParticleSimulationData *sim)
psys->hair_out_dm->getVertCos(psys->hair_out_dm, deformedVerts);
clothModifier_do(psys->clmd, sim->scene, sim->ob, psys->hair_in_dm, deformedVerts);
+#ifdef USE_PARTICLE_PREVIEW
+ hair_deform_preview_hairs(sim, deformedVerts, psys->clmd->roots);
+#endif
CDDM_apply_vert_coords(psys->hair_out_dm, deformedVerts);
@@ -3210,7 +3473,14 @@ static void hair_step(ParticleSimulationData *sim, float cfra)
ParticleSystem *psys = sim->psys;
ParticleSettings *part = psys->part;
PARTICLE_P;
+ PARTICLE_PSMD;
float disp = psys_get_current_display_percentage(psys);
+ float *shapekey_data = NULL, *shapekey;
+ int totshapekey;
+
+ if (part->type == PART_HAIR) {
+ shapekey = shapekey_data = BKE_key_evaluate_particles(sim->ob, sim->psys, sim->scene ? sim->scene->r.cfra : 0.0f, &totshapekey);
+ }
LOOP_PARTICLES {
pa->size = part->size;
@@ -3221,8 +3491,35 @@ static void hair_step(ParticleSimulationData *sim, float cfra)
pa->flag |= PARS_NO_DISP;
else
pa->flag &= ~PARS_NO_DISP;
+
+ if (part->type == PART_HAIR) {
+ float hairmat[4][4];
+ HairKey *hkey;
+ int k;
+
+ psys_mat_hair_to_global(sim->ob, psmd->dm, psys->part->from, pa, hairmat);
+
+ /* update world coordinates and calculate shapekey result if needed */
+ for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) {
+ const float *co;
+ if (shapekey) {
+ co = shapekey;
+ shapekey += 3;
+
+ copy_v3_v3(hkey->co, co);
+ }
+ else {
+ co = hkey->co;
+ }
+
+ mul_v3_m4v3(hkey->world_co, hairmat, co);
+ }
+ }
}
+ if (shapekey_data)
+ MEM_freeN(shapekey_data);
+
if (psys->recalc & PSYS_RECALC_RESET) {
/* need this for changing subsurf levels */
psys_calc_dmcache(sim->ob, sim->psmd->dm, psys);
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index 2f4a45828ea..4711a5900f0 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -2537,7 +2537,7 @@ int BKE_ptcache_write(PTCacheID *pid, unsigned int cfra)
*/
/* Clears & resets */
-void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra)
+void BKE_ptcache_id_clear_ex(PTCacheID *pid, int mode, unsigned int cfra, bool allow_file_delete)
{
unsigned int len; /* store the length of the string */
unsigned int sta, end;
@@ -2595,8 +2595,10 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra)
if (STREQLEN(filename, de->d_name, len)) { /* do we have the right prefix */
if (mode == PTCACHE_CLEAR_ALL) {
pid->cache->last_exact = MIN2(pid->cache->startframe, 0);
- BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name);
- BLI_delete(path_full, false, false);
+ if (allow_file_delete) {
+ BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name);
+ BLI_delete(path_full, false, false);
+ }
}
else {
/* read the number of the file */
@@ -2611,8 +2613,10 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra)
(mode == PTCACHE_CLEAR_AFTER && frame > cfra))
{
- BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name);
- BLI_delete(path_full, false, false);
+ if (allow_file_delete) {
+ BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name);
+ BLI_delete(path_full, false, false);
+ }
if (pid->cache->cached_frames && frame >=sta && frame <= end)
pid->cache->cached_frames[frame-sta] = 0;
}
@@ -2665,8 +2669,10 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra)
case PTCACHE_CLEAR_FRAME:
if (pid->cache->flag & PTCACHE_DISK_CACHE) {
if (BKE_ptcache_id_exist(pid, cfra)) {
- ptcache_filename(pid, filename, cfra, 1, 1); /* no path */
- BLI_delete(filename, false, false);
+ if (allow_file_delete) {
+ ptcache_filename(pid, filename, cfra, 1, 1); /* no path */
+ BLI_delete(filename, false, false);
+ }
}
}
else {
@@ -2688,6 +2694,13 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra)
BKE_ptcache_update_info(pid);
}
+
+/* Clears & resets */
+void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra)
+{
+ BKE_ptcache_id_clear_ex(pid, mode, cfra, false);
+}
+
int BKE_ptcache_id_exist(PTCacheID *pid, int cfra)
{
if (!pid->cache)
@@ -2959,49 +2972,6 @@ int BKE_ptcache_object_reset(Scene *scene, Object *ob, int mode)
return reset;
}
-/* Use this when quitting blender, with unsaved files */
-void BKE_ptcache_remove(void)
-{
- char path[MAX_PTCACHE_PATH];
- char path_full[MAX_PTCACHE_PATH];
- int rmdir = 1;
-
- ptcache_path(NULL, path);
-
- if (BLI_exists(path)) {
- /* The pointcache dir exists? - remove all pointcache */
-
- DIR *dir;
- struct dirent *de;
-
- dir = opendir(path);
- if (dir==NULL)
- return;
-
- while ((de = readdir(dir)) != NULL) {
- if (FILENAME_IS_CURRPAR(de->d_name)) {
- /* do nothing */
- }
- else if (strstr(de->d_name, PTCACHE_EXT)) { /* do we have the right extension?*/
- BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name);
- BLI_delete(path_full, false, false);
- }
- else {
- rmdir = 0; /* unknown file, don't remove the dir */
- }
- }
-
- closedir(dir);
- }
- else {
- rmdir = 0; /* path dosnt exist */
- }
-
- if (rmdir) {
- BLI_delete(path, true, false);
- }
-}
-
/* Point Cache handling */
PointCache *BKE_ptcache_add(ListBase *ptcaches)
@@ -3412,21 +3382,24 @@ void BKE_ptcache_bake(PTCacheBaker *baker)
/* TODO: call redraw all windows somehow */
}
/* Helpers */
-void BKE_ptcache_disk_to_mem(PTCacheID *pid)
+void BKE_ptcache_disk_to_mem(PTCacheID *pid, bool clear)
{
PointCache *cache = pid->cache;
PTCacheMem *pm = NULL;
- int baked = cache->flag & PTCACHE_BAKED;
int cfra, sfra = cache->startframe, efra = cache->endframe;
- /* Remove possible bake flag to allow clear */
- cache->flag &= ~PTCACHE_BAKED;
-
- /* PTCACHE_DISK_CACHE flag was cleared already */
- BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0);
-
- /* restore possible bake flag */
- cache->flag |= baked;
+ if (clear) {
+ int baked = cache->flag & PTCACHE_BAKED;
+
+ /* Remove possible bake flag to allow clear */
+ cache->flag &= ~PTCACHE_BAKED;
+
+ /* PTCACHE_DISK_CACHE flag was cleared already */
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0);
+
+ /* restore possible bake flag */
+ cache->flag |= baked;
+ }
for (cfra=sfra; cfra <= efra; cfra++) {
pm = ptcache_disk_frame_to_mem(pid, cfra);
@@ -3435,20 +3408,23 @@ void BKE_ptcache_disk_to_mem(PTCacheID *pid)
BLI_addtail(&pid->cache->mem_cache, pm);
}
}
-void BKE_ptcache_mem_to_disk(PTCacheID *pid)
+void BKE_ptcache_mem_to_disk(PTCacheID *pid, bool clear)
{
PointCache *cache = pid->cache;
PTCacheMem *pm = cache->mem_cache.first;
- int baked = cache->flag & PTCACHE_BAKED;
-
- /* Remove possible bake flag to allow clear */
- cache->flag &= ~PTCACHE_BAKED;
-
- /* PTCACHE_DISK_CACHE flag was set already */
- BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0);
- /* restore possible bake flag */
- cache->flag |= baked;
+ if (clear) {
+ int baked = cache->flag & PTCACHE_BAKED;
+
+ /* Remove possible bake flag to allow clear */
+ cache->flag &= ~PTCACHE_BAKED;
+
+ /* PTCACHE_DISK_CACHE flag was set already */
+ BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0);
+
+ /* restore possible bake flag */
+ cache->flag |= baked;
+ }
for (; pm; pm=pm->next) {
if (ptcache_mem_frame_to_disk(pid, pm)==0) {
@@ -3479,9 +3455,9 @@ void BKE_ptcache_toggle_disk_cache(PTCacheID *pid)
}
if (cache->flag & PTCACHE_DISK_CACHE)
- BKE_ptcache_mem_to_disk(pid);
+ BKE_ptcache_mem_to_disk(pid, true);
else
- BKE_ptcache_disk_to_mem(pid);
+ BKE_ptcache_disk_to_mem(pid, true);
cache->flag ^= PTCACHE_DISK_CACHE;
BKE_ptcache_id_clear(pid, PTCACHE_CLEAR_ALL, 0);
diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c
index ff9e1a2b831..b4218378eb8 100644
--- a/source/blender/blenkernel/intern/rigidbody.c
+++ b/source/blender/blenkernel/intern/rigidbody.c
@@ -1238,7 +1238,7 @@ static void rigidbody_update_sim_ob(Scene *scene, RigidBodyWorld *rbw, Object *o
ListBase *effectors;
/* get effectors present in the group specified by effector_weights */
- effectors = pdInitEffectors(scene, ob, NULL, effector_weights, true);
+ effectors = pdInitEffectors(scene, ob, NULL, effector_weights);
if (effectors) {
float eff_force[3] = {0.0f, 0.0f, 0.0f};
float eff_loc[3], eff_vel[3];
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 28c94c889a2..114a63bed48 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -64,6 +64,7 @@
#include "BKE_animsys.h"
#include "BKE_action.h"
#include "BKE_armature.h"
+#include "BKE_cache_library.h"
#include "BKE_colortools.h"
#include "BKE_depsgraph.h"
#include "BKE_editmesh.h"
@@ -1840,6 +1841,9 @@ void BKE_scene_update_for_newframe_ex(EvaluationContext *eval_ctx, Main *bmain,
/* clear animation overrides */
/* XXX TODO... */
+ /* tag cached objects */
+ BKE_cache_library_dag_recalc_tag(eval_ctx, bmain);
+
for (sce_iter = sce; sce_iter; sce_iter = sce_iter->set)
DAG_scene_relations_update(bmain, sce_iter);
@@ -1925,6 +1929,45 @@ void BKE_scene_update_for_newframe_ex(EvaluationContext *eval_ctx, Main *bmain,
#endif
}
+void BKE_scene_update_group_for_newframe(EvaluationContext *eval_ctx,
+ Main *bmain,
+ Scene *scene,
+ Group *group,
+ unsigned int lay)
+{
+ float ctime = BKE_scene_frame_get(scene);
+ Scene *sce_iter;
+
+ /* Step 1: Preparation, same as in regular frame update. */
+ BKE_image_update_frame(bmain, scene->r.cfra);
+ scene_rebuild_rbw_recursive(scene, ctime);
+ BKE_cache_library_dag_recalc_tag(eval_ctx, bmain);
+ for (sce_iter = scene; sce_iter; sce_iter = sce_iter->set) {
+ DAG_scene_relations_update(bmain, sce_iter);
+ }
+
+ /* Step 2: Tag objects which we need to update. */
+ DAG_ids_flush_tagged(bmain);
+ DAG_scene_update_group_flags(bmain, scene, group, lay, true, false);
+
+ /* Step 3: Update animation. */
+#ifdef POSE_ANIMATION_WORKAROUND
+ scene_armature_depsgraph_workaround(bmain);
+#endif
+ BKE_animsys_evaluate_all_animation(bmain, scene, ctime);
+
+ /* Step 4: Actual evaluation. */
+ BKE_main_id_tag_idcode(bmain, ID_MA, false);
+ BKE_main_id_tag_idcode(bmain, ID_LA, false);
+ scene_do_rb_simulation_recursive(scene, ctime);
+ scene_update_tagged_recursive(eval_ctx, bmain, scene, scene);
+ scene_depsgraph_hack(eval_ctx, scene, scene);
+
+ /* Step 5: Cleanup after evaluaiton. */
+ DAG_ids_check_recalc(bmain, scene, true);
+ DAG_ids_clear_recalc(bmain);
+}
+
/* return default layer, also used to patch old files */
SceneRenderLayer *BKE_scene_add_render_layer(Scene *sce, const char *name)
{
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index 4125a35cb33..83aac79ae0b 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -53,6 +53,9 @@
#include "BKE_idprop.h"
#include "BKE_screen.h"
+#include "WM_api.h"
+#include "WM_types.h"
+
/* ************ Spacetype/regiontype handling ************** */
/* keep global; this has to be accessible outside of windowmanager */
@@ -179,6 +182,7 @@ ARegion *BKE_area_region_copy(SpaceType *st, ARegion *ar)
BLI_listbase_clear(&newar->panels_category);
BLI_listbase_clear(&newar->panels_category_active);
BLI_listbase_clear(&newar->ui_lists);
+ BLI_listbase_clear(&newar->widgetmaps);
newar->swinid = 0;
/* use optional regiondata callback */
@@ -290,6 +294,7 @@ void BKE_spacedata_id_unref(struct SpaceLink *sl, const struct ID *id)
void BKE_area_region_free(SpaceType *st, ARegion *ar)
{
uiList *uilst;
+ struct wmWidgetMap *wmap, *wmap_tmp;
if (st) {
ARegionType *art = BKE_regiontype_from_id(st, ar->regiontype);
@@ -326,6 +331,12 @@ void BKE_area_region_free(SpaceType *st, ARegion *ar)
MEM_freeN(uilst->properties);
}
}
+
+ for (wmap = ar->widgetmaps.first; wmap; wmap = wmap_tmp) {
+ wmap_tmp = wmap->next;
+ WM_widgetmap_delete(wmap);
+ }
+ BLI_listbase_clear(&ar->widgetmaps);
BLI_freelistN(&ar->ui_lists);
BLI_freelistN(&ar->ui_previews);
BLI_freelistN(&ar->panels_category);
diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c
index abfc858fd03..69ba7618981 100644
--- a/source/blender/blenkernel/intern/seqcache.c
+++ b/source/blender/blenkernel/intern/seqcache.c
@@ -27,8 +27,10 @@
*/
#include <stddef.h>
+#include <stdlib.h>
#include "BLI_sys_types.h" /* for intptr_t */
+#include "BKE_global.h"
#include "MEM_guardedalloc.h"
@@ -121,6 +123,36 @@ static bool seqcache_hashcmp(const void *a_, const void *b_)
seq_cmp_render_data(&a->context, &b->context));
}
+typedef struct {
+ int framenr;
+} SeqCachePriorityData;
+
+static void *moviecache_getprioritydata(void *key_v)
+{
+ SeqCacheKey *key = (SeqCacheKey *) key_v;
+ SeqCachePriorityData *priority_data;
+
+ priority_data = MEM_callocN(sizeof(*priority_data), "movie cache clip priority data");
+ priority_data->framenr = key->cfra;
+
+ return priority_data;
+}
+
+static int moviecache_getitempriority(void *last_userkey_v, void *priority_data_v)
+{
+ SeqCacheKey *last_userkey = (SeqCacheKey *) last_userkey_v;
+ SeqCachePriorityData *priority_data = (SeqCachePriorityData *) priority_data_v;
+
+ return -abs(last_userkey->cfra - priority_data->framenr);
+}
+
+static void moviecache_prioritydeleter(void *priority_data_v)
+{
+ SeqCachePriorityData *priority_data = (SeqCachePriorityData *) priority_data_v;
+
+ MEM_freeN(priority_data);
+}
+
void BKE_sequencer_cache_destruct(void)
{
if (moviecache)
@@ -134,6 +166,8 @@ void BKE_sequencer_cache_cleanup(void)
if (moviecache) {
IMB_moviecache_free(moviecache);
moviecache = IMB_moviecache_create("seqcache", sizeof(SeqCacheKey), seqcache_hashhash, seqcache_hashcmp);
+ IMB_moviecache_set_priority_callback(moviecache, moviecache_getprioritydata, moviecache_getitempriority,
+ moviecache_prioritydeleter);
}
BKE_sequencer_preprocessed_cache_cleanup();
@@ -173,12 +207,14 @@ void BKE_sequencer_cache_put(const SeqRenderData *context, Sequence *seq, float
{
SeqCacheKey key;
- if (i == NULL || context->skip_cache) {
+ if (i == NULL || context->skip_cache || G.debug_value == 314) {
return;
}
if (!moviecache) {
moviecache = IMB_moviecache_create("seqcache", sizeof(SeqCacheKey), seqcache_hashhash, seqcache_hashcmp);
+ IMB_moviecache_set_priority_callback(moviecache, moviecache_getprioritydata, moviecache_getitempriority,
+ moviecache_prioritydeleter);
}
key.seq = seq;
@@ -219,7 +255,7 @@ ImBuf *BKE_sequencer_preprocessed_cache_get(const SeqRenderData *context, Sequen
{
SeqPreprocessCacheElem *elem;
- if (!preprocess_cache)
+ if (!preprocess_cache || G.debug_value == 314)
return NULL;
if (preprocess_cache->cfra != cfra)
diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c
index 19124ddc549..c9af73ce4a9 100644
--- a/source/blender/blenkernel/intern/sequencer.c
+++ b/source/blender/blenkernel/intern/sequencer.c
@@ -1135,6 +1135,33 @@ const char *BKE_sequence_give_name(Sequence *seq)
return name;
}
+ListBase *BKE_sequence_seqbase_get(Sequence *seq, int *r_offset)
+{
+ ListBase *seqbase = NULL;
+
+ switch (seq->type) {
+ case SEQ_TYPE_META:
+ {
+ seqbase = &seq->seqbase;
+ *r_offset = seq->start;
+ break;
+ }
+ case SEQ_TYPE_SCENE:
+ {
+ if (seq->flag & SEQ_SCENE_STRIPS) {
+ Editing *ed = BKE_sequencer_editing_get(seq->scene, false);
+ if (ed) {
+ seqbase = &ed->seqbase;
+ *r_offset = seq->scene->r.sfra;
+ }
+ }
+ break;
+ }
+ }
+
+ return seqbase;
+}
+
/*********************** DO THE SEQUENCE *************************/
static void make_black_ibuf(ImBuf *ibuf)
@@ -2466,12 +2493,14 @@ static ImBuf *input_preprocess(const SeqRenderData *context, Sequence *seq, floa
multibuf(ibuf, mul);
}
- if (ibuf->x != context->rectx || ibuf->y != context->recty) {
- if (scene->r.mode & R_OSA) {
- IMB_scaleImBuf(ibuf, (short)context->rectx, (short)context->recty);
- }
- else {
- IMB_scalefastImBuf(ibuf, (short)context->rectx, (short)context->recty);
+ if (!is_proxy_image) {
+ if (ibuf->x != context->rectx || ibuf->y != context->recty) {
+ if (scene->r.mode & R_OSA) {
+ IMB_scaleImBuf(ibuf, (short)context->rectx, (short)context->recty);
+ }
+ else {
+ IMB_scalefastImBuf(ibuf, (short)context->rectx, (short)context->recty);
+ }
}
}
@@ -3284,6 +3313,40 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq
return ibuf;
}
+/**
+ * Used for meta-strips & scenes with #SEQ_SCENE_STRIPS flag set.
+ */
+static ImBuf *do_render_strip_seqbase(
+ const SeqRenderData *context, Sequence *seq, float nr,
+ bool use_preprocess)
+{
+ ImBuf *meta_ibuf = NULL, *ibuf = NULL;
+ ListBase *seqbase = NULL;
+ int offset;
+
+ seqbase = BKE_sequence_seqbase_get(seq, &offset);
+
+ if (seqbase && !BLI_listbase_is_empty(seqbase)) {
+ meta_ibuf = seq_render_strip_stack(
+ context, seqbase,
+ /* scene strips don't have their start taken into account */
+ nr + offset, 0);
+ }
+
+ if (meta_ibuf) {
+ ibuf = meta_ibuf;
+ if (ibuf && use_preprocess) {
+ ImBuf *i = IMB_dupImBuf(ibuf);
+
+ IMB_freeImBuf(ibuf);
+
+ ibuf = i;
+ }
+ }
+
+ return ibuf;
+}
+
static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *seq, float cfra)
{
ImBuf *ibuf = NULL;
@@ -3294,22 +3357,27 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s
switch (type) {
case SEQ_TYPE_META:
{
- ImBuf *meta_ibuf = NULL;
-
- if (seq->seqbase.first)
- meta_ibuf = seq_render_strip_stack(context, &seq->seqbase, seq->start + nr, 0);
-
- if (meta_ibuf) {
- ibuf = meta_ibuf;
- if (ibuf && use_preprocess) {
- ImBuf *i = IMB_dupImBuf(ibuf);
-
- IMB_freeImBuf(ibuf);
+ ibuf = do_render_strip_seqbase(context, seq, nr, use_preprocess);
+ break;
+ }
- ibuf = i;
+ case SEQ_TYPE_SCENE:
+ {
+ if (seq->flag & SEQ_SCENE_STRIPS) {
+ /* TODO, full recursive check */
+ if (context->scene != seq->scene) {
+ ibuf = do_render_strip_seqbase(context, seq, nr, use_preprocess);
}
}
+ else {
+ /* scene can be NULL after deletions */
+ ibuf = seq_render_scene_strip(context, seq, nr, cfra);
+
+ /* Scene strips update all animation, so we need to restore original state.*/
+ BKE_animsys_evaluate_all_animation(context->bmain, context->scene, cfra);
+ copy_to_ibuf_still(context, seq, nr, ibuf);
+ }
break;
}
@@ -3360,18 +3428,6 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s
break;
}
- case SEQ_TYPE_SCENE:
- {
- /* scene can be NULL after deletions */
- ibuf = seq_render_scene_strip(context, seq, nr, cfra);
-
- /* Scene strips update all animation, so we need to restore original state.*/
- BKE_animsys_evaluate_all_animation(context->bmain, context->scene, cfra);
-
- copy_to_ibuf_still(context, seq, nr, ibuf);
- break;
- }
-
case SEQ_TYPE_MOVIECLIP:
{
ibuf = seq_render_movieclip_strip(context, seq, nr);
@@ -3423,6 +3479,7 @@ static ImBuf *seq_render_strip(const SeqRenderData *context, Sequence *seq, floa
ibuf = copy_from_ibuf_still(context, seq, nr);
if (ibuf == NULL) {
+ /* disable caching in that case */
ibuf = BKE_sequencer_preprocessed_cache_get(context, seq, cfra, SEQ_STRIPELEM_IBUF);
if (ibuf == NULL) {
@@ -3659,14 +3716,16 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, ListBase *seq
for (; i < count; i++) {
Sequence *seq = seq_arr[i];
- if (seq_get_early_out_for_blend_mode(seq) == EARLY_DO_EFFECT) {
- ImBuf *ibuf1 = out;
- ImBuf *ibuf2 = seq_render_strip(context, seq, cfra);
+ if (context->preview_render_size >= 100) {
+ if (seq_get_early_out_for_blend_mode(seq) == EARLY_DO_EFFECT) {
+ ImBuf *ibuf1 = out;
+ ImBuf *ibuf2 = seq_render_strip(context, seq, cfra);
- out = seq_render_strip_stack_apply_effect(context, seq, cfra, ibuf1, ibuf2);
+ out = seq_render_strip_stack_apply_effect(context, seq, cfra, ibuf1, ibuf2);
- IMB_freeImBuf(ibuf1);
- IMB_freeImBuf(ibuf2);
+ IMB_freeImBuf(ibuf1);
+ IMB_freeImBuf(ibuf2);
+ }
}
BKE_sequencer_cache_put(context, seq_arr[i], cfra, SEQ_STRIPELEM_IBUF_COMP, out);
diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c
index 28a2c42546d..c8fc991a645 100644
--- a/source/blender/blenkernel/intern/smoke.c
+++ b/source/blender/blenkernel/intern/smoke.c
@@ -91,6 +91,10 @@
#include "smoke_API.h"
+#ifdef WITH_OPENVDB
+# include "openvdb_capi.h"
+#endif
+
#ifdef WITH_SMOKE
#ifdef _WIN32
@@ -153,7 +157,7 @@ struct SmokeModifierData;
#else /* WITH_SMOKE */
/* Stubs to use when smoke is disabled */
-struct WTURBULENCE *smoke_turbulence_init(int *UNUSED(res), int UNUSED(amplify), int UNUSED(noisetype), const char *UNUSED(noisefile_path), int UNUSED(use_fire), int UNUSED(use_colors)) { return NULL; }
+struct WTURBULENCE *smoke_turbulence_init(int *UNUSED(res), int UNUSED(amplify), int UNUSED(noisetype), const char *UNUSED(noisefile_path), int UNUSED(use_fire), int UNUSED(use_colors), int UNUSED(use_sim)) { return NULL; }
//struct FLUID_3D *smoke_init(int *UNUSED(res), float *UNUSED(dx), float *UNUSED(dtdef), int UNUSED(use_heat), int UNUSED(use_fire), int UNUSED(use_colors)) { return NULL; }
void smoke_free(struct FLUID_3D *UNUSED(fluid)) {}
float *smoke_get_density(struct FLUID_3D *UNUSED(fluid)) { return NULL; }
@@ -196,6 +200,8 @@ void smoke_reallocate_highres_fluid(SmokeDomainSettings *sds, float dx, int res[
{
int use_fire = (sds->active_fields & (SM_ACTIVE_HEAT | SM_ACTIVE_FIRE));
int use_colors = (sds->active_fields & SM_ACTIVE_COLORS);
+ int use_sim = !((sds->point_cache[0] != NULL) &&
+ (sds->point_cache[0]->flag & (PTCACHE_BAKED|PTCACHE_DISK_CACHE)) == (PTCACHE_BAKED|PTCACHE_DISK_CACHE));
if (free_old && sds->wt)
smoke_turbulence_free(sds->wt);
@@ -207,7 +213,7 @@ void smoke_reallocate_highres_fluid(SmokeDomainSettings *sds, float dx, int res[
/* smoke_turbulence_init uses non-threadsafe functions from fftw3 lib (like fftw_plan & co). */
BLI_lock_thread(LOCK_FFTW);
- sds->wt = smoke_turbulence_init(res, sds->amplify + 1, sds->noise, BKE_tempdir_session(), use_fire, use_colors);
+ sds->wt = smoke_turbulence_init(res, sds->amplify + 1, sds->noise, BKE_tempdir_session(), use_fire, use_colors, use_sim);
BLI_unlock_thread(LOCK_FFTW);
@@ -347,6 +353,9 @@ static int smokeModifier_init(SmokeModifierData *smd, Object *ob, Scene *scene,
if (!sds->shadow)
sds->shadow = MEM_callocN(sizeof(float) * sds->res[0] * sds->res[1] * sds->res[2], "SmokeDomainShadow");
+ sds->density = NULL;
+ sds->density_high = NULL;
+
return 1;
}
else if ((smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow)
@@ -376,6 +385,8 @@ static void smokeModifier_freeDomain(SmokeModifierData *smd)
{
if (smd->domain)
{
+ OpenVDBCache *cache;
+
if (smd->domain->shadow)
MEM_freeN(smd->domain->shadow);
smd->domain->shadow = NULL;
@@ -396,6 +407,14 @@ static void smokeModifier_freeDomain(SmokeModifierData *smd)
BKE_ptcache_free_list(&(smd->domain->ptcaches[0]));
smd->domain->point_cache[0] = NULL;
+ while ((cache = BLI_pophead(&(smd->domain->vdb_caches)))) {
+#ifdef WITH_OPENVDB
+ OpenVDBWriter_free(cache->writer);
+ OpenVDBReader_free(cache->reader);
+#endif
+ MEM_freeN(cache);
+ }
+
MEM_freeN(smd->domain);
smd->domain = NULL;
}
@@ -564,6 +583,10 @@ void smokeModifier_createType(struct SmokeModifierData *smd)
smd->domain->viewsettings = MOD_SMOKE_VIEW_SHOWBIG;
smd->domain->effector_weights = BKE_add_effector_weights(NULL);
+
+ smd->domain->display_thickness = 1.0f;
+
+ smd->domain->use_openvdb = false;
}
else if (smd->type & MOD_SMOKE_TYPE_FLOW)
{
@@ -1027,6 +1050,7 @@ typedef struct EmissionMap {
float *velocity;
int min[3], max[3], res[3];
int hmin[3], hmax[3], hres[3];
+ float *color;
int total_cells, valid;
} EmissionMap;
@@ -1071,7 +1095,7 @@ static void clampBoundsInDomain(SmokeDomainSettings *sds, int min[3], int max[3]
}
}
-static void em_allocateData(EmissionMap *em, bool use_velocity, int hires_mul)
+static void em_allocateData(EmissionMap *em, bool use_velocity, bool use_color, int hires_mul)
{
int i, res[3];
@@ -1087,6 +1111,8 @@ static void em_allocateData(EmissionMap *em, bool use_velocity, int hires_mul)
em->influence = MEM_callocN(sizeof(float) * em->total_cells, "smoke_flow_influence");
if (use_velocity)
em->velocity = MEM_callocN(sizeof(float) * em->total_cells * 3, "smoke_flow_velocity");
+ if (use_color)
+ em->color = MEM_callocN(sizeof(float) * em->total_cells * 3, "smoke_flow_color");
/* allocate high resolution map if required */
if (hires_mul > 1) {
@@ -1111,6 +1137,8 @@ static void em_freeData(EmissionMap *em)
MEM_freeN(em->influence_high);
if (em->velocity)
MEM_freeN(em->velocity);
+ if (em->color)
+ MEM_freeN(em->color);
}
static void em_combineMaps(EmissionMap *output, EmissionMap *em2, int hires_multiplier, int additive, float sample_size)
@@ -1133,7 +1161,7 @@ static void em_combineMaps(EmissionMap *output, EmissionMap *em2, int hires_mult
}
}
/* allocate output map */
- em_allocateData(output, (em1.velocity || em2->velocity), hires_multiplier);
+ em_allocateData(output, (em1.velocity || em2->velocity), (em1.color || em2->color), hires_multiplier);
/* base resolution inputs */
for (x = output->min[0]; x < output->max[0]; x++)
@@ -1152,6 +1180,9 @@ static void em_combineMaps(EmissionMap *output, EmissionMap *em2, int hires_mult
if (output->velocity && em1.velocity) {
copy_v3_v3(&output->velocity[index_out * 3], &em1.velocity[index_in * 3]);
}
+ if (output->color && em1.color) {
+ copy_v3_v3(&output->color[index_out * 3], &em1.color[index_in * 3]);
+ }
}
/* apply second input if in range */
@@ -1173,6 +1204,13 @@ static void em_combineMaps(EmissionMap *output, EmissionMap *em2, int hires_mult
output->velocity[index_out * 3 + 1] = ADD_IF_LOWER(output->velocity[index_out * 3 + 1], em2->velocity[index_in * 3 + 1]);
output->velocity[index_out * 3 + 2] = ADD_IF_LOWER(output->velocity[index_out * 3 + 2], em2->velocity[index_in * 3 + 2]);
}
+ if (output->color && em2->color) {
+ /* mix by influence */
+ float f1 = output->influence[index_out];
+ float f2 = em2->influence[index_in];
+ float f = f1 + f2 > 0.0f ? f1 / (f1 + f2) : 0.5f;
+ interp_v3_v3v3(&output->color[index_out * 3], &output->color[index_out * 3], &em2->color[index_in * 3], f);
+ }
}
} // low res loop
@@ -1224,6 +1262,7 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke
ParticleSystem *psys = sfs->psys;
float *particle_pos;
float *particle_vel;
+ float *particle_texcol;
int totpart = psys->totpart, totchild;
int p = 0;
int valid_particles = 0;
@@ -1270,6 +1309,11 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke
bounds_margin = (int)ceil(solid + smooth);
}
+ if (sfs->flags & MOD_SMOKE_FLOW_USE_PART_TEXCOLOR)
+ particle_texcol = MEM_callocN(sizeof(float) * (totpart + totchild) * 3, "smoke_flow_particles");
+ else
+ particle_texcol = NULL;
+
/* calculate local position for each particle */
for (p = 0; p < totpart + totchild; p++)
{
@@ -1303,6 +1347,16 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke
BLI_kdtree_insert(tree, valid_particles, pos);
}
+ if (particle_texcol) {
+ if (p < totpart) {
+ ParticleTexture ptex;
+ psys_get_texture(&sim, &psys->particles[p], &ptex, PAMAP_COLOR, state.time);
+ copy_v3_v3(&particle_texcol[valid_particles * 3], ptex.color);
+ }
+ else
+ zero_v3(&particle_texcol[valid_particles * 3]);
+ }
+
/* calculate emission map bounds */
em_boundInsert(em, pos);
valid_particles++;
@@ -1310,7 +1364,7 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke
/* set emission map */
clampBoundsInDomain(sds, em->min, em->max, NULL, NULL, bounds_margin, dt);
- em_allocateData(em, sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY, hires_multiplier);
+ em_allocateData(em, sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY, sfs->flags & MOD_SMOKE_FLOW_USE_PART_TEXCOLOR, hires_multiplier);
if (!(sfs->flags & MOD_SMOKE_FLOW_USE_PART_SIZE)) {
for (p = 0; p < valid_particles; p++)
@@ -1342,6 +1396,9 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke
{
VECADDFAC(&em->velocity[index * 3], &em->velocity[index * 3], &particle_vel[p * 3], sfs->vel_multi);
}
+ if (particle_texcol && em->color) {
+ copy_v3_v3(&em->color[index * 3], &particle_texcol[p * 3]);
+ }
} // particles loop
}
else if (valid_particles > 0) { // MOD_SMOKE_FLOW_USE_PART_SIZE
@@ -1391,6 +1448,9 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke
{
VECADDFAC(&em->velocity[index * 3], &em->velocity[index * 3], &particle_vel[nearest.index * 3], sfs->vel_multi);
}
+ if (particle_texcol && em->color) {
+ copy_v3_v3(&em->color[index * 3], &particle_texcol[nearest.index * 3]);
+ }
}
}
@@ -1428,6 +1488,8 @@ static void emit_from_particles(Object *flow_ob, SmokeDomainSettings *sds, Smoke
MEM_freeN(particle_pos);
if (particle_vel)
MEM_freeN(particle_vel);
+ if (particle_texcol)
+ MEM_freeN(particle_texcol);
}
}
@@ -1641,7 +1703,7 @@ static void emit_from_derivedmesh(Object *flow_ob, SmokeDomainSettings *sds, Smo
/* set emission map */
clampBoundsInDomain(sds, em->min, em->max, NULL, NULL, (int)ceil(sfs->surface_distance), dt);
- em_allocateData(em, sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY, hires_multiplier);
+ em_allocateData(em, sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY, false, hires_multiplier);
/* setup loop bounds */
for (i = 0; i < 3; i++) {
@@ -1983,7 +2045,7 @@ BLI_INLINE void apply_outflow_fields(int index, float *density, float *heat, flo
}
}
-BLI_INLINE void apply_inflow_fields(SmokeFlowSettings *sfs, float emission_value, int index, float *density, float *heat, float *fuel, float *react, float *color_r, float *color_g, float *color_b)
+BLI_INLINE void apply_inflow_fields(SmokeFlowSettings *sfs, float emission_value, const float *color_value, int index, float *density, float *heat, float *fuel, float *react, float *color_r, float *color_g, float *color_b)
{
int absolute_flow = (sfs->flags & MOD_SMOKE_FLOW_ABSOLUTE);
float dens_old = density[index];
@@ -2020,9 +2082,10 @@ BLI_INLINE void apply_inflow_fields(SmokeFlowSettings *sfs, float emission_value
/* set color */
if (color_r && dens_flow) {
float total_dens = density[index] / (dens_old + dens_flow);
- color_r[index] = (color_r[index] + sfs->color[0] * dens_flow) * total_dens;
- color_g[index] = (color_g[index] + sfs->color[1] * dens_flow) * total_dens;
- color_b[index] = (color_b[index] + sfs->color[2] * dens_flow) * total_dens;
+ const float *color = (color_value ? color_value : sfs->color);
+ color_r[index] = (color_r[index] + color[0] * dens_flow) * total_dens;
+ color_g[index] = (color_g[index] + color[1] * dens_flow) * total_dens;
+ color_b[index] = (color_b[index] + color[2] * dens_flow) * total_dens;
}
/* set fire reaction coordinate */
@@ -2169,7 +2232,10 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd
}
/* activate color field if flows add smoke with varying colors */
if (sfs->type != MOD_SMOKE_FLOW_TYPE_FIRE && sfs->density) {
- if (!(active_fields & SM_ACTIVE_COLOR_SET)) {
+ if (sfs->flags & MOD_SMOKE_FLOW_USE_PART_TEXCOLOR) {
+ active_fields |= SM_ACTIVE_COLORS;
+ }
+ else if (!(active_fields & SM_ACTIVE_COLOR_SET)) {
copy_v3_v3(sds->active_color, sfs->color);
active_fields |= SM_ACTIVE_COLOR_SET;
}
@@ -2250,6 +2316,7 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd
float *velocity_map = em->velocity;
float *emission_map = em->influence;
float *emission_map_high = em->influence_high;
+ float *color_map = em->color;
int ii, jj, kk, gx, gy, gz, ex, ey, ez, dx, dy, dz, block_size;
size_t e_index, d_index, index_big;
@@ -2277,7 +2344,7 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd
apply_outflow_fields(d_index, density, heat, fuel, react, color_r, color_g, color_b);
}
else { // inflow
- apply_inflow_fields(sfs, emission_map[e_index], d_index, density, heat, fuel, react, color_r, color_g, color_b);
+ apply_inflow_fields(sfs, emission_map[e_index], color_map ? &color_map[e_index * 3] : NULL, d_index, density, heat, fuel, react, color_r, color_g, color_b);
/* initial velocity */
if (sfs->flags & MOD_SMOKE_FLOW_INITVELOCITY) {
@@ -2291,6 +2358,7 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd
if (bigdensity) {
// neighbor cell emission densities (for high resolution smoke smooth interpolation)
float c000, c001, c010, c011, c100, c101, c110, c111;
+ float col000[3], col001[3], col010[3], col011[3], col100[3], col101[3], col110[3], col111[3];
smoke_turbulence_get_res(sds->wt, bigres);
block_size = sds->amplify + 1; // high res block size
@@ -2305,14 +2373,67 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd
c110 = (ez > 0) ? emission_map[smoke_get_index(ex, em->res[0], ey, em->res[1], ez - 1)] : 0;
c111 = emission_map[smoke_get_index(ex, em->res[0], ey, em->res[1], ez)]; // this cell
+ if (color_map) {
+ static const float Z[3] = {0.0f, 0.0f, 0.0f};
+
+ copy_v3_v3(col000, (ex > 0 && ey > 0 && ez > 0) ? &color_map[smoke_get_index(ex - 1, em->res[0], ey - 1, em->res[1], ez - 1) * 3] : Z);
+ copy_v3_v3(col001, (ex > 0 && ey > 0) ? &color_map[smoke_get_index(ex - 1, em->res[0], ey - 1, em->res[1], ez) * 3] : Z);
+ copy_v3_v3(col010, (ex > 0 && ez > 0) ? &color_map[smoke_get_index(ex - 1, em->res[0], ey, em->res[1], ez - 1) * 3] : Z);
+ copy_v3_v3(col011, (ex > 0) ? &color_map[smoke_get_index(ex - 1, em->res[0], ey, em->res[1], ez) * 3] : Z);
+
+ copy_v3_v3(col100, (ey > 0 && ez > 0) ? &color_map[smoke_get_index(ex, em->res[0], ey - 1, em->res[1], ez - 1) * 3] : Z);
+ copy_v3_v3(col101, (ey > 0) ? &color_map[smoke_get_index(ex, em->res[0], ey - 1, em->res[1], ez) * 3] : Z);
+ copy_v3_v3(col110, (ez > 0) ? &color_map[smoke_get_index(ex, em->res[0], ey, em->res[1], ez - 1) * 3] : Z);
+ copy_v3_v3(col111, &color_map[smoke_get_index(ex, em->res[0], ey, em->res[1], ez) * 3]); // this cell
+ }
+ else {
+ zero_v3(col000);
+ zero_v3(col001);
+ zero_v3(col010);
+ zero_v3(col011);
+ zero_v3(col100);
+ zero_v3(col101);
+ zero_v3(col110);
+ zero_v3(col111);
+ }
+
for (ii = 0; ii < block_size; ii++)
for (jj = 0; jj < block_size; jj++)
for (kk = 0; kk < block_size; kk++)
{
- float fx, fy, fz, interpolated_value;
+ float col[3], interpolated_value, *interpolated_color;
int shift_x = 0, shift_y = 0, shift_z = 0;
+ float w[2][2][2];
+ bool do_interpolation = ((!((sds->highres_sampling == SM_HRES_FULLSAMPLE) && emission_map_high) &&
+ !(sds->highres_sampling == SM_HRES_NEAREST)) ||
+ color_map);
+
+ /* weights are used for both density and color,
+ * so calculate them once in advance
+ */
+ if (do_interpolation) {
+ /* get relative block position
+ * for interpolation smoothing */
+ float fx = (float)ii / block_size + 0.5f / block_size;
+ float fy = (float)jj / block_size + 0.5f / block_size;
+ float fz = (float)kk / block_size + 0.5f / block_size;
+ float mx = 1.0f - fx;
+ float my = 1.0f - fy;
+ float mz = 1.0f - fz;
+
+ w[0][0][0] = mx * my * mz;
+ w[1][0][0] = fx * my * mz;
+ w[0][1][0] = mx * fy * mz;
+ w[1][1][0] = fx * fy * mz;
+ w[0][0][1] = mx * my * fz;
+ w[1][0][1] = fx * my * fz;
+ w[0][1][1] = mx * fy * fz;
+ w[1][1][1] = fx * fy * fz;
+ }
+ else
+ memset(w, 0, sizeof(float) * 8);
/* Use full sample emission map if enabled and available */
if ((sds->highres_sampling == SM_HRES_FULLSAMPLE) && emission_map_high) {
@@ -2326,22 +2447,17 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd
/* Fall back to interpolated */
else
{
- /* get relative block position
- * for interpolation smoothing */
- fx = (float)ii / block_size + 0.5f / block_size;
- fy = (float)jj / block_size + 0.5f / block_size;
- fz = (float)kk / block_size + 0.5f / block_size;
/* calculate trilinear interpolation */
- interpolated_value = c000 * (1 - fx) * (1 - fy) * (1 - fz) +
- c100 * fx * (1 - fy) * (1 - fz) +
- c010 * (1 - fx) * fy * (1 - fz) +
- c001 * (1 - fx) * (1 - fy) * fz +
- c101 * fx * (1 - fy) * fz +
- c011 * (1 - fx) * fy * fz +
- c110 * fx * fy * (1 - fz) +
- c111 * fx * fy * fz;
-
+ interpolated_value = 0.0f;
+ interpolated_value += c000 * w[0][0][0];
+ interpolated_value += c100 * w[1][0][0];
+ interpolated_value += c010 * w[0][1][0];
+ interpolated_value += c110 * w[1][1][0];
+ interpolated_value += c001 * w[0][0][1];
+ interpolated_value += c101 * w[1][0][1];
+ interpolated_value += c011 * w[0][1][1];
+ interpolated_value += c111 * w[1][1][1];
/* add some contrast / sharpness
* depending on hi-res block size */
@@ -2355,6 +2471,48 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd
shift_y = (dy < 1) ? 0 : block_size / 2;
shift_z = (dz < 1) ? 0 : block_size / 2;
}
+
+ if (color_map) {
+ float wcol[2][2][2], totw, invtotw;
+
+ /* colors are zero (black) in zero-emission cells,
+ * so use weighted average based on density to avoid artifacts!
+ */
+ wcol[0][0][0] = w[0][0][0] * c000;
+ wcol[1][0][0] = w[1][0][0] * c100;
+ wcol[0][1][0] = w[0][1][0] * c010;
+ wcol[1][1][0] = w[1][1][0] * c110;
+ wcol[0][0][1] = w[0][0][1] * c001;
+ wcol[1][0][1] = w[1][0][1] * c101;
+ wcol[0][1][1] = w[0][1][1] * c011;
+ wcol[1][1][1] = w[1][1][1] * c111;
+
+ totw = wcol[0][0][0] + wcol[1][0][0] + wcol[0][1][0] + wcol[1][1][0] +
+ wcol[0][0][1] + wcol[1][0][1] + wcol[0][1][1] + wcol[1][1][1];
+ invtotw = totw > 0.0f ? 1.0f/totw : 0.0f;
+ wcol[0][0][0] *= invtotw;
+ wcol[1][0][0] *= invtotw;
+ wcol[0][1][0] *= invtotw;
+ wcol[1][1][0] *= invtotw;
+ wcol[0][0][1] *= invtotw;
+ wcol[1][0][1] *= invtotw;
+ wcol[0][1][1] *= invtotw;
+ wcol[1][1][1] *= invtotw;
+
+ zero_v3(col);
+ madd_v3_v3fl(col, col000, wcol[0][0][0]);
+ madd_v3_v3fl(col, col100, wcol[1][0][0]);
+ madd_v3_v3fl(col, col010, wcol[0][1][0]);
+ madd_v3_v3fl(col, col110, wcol[1][1][0]);
+ madd_v3_v3fl(col, col001, wcol[0][0][1]);
+ madd_v3_v3fl(col, col101, wcol[1][0][1]);
+ madd_v3_v3fl(col, col011, wcol[0][1][1]);
+ madd_v3_v3fl(col, col111, wcol[1][1][1]);
+
+ interpolated_color = col;
+ }
+ else
+ interpolated_color = NULL;
/* get shifted index for current high resolution block */
index_big = smoke_get_index(block_size * dx + ii - shift_x, bigres[0], block_size * dy + jj - shift_y, bigres[1], block_size * dz + kk - shift_z);
@@ -2365,7 +2523,7 @@ static void update_flowsfluids(Scene *scene, Object *ob, SmokeDomainSettings *sd
}
}
else { // inflow
- apply_inflow_fields(sfs, interpolated_value, index_big, bigdensity, NULL, bigfuel, bigreact, bigcolor_r, bigcolor_g, bigcolor_b);
+ apply_inflow_fields(sfs, interpolated_value, interpolated_color, index_big, bigdensity, NULL, bigfuel, bigreact, bigcolor_r, bigcolor_g, bigcolor_b);
}
} // hires loop
} // bigdensity
@@ -2389,7 +2547,7 @@ static void update_effectors(Scene *scene, Object *ob, SmokeDomainSettings *sds,
ListBase *effectors;
/* make sure smoke flow influence is 0.0f */
sds->effector_weights->weight[PFIELD_SMOKEFLOW] = 0.0f;
- effectors = pdInitEffectors(scene, ob, NULL, sds->effector_weights, true);
+ effectors = pdInitEffectors(scene, ob, NULL, sds->effector_weights);
if (effectors)
{
@@ -2667,11 +2825,12 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object *
{
SmokeDomainSettings *sds = smd->domain;
PointCache *cache = NULL;
+ OpenVDBCache *vdb_cache = NULL;
PTCacheID pid;
int startframe, endframe, framenr;
float timescale;
- framenr = scene->r.cfra;
+ framenr = scene->r.cfra - sds->point_cache_offset;
//printf("time: %d\n", scene->r.cfra);
@@ -2703,8 +2862,18 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object *
return;
}
+ /* try to read from openvdb cache */
+// vdb_cache = BKE_openvdb_get_current_cache(sds);
+// if (sds->use_openvdb && vdb_cache) {
+// if (vdb_cache->flags & VDB_CACHE_SMOKE_EXPORTED) {
+// smokeModifier_OpenVDB_import(smd, scene, ob, vdb_cache);
+// smd->time = framenr;
+// return;
+// }
+// }
+
/* try to read from cache */
- if (BKE_ptcache_read(&pid, (float)framenr) == PTCACHE_READ_EXACT) {
+ if (/*!sds->use_openvdb && */(BKE_ptcache_read(&pid, (float)framenr) == PTCACHE_READ_EXACT)) {
BKE_ptcache_validate(cache, framenr);
smd->time = framenr;
return;
@@ -2752,6 +2921,7 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object *
if (sds->wt)
{
+ smoke_ensure_simulation(sds->fluid, sds->wt);
smoke_turbulence_step(sds->wt, sds->fluid);
}
@@ -3025,4 +3195,466 @@ int smoke_get_data_flags(SmokeDomainSettings *sds)
return flags;
}
+#ifdef WITH_OPENVDB
+
+/* Construct matrices which represent the fluid object, for low and high res:
+ * vs 0 0 0
+ * 0 vs 0 0
+ * 0 0 vs 0
+ * px py pz 1
+ * with vs = voxel size, and px, py, pz, the min position of the domain's
+ * bounding box.
+ */
+static void compute_fluid_matrices(SmokeDomainSettings *sds)
+{
+ float bbox_min[3];
+
+ copy_v3_v3(bbox_min, sds->p0);
+
+ if (sds->flags & MOD_SMOKE_ADAPTIVE_DOMAIN) {
+ bbox_min[0] += (sds->cell_size[0] * (float)sds->res_min[0]);
+ bbox_min[1] += (sds->cell_size[1] * (float)sds->res_min[1]);
+ bbox_min[2] += (sds->cell_size[2] * (float)sds->res_min[2]);
+ add_v3_v3(bbox_min, sds->obj_shift_f);
+ }
+
+ /* construct low res matrix */
+ size_to_mat4(sds->fluidmat, sds->cell_size);
+ copy_v3_v3(sds->fluidmat[3], bbox_min);
+
+ /* The smoke simulator stores voxels cell-centered, whilst VDB is node
+ * centered, so we offset the matrix by half a voxel to compensate. */
+ madd_v3_v3fl(sds->fluidmat[3], sds->cell_size, 0.5f);
+
+ mul_m4_m4m4(sds->fluidmat, sds->obmat, sds->fluidmat);
+
+ if (sds->wt) {
+ float voxel_size_high[3];
+ /* construct high res matrix */
+ mul_v3_v3fl(voxel_size_high, sds->cell_size, 1.0f / (float)(sds->amplify + 1));
+ size_to_mat4(sds->fluidmat_wt, voxel_size_high);
+ copy_v3_v3(sds->fluidmat_wt[3], bbox_min);
+
+ /* Same here, add half a voxel to adjust the position of the fluid. */
+ madd_v3_v3fl(sds->fluidmat_wt[3], voxel_size_high, 0.5f);
+
+ mul_m4_m4m4(sds->fluidmat_wt, sds->obmat, sds->fluidmat_wt);
+ }
+}
+
+static void OpenVDB_read_fluid_settings(SmokeDomainSettings *sds, struct OpenVDBReader *reader)
+{
+ OpenVDBReader_get_meta_v3_int(reader, "min_resolution", sds->res_min);
+ OpenVDBReader_get_meta_v3_int(reader, "max_resolution", sds->res_max);
+ OpenVDBReader_get_meta_v3_int(reader, "base_resolution", sds->base_res);
+ OpenVDBReader_get_meta_fl(reader, "delta_x", &sds->dx);
+ OpenVDBReader_get_meta_v3(reader, "min_bbox", sds->p0);
+ OpenVDBReader_get_meta_v3(reader, "max_bbox", sds->p1);
+ OpenVDBReader_get_meta_v3(reader, "dp0", sds->dp0);
+ OpenVDBReader_get_meta_v3_int(reader, "shift", sds->shift);
+ OpenVDBReader_get_meta_v3(reader, "obj_shift_f", sds->obj_shift_f);
+ OpenVDBReader_get_meta_v3(reader, "active_color", sds->active_color);
+ OpenVDBReader_get_meta_mat4(reader, "obmat", sds->obmat);
+}
+
+static void OpenVDB_write_fluid_settings(SmokeDomainSettings *sds, struct OpenVDBWriter *writer)
+{
+ OpenVDBWriter_add_meta_int(writer, "active_fields", sds->active_fields);
+ OpenVDBWriter_add_meta_v3_int(writer, "resolution", sds->res);
+ OpenVDBWriter_add_meta_v3_int(writer, "min_resolution", sds->res_min);
+ OpenVDBWriter_add_meta_v3_int(writer, "max_resolution", sds->res_max);
+ OpenVDBWriter_add_meta_v3_int(writer, "base_resolution", sds->base_res);
+ OpenVDBWriter_add_meta_fl(writer, "delta_x", sds->dx);
+ OpenVDBWriter_add_meta_v3(writer, "min_bbox", sds->p0);
+ OpenVDBWriter_add_meta_v3(writer, "max_bbox", sds->p1);
+ OpenVDBWriter_add_meta_v3(writer, "dp0", sds->dp0);
+ OpenVDBWriter_add_meta_v3_int(writer, "shift", sds->shift);
+ OpenVDBWriter_add_meta_v3(writer, "obj_shift_f", sds->obj_shift_f);
+ OpenVDBWriter_add_meta_v3(writer, "active_color", sds->active_color);
+ OpenVDBWriter_add_meta_mat4(writer, "obmat", sds->obmat);
+}
+
+void BKE_openvdb_cache_filename(char *r_filename, const char *path, const char *fname, const char *relbase, int frame)
+{
+ char cachepath[FILE_MAX];
+
+ BLI_strncpy(cachepath, path, FILE_MAX - 10);
+ BLI_path_abs(cachepath, relbase);
+
+ if (!BLI_exists(cachepath)) {
+ BLI_dir_create_recursive(cachepath);
+ }
+
+ BLI_join_dirfile(r_filename, sizeof(cachepath), cachepath, fname);
+ BLI_path_suffix(r_filename, FILE_MAX, "", "_");
+ BLI_path_frame(r_filename, frame, 4);
+ BLI_ensure_extension(r_filename, FILE_MAX, ".vdb");
+}
+
+static void OpenVDB_export_smoke(SmokeDomainSettings *sds, struct OpenVDBWriter *writer)
+{
+ int fluid_fields = smoke_get_data_flags(sds);
+ struct OpenVDBFloatGrid *clip_grid = NULL;
+
+ compute_fluid_matrices(sds);
+
+ OpenVDBWriter_add_meta_int(writer, "fluid_fields", fluid_fields);
+
+ if (sds->wt) {
+ struct OpenVDBFloatGrid *wt_density_grid;
+ float *dens, *react, *fuel, *flame, *tcu, *tcv, *tcw, *r, *g, *b;
+
+ smoke_turbulence_export(sds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw);
+
+ wt_density_grid = OpenVDB_export_grid_fl(writer, "Density High", dens, sds->res_wt, sds->fluidmat_wt, NULL);
+ clip_grid = wt_density_grid;
+
+ if (fluid_fields & SM_ACTIVE_FIRE) {
+ OpenVDB_export_grid_fl(writer, "Flame High", flame, sds->res_wt, sds->fluidmat_wt, wt_density_grid);
+ OpenVDB_export_grid_fl(writer, "Fuel High", fuel, sds->res_wt, sds->fluidmat_wt, wt_density_grid);
+ OpenVDB_export_grid_fl(writer, "React High", react, sds->res_wt, sds->fluidmat_wt, wt_density_grid);
+ }
+
+ if (fluid_fields & SM_ACTIVE_COLORS) {
+ OpenVDB_export_grid_vec(writer, "Color High", r, g, b, sds->res_wt, sds->fluidmat_wt, VEC_INVARIANT, true, wt_density_grid);
+ }
+
+ OpenVDB_export_grid_vec(writer, "Texture Coordinates", tcu, tcv, tcw, sds->res, sds->fluidmat, VEC_INVARIANT, false, wt_density_grid);
+ }
+
+ if (sds->fluid) {
+ struct OpenVDBFloatGrid *density_grid;
+ float dt, dx, *dens, *react, *fuel, *flame, *heat, *heatold, *vx, *vy, *vz, *r, *g, *b;
+ unsigned char *obstacles;
+
+ smoke_export(sds->fluid, &dt, &dx, &dens, &react, &flame, &fuel, &heat,
+ &heatold, &vx, &vy, &vz, &r, &g, &b, &obstacles);
+
+ OpenVDBWriter_add_meta_fl(writer, "dx", dx);
+ OpenVDBWriter_add_meta_fl(writer, "dt", dt);
+
+ density_grid = OpenVDB_export_grid_fl(writer, "Density", dens, sds->res, sds->fluidmat, NULL);
+ clip_grid = sds->wt ? clip_grid : density_grid;
+
+ OpenVDB_export_grid_fl(writer, "Shadow", sds->shadow, sds->res, sds->fluidmat, density_grid);
+
+ if (fluid_fields & SM_ACTIVE_HEAT) {
+ OpenVDB_export_grid_fl(writer, "Heat", heat, sds->res, sds->fluidmat, clip_grid);
+ OpenVDB_export_grid_fl(writer, "Heat Old", heatold, sds->res, sds->fluidmat, clip_grid);
+ }
+
+ if (fluid_fields & SM_ACTIVE_FIRE) {
+ OpenVDB_export_grid_fl(writer, "Flame", flame, sds->res, sds->fluidmat, density_grid);
+ OpenVDB_export_grid_fl(writer, "Fuel", fuel, sds->res, sds->fluidmat, density_grid);
+ OpenVDB_export_grid_fl(writer, "React", react, sds->res, sds->fluidmat, density_grid);
+ }
+
+ if (fluid_fields & SM_ACTIVE_COLORS) {
+ OpenVDB_export_grid_vec(writer, "Color", r, g, b, sds->res, sds->fluidmat, VEC_INVARIANT, true, density_grid);
+ }
+
+ OpenVDB_export_grid_vec(writer, "Velocity", vx, vy, vz, sds->res, sds->fluidmat, VEC_CONTRAVARIANT_RELATIVE, false, clip_grid);
+ //OpenVDB_export_grid_ch(writer, "Obstacles", obstacles, sds->res, sds->fluidmat, NULL);
+ }
+}
+
+static void OpenVDB_import_smoke(SmokeDomainSettings *sds, struct OpenVDBReader *reader, bool for_display)
+{
+ int fluid_fields = smoke_get_data_flags(sds);
+ int active_fields, cache_fields = 0;
+ int cache_res[3];
+ float cache_dx;
+ bool reallocate = false;
+
+ OpenVDBReader_get_meta_int(reader, "fluid_fields", &cache_fields);
+ OpenVDBReader_get_meta_int(reader, "active_fields", &active_fields);
+ OpenVDBReader_get_meta_fl(reader, "dx", &cache_dx);
+ OpenVDBReader_get_meta_v3_int(reader, "resolution", cache_res);
+
+ /* check if resolution has changed */
+ if (sds->res[0] != cache_res[0] ||
+ sds->res[1] != cache_res[1] ||
+ sds->res[2] != cache_res[2])
+ {
+ if (sds->flags & MOD_SMOKE_ADAPTIVE_DOMAIN) {
+ reallocate = true;
+ }
+ else {
+ return;
+ }
+ }
+
+ /* check if active fields have changed */
+ if ((fluid_fields != cache_fields) || (active_fields != sds->active_fields)) {
+ reallocate = true;
+ }
+
+ /* reallocate fluid if needed*/
+ if (reallocate) {
+ sds->active_fields = active_fields | cache_fields;
+ smoke_reallocate_fluid(sds, cache_dx, cache_res, 1);
+ sds->dx = cache_dx;
+ copy_v3_v3_int(sds->res, cache_res);
+ sds->total_cells = cache_res[0] * cache_res[1] * cache_res[2];
+
+ if (sds->flags & MOD_SMOKE_HIGHRES) {
+ smoke_reallocate_highres_fluid(sds, cache_dx, cache_res, 1);
+ }
+ }
+
+ if (sds->fluid) {
+ float dt, dx, *dens, *react, *fuel, *flame, *heat, *heatold, *vx, *vy, *vz, *r, *g, *b;
+ unsigned char *obstacles;
+ bool for_low_display = for_display && (!sds->wt || !(sds->viewsettings & MOD_SMOKE_VIEW_SHOWBIG));
+
+ smoke_export(sds->fluid, &dt, &dx, &dens, &react, &flame, &fuel, &heat,
+ &heatold, &vx, &vy, &vz, &r, &g, &b, &obstacles);
+
+ OpenVDBReader_get_meta_fl(reader, "dt", &dt);
+
+ OpenVDB_import_grid_fl(reader, "Shadow", &sds->shadow, sds->res);
+
+ if (for_low_display) {
+ sds->density = OpenVDB_import_grid_fl(reader, "Density", &dens, sds->res);
+ }
+
+ if (fluid_fields & SM_ACTIVE_HEAT && !for_low_display) {
+ OpenVDB_import_grid_fl(reader, "Heat", &heat, sds->res);
+ OpenVDB_import_grid_fl(reader, "Heat Old", &heatold, sds->res);
+ }
+
+ if (fluid_fields & SM_ACTIVE_FIRE && for_low_display) {
+ OpenVDB_import_grid_fl(reader, "Flame", &flame, sds->res);
+
+ if (!for_low_display) { // should be "for_sim_cont" or so
+ OpenVDB_import_grid_fl(reader, "Fuel", &fuel, sds->res);
+ OpenVDB_import_grid_fl(reader, "React", &react, sds->res);
+ }
+ }
+
+ if (fluid_fields & SM_ACTIVE_COLORS && for_low_display) {
+ OpenVDB_import_grid_vec(reader, "Color", &r, &g, &b, sds->res);
+ }
+
+ if (!for_low_display) {
+ OpenVDB_import_grid_vec(reader, "Velocity", &vx, &vy, &vz, sds->res);
+ //OpenVDB_import_grid_ch(reader, "Obstacles", &obstacles, sds->res);
+ }
+ }
+
+ if (sds->wt) {
+ float *dens, *react, *fuel, *flame, *tcu, *tcv, *tcw, *r, *g, *b;
+ bool for_wt_display = for_display && (sds->viewsettings & MOD_SMOKE_VIEW_SHOWBIG);
+
+ smoke_turbulence_export(sds->wt, &dens, &react, &flame, &fuel, &r, &g, &b, &tcu, &tcv, &tcw);
+
+ if (for_wt_display) {
+ sds->density_high = OpenVDB_import_grid_fl(reader, "Density High", &dens, sds->res_wt);
+ }
+
+ if (fluid_fields & SM_ACTIVE_FIRE && for_wt_display) {
+ OpenVDB_import_grid_fl(reader, "Flame High", &flame, sds->res_wt);
+
+ if (!for_wt_display) { // should be "for_sim_cont" or so
+ OpenVDB_import_grid_fl(reader, "Fuel High", &fuel, sds->res_wt);
+ OpenVDB_import_grid_fl(reader, "React High", &react, sds->res_wt);
+ }
+ }
+
+ if (fluid_fields & SM_ACTIVE_COLORS && for_wt_display) {
+ OpenVDB_import_grid_vec(reader, "Color High", &r, &g, &b, sds->res_wt);
+ }
+
+ if (!for_wt_display) {
+ OpenVDB_import_grid_vec(reader, "Texture Coordinates", &tcu, &tcv, &tcw, sds->res);
+ }
+ }
+}
+
+void smokeModifier_OpenVDB_export(SmokeModifierData *smd, Scene *scene, Object *ob, DerivedMesh *dm,
+ update_cb update, void *update_cb_data)
+{
+ SmokeDomainSettings *sds = smd->domain;
+ OpenVDBCache *cache;
+ int orig_frame, fr, cancel = 0;
+ float progress;
+ const char *relbase = modifier_path_relbase(ob);
+ char filename[FILE_MAX];
+
+ orig_frame = scene->r.cfra;
+
+ cache = BKE_openvdb_get_current_cache(sds);
+
+ if (cache->writer == NULL) {
+ cache->writer = OpenVDBWriter_create();
+ }
+
+ OpenVDBWriter_set_compression(cache->writer, cache->compression);
+
+ for (fr = cache->startframe; fr <= cache->endframe; fr++) {
+ /* smd->time is overwritten with scene->r.cfra in smokeModifier_process,
+ * so we can't use it here... */
+ scene->r.cfra = fr;
+
+ BKE_openvdb_cache_filename(filename, cache->path, cache->name, relbase, fr);
+
+ smokeModifier_process(smd, scene, ob, dm, false);
+
+ /* XXX hack: for some reason the smoke sim stores zero matrix for frame 1 */
+ {
+ PTCacheID pid;
+ int ptcache_start, ptcache_end;
+ float ptcache_timescale;
+ BKE_ptcache_id_from_smoke(&pid, ob, smd);
+ BKE_ptcache_id_time(&pid, scene, fr, &ptcache_start, &ptcache_end, &ptcache_timescale);
+
+ if (fr == ptcache_start) {
+ unit_m4(sds->obmat);
+ }
+ }
+
+ OpenVDB_write_fluid_settings(sds, cache->writer);
+ OpenVDB_export_smoke(sds, cache->writer);
+
+ progress = (fr - cache->startframe) / (float)cache->endframe;
+
+ OpenVDBWriter_write(cache->writer, filename);
+
+ update(update_cb_data, progress, &cancel);
+
+ if (cancel) {
+ scene->r.cfra = orig_frame;
+ return;
+ }
+ }
+
+ cache->flags |= VDB_CACHE_SMOKE_EXPORTED;
+
+ scene->r.cfra = orig_frame;
+}
+
+void smokeModifier_OpenVDB_import(SmokeModifierData *smd, Scene *scene, Object *ob, OpenVDBCache *cache)
+{
+ SmokeDomainSettings *sds = smd->domain;
+ int startframe, endframe;
+ char filename[FILE_MAX];
+ const char *relbase = modifier_path_relbase(ob);
+ int ret = OPENVDB_NO_ERROR;
+ bool for_display = false;
+
+ cache = BKE_openvdb_get_current_cache(sds);
+ startframe = cache->startframe;
+ endframe = cache->endframe;
+
+ if (CFRA < startframe || CFRA > endframe) {
+ return;
+ }
+
+ if (cache->reader == NULL) {
+ cache->reader = OpenVDBReader_create();
+ }
+
+ for_display = true;
+
+ BKE_openvdb_cache_filename(filename, cache->path, cache->name, relbase, CFRA);
+ OpenVDBReader_open(cache->reader, filename);
+ OpenVDB_read_fluid_settings(sds, cache->reader);
+ OpenVDB_import_smoke(sds, cache->reader, for_display);
+
+ if (ret == OPENVDB_IO_ERROR) {
+ /* TODO(kevin): report error "OpenVDB import error, see console for details" */
+ return;
+ }
+
+ if (ret == OPENVDB_KEY_ERROR) {
+ /* It may happen that some grids are missing on the first frame if the
+ * simulation hasn't started yet, so it's safe to ignore it. */
+ if (CFRA > startframe) {
+ /* TODO(kevin): report error "OpenVDB import error, see console for details" */
+ return;
+ }
+ }
+}
+
+void smokeModifier_OpenVDB_update_transform(SmokeModifierData *smd,
+ Scene *scene,
+ Object *ob,
+ update_cb update,
+ void *update_cb_data)
+{
+ SmokeDomainSettings *sds = smd->domain;
+ OpenVDBCache *cache;
+ int orig_frame, fr, cancel = 0;
+ float progress;
+ const char *relbase = modifier_path_relbase(ob);
+ char filename[FILE_MAX];
+
+ orig_frame = scene->r.cfra;
+
+ cache = BKE_openvdb_get_current_cache(sds);
+
+ for (fr = cache->startframe; fr <= cache->endframe; fr++) {
+ /* smd->time is overwritten with scene->r.cfra in smokeModifier_process,
+ * so we can't use it here... */
+ scene->r.cfra = fr;
+
+ BKE_openvdb_cache_filename(filename, cache->path, cache->name, relbase, fr);
+ compute_fluid_matrices(sds);
+ OpenVDB_update_fluid_transform(filename, sds->fluidmat, sds->fluidmat_wt);
+
+ progress = (fr - cache->startframe) / (float)cache->endframe;
+
+ update(update_cb_data, progress, &cancel);
+
+ if (cancel) {
+ scene->r.cfra = orig_frame;
+ return;
+ }
+ }
+
+ scene->r.cfra = orig_frame;
+}
+
+#endif /* WITH_OPENVDB */
+
#endif /* WITH_SMOKE */
+
+#if !defined(WITH_SMOKE) || !defined(WITH_OPENVDB)
+
+void smokeModifier_OpenVDB_export(SmokeModifierData *smd, Scene *scene, Object *ob,
+ DerivedMesh *dm, update_cb update, void *update_cb_data)
+{
+ UNUSED_VARS(smd, scene, ob, dm, update, update_cb_data);
+}
+
+void smokeModifier_OpenVDB_import(SmokeModifierData *smd, Scene *scene, Object *ob, OpenVDBCache *cache)
+{
+ UNUSED_VARS(smd, scene, ob, cache);
+}
+
+void smokeModifier_OpenVDB_update_transform(SmokeModifierData *smd, Scene *scene,
+ Object *ob, update_cb update, void *update_cb_data)
+{
+ UNUSED_VARS(smd, scene, ob, update, update_cb_data);
+}
+
+void BKE_openvdb_cache_filename(char *r_filename, const char *path, const char *fname, const char *relbase, int frame)
+{
+ r_filename[0] = '\0';
+ UNUSED_VARS(path, fname, relbase, frame);
+}
+
+#endif
+
+OpenVDBCache *BKE_openvdb_get_current_cache(SmokeDomainSettings *sds)
+{
+ OpenVDBCache *cache = sds->vdb_caches.first;
+
+ for (; cache; cache = cache->next) {
+ if (cache->flags & VDB_CACHE_CURRENT) {
+ break;
+ }
+ }
+
+ return cache;
+}
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index 607f89699a4..dc5b77a1bf4 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -1647,7 +1647,7 @@ static void scan_for_ext_spring_forces(Scene *scene, Object *ob, float timenow)
SoftBody *sb = ob->soft;
ListBase *do_effector = NULL;
- do_effector = pdInitEffectors(scene, ob, NULL, sb->effector_weights, true);
+ do_effector = pdInitEffectors(scene, ob, NULL, sb->effector_weights);
_scan_for_ext_spring_forces(scene, ob, timenow, 0, sb->totspring, do_effector);
pdEndEffectors(&do_effector);
}
@@ -1667,7 +1667,7 @@ static void sb_sfesf_threads_run(Scene *scene, struct Object *ob, float timenow,
int i, totthread, left, dec;
int lowsprings =100; /* wild guess .. may increase with better thread management 'above' or even be UI option sb->spawn_cf_threads_nopts */
- do_effector= pdInitEffectors(scene, ob, NULL, ob->soft->effector_weights, true);
+ do_effector= pdInitEffectors(scene, ob, NULL, ob->soft->effector_weights);
/* figure the number of threads while preventing pretty pointless threading overhead */
totthread= BKE_scene_num_threads(scene);
@@ -2475,7 +2475,7 @@ static void softbody_calc_forcesEx(Scene *scene, Object *ob, float forcetime, fl
sb_sfesf_threads_run(scene, ob, timenow, sb->totspring, NULL);
/* after spring scan because it uses Effoctors too */
- do_effector= pdInitEffectors(scene, ob, NULL, sb->effector_weights, true);
+ do_effector= pdInitEffectors(scene, ob, NULL, sb->effector_weights);
if (do_deflector) {
float defforce[3];
@@ -2550,7 +2550,7 @@ static void softbody_calc_forces(Scene *scene, Object *ob, float forcetime, floa
if (do_springcollision || do_aero) scan_for_ext_spring_forces(scene, ob, timenow);
/* after spring scan because it uses Effoctors too */
- do_effector= pdInitEffectors(scene, ob, NULL, ob->soft->effector_weights, true);
+ do_effector= pdInitEffectors(scene, ob, NULL, ob->soft->effector_weights);
if (do_deflector) {
float defforce[3];
diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c
index 46a50917427..81cc55f754d 100644
--- a/source/blender/blenkernel/intern/sound.c
+++ b/source/blender/blenkernel/intern/sound.c
@@ -604,7 +604,7 @@ void BKE_sound_seek_scene(struct Main *bmain, struct Scene *scene)
}
}
- if (scene->audio.flag & AUDIO_SCRUB && !animation_playing) {
+ if ((scene->audio.flag & AUDIO_SCRUB) && !animation_playing) {
if (scene->audio.flag & AUDIO_SYNC) {
AUD_seek(scene->playback_handle, cur_time);
AUD_seekSequencer(scene->playback_handle, cur_time);
diff --git a/source/blender/blenkernel/intern/strands.c b/source/blender/blenkernel/intern/strands.c
new file mode 100644
index 00000000000..38d35a7309e
--- /dev/null
+++ b/source/blender/blenkernel/intern/strands.c
@@ -0,0 +1,407 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_string.h"
+
+#include "BKE_strands.h"
+
+Strands *BKE_strands_new(int curves, int verts)
+{
+ Strands *strands = MEM_mallocN(sizeof(Strands), "strands");
+
+ strands->totcurves = curves;
+ strands->curves = MEM_mallocN(sizeof(StrandsCurve) * curves, "strand curves");
+
+ strands->totverts = verts;
+ strands->verts = MEM_mallocN(sizeof(StrandsVertex) * verts, "strand vertices");
+
+ /* must be added explicitly */
+ strands->state = NULL;
+
+ return strands;
+}
+
+Strands *BKE_strands_copy(Strands *strands)
+{
+ Strands *new_strands = MEM_dupallocN(strands);
+ if (new_strands->curves)
+ new_strands->curves = MEM_dupallocN(new_strands->curves);
+ if (new_strands->verts)
+ new_strands->verts = MEM_dupallocN(new_strands->verts);
+ if (new_strands->state)
+ new_strands->state = MEM_dupallocN(new_strands->state);
+ return new_strands;
+}
+
+void BKE_strands_free(Strands *strands)
+{
+ if (strands) {
+ if (strands->curves)
+ MEM_freeN(strands->curves);
+ if (strands->verts)
+ MEM_freeN(strands->verts);
+ if (strands->state)
+ MEM_freeN(strands->state);
+ MEM_freeN(strands);
+ }
+}
+
+/* copy the rest positions to initialize the motion state */
+void BKE_strands_state_copy_rest_positions(Strands *strands)
+{
+ if (strands->state) {
+ int i;
+ for (i = 0; i < strands->totverts; ++i) {
+ copy_v3_v3(strands->state[i].co, strands->verts[i].co);
+ }
+ }
+}
+
+/* copy the rest positions to initialize the motion state */
+void BKE_strands_state_clear_velocities(Strands *strands)
+{
+ if (strands->state) {
+ int i;
+
+ for (i = 0; i < strands->totverts; ++i) {
+ zero_v3(strands->state[i].vel);
+ }
+ }
+}
+
+void BKE_strands_add_motion_state(Strands *strands)
+{
+ if (!strands->state) {
+ int i;
+
+ strands->state = MEM_mallocN(sizeof(StrandsMotionState) * strands->totverts, "strand motion states");
+
+ BKE_strands_state_copy_rest_positions(strands);
+ BKE_strands_state_clear_velocities(strands);
+
+ /* initialize normals */
+ for (i = 0; i < strands->totverts; ++i) {
+ copy_v3_v3(strands->state[i].nor, strands->verts[i].nor);
+ }
+ }
+}
+
+void BKE_strands_remove_motion_state(Strands *strands)
+{
+ if (strands) {
+ if (strands->state) {
+ MEM_freeN(strands->state);
+ strands->state = NULL;
+ }
+ }
+}
+
+static void calc_normals(Strands *strands, bool use_motion_state)
+{
+ StrandIterator it_strand;
+ for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ StrandEdgeIterator it_edge;
+ int numverts = it_strand.curve->numverts;
+ if (use_motion_state) {
+ for (BKE_strand_edge_iter_init(&it_edge, &it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge)) {
+ sub_v3_v3v3(it_edge.state0->nor, it_edge.state1->co, it_edge.state0->co);
+ normalize_v3(it_edge.state0->nor);
+ }
+ if (numverts > 1)
+ copy_v3_v3(it_strand.state[numverts-1].nor, it_strand.state[numverts-2].nor);
+ }
+ else {
+ for (BKE_strand_edge_iter_init(&it_edge, &it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge)) {
+ sub_v3_v3v3(it_edge.vertex0->nor, it_edge.vertex1->co, it_edge.vertex0->co);
+ normalize_v3(it_edge.vertex0->nor);
+ }
+ if (numverts > 1)
+ copy_v3_v3(it_strand.verts[numverts-1].nor, it_strand.verts[numverts-2].nor);
+ }
+ }
+}
+
+void BKE_strands_ensure_normals(Strands *strands)
+{
+ const bool use_motion_state = (strands->state);
+
+ calc_normals(strands, false);
+
+ if (use_motion_state)
+ calc_normals(strands, true);
+}
+
+void BKE_strands_get_minmax(Strands *strands, float min[3], float max[3], bool use_motion_state)
+{
+ int numverts = strands->totverts;
+ int i;
+
+ if (use_motion_state && strands->state) {
+ for (i = 0; i < numverts; ++i) {
+ minmax_v3v3_v3(min, max, strands->state[i].co);
+ }
+ }
+ else {
+ for (i = 0; i < numverts; ++i) {
+ minmax_v3v3_v3(min, max, strands->verts[i].co);
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+StrandsChildren *BKE_strands_children_new(int curves, int verts)
+{
+ StrandsChildren *strands = MEM_mallocN(sizeof(StrandsChildren), "strands children");
+
+ strands->totcurves = curves;
+ strands->curves = MEM_mallocN(sizeof(StrandsChildCurve) * curves, "strand children curves");
+
+ strands->totverts = verts;
+ strands->verts = MEM_mallocN(sizeof(StrandsChildVertex) * verts, "strand children vertices");
+
+ /* must be added explicitly */
+ strands->curve_uvs = NULL;
+ strands->numuv = 0;
+ strands->curve_vcols = NULL;
+ strands->numvcol = 0;
+
+ return strands;
+}
+
+StrandsChildren *BKE_strands_children_copy(StrandsChildren *strands)
+{
+ StrandsChildren *new_strands = MEM_dupallocN(strands);
+ if (new_strands->curves)
+ new_strands->curves = MEM_dupallocN(new_strands->curves);
+ if (new_strands->curve_uvs)
+ new_strands->curve_uvs = MEM_dupallocN(new_strands->curve_uvs);
+ if (new_strands->curve_vcols)
+ new_strands->curve_vcols = MEM_dupallocN(new_strands->curve_vcols);
+ if (new_strands->verts)
+ new_strands->verts = MEM_dupallocN(new_strands->verts);
+ return new_strands;
+}
+
+void BKE_strands_children_free(StrandsChildren *strands)
+{
+ if (strands) {
+ if (strands->curves)
+ MEM_freeN(strands->curves);
+ if (strands->curve_uvs)
+ MEM_freeN(strands->curve_uvs);
+ if (strands->curve_vcols)
+ MEM_freeN(strands->curve_vcols);
+ if (strands->verts)
+ MEM_freeN(strands->verts);
+ MEM_freeN(strands);
+ }
+}
+
+void BKE_strands_children_add_uvs(StrandsChildren *strands, int num_layers)
+{
+ if (strands->curve_uvs && strands->numuv != num_layers) {
+ MEM_freeN(strands->curve_uvs);
+ strands->curve_uvs = NULL;
+ strands->numuv = 0;
+ }
+
+ if (!strands->curve_uvs) {
+ strands->curve_uvs = MEM_callocN(sizeof(StrandsChildCurveUV) * strands->totcurves * num_layers, "strands children uv layers");
+ strands->numuv = num_layers;
+ }
+}
+
+void BKE_strands_children_add_vcols(StrandsChildren *strands, int num_layers)
+{
+ if (strands->curve_vcols && strands->numvcol != num_layers) {
+ MEM_freeN(strands->curve_vcols);
+ strands->curve_vcols = NULL;
+ strands->numvcol = 0;
+ }
+
+ if (!strands->curve_vcols) {
+ strands->curve_vcols = MEM_callocN(sizeof(StrandsChildCurveVCol) * strands->totcurves * num_layers, "strands children vcol layers");
+ strands->numvcol = num_layers;
+ }
+}
+
+static int *strands_calc_vertex_start(Strands *strands)
+{
+ int *vertstart = MEM_mallocN(sizeof(int) * strands->totcurves, "strand curves vertex start");
+ StrandIterator it_strand;
+ int start;
+
+ start = 0;
+ for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ vertstart[it_strand.index] = start;
+ start += it_strand.curve->numverts;
+ }
+
+ return vertstart;
+}
+
+
+/* 'out' is an optional array to write final positions to, instead of writing back to vertex locations.
+ * It must be at least as large as the number of vertices.
+ */
+static void strands_children_strand_deform(StrandChildIterator *it_strand, Strands *parents, int *vertstart, bool use_motion, float (*out)[3])
+{
+ int i;
+
+ if (!parents || !vertstart)
+ return;
+
+ if (!parents->state)
+ use_motion = false;
+
+ for (i = 0; i < 4; ++i) {
+ int p = it_strand->curve->parents[i];
+ float w = it_strand->curve->parent_weights[i];
+ if (p >= 0 && w > 0.0f) {
+ StrandsCurve *parent = &parents->curves[p];
+ StrandsVertex *pverts;
+ StrandsMotionState *pstate;
+ int pv0, pv1;
+ StrandChildVertexIterator it_vert;
+
+ if (parent->numverts <= 0)
+ continue;
+
+ pverts = &parents->verts[vertstart[p]];
+ pstate = &parents->state[vertstart[p]];
+ pv0 = 0;
+ for (BKE_strand_child_vertex_iter_init(&it_vert, it_strand); BKE_strand_child_vertex_iter_valid(&it_vert); BKE_strand_child_vertex_iter_next(&it_vert)) {
+ float time = it_vert.vertex->time;
+ float dt, x;
+ float poffset0[3], poffset1[3], offset[3];
+
+ /* advance to the matching parent edge for interpolation */
+ while (pv0 < parent->numverts-1 && pverts[pv0+1].time < time)
+ ++pv0;
+ pv1 = (pv0 < parent->numverts-1)? pv0+1 : pv0;
+
+ if (use_motion) {
+ sub_v3_v3v3(poffset0, pstate[pv0].co, pverts[pv0].base);
+ sub_v3_v3v3(poffset1, pstate[pv1].co, pverts[pv1].base);
+ }
+ else {
+ sub_v3_v3v3(poffset0, pverts[pv0].co, pverts[pv0].base);
+ sub_v3_v3v3(poffset1, pverts[pv1].co, pverts[pv1].base);
+ }
+
+ dt = pverts[pv1].time - pverts[pv0].time;
+ x = dt > 0.0f ? (time - pverts[pv0].time) / dt : 0.0f;
+ CLAMP(x, 0.0f, 1.0f);
+ interp_v3_v3v3(offset, poffset0, poffset1, x);
+
+ if (out)
+ madd_v3_v3fl(out[it_vert.index], offset, w);
+ else
+ madd_v3_v3fl(it_vert.vertex->co, offset, w);
+ }
+ }
+ }
+}
+
+void BKE_strands_children_deform(StrandsChildren *strands, Strands *parents, bool use_motion)
+{
+ int *vertstart = NULL;
+ StrandChildIterator it_strand;
+
+ if (parents)
+ vertstart = strands_calc_vertex_start(parents);
+
+ for (BKE_strand_child_iter_init(&it_strand, strands); BKE_strand_child_iter_valid(&it_strand); BKE_strand_child_iter_next(&it_strand)) {
+ /* move child strands from their local root space to object space */
+ StrandChildVertexIterator it_vert;
+ for (BKE_strand_child_vertex_iter_init(&it_vert, &it_strand); BKE_strand_child_vertex_iter_valid(&it_vert); BKE_strand_child_vertex_iter_next(&it_vert)) {
+ mul_v3_m4v3(it_vert.vertex->co, it_strand.curve->root_matrix, it_vert.vertex->base);
+ }
+
+ strands_children_strand_deform(&it_strand, parents, vertstart, use_motion, NULL);
+ }
+
+ if (vertstart)
+ MEM_freeN(vertstart);
+}
+
+static void calc_child_normals(StrandsChildren *strands)
+{
+ StrandChildIterator it_strand;
+ for (BKE_strand_child_iter_init(&it_strand, strands); BKE_strand_child_iter_valid(&it_strand); BKE_strand_child_iter_next(&it_strand)) {
+ StrandChildEdgeIterator it_edge;
+ int numverts = it_strand.curve->numverts;
+ for (BKE_strand_child_edge_iter_init(&it_edge, &it_strand); BKE_strand_child_edge_iter_valid(&it_edge); BKE_strand_child_edge_iter_next(&it_edge)) {
+ sub_v3_v3v3(it_edge.vertex0->nor, it_edge.vertex1->co, it_edge.vertex0->co);
+ normalize_v3(it_edge.vertex0->nor);
+ }
+ if (numverts > 1)
+ copy_v3_v3(it_strand.verts[numverts-1].nor, it_strand.verts[numverts-2].nor);
+ }
+}
+
+void BKE_strands_children_ensure_normals(StrandsChildren *strands)
+{
+ calc_child_normals(strands);
+}
+
+void BKE_strands_children_get_minmax(StrandsChildren *strands, float min[3], float max[3])
+{
+ int numverts = strands->totverts;
+ int i;
+
+ for (i = 0; i < numverts; ++i) {
+ minmax_v3v3_v3(min, max, strands->verts[i].co);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+void BKE_strand_bend_iter_transform_rest(StrandBendIterator *iter, float mat[3][3])
+{
+ float dir0[3], dir1[3];
+
+ sub_v3_v3v3(dir0, iter->vertex1->co, iter->vertex0->co);
+ sub_v3_v3v3(dir1, iter->vertex2->co, iter->vertex1->co);
+ normalize_v3(dir0);
+ normalize_v3(dir1);
+
+ /* rotation between segments */
+ rotation_between_vecs_to_mat3(mat, dir0, dir1);
+}
+
+void BKE_strand_bend_iter_transform_state(StrandBendIterator *iter, float mat[3][3])
+{
+ if (iter->state0) {
+ float dir0[3], dir1[3];
+
+ sub_v3_v3v3(dir0, iter->state1->co, iter->state0->co);
+ sub_v3_v3v3(dir1, iter->state2->co, iter->state1->co);
+ normalize_v3(dir0);
+ normalize_v3(dir1);
+
+ /* rotation between segments */
+ rotation_between_vecs_to_mat3(mat, dir0, dir1);
+ }
+ else
+ unit_m3(mat);
+}
diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index edf0bfce8c8..f505533b6c6 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -72,6 +72,7 @@
#include "GPU_draw.h"
#include "GPU_extensions.h"
#include "GPU_glew.h"
+#include "GPU_buffers.h"
#include "CCGSubSurf.h"
@@ -1734,12 +1735,25 @@ static void ccgDM_drawLooseEdges(DerivedMesh *dm)
}
}
+static void ccgDM_NormalFast(float *a, float *b, float *c, float *d, float no[3])
+{
+ float a_cX = c[0] - a[0], a_cY = c[1] - a[1], a_cZ = c[2] - a[2];
+ float b_dX = d[0] - b[0], b_dY = d[1] - b[1], b_dZ = d[2] - b[2];
+
+ no[0] = b_dY * a_cZ - b_dZ * a_cY;
+ no[1] = b_dZ * a_cX - b_dX * a_cZ;
+ no[2] = b_dX * a_cY - b_dY * a_cX;
+
+ normalize_v3(no);
+}
+
+
static void ccgDM_glNormalFast(float *a, float *b, float *c, float *d)
{
float a_cX = c[0] - a[0], a_cY = c[1] - a[1], a_cZ = c[2] - a[2];
float b_dX = d[0] - b[0], b_dY = d[1] - b[1], b_dZ = d[2] - b[2];
float no[3];
-
+
no[0] = b_dY * a_cZ - b_dZ * a_cY;
no[1] = b_dZ * a_cX - b_dX * a_cZ;
no[2] = b_dX * a_cY - b_dY * a_cX;
@@ -1749,7 +1763,8 @@ static void ccgDM_glNormalFast(float *a, float *b, float *c, float *d)
}
/* Only used by non-editmesh types */
-static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes)[4], bool fast, DMSetMaterial setMaterial)
+static void ccgDM_prepare_normal_data(DerivedMesh *dm, float *varray, int *vindex,
+ int *mat_orig_to_new, void *UNUSED(user_data))
{
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm;
CCGSubSurf *ss = ccgdm->ss;
@@ -1758,13 +1773,277 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes)
int gridSize = ccgSubSurf_getGridSize(ss);
int gridFaces = gridSize - 1;
DMFlagMat *faceFlags = ccgdm->faceFlags;
- int step = (fast) ? gridSize - 1 : 1;
int i, totface = ccgSubSurf_getNumFaces(ss);
- int drawcurrent = 0, matnr = -1, shademodel = -1;
+ int matnr, shademodel;
+ int start;
CCG_key_top_level(&key, ss);
ccgdm_pbvh_update(ccgdm);
+ for (i = 0; i < totface; i++) {
+ CCGFace *f = ccgdm->faceMap[i].face;
+ int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f);
+ int index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(f));
+ short (*ln)[4][3] = NULL;
+
+ if (faceFlags) {
+ shademodel = (lnors || (faceFlags[index].flag & ME_SMOOTH)) ? GL_SMOOTH : GL_FLAT;
+ matnr = faceFlags[index].mat_nr;
+ }
+ else {
+ shademodel = GL_SMOOTH;
+ matnr = 0;
+ }
+
+ if (lnors) {
+ ln = lnors;
+ lnors += gridFaces * gridFaces * numVerts;
+ }
+
+ for (S = 0; S < numVerts; S++) {
+ CCGElem *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S);
+
+ if (ln) {
+ /* Can't use quad strips here... */
+ for (y = 0; y < gridFaces; y ++) {
+ for (x = 0; x < gridFaces; x ++) {
+ start = vindex[mat_orig_to_new[matnr]];
+
+ normal_short_to_float_v3(&varray[start], ln[0][1]);
+ normal_short_to_float_v3(&varray[start + 3], ln[0][2]);
+ normal_short_to_float_v3(&varray[start + 6], ln[0][3]);
+
+ normal_short_to_float_v3(&varray[start + 9], ln[0][3]);
+ normal_short_to_float_v3(&varray[start + 12], ln[0][1]);
+ normal_short_to_float_v3(&varray[start + 15], ln[0][0]);
+
+ vindex[mat_orig_to_new[matnr]] += 18;
+
+ ln ++;
+ }
+ }
+ }
+ else if (shademodel == GL_SMOOTH) {
+ for (y = 0; y < gridFaces; y ++) {
+ for (x = 0; x < gridFaces; x ++) {
+ float *a = CCG_grid_elem_no(&key, faceGridData, x, y );
+ float *b = CCG_grid_elem_no(&key, faceGridData, x + 1, y);
+ float *c = CCG_grid_elem_no(&key, faceGridData, x + 1, y + 1);
+ float *d = CCG_grid_elem_no(&key, faceGridData, x, y + 1);
+
+ start = vindex[mat_orig_to_new[matnr]];
+
+ copy_v3_v3(&varray[start], d);
+ copy_v3_v3(&varray[start + 3], c);
+ copy_v3_v3(&varray[start + 6], b);
+
+ copy_v3_v3(&varray[start + 9], d);
+ copy_v3_v3(&varray[start + 12], b);
+ copy_v3_v3(&varray[start + 15], a);
+
+ vindex[mat_orig_to_new[matnr]] += 18;
+ }
+ }
+ }
+ else {
+ for (y = 0; y < gridFaces; y ++) {
+ for (x = 0; x < gridFaces; x ++) {
+ float no[3];
+ float *a = CCG_grid_elem_co(&key, faceGridData, x, y );
+ float *b = CCG_grid_elem_co(&key, faceGridData, x + 1, y );
+ float *c = CCG_grid_elem_co(&key, faceGridData, x + 1, y + 1);
+ float *d = CCG_grid_elem_co(&key, faceGridData, x, y + 1);
+
+ ccgDM_NormalFast(a, b, c, d, no);
+
+ start = vindex[mat_orig_to_new[matnr]];
+
+ copy_v3_v3(&varray[start], no);
+ copy_v3_v3(&varray[start + 3], no);
+ copy_v3_v3(&varray[start + 6], no);
+
+ copy_v3_v3(&varray[start + 9], no);
+ copy_v3_v3(&varray[start + 12], no);
+ copy_v3_v3(&varray[start + 15], no);
+
+ vindex[mat_orig_to_new[matnr]] += 18;
+ }
+ }
+ }
+ }
+ }
+}
+
+/* Only used by non-editmesh types */
+static void ccgDM_prepare_vertex_data(DerivedMesh *dm, float *varray, int *vindex,
+ int *mat_orig_to_new, void *UNUSED(user_data))
+{
+ CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm;
+ CCGSubSurf *ss = ccgdm->ss;
+ CCGKey key;
+ int gridSize = ccgSubSurf_getGridSize(ss);
+ int gridFaces = gridSize - 1;
+ DMFlagMat *faceFlags = ccgdm->faceFlags;
+ int i, totface = ccgSubSurf_getNumFaces(ss);
+ int matnr = -1, start;
+
+ CCG_key_top_level(&key, ss);
+ ccgdm_pbvh_update(ccgdm);
+
+ for (i = 0; i < totface; i++) {
+ CCGFace *f = ccgdm->faceMap[i].face;
+ int S, x, y, numVerts = ccgSubSurf_getFaceNumVerts(f);
+ int index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(f));
+
+ if (faceFlags) {
+ matnr = faceFlags[index].mat_nr;
+ }
+ else {
+ matnr = 0;
+ }
+
+ for (S = 0; S < numVerts; S++) {
+ CCGElem *faceGridData = ccgSubSurf_getFaceGridDataArray(ss, f, S);
+ for (y = 0; y < gridFaces; y++) {
+ for (x = 0; x < gridFaces; x++) {
+ float *a = CCG_grid_elem_co(&key, faceGridData, x, y);
+ float *b = CCG_grid_elem_co(&key, faceGridData, x + 1, y);
+ float *c = CCG_grid_elem_co(&key, faceGridData, x + 1, y + 1);
+ float *d = CCG_grid_elem_co(&key, faceGridData, x, y + 1);
+
+ start = vindex[mat_orig_to_new[matnr]];
+
+ copy_v3_v3(&varray[start], d);
+ copy_v3_v3(&varray[start + 3], c);
+ copy_v3_v3(&varray[start + 6], b);
+
+ copy_v3_v3(&varray[start + 9], d);
+ copy_v3_v3(&varray[start + 12], b);
+ copy_v3_v3(&varray[start + 15], a);
+
+ vindex[mat_orig_to_new[matnr]] += 18;
+ }
+ }
+ }
+ }
+}
+
+static void ccgDM_copy_gpu_data(DerivedMesh *dm, int type, float *varray, int *index,
+ int *mat_orig_to_new, void *UNUSED(user_data))
+{
+ switch(type) {
+ case GPU_BUFFER_VERTEX:
+ ccgDM_prepare_vertex_data(dm, varray, index, mat_orig_to_new, NULL);
+ break;
+ case GPU_BUFFER_NORMAL:
+ ccgDM_prepare_normal_data(dm, varray, index, mat_orig_to_new, NULL);
+ break;
+ default:
+ break;
+ }
+}
+
+static GPUDrawObject *ccgDM_GPUObjectNew(DerivedMesh *dm) {
+// GPUBufferMaterial *mat;
+ int *mat_orig_to_new;
+ CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm;
+ CCGSubSurf *ss = ccgdm->ss;
+ GPUDrawObject *gdo;
+ DMFlagMat *faceFlags = ccgdm->faceFlags;
+ int gridSize = ccgSubSurf_getGridSize(ss);
+ int gridFaces = gridSize - 1;
+ int totmat = (faceFlags) ? dm->totmat : 1;
+ int *points_per_mat;
+ int i, curmat, curpoint, totface;
+
+ /* object contains at least one material (default included) so zero means uninitialized dm */
+ BLI_assert(totmat != 0);
+
+ totface = ccgSubSurf_getNumFaces(ss);
+
+ points_per_mat = MEM_callocN(sizeof(*points_per_mat) * totmat, "GPU_drawobject_new.mat_orig_to_new");
+
+ if (faceFlags) {
+ for (i = 0; i < totface; i++) {
+ CCGFace *f = ccgdm->faceMap[i].face;
+ int numVerts = ccgSubSurf_getFaceNumVerts(f);
+ int index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(f));
+ int new_matnr = faceFlags[index].mat_nr;
+ points_per_mat[new_matnr] += numVerts * gridFaces * gridFaces * 6;
+ }
+ }
+ else {
+ for (i = 0; i < totface; i++) {
+ points_per_mat[0] += gridFaces * gridFaces * 6;
+ }
+ }
+
+ /* create the GPUDrawObject */
+ gdo = MEM_callocN(sizeof(GPUDrawObject), "GPUDrawObject");
+ gdo->totvert = ccgSubSurf_getNumFinalFaces(ss) * 6;
+ gdo->totedge = ccgSubSurf_getNumFinalEdges(ss) * 2;
+
+ /* count the number of materials used by this DerivedMesh */
+ for (i = 0; i < totmat; i++) {
+ if (points_per_mat[i] > 0)
+ gdo->totmaterial++;
+ }
+
+ /* allocate an array of materials used by this DerivedMesh */
+ gdo->materials = MEM_mallocN(sizeof(GPUBufferMaterial) * gdo->totmaterial,
+ "GPUDrawObject.materials");
+
+ /* initialize the materials array */
+ for (i = 0, curmat = 0, curpoint = 0; i < totmat; i++) {
+ if (points_per_mat[i] > 0) {
+ gdo->materials[curmat].start = curpoint;
+ gdo->materials[curmat].totpoint = points_per_mat[i];
+ gdo->materials[curmat].mat_nr = i;
+
+ curpoint += points_per_mat[i];
+ curmat++;
+ }
+ }
+
+ /* store total number of points used for triangles */
+ gdo->tot_triangle_point = curpoint;
+
+ mat_orig_to_new = MEM_callocN(sizeof(*mat_orig_to_new) * totmat,
+ "GPUDrawObject.mat_orig_to_new");
+
+ /* build a map from the original material indices to the new
+ * GPUBufferMaterial indices */
+ for (i = 0; i < gdo->totmaterial; i++) {
+ mat_orig_to_new[gdo->materials[i].mat_nr] = i;
+
+ }
+
+ /*
+ for (i = 0; i < totface; i++) {
+ CCGFace *f = ccgdm->faceMap[i].face;
+ int index = GET_INT_FROM_POINTER(ccgSubSurf_getFaceFaceHandle(f));
+ int new_matnr = faceFlags[index].mat_nr;
+
+ mat = &gdo->materials[mat_orig_to_new[new_matnr]];
+
+ }
+ */
+
+
+ MEM_freeN(mat_orig_to_new);
+ MEM_freeN(points_per_mat);
+
+ return gdo;
+}
+
+/* Only used by non-editmesh types */
+static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes)[4], bool fast, DMSetMaterial setMaterial)
+{
+ int a;
+ CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm;
+
+ ccgdm_pbvh_update(ccgdm);
+
if (ccgdm->pbvh && ccgdm->multires.mmd && !fast) {
if (BKE_pbvh_has_faces(ccgdm->pbvh)) {
BKE_pbvh_draw(ccgdm->pbvh, partial_redraw_planes, NULL,
@@ -1774,6 +2053,34 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes)
return;
}
+
+ GPU_vertex_setup(dm);
+ GPU_normal_setup(dm);
+ glShadeModel(GL_SMOOTH);
+ for (a = 0; a < dm->drawObject->totmaterial; a++) {
+ if (!setMaterial || setMaterial(dm->drawObject->materials[a].mat_nr + 1, NULL)) {
+ glDrawArrays(GL_TRIANGLES, dm->drawObject->materials[a].start,
+ dm->drawObject->materials[a].totpoint);
+ }
+ }
+ GPU_buffer_unbind();
+
+#if 0
+
+ CCGDerivedMesh *ccgdm = (CCGDerivedMesh *) dm;
+ CCGSubSurf *ss = ccgdm->ss;
+ CCGKey key;
+ short (*lnors)[4][3] = dm->getTessFaceDataArray(dm, CD_TESSLOOPNORMAL);
+ int gridSize = ccgSubSurf_getGridSize(ss);
+ int gridFaces = gridSize - 1;
+ DMFlagMat *faceFlags = ccgdm->faceFlags;
+ int step = (fast) ? gridSize - 1 : 1;
+ int i, totface = ccgSubSurf_getNumFaces(ss);
+ int drawcurrent = 0, matnr = -1, shademodel = -1;
+
+ CCG_key_top_level(&key, ss);
+ ccgdm_pbvh_update(ccgdm);
+
for (i = 0; i < totface; i++) {
CCGFace *f = ccgdm->faceMap[i].face;
@@ -1873,6 +2180,8 @@ static void ccgDM_drawFacesSolid(DerivedMesh *dm, float (*partial_redraw_planes)
}
}
}
+
+#endif
}
/* Only used by non-editmesh types */
@@ -3435,6 +3744,8 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss,
ccgdm->dm.drawMappedEdgesInterp = ccgDM_drawMappedEdgesInterp;
ccgdm->dm.drawMappedEdges = ccgDM_drawMappedEdges;
+ ccgdm->dm.gpuObjectNew = ccgDM_GPUObjectNew;
+ ccgdm->dm.copy_gpu_data = ccgDM_copy_gpu_data;
ccgdm->dm.release = ccgDM_release;
diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c
index 2ea903247b2..88a412d5e95 100644
--- a/source/blender/blenkernel/intern/texture.c
+++ b/source/blender/blenkernel/intern/texture.c
@@ -1470,11 +1470,8 @@ void BKE_texture_envmap_free(EnvMap *env)
/* ------------------------------------------------------------------------- */
-PointDensity *BKE_texture_pointdensity_add(void)
+void BKE_texture_pointdensity_init_data(PointDensity *pd)
{
- PointDensity *pd;
-
- pd = MEM_callocN(sizeof(PointDensity), "pointdensity");
pd->flag = 0;
pd->radius = 0.3f;
pd->falloff_type = TEX_PD_FALLOFF_STD;
@@ -1498,7 +1495,12 @@ PointDensity *BKE_texture_pointdensity_add(void)
pd->falloff_curve->cm->flag &= ~CUMA_EXTEND_EXTRAPOLATE;
curvemap_reset(pd->falloff_curve->cm, &pd->falloff_curve->clipr, pd->falloff_curve->preset, CURVEMAP_SLOPE_POSITIVE);
curvemapping_changed(pd->falloff_curve, false);
+}
+PointDensity *BKE_texture_pointdensity_add(void)
+{
+ PointDensity *pd = MEM_callocN(sizeof(PointDensity), "pointdensity");
+ BKE_texture_pointdensity_init_data(pd);
return pd;
}
diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h
index 01aa5d39b96..642896ac701 100644
--- a/source/blender/blenlib/BLI_fileops.h
+++ b/source/blender/blenlib/BLI_fileops.h
@@ -106,6 +106,7 @@ int BLI_access(const char *filename, int mode) ATTR_WARN_UNUSED_RESULT ATTR_N
bool BLI_file_is_writable(const char *file) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
bool BLI_file_touch(const char *file) ATTR_NONNULL();
+void BLI_file_size_string(off_t st_size, char *size, size_t len);
#if 0 /* UNUSED */
int BLI_file_gzip(const char *from, const char *to) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
diff --git a/source/blender/blenlib/BLI_fileops_types.h b/source/blender/blenlib/BLI_fileops_types.h
index 0e6eab687ad..e596fa55877 100644
--- a/source/blender/blenlib/BLI_fileops_types.h
+++ b/source/blender/blenlib/BLI_fileops_types.h
@@ -34,6 +34,7 @@
*/
#include <sys/stat.h>
+#include "BLI_listbase.h"
#if defined(WIN32) && !defined(FREE_WINDOWS)
typedef unsigned int mode_t;
@@ -41,6 +42,19 @@ typedef unsigned int mode_t;
struct ImBuf;
+typedef struct CollapsedEntry {
+ /* list that gets populated during file open */
+ ListBase list;
+ /* sorted array of the files for quick access of frames */
+ struct direntry **darray;
+ off_t totalsize;
+ int minframe;
+ int maxframe;
+ int numdigits;
+ int totfiles;
+ int curfra;
+} CollapsedEntry;
+
struct direntry {
mode_t type;
char *relname;
@@ -69,6 +83,10 @@ struct direntry {
int nr;
struct ImBuf *image;
unsigned int selflag; /* selection flag */
+ off_t realsize; /* real size of file */
+ int frame; /* frame of file in a movie sequence */
+
+ CollapsedEntry collapsed_info;
};
struct dirlink {
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index 2c91aca27ef..a3687ae41f7 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -233,6 +233,8 @@ void fill_poly_v2i_n(
/* tri or quad, d can be NULL */
void interp_weights_face_v3(float w[4],
const float a[3], const float b[3], const float c[3], const float d[3], const float p[3]);
+/* also returns three indices of the triangle actually used */
+void interp_weights_face_v3_index(int tri[3], float w[4], const float v1[3], const float v2[3], const float v3[3], const float v4[3], const float co[3]);
void interp_weights_poly_v3(float w[], float v[][3], const int n, const float co[3]);
void interp_weights_poly_v2(float w[], float v[][2], const int n, const float co[2]);
diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h
index 63b5207f941..40aef5eaf2f 100644
--- a/source/blender/blenlib/BLI_path_util.h
+++ b/source/blender/blenlib/BLI_path_util.h
@@ -133,7 +133,7 @@ bool BLI_path_abs(char *path, const char *basepath) ATTR_NONNULL();
bool BLI_path_frame(char *path, int frame, int digits) ATTR_NONNULL();
bool BLI_path_frame_range(char *path, int sta, int end, int digits) ATTR_NONNULL();
bool BLI_path_frame_get(char *path, int *r_frame, int *numdigits) ATTR_NONNULL();
-void BLI_path_frame_strip(char *path, bool setsharp, char *ext) ATTR_NONNULL();
+bool BLI_path_frame_strip(char *path, bool setsharp, char *ext);
bool BLI_path_frame_check_chars(const char *path) ATTR_NONNULL();
bool BLI_path_cwd(char *path) ATTR_NONNULL();
void BLI_path_rel(char *file, const char *relfile) ATTR_NONNULL();
diff --git a/source/blender/blenlib/intern/BLI_filelist.c b/source/blender/blenlib/intern/BLI_filelist.c
index 786eaa74df8..a305ab3bf27 100644
--- a/source/blender/blenlib/intern/BLI_filelist.c
+++ b/source/blender/blenlib/intern/BLI_filelist.c
@@ -289,6 +289,7 @@ static void bli_adddirstrings(struct BuildDirCtx *dir_ctx)
* everyone starts using __USE_FILE_OFFSET64 or equivalent.
*/
size = (double)file->s.st_size;
+ file->realsize = size;
if (size > 1024.0 * 1024.0 * 1024.0 * 1024.0) {
BLI_snprintf(file->size, sizeof(file->size), "%.1f TiB", size / (1024.0 * 1024.0 * 1024.0 * 1024.0));
@@ -364,6 +365,9 @@ void BLI_filelist_duplicate(
if (dest->poin && dup_poin) {
dest->poin = dup_poin(src->poin);
}
+ if (dest->collapsed_info.darray) {
+ dest->collapsed_info.darray = NULL;
+ }
}
}
@@ -384,6 +388,8 @@ void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries, void (
MEM_freeN(entry->path);
if (entry->poin && free_poin)
free_poin(entry->poin);
+ if (entry->collapsed_info.darray)
+ MEM_freeN(entry->collapsed_info.darray);
}
if (filelist != NULL) {
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index 7329a1177a8..aebe030cd31 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -2487,6 +2487,71 @@ void interp_weights_face_v3(float w[4], const float v1[3], const float v2[3], co
}
}
+void interp_weights_face_v3_index(int tri[3], float w[4], const float v1[3], const float v2[3], const float v3[3], const float v4[3], const float co[3])
+{
+ float w2[3];
+
+ w[0] = w[1] = w[2] = w[3] = 0.0f;
+ tri[0] = tri[1] = tri[2] = -1;
+
+ /* first check for exact match */
+ if (equals_v3v3(co, v1)) {
+ w[0] = 1.0f;
+ tri[0] = 0; tri[1] = 1; tri[2] = 3;
+ }
+ else if (equals_v3v3(co, v2)) {
+ w[1] = 1.0f;
+ tri[0] = 0; tri[1] = 1; tri[2] = 3;
+ }
+ else if (equals_v3v3(co, v3)) {
+ w[2] = 1.0f;
+ tri[0] = 1; tri[1] = 2; tri[2] = 3;
+ }
+ else if (v4 && equals_v3v3(co, v4)) {
+ w[3] = 1.0f;
+ tri[0] = 1; tri[1] = 2; tri[2] = 3;
+ }
+ else {
+ /* otherwise compute barycentric interpolation weights */
+ float n1[3], n2[3], n[3];
+ bool degenerate;
+
+ sub_v3_v3v3(n1, v1, v3);
+ if (v4) {
+ sub_v3_v3v3(n2, v2, v4);
+ }
+ else {
+ sub_v3_v3v3(n2, v2, v3);
+ }
+ cross_v3_v3v3(n, n1, n2);
+
+ /* OpenGL seems to split this way, so we do too */
+ if (v4) {
+ degenerate = barycentric_weights(v1, v2, v4, co, n, w);
+ SWAP(float, w[2], w[3]);
+ tri[0] = 0; tri[1] = 1; tri[2] = 3;
+
+ if (degenerate || (w[0] < 0.0f)) {
+ /* if w[1] is negative, co is on the other side of the v1-v3 edge,
+ * so we interpolate using the other triangle */
+ degenerate = barycentric_weights(v2, v3, v4, co, n, w2);
+
+ if (!degenerate) {
+ w[0] = 0.0f;
+ w[1] = w2[0];
+ w[2] = w2[1];
+ w[3] = w2[2];
+ tri[0] = 1; tri[1] = 2; tri[2] = 3;
+ }
+ }
+ }
+ else {
+ barycentric_weights(v1, v2, v3, co, n, w);
+ tri[0] = 0; tri[1] = 1; tri[2] = 2;
+ }
+ }
+}
+
/* return 1 of point is inside triangle, 2 if it's on the edge, 0 if point is outside of triangle */
int barycentric_inside_triangle_v2(const float w[3])
{
diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c
index c1f6cc4b49a..9f16b604432 100644
--- a/source/blender/blenlib/intern/path_util.c
+++ b/source/blender/blenlib/intern/path_util.c
@@ -912,7 +912,7 @@ bool BLI_path_frame_get(char *path, int *r_frame, int *r_numdigits)
return false;
}
-void BLI_path_frame_strip(char *path, bool setsharp, char *ext)
+bool BLI_path_frame_strip(char *path, bool setsharp, char *ext)
{
if (path && *path) {
char *file = (char *)BLI_last_slash(path);
@@ -946,20 +946,41 @@ void BLI_path_frame_strip(char *path, bool setsharp, char *ext)
c++;
- if (numdigits) {
- /* replace the number with the suffix and terminate the string */
- while (numdigits--) {
- if (ext) *ext++ = *suffix;
-
- if (setsharp) *c++ = '#';
- else *c++ = *suffix;
+ if(numdigits) {
+ /* logic here is a bit complex. Idea is: if ext has been provided,
+ * fill it with the extension part and do not keep it in filename
+ * if no ext has been provided, just strip the number or fill it with #
+ */
+ if (ext) {
+ while (*suffix) {
+ *ext++ = *suffix++;
+ }
+ *ext = 0;
- suffix++;
+ if (setsharp) {
+ while (numdigits--) {
+ *c++ = '#';
+ }
+ }
+ *c = 0;
}
- *c = 0;
- if (ext) *ext = 0;
+ else {
+ if (setsharp) {
+ while (numdigits--) {
+ *c++ = '#';
+ }
+ }
+ while (*suffix) {
+ *c++ = *suffix++;
+ }
+ *c = 0;
+ }
+
+ return true;
}
}
+
+ return false;
}
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 9976594ad0b..8c3f3c4f513 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -58,6 +58,7 @@
#include "DNA_armature_types.h"
#include "DNA_actuator_types.h"
#include "DNA_brush_types.h"
+#include "DNA_cache_library_types.h"
#include "DNA_camera_types.h"
#include "DNA_cloth_types.h"
#include "DNA_controller_types.h"
@@ -114,6 +115,7 @@
#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_brush.h"
+#include "BKE_cache_library.h"
#include "BKE_cloth.h"
#include "BKE_constraint.h"
#include "BKE_context.h"
@@ -123,6 +125,7 @@
#include "BKE_fcurve.h"
#include "BKE_global.h" // for G
#include "BKE_group.h"
+#include "BKE_key.h"
#include "BKE_library.h" // for which_libbase
#include "BKE_idcode.h"
#include "BKE_material.h"
@@ -1983,7 +1986,6 @@ static void direct_link_paint_curve(FileData *fd, PaintCurve *pc)
pc->points = newdataadr(fd, pc->points);
}
-
static void direct_link_script(FileData *UNUSED(fd), Script *script)
{
script->id.us = 1;
@@ -1991,6 +1993,96 @@ static void direct_link_script(FileData *UNUSED(fd), Script *script)
}
+/* ************ READ CacheLibrary *************** */
+
+static void lib_link_cache_modifiers_cb(void *userData, CacheLibrary *cachelib, CacheModifier *UNUSED(md), ID **idpoin)
+{
+ FileData *fd = userData;
+
+ *idpoin = newlibadr(fd, cachelib->id.lib, *idpoin);
+ /* hardcoded bad exception; non-object modifier data gets user count (texture, displace) */
+ if (*idpoin && GS((*idpoin)->name)!=ID_OB)
+ (*idpoin)->us++;
+}
+static void lib_link_cache_modifiers(FileData *fd, CacheLibrary *cachelib)
+{
+ CacheModifier *md;
+ for (md = cachelib->modifiers.first; md; md = md->next) {
+ BKE_cache_modifier_foreachIDLink(cachelib, md, lib_link_cache_modifiers_cb, fd);
+
+ /* special cases */
+ switch (md->type) {
+ case eCacheModifierType_StrandsKey: {
+ StrandsKeyCacheModifier *skmd = (StrandsKeyCacheModifier *)md;
+ /* Key is a local ID block, not handled by foreachIDLink */
+ skmd->key = newlibadr_us(fd, cachelib->id.lib, skmd->key);
+ break;
+ }
+ }
+ }
+}
+
+static void lib_link_cache_library(FileData *fd, Main *main)
+{
+ CacheLibrary *cachelib;
+
+ for (cachelib = main->cache_library.first; cachelib; cachelib = cachelib->id.next) {
+ if (cachelib->id.flag & LIB_NEED_LINK) {
+ cachelib->id.flag -= LIB_NEED_LINK;
+
+ cachelib->filter_group = newlibadr_us(fd, cachelib->id.lib, cachelib->filter_group);
+
+ lib_link_cache_modifiers(fd, cachelib);
+ }
+ }
+}
+
+static void direct_link_cache_modifiers(FileData *fd, ListBase *modifiers)
+{
+ CacheModifier *md;
+
+ link_list(fd, modifiers);
+
+ for (md = modifiers->first; md; md = md->next) {
+ /* if modifiers disappear, or for upward compatibility */
+ if (md->type >= NUM_CACHE_MODIFIER_TYPES)
+ md->type = eCacheModifierType_None;
+
+
+ switch (md->type) {
+ case eCacheModifierType_HairSimulation: {
+ HairSimCacheModifier *hsmd = (HairSimCacheModifier *)md;
+ hsmd->sim_params.effector_weights = newdataadr(fd, hsmd->sim_params.effector_weights);
+ hsmd->sim_params.goal_stiffness_mapping = newdataadr(fd, hsmd->sim_params.goal_stiffness_mapping);
+ if (hsmd->sim_params.goal_stiffness_mapping)
+ direct_link_curvemapping(fd, hsmd->sim_params.goal_stiffness_mapping);
+ hsmd->sim_params.bend_stiffness_mapping = newdataadr(fd, hsmd->sim_params.bend_stiffness_mapping);
+ if (hsmd->sim_params.bend_stiffness_mapping)
+ direct_link_curvemapping(fd, hsmd->sim_params.bend_stiffness_mapping);
+ break;
+ }
+ case eCacheModifierType_ForceField: {
+ ForceFieldCacheModifier *ffmd = (ForceFieldCacheModifier *)md;
+ ffmd->vertex_cache = NULL;
+ break;
+ }
+ case eCacheModifierType_StrandsKey: {
+ StrandsKeyCacheModifier *skmd = (StrandsKeyCacheModifier *)md;
+ skmd->edit = NULL;
+ break;
+ }
+ }
+ }
+}
+
+static void direct_link_cache_library(FileData *fd, CacheLibrary *cachelib)
+{
+ direct_link_cache_modifiers(fd, &cachelib->modifiers);
+
+ cachelib->archive_info = NULL; /* runtime */
+}
+
+
/* ************ READ PACKEDFILE *************** */
static PackedFile *direct_link_packedfile(FileData *fd, PackedFile *oldpf)
@@ -2805,6 +2897,10 @@ static void direct_link_nodetree(FileData *fd, bNodeTree *ntree)
NodeShaderScript *nss = (NodeShaderScript *) node->storage;
nss->bytecode = newdataadr(fd, nss->bytecode);
}
+ else if (node->type==SH_NODE_OPENVDB) {
+ NodeShaderOpenVDB *vdb = (NodeShaderOpenVDB *)node->storage;
+ link_list(fd, &vdb->grid_info);
+ }
}
else if (ntree->type==NTREE_COMPOSIT) {
if (ELEM(node->type, CMP_NODE_TIME, CMP_NODE_CURVE_VEC, CMP_NODE_CURVE_RGB, CMP_NODE_HUECORRECT))
@@ -3173,6 +3269,10 @@ static void lib_link_key(FileData *fd, Main *main)
key->ipo = newlibadr_us(fd, key->id.lib, key->ipo); // XXX deprecated - old animation system
key->from = newlibadr(fd, key->id.lib, key->from);
+ /* versioning: initialize extra owner info */
+ if (!key->fromtype && key->from) {
+ BKE_key_set_from_id(key, key->from);
+ }
key->id.flag -= LIB_NEED_LINK;
}
@@ -3216,7 +3316,7 @@ static void direct_link_key(FileData *fd, Key *key)
key->adt = newdataadr(fd, key->adt);
direct_link_animdata(fd, key->adt);
-
+
key->refkey= newdataadr(fd, key->refkey);
for (kb = key->block.first; kb; kb = kb->next) {
@@ -3956,6 +4056,8 @@ static void lib_link_particlesystems(FileData *fd, Object *ob, ID *id, ListBase
BLI_remlink(particles, psys);
MEM_freeN(psys);
}
+
+ psys->key = newlibadr_us(fd, id->lib, psys->key);
}
}
static void direct_link_particlesystems(FileData *fd, ListBase *particles)
@@ -4001,6 +4103,7 @@ static void direct_link_particlesystems(FileData *fd, ListBase *particles)
psys->edit = NULL;
psys->free_edit = NULL;
+ psys->hairedit = NULL;
psys->pathcache = NULL;
psys->childcache = NULL;
BLI_listbase_clear(&psys->pathcachebufs);
@@ -4437,6 +4540,7 @@ static void lib_link_object(FileData *fd, Main *main)
ob->track = newlibadr(fd, ob->id.lib, ob->track);
ob->poselib = newlibadr_us(fd, ob->id.lib, ob->poselib);
ob->dup_group = newlibadr_us(fd, ob->id.lib, ob->dup_group);
+ ob->cache_library = newlibadr_us(fd, ob->id.lib, ob->cache_library);
ob->proxy = newlibadr_us(fd, ob->id.lib, ob->proxy);
if (ob->proxy) {
@@ -4809,6 +4913,16 @@ static void direct_link_modifiers(FileData *fd, ListBase *lb)
BLI_listbase_clear(&smd->domain->ptcaches[1]);
smd->domain->point_cache[1] = NULL;
}
+
+ {
+ OpenVDBCache *cache;
+
+ link_list(fd, &smd->domain->vdb_caches);
+ for (cache = smd->domain->vdb_caches.first; cache; cache = cache->next) {
+ cache->reader = NULL;
+ cache->writer = NULL;
+ }
+ }
}
else if (smd->type == MOD_SMOKE_TYPE_FLOW) {
smd->domain = NULL;
@@ -5020,7 +5134,7 @@ static void direct_link_object(FileData *fd, Object *ob)
* See [#34776, #42780] for more information.
*/
if (fd->memfile || (ob->id.flag & (LIB_EXTERN | LIB_INDIRECT))) {
- ob->mode &= ~(OB_MODE_EDIT | OB_MODE_PARTICLE_EDIT);
+ ob->mode &= ~(OB_MODE_EDIT | OB_MODE_PARTICLE_EDIT | OB_MODE_HAIR_EDIT);
if (!fd->memfile) {
ob->mode &= ~OB_MODE_POSE;
}
@@ -5037,6 +5151,7 @@ static void direct_link_object(FileData *fd, Object *ob)
direct_link_motionpath(fd, ob->mpath);
link_list(fd, &ob->defbase);
+ link_list(fd, &ob->fmaps);
// XXX deprecated - old animation system <<<
direct_link_nlastrips(fd, &ob->nlastrips);
link_list(fd, &ob->constraintChannels);
@@ -5049,6 +5164,8 @@ static void direct_link_object(FileData *fd, Object *ob)
/* do it here, below old data gets converted */
direct_link_modifiers(fd, &ob->modifiers);
+ ob->dup_cache = NULL;
+
link_list(fd, &ob->effect);
paf= ob->effect.first;
while (paf) {
@@ -5356,6 +5473,14 @@ static void lib_link_scene(FileData *fd, Main *main)
sce->toolsettings->particle.shape_object = newlibadr(fd, sce->id.lib, sce->toolsettings->particle.shape_object);
+ {
+ HairEditSettings *hair_edit = &sce->toolsettings->hair_edit;
+ if (hair_edit->brush)
+ hair_edit->brush = newlibadr(fd, sce->id.lib, hair_edit->brush);
+ if (hair_edit->shape_object)
+ hair_edit->shape_object = newlibadr(fd, sce->id.lib, hair_edit->shape_object);
+ }
+
for (base = sce->base.first; base; base = next) {
next = base->next;
@@ -5610,7 +5735,8 @@ static void direct_link_scene(FileData *fd, Scene *sce)
sce->toolsettings->particle.paintcursor = NULL;
sce->toolsettings->particle.scene = NULL;
sce->toolsettings->particle.object = NULL;
-
+ sce->toolsettings->hair_edit.paint_cursor = NULL;
+
/* in rare cases this is needed, see [#33806] */
if (sce->toolsettings->vpaint) {
sce->toolsettings->vpaint->vpaint_prev = NULL;
@@ -5981,6 +6107,7 @@ static void lib_link_screen(FileData *fd, Main *main)
ads->source = newlibadr(fd, sc->id.lib, ads->source);
ads->filter_grp = newlibadr(fd, sc->id.lib, ads->filter_grp);
}
+ sipo->backdrop_camera = newlibadr(fd, sc->id.lib, sipo->backdrop_camera);
}
else if (sl->spacetype == SPACE_BUTS) {
SpaceButs *sbuts = (SpaceButs *)sl;
@@ -6308,6 +6435,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
if (ads->filter_grp)
ads->filter_grp = restore_pointer_by_name(newmain, (ID *)ads->filter_grp, USER_IGNORE);
}
+ sipo->backdrop_camera = restore_pointer_by_name(newmain, (ID *)sipo->backdrop_camera, USER_IGNORE);
/* force recalc of list of channels (i.e. includes calculating F-Curve colors)
* thus preventing the "black curves" problem post-undo
@@ -6541,6 +6669,7 @@ static void direct_link_region(FileData *fd, ARegion *ar, int spacetype)
BLI_listbase_clear(&ar->panels_category);
BLI_listbase_clear(&ar->handlers);
BLI_listbase_clear(&ar->uiblocks);
+ BLI_listbase_clear(&ar->widgetmaps);
ar->headerstr = NULL;
ar->swinid = 0;
ar->type = NULL;
@@ -6676,6 +6805,7 @@ static bool direct_link_screen(FileData *fd, bScreen *sc)
}
v3d->localvd = newdataadr(fd, v3d->localvd);
BLI_listbase_clear(&v3d->afterdraw_transp);
+ BLI_listbase_clear(&v3d->afterdraw_nodepth);
BLI_listbase_clear(&v3d->afterdraw_xray);
BLI_listbase_clear(&v3d->afterdraw_xraytransp);
v3d->properties_storage = NULL;
@@ -7514,6 +7644,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_CL: return "Data from CL";
}
return "Data from Lib Block";
@@ -7700,6 +7831,9 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, int flag, ID
case ID_PC:
direct_link_paint_curve(fd, (PaintCurve *)id);
break;
+ case ID_CL:
+ direct_link_cache_library(fd, (CacheLibrary *)id);
+ break;
}
oldnewmap_free_unused(fd->datamap);
@@ -7893,6 +8027,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_cache_library(fd, main);
lib_link_mesh(fd, main); /* as last: tpage images with users at zero */
@@ -8769,6 +8904,8 @@ static void expand_object(FileData *fd, Main *mainvar, Object *ob)
if (ob->dup_group)
expand_doit(fd, mainvar, ob->dup_group);
+ if (ob->cache_library)
+ expand_doit(fd, mainvar, ob->cache_library);
if (ob->proxy)
expand_doit(fd, mainvar, ob->proxy);
@@ -9043,6 +9180,12 @@ static void expand_gpencil(FileData *fd, Main *mainvar, bGPdata *gpd)
expand_animdata(fd, mainvar, gpd->adt);
}
+static void expand_cache_library(FileData *fd, Main *mainvar, CacheLibrary *cachelib)
+{
+ if (cachelib->filter_group)
+ expand_doit(fd, mainvar, cachelib->filter_group);
+}
+
void BLO_main_expander(void (*expand_doit_func)(void *, Main *, void *))
{
expand_doit = expand_doit_func;
@@ -9140,6 +9283,9 @@ void BLO_expand_main(void *fdhandle, Main *mainvar)
case ID_GD:
expand_gpencil(fd, mainvar, (bGPdata *)id);
break;
+ case ID_CL:
+ expand_cache_library(fd, mainvar, (CacheLibrary *)id);
+ break;
}
do_it = true;
diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c
index 72a320281fe..6a1052969c7 100644
--- a/source/blender/blenloader/intern/versioning_260.c
+++ b/source/blender/blenloader/intern/versioning_260.c
@@ -2285,7 +2285,6 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *main)
if (!MAIN_VERSION_ATLEAST(main, 268, 4)) {
- bScreen *sc;
Object *ob;
for (ob = main->object.first; ob; ob = ob->id.next) {
@@ -2315,26 +2314,6 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
- /*
- * FIX some files have a zoom level of 0, and was checked during the drawing of the node space
- *
- * We moved this check to the do versions to be sure the value makes any sense.
- */
- for (sc = main->screen.first; sc; sc = sc->id.next) {
- ScrArea *sa;
- for (sa = sc->areabase.first; sa; sa = sa->next) {
- SpaceLink *sl;
- for (sl = sa->spacedata.first; sl; sl = sl->next) {
- if (sl->spacetype == SPACE_NODE) {
- SpaceNode *snode = (SpaceNode *)sl;
- if (snode->zoom < 0.02f) {
- snode->zoom = 1.0;
- }
- }
- }
- }
- }
-
for (ob = main->object.first; ob; ob = ob->id.next) {
bSensor *sens;
bTouchSensor *ts;
diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c
index 6ab655e9b45..1d495398fb3 100644
--- a/source/blender/blenloader/intern/versioning_270.c
+++ b/source/blender/blenloader/intern/versioning_270.c
@@ -35,9 +35,11 @@
#define DNA_DEPRECATED_ALLOW
#include "DNA_brush_types.h"
+#include "DNA_cache_library_types.h"
#include "DNA_camera_types.h"
#include "DNA_cloth_types.h"
#include "DNA_constraint_types.h"
+#include "DNA_key_types.h"
#include "DNA_sdna_types.h"
#include "DNA_sequence_types.h"
#include "DNA_space_types.h"
@@ -49,9 +51,11 @@
#include "DNA_linestyle_types.h"
#include "DNA_actuator_types.h"
#include "DNA_view3d_types.h"
+#include "DNA_smoke_types.h"
#include "DNA_genfile.h"
+#include "BKE_colortools.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_node.h"
@@ -443,6 +447,55 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
}
+
+ if (!DNA_struct_elem_find(fd->filesdna, "ClothSimSettings", "float", "bending_damping")) {
+ Object *ob;
+ ModifierData *md;
+ for (ob = main->object.first; ob; ob = ob->id.next) {
+ for (md = ob->modifiers.first; md; md = md->next) {
+ if (md->type == eModifierType_Cloth) {
+ ClothModifierData *clmd = (ClothModifierData*) md;
+ clmd->sim_parms->bending_damping = 0.5f;
+ }
+ else if (md->type == eModifierType_ParticleSystem) {
+ ParticleSystemModifierData *pmd = (ParticleSystemModifierData*) md;
+ if (pmd->psys->clmd) {
+ pmd->psys->clmd->sim_parms->bending_damping = 0.5f;
+ }
+ }
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(main, 272, 3)) {
+ bScreen *sc;
+ for (sc = main->screen.first; sc; sc = sc->id.next) {
+ ScrArea *sa;
+ for (sa = sc->areabase.first; sa; sa = sa->next) {
+ SpaceLink *sl;
+ for (sl = sa->spacedata.first; sl; sl = sl->next) {
+ if (sl->spacetype == SPACE_NODE) {
+ SpaceNode *snode = (SpaceNode *)sl;
+ snode->backdrop_zoom = 1.0;
+ }
+ if (sl->spacetype == SPACE_SEQ) {
+ SpaceSeq *sseq = (SpaceSeq *)sl;
+ sseq->overdrop_zoom = 1.0;
+ }
+
+ }
+ }
+ }
+ }
+
+ if (!DNA_struct_elem_find(fd->filesdna, "ParticleSystem", "float", "hair_preview_factor")) {
+ Object *ob;
+ ParticleSystem *psys;
+ for (ob = main->object.first; ob; ob = ob->id.next) {
+ for(psys = ob->particlesystem.first; psys; psys = psys->next)
+ psys->hair_preview_factor = 100.0f;
+ }
+ }
if (!MAIN_VERSION_ATLEAST(main, 273, 1)) {
#define BRUSH_RAKE (1 << 7)
@@ -467,6 +520,43 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
#undef BRUSH_RAKE
#undef BRUSH_RANDOM_ROTATION
+ if (!DNA_struct_elem_find(fd->filesdna, "ParticleSettings", "float", "clump_noise_size")) {
+ ParticleSettings *part;
+ for (part = main->particle.first; part; part = part->id.next) {
+ part->clump_noise_size = 1.0f;
+ }
+ }
+
+ if (!DNA_struct_elem_find(fd->filesdna, "ParticleSettings", "int", "kink_extra_steps")) {
+ ParticleSettings *part;
+ for (part = main->particle.first; part; part = part->id.next) {
+ part->kink_extra_steps = 4;
+ }
+ }
+
+ if (!DNA_struct_elem_find(fd->filesdna, "MTex", "float", "kinkampfac")) {
+ ParticleSettings *part;
+ for (part = main->particle.first; part; part = part->id.next) {
+ int a;
+ for (a = 0; a < MAX_MTEX; a++) {
+ MTex *mtex = part->mtex[a];
+ if (mtex) {
+ mtex->kinkampfac = 1.0f;
+ }
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(main, 273, 3)) {
+ ParticleSettings *part;
+ for (part = main->particle.first; part; part = part->id.next) {
+ if (part->clumpcurve)
+ part->child_flag |= PART_CHILD_USE_CLUMP_CURVE;
+ if (part->roughcurve)
+ part->child_flag |= PART_CHILD_USE_ROUGH_CURVE;
+ }
+ }
+
/* Customizable Safe Areas */
if (!MAIN_VERSION_ATLEAST(main, 273, 2)) {
if (!DNA_struct_elem_find(fd->filesdna, "Scene", "DisplaySafeAreas", "safe_areas")) {
@@ -491,6 +581,26 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
+ if (!MAIN_VERSION_ATLEAST(main, 273, 4)) {
+ bScreen *sc;
+ for (sc = main->screen.first; sc; sc = sc->id.next) {
+ ScrArea *sa;
+ for (sa = sc->areabase.first; sa; sa = sa->next) {
+ SpaceLink *sl;
+ for (sl = sa->spacedata.first; sl; sl = sl->next) {
+ if (sl->spacetype == SPACE_NODE) {
+ SpaceNode *snode = (SpaceNode *)sl;
+ snode->backdrop_zoom = 1.0;
+ }
+ if (sl->spacetype == SPACE_SEQ) {
+ SpaceSeq *sseq = (SpaceSeq *)sl;
+ sseq->overdrop_zoom = 1.0;
+ }
+ }
+ }
+ }
+ }
+
if (!MAIN_VERSION_ATLEAST(main, 273, 6)) {
if (!DNA_struct_elem_find(fd->filesdna, "ClothSimSettings", "float", "bending_damping")) {
Object *ob;
@@ -608,6 +718,22 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
+ if (!DNA_struct_elem_find(fd->filesdna, "ParticleInstanceModifierData", "float", "particle_amount")) {
+ Object *ob;
+ ModifierData *md;
+
+ for (ob = main->object.first; ob; ob = ob->id.next) {
+ for (md = ob->modifiers.first; md; md = md->next) {
+ if (md->type == eModifierType_ParticleInstance) {
+ ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *)md;
+
+ pimd->particle_amount = 1.0f;
+ pimd->particle_offset = 0.0f;
+ }
+ }
+ }
+ }
+
if (!MAIN_VERSION_ATLEAST(main, 273, 8)) {
Object *ob;
for (ob = main->object.first; ob != NULL; ob = ob->id.next) {
@@ -647,7 +773,27 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
- if (!MAIN_VERSION_ATLEAST(main, 274, 1)) {
+ if (!MAIN_VERSION_ATLEAST(main, 274, 0)) {
+ if (!DNA_struct_elem_find(fd->filesdna, "SpaceNode", "float", "backdrop_zoom")) {
+ bScreen *sc;
+ for (sc = main->screen.first; sc; sc = sc->id.next) {
+ ScrArea *sa;
+ for (sa = sc->areabase.first; sa; sa = sa->next) {
+ SpaceLink *sl;
+ for (sl = sa->spacedata.first; sl; sl = sl->next) {
+ if (sl->spacetype == SPACE_NODE) {
+ SpaceNode *snode = (SpaceNode *)sl;
+ snode->backdrop_zoom = 1.0;
+ }
+ if (sl->spacetype == SPACE_SEQ) {
+ SpaceSeq *sseq = (SpaceSeq *)sl;
+ sseq->overdrop_zoom = 1.0;
+ }
+ }
+ }
+ }
+ }
+
/* particle systems need to be forced to redistribute for jitter mode fix */
{
Object *ob;
@@ -705,6 +851,29 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
} FOREACH_NODETREE_END
}
+ {
+ bScreen *scr;
+ ScrArea *sa;
+ SpaceLink *sl;
+ ARegion *ar;
+ /* Make sure sequencer preview area limits zoom */
+ for (scr = main->screen.first; scr; scr = scr->id.next) {
+ for (sa = scr->areabase.first; sa; sa = sa->next) {
+ for (sl = sa->spacedata.first; sl; sl = sl->next) {
+ if (sl->spacetype == SPACE_SEQ) {
+ ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
+
+ for (ar = lb->first; ar; ar = ar->next) {
+ if (ar->regiontype == RGN_TYPE_WINDOW) {
+ ar->v2d.max[1] = MAXSEQ * 4;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
if (!MAIN_VERSION_ATLEAST(main, 274, 4)) {
SceneRenderView *srv;
wmWindowManager *wm;
@@ -728,6 +897,11 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
SEQ_BEGIN (scene->ed, seq)
{
seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo Display 3d Format");
+ /* patch sequencer scene strips (used to be the same flag before multiview merge)*/
+ if (seq->flag & SEQ_USE_VIEWS) {
+ seq->flag |= SEQ_SCENE_STRIPS;
+ seq->flag &= ~SEQ_USE_VIEWS;
+ }
#define SEQ_USE_PROXY_CUSTOM_DIR (1 << 19)
#define SEQ_USE_PROXY_CUSTOM_FILE (1 << 21)
@@ -797,6 +971,37 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
+ if (!DNA_struct_elem_find(fd->filesdna, "SmokeDomainSettings", "float", "display_thickness")) {
+ Object *ob;
+ ModifierData *md;
+ for (ob = main->object.first; ob; ob = ob->id.next) {
+ for (md = ob->modifiers.first; md; md = md->next) {
+ if (md->type == eModifierType_Smoke) {
+ SmokeModifierData *smd = (SmokeModifierData *)md;
+ if (smd->domain) {
+ smd->domain->display_thickness = 1.0f;
+ }
+ }
+ }
+ }
+ }
+ if (!DNA_struct_elem_find(fd->filesdna, "SpaceIpo", "float", "backdrop_zoom")) {
+ bScreen *sc;
+ for (sc = main->screen.first; sc; sc = sc->id.next) {
+ ScrArea *sa;
+ for (sa = sc->areabase.first; sa; sa = sa->next) {
+ SpaceLink *sl;
+ for (sl = sa->spacedata.first; sl; sl = sl->next) {
+ if (sl->spacetype == SPACE_IPO) {
+ SpaceIpo *sipo = (SpaceIpo *)sl;
+ sipo->backdrop_zoom = 1.0f;
+ sipo->backdrop_opacity = 0.7f;
+ }
+ }
+ }
+ }
+ }
+
if (!MAIN_VERSION_ATLEAST(main, 274, 6)) {
bScreen *screen;
@@ -842,4 +1047,33 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
}
}
}
+
+ if (!DNA_struct_elem_find(fd->filesdna, "HairSimParams", "CurveMapping", "*bend_stiffness_mapping")) {
+ CacheLibrary *cachelib;
+ for (cachelib = main->cache_library.first; cachelib; cachelib = cachelib->id.next) {
+ CacheModifier *md;
+ for (md = cachelib->modifiers.first; md; md = md->next) {
+ if (md->type == eCacheModifierType_HairSimulation) {
+ HairSimCacheModifier *hsmd = (HairSimCacheModifier *)md;
+ {
+ CurveMapping *cm = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ cm->cm[0].curve[0].x = 0.0f;
+ cm->cm[0].curve[0].y = 1.0f;
+ cm->cm[0].curve[1].x = 1.0f;
+ cm->cm[0].curve[1].y = 1.0f;
+ hsmd->sim_params.bend_stiffness_mapping = cm;
+ }
+ }
+ }
+ }
+ }
+
+ /* from_extra has been moved to fromtype, fromindex */
+ if (!DNA_struct_elem_find(fd->filesdna, "Key", "int", "fromindex")) {
+ Key *key;
+ for (key = main->key.first; key; key = key->id.next) {
+ key->fromtype = key->from_extra.type;
+ key->fromindex = key->from_extra.index;
+ }
+ }
}
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index f5954ab75a7..0d7d40666f0 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -97,6 +97,7 @@
#include "DNA_armature_types.h"
#include "DNA_actuator_types.h"
#include "DNA_brush_types.h"
+#include "DNA_cache_library_types.h"
#include "DNA_camera_types.h"
#include "DNA_cloth_types.h"
#include "DNA_constraint_types.h"
@@ -147,6 +148,7 @@
#include "BKE_action.h"
#include "BKE_blender.h"
#include "BKE_bpath.h"
+#include "BKE_cache_library.h"
#include "BKE_curve.h"
#include "BKE_constraint.h"
#include "BKE_global.h" // for G
@@ -869,23 +871,38 @@ static void write_nodetree(WriteData *wd, bNodeTree *ntree)
writestruct(wd, DATA, "bNodeLink", 1, link);
if (node->storage) {
/* could be handlerized at some point, now only 1 exception still */
- if (ntree->type==NTREE_SHADER && (node->type==SH_NODE_CURVE_VEC || node->type==SH_NODE_CURVE_RGB))
- write_curvemapping(wd, node->storage);
- else if (ntree->type==NTREE_SHADER && node->type==SH_NODE_SCRIPT) {
- NodeShaderScript *nss = (NodeShaderScript *)node->storage;
- if (nss->bytecode)
- writedata(wd, DATA, strlen(nss->bytecode)+1, nss->bytecode);
- writestruct(wd, DATA, node->typeinfo->storagename, 1, node->storage);
- }
- else if (ntree->type==NTREE_COMPOSIT && ELEM(node->type, CMP_NODE_TIME, CMP_NODE_CURVE_VEC, CMP_NODE_CURVE_RGB, CMP_NODE_HUECORRECT))
- write_curvemapping(wd, node->storage);
- else if (ntree->type==NTREE_TEXTURE && (node->type==TEX_NODE_CURVE_RGB || node->type==TEX_NODE_CURVE_TIME) )
- write_curvemapping(wd, node->storage);
- else if (ntree->type==NTREE_COMPOSIT && node->type==CMP_NODE_MOVIEDISTORTION) {
- /* pass */
- }
- else
- writestruct(wd, DATA, node->typeinfo->storagename, 1, node->storage);
+ if (ntree->type==NTREE_SHADER) {
+ if (node->type==SH_NODE_CURVE_VEC || node->type==SH_NODE_CURVE_RGB)
+ write_curvemapping(wd, node->storage);
+ else if (node->type==SH_NODE_SCRIPT) {
+ NodeShaderScript *nss = (NodeShaderScript *)node->storage;
+ if (nss->bytecode)
+ writedata(wd, DATA, strlen(nss->bytecode)+1, nss->bytecode);
+ writestruct(wd, DATA, node->typeinfo->storagename, 1, node->storage);
+ }
+ else if (node->type==SH_NODE_OPENVDB) {
+ NodeShaderOpenVDB *vdb = (NodeShaderOpenVDB *)node->storage;
+ writelist(wd, DATA, "OpenVDBGridInfo", &vdb->grid_info);
+ writestruct(wd, DATA, node->typeinfo->storagename, 1, node->storage);
+ }
+ else
+ writestruct(wd, DATA, node->typeinfo->storagename, 1, node->storage);
+ }
+ else if (ntree->type==NTREE_COMPOSIT) {
+ if (ELEM(node->type, CMP_NODE_TIME, CMP_NODE_CURVE_VEC, CMP_NODE_CURVE_RGB, CMP_NODE_HUECORRECT))
+ write_curvemapping(wd, node->storage);
+ else if (node->type==CMP_NODE_MOVIEDISTORTION) {
+ /* pass */
+ }
+ else
+ writestruct(wd, DATA, node->typeinfo->storagename, 1, node->storage);
+ }
+ else if (ntree->type==NTREE_TEXTURE) {
+ if (node->type==TEX_NODE_CURVE_RGB || node->type==TEX_NODE_CURVE_TIME)
+ write_curvemapping(wd, node->storage);
+ else
+ writestruct(wd, DATA, node->typeinfo->storagename, 1, node->storage);
+ }
}
if (node->type==CMP_NODE_OUTPUT_FILE) {
@@ -1494,6 +1511,14 @@ static void write_defgroups(WriteData *wd, ListBase *defbase)
writestruct(wd, DATA, "bDeformGroup", 1, defgroup);
}
+static void write_fmaps(WriteData *wd, ListBase *fbase)
+{
+ bFaceMap *fmap;
+
+ for (fmap=fbase->first; fmap; fmap=fmap->next)
+ writestruct(wd, DATA, "bFaceMap", 1, fmap);
+}
+
static void write_modifiers(WriteData *wd, ListBase *modbase)
{
ModifierData *md;
@@ -1524,6 +1549,7 @@ static void write_modifiers(WriteData *wd, ListBase *modbase)
}
else if (md->type==eModifierType_Smoke) {
SmokeModifierData *smd = (SmokeModifierData*) md;
+ OpenVDBCache *cache;
if (smd->type & MOD_SMOKE_TYPE_DOMAIN) {
if (smd->domain) {
@@ -1546,6 +1572,12 @@ static void write_modifiers(WriteData *wd, ListBase *modbase)
writestruct(wd, DATA, "EffectorWeights", 1, smd->domain->effector_weights);
}
+
+ cache = smd->domain->vdb_caches.first;
+ for (; cache; cache = cache->next) {
+ writestruct(wd, DATA, "OpenVDBCache", 1, cache);
+ }
+
}
else if (smd->type & MOD_SMOKE_TYPE_FLOW)
writestruct(wd, DATA, "SmokeFlowSettings", 1, smd->flow);
@@ -1664,6 +1696,7 @@ static void write_objects(WriteData *wd, ListBase *idbase)
write_pose(wd, ob->pose);
write_defgroups(wd, &ob->defbase);
+ write_fmaps(wd, &ob->fmaps);
write_constraints(wd, &ob->constraints);
write_motionpath(wd, ob->mpath);
@@ -1949,6 +1982,10 @@ static void write_customdata(WriteData *wd, ID *id, int count, CustomData *data,
else if (layer->type == CD_GRID_PAINT_MASK) {
write_grid_paint_mask(wd, count, layer->data);
}
+ else if (layer->type == CD_FACEMAP) {
+ const int *layer_data = layer->data;
+ writedata(wd, DATA, sizeof(*layer_data) * count, layer_data);
+ }
else {
CustomData_file_write_info(layer->type, &structname, &structnum);
if (structnum) {
@@ -2765,7 +2802,10 @@ static void write_screens(WriteData *wd, ListBase *scrbase)
writestruct(wd, DATA, "SpaceIpo", 1, sl);
if (sipo->ads) writestruct(wd, DATA, "bDopeSheet", 1, sipo->ads);
-
+
+ if (sipo->backdrop_camera)
+ writestruct(wd, DATA, "Object", 1, sipo->backdrop_camera);
+
/* reenable ghost curves */
sipo->ghostCurves= tmpGhosts;
}
@@ -3541,6 +3581,46 @@ static void write_linestyles(WriteData *wd, ListBase *idbase)
}
}
+static void write_cache_modifiers(WriteData *wd, CacheLibrary *cachelib)
+{
+ CacheModifier *md;
+ for (md = cachelib->modifiers.first; md; md = md->next) {
+ const char *struct_name = BKE_cache_modifier_type_struct_name(md->type);
+ if (!struct_name || struct_name[0] == '\0')
+ continue;
+
+ writestruct(wd, DATA, struct_name, 1, md);
+
+ switch (md->type) {
+ case eCacheModifierType_HairSimulation: {
+ HairSimCacheModifier *hsmd = (HairSimCacheModifier *)md;
+ writestruct(wd, DATA, "EffectorWeights", 1, hsmd->sim_params.effector_weights);
+ if (hsmd->sim_params.goal_stiffness_mapping)
+ write_curvemapping(wd, hsmd->sim_params.goal_stiffness_mapping);
+ if (hsmd->sim_params.bend_stiffness_mapping)
+ write_curvemapping(wd, hsmd->sim_params.bend_stiffness_mapping);
+ break;
+ }
+ }
+ }
+}
+
+static void write_cachelibraries(WriteData *wd, ListBase *idbase)
+{
+ CacheLibrary *cachelib;
+
+ for (cachelib = idbase->first; cachelib; cachelib = cachelib->id.next) {
+ if (cachelib->id.us > 0 || wd->current) {
+
+ writestruct(wd, ID_CL, "CacheLibrary", 1, cachelib);
+ if (cachelib->id.properties)
+ IDP_WriteProperty(cachelib->id.properties, wd);
+
+ write_cache_modifiers(wd, cachelib);
+ }
+ }
+}
+
/* context is usually defined by WM, two cases where no WM is available:
* - for forward compatibility, curscreen has to be saved
* - for undofile, curscene needs to be saved */
@@ -3669,6 +3749,7 @@ static int write_file_handle(
write_scripts (wd, &mainvar->script);
write_gpencils (wd, &mainvar->gpencil);
write_linestyles(wd, &mainvar->linestyle);
+ write_cachelibraries(wd, &mainvar->cache_library);
write_libraries(wd, mainvar->next);
if (write_user_block) {
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt
index 66ee184a5bc..5d5205e33c2 100644
--- a/source/blender/bmesh/CMakeLists.txt
+++ b/source/blender/bmesh/CMakeLists.txt
@@ -116,6 +116,10 @@ set(SRC
intern/bmesh_queries.c
intern/bmesh_queries.h
intern/bmesh_queries_inline.h
+ intern/bmesh_strands.c
+ intern/bmesh_strands.h
+ intern/bmesh_strands_conv.c
+ intern/bmesh_strands_conv.h
intern/bmesh_structure.c
intern/bmesh_structure.h
intern/bmesh_structure_inline.h
diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h
index 87b1818fa5d..7ba700d17a2 100644
--- a/source/blender/bmesh/bmesh.h
+++ b/source/blender/bmesh/bmesh.h
@@ -256,6 +256,8 @@ extern "C" {
#include "intern/bmesh_mesh_validate.h"
#include "intern/bmesh_mods.h"
#include "intern/bmesh_operators.h"
+#include "intern/bmesh_strands.h"
+#include "intern/bmesh_strands_conv.h"
#include "intern/bmesh_polygon.h"
#include "intern/bmesh_queries.h"
#include "intern/bmesh_walkers.h"
diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h
index ada0fabd28e..058fec548a3 100644
--- a/source/blender/bmesh/bmesh_class.h
+++ b/source/blender/bmesh/bmesh_class.h
@@ -276,8 +276,16 @@ enum {
#define BM_CHECK_TYPE_ELEM(ele) \
CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_ELEM_NONCONST, _BM_GENERIC_TYPE_ELEM_CONST)
+#ifndef __cplusplus
#define BM_CHECK_TYPE_ELEM_ASSIGN(ele) \
(BM_CHECK_TYPE_ELEM(ele)), ele
+#else
+/* for C++: cast the lhs to a void*,
+ * because C++ does not allow implicit void* casting of the rhs
+ */
+#define BM_CHECK_TYPE_ELEM_ASSIGN(ele) \
+ (BM_CHECK_TYPE_ELEM(ele), CHECK_TYPE_NONCONST(ele)), *(void**)(&(ele))
+#endif
/* BMHeader->hflag (char) */
enum {
diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c
index 6e468bf44f2..d5c5b20723e 100644
--- a/source/blender/bmesh/intern/bmesh_interp.c
+++ b/source/blender/bmesh/intern/bmesh_interp.c
@@ -911,6 +911,34 @@ void BM_elem_float_data_set(CustomData *cd, void *element, int type, const float
if (f) *f = val;
}
+float BM_elem_float_data_named_get(CustomData *cd, void *element, int type, const char *name)
+{
+ const float *f = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name);
+ return f ? *f : 0.0f;
+}
+
+void BM_elem_float_data_named_set(CustomData *cd, void *element, int type, const char *name, const float val)
+{
+ float *f = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name);
+ if (f) *f = val;
+}
+
+void BM_elem_meshsample_data_named_get(CustomData *cd, void *element, int type, const char *name, MSurfaceSample *val)
+{
+ const MSurfaceSample *s = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name);
+ if (s)
+ memcpy(val, s, sizeof(MSurfaceSample));
+ else
+ memset(val, 0, sizeof(MSurfaceSample));
+}
+
+void BM_elem_meshsample_data_named_set(CustomData *cd, void *element, int type, const char *name, const MSurfaceSample *val)
+{
+ MSurfaceSample *s = CustomData_bmesh_get_named(cd, ((BMHeader *)element)->data, type, name);
+ if (s)
+ memcpy(s, val, sizeof(MSurfaceSample));
+}
+
/** \name Loop interpolation functions: BM_vert_loop_groups_data_layer_***
*
* Handling loop custom-data such as UV's, while keeping contiguous fans is rather tedious.
diff --git a/source/blender/bmesh/intern/bmesh_interp.h b/source/blender/bmesh/intern/bmesh_interp.h
index 969e92f37db..6168a655c93 100644
--- a/source/blender/bmesh/intern/bmesh_interp.h
+++ b/source/blender/bmesh/intern/bmesh_interp.h
@@ -29,6 +29,8 @@
struct LinkNode;
struct MemArena;
+struct MSurfaceSample;
+
void BM_loop_interp_multires(BMesh *bm, BMLoop *l_dst, const BMFace *f_src);
void BM_vert_interp_from_face(BMesh *bm, BMVert *v_dst, const BMFace *f_src);
@@ -44,6 +46,10 @@ void BM_data_layer_copy(BMesh *bm, CustomData *data, int type, int src_n, int d
float BM_elem_float_data_get(CustomData *cd, void *element, int type);
void BM_elem_float_data_set(CustomData *cd, void *element, int type, const float val);
+float BM_elem_float_data_named_get(CustomData *cd, void *element, int type, const char *name);
+void BM_elem_float_data_named_set(CustomData *cd, void *element, int type, const char *name, const float val);
+void BM_elem_meshsample_data_named_get(CustomData *cd, void *element, int type, const char *name, struct MSurfaceSample *val);
+void BM_elem_meshsample_data_named_set(CustomData *cd, void *element, int type, const char *name, const struct MSurfaceSample *val);
void BM_face_interp_from_face_ex(
BMesh *bm, BMFace *f_dst, const BMFace *f_src, const bool do_vertex,
diff --git a/source/blender/bmesh/intern/bmesh_iterators_inline.h b/source/blender/bmesh/intern/bmesh_iterators_inline.h
index e68440021e6..43b054d7845 100644
--- a/source/blender/bmesh/intern/bmesh_iterators_inline.h
+++ b/source/blender/bmesh/intern/bmesh_iterators_inline.h
@@ -29,6 +29,8 @@
#ifndef __BMESH_ITERATORS_INLINE_H__
#define __BMESH_ITERATORS_INLINE_H__
+#include "BLI_mempool.h"
+
/* inline here optimizes out the switch statement when called with
* constant values (which is very common), nicer for loop-in-loop situations */
@@ -43,7 +45,6 @@ BLI_INLINE void *BM_iter_step(BMIter *iter)
return iter->step(iter);
}
-
/**
* \brief Iterator Init
*
diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.c b/source/blender/bmesh/intern/bmesh_mesh_conv.c
index 24d70cefb2e..a0ef12c28db 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_conv.c
+++ b/source/blender/bmesh/intern/bmesh_mesh_conv.c
@@ -98,6 +98,9 @@
#include "bmesh.h"
#include "intern/bmesh_private.h" /* for element checking */
+/* XXX stupid hack: linker otherwise strips bmesh_strands_conv.c because it is not used inside bmesh */
+void *__dummy_hack__ = &BM_strands_count_psys_keys;
+
/**
* Currently this is only used for Python scripts
* which may fail to keep matching UV/TexFace layers.
@@ -226,6 +229,17 @@ void BM_mesh_bm_from_me(
BMesh *bm, Mesh *me,
const bool calc_face_normal, const bool set_key, int act_key_nr)
{
+ BM_mesh_bm_from_me_ex(bm, me, CD_MASK_BMESH, calc_face_normal, set_key, act_key_nr);
+}
+
+/**
+ * \brief Mesh -> BMesh
+ *
+ * \warning This function doesn't calculate face normals.
+ */
+void BM_mesh_bm_from_me_ex(BMesh *bm, Mesh *me, CustomDataMask mask,
+ const bool calc_face_normal, const bool set_key, int act_key_nr)
+{
MVert *mvert;
MEdge *medge;
MLoop *mloop;
@@ -251,10 +265,10 @@ void BM_mesh_bm_from_me(
if (!me || !me->totvert) {
if (me) { /*no verts? still copy customdata layout*/
- CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_ASSIGN, 0);
- CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_ASSIGN, 0);
- CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_ASSIGN, 0);
- CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_ASSIGN, 0);
+ CustomData_copy(&me->vdata, &bm->vdata, mask, CD_ASSIGN, 0);
+ CustomData_copy(&me->edata, &bm->edata, mask, CD_ASSIGN, 0);
+ CustomData_copy(&me->ldata, &bm->ldata, mask, CD_ASSIGN, 0);
+ CustomData_copy(&me->pdata, &bm->pdata, mask, CD_ASSIGN, 0);
CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT);
CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE);
@@ -266,10 +280,10 @@ void BM_mesh_bm_from_me(
vtable = MEM_mallocN(sizeof(void **) * me->totvert, "mesh to bmesh vtable");
- CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0);
- CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0);
- CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0);
- CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0);
+ CustomData_copy(&me->vdata, &bm->vdata, mask, CD_CALLOC, 0);
+ CustomData_copy(&me->edata, &bm->edata, mask, CD_CALLOC, 0);
+ CustomData_copy(&me->ldata, &bm->ldata, mask, CD_CALLOC, 0);
+ CustomData_copy(&me->pdata, &bm->pdata, mask, CD_CALLOC, 0);
/* make sure uv layer names are consisten */
totuv = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY);
@@ -570,6 +584,11 @@ BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
void BM_mesh_bm_to_me(BMesh *bm, Mesh *me, bool do_tessface)
{
+ BM_mesh_bm_to_me_ex(bm, me, CD_MASK_MESH, do_tessface);
+}
+
+void BM_mesh_bm_to_me_ex(BMesh *bm, Mesh *me, CustomDataMask mask, bool do_tessface)
+{
MLoop *mloop;
MPoly *mpoly;
MVert *mvert, *oldverts;
@@ -629,10 +648,10 @@ void BM_mesh_bm_to_me(BMesh *bm, Mesh *me, bool do_tessface)
me->totface = 0;
me->act_face = -1;
- CustomData_copy(&bm->vdata, &me->vdata, CD_MASK_MESH, CD_CALLOC, me->totvert);
- CustomData_copy(&bm->edata, &me->edata, CD_MASK_MESH, CD_CALLOC, me->totedge);
- CustomData_copy(&bm->ldata, &me->ldata, CD_MASK_MESH, CD_CALLOC, me->totloop);
- CustomData_copy(&bm->pdata, &me->pdata, CD_MASK_MESH, CD_CALLOC, me->totpoly);
+ CustomData_copy(&bm->vdata, &me->vdata, mask, CD_CALLOC, me->totvert);
+ CustomData_copy(&bm->edata, &me->edata, mask, CD_CALLOC, me->totedge);
+ CustomData_copy(&bm->ldata, &me->ldata, mask, CD_CALLOC, me->totloop);
+ CustomData_copy(&bm->pdata, &me->pdata, mask, CD_CALLOC, me->totpoly);
CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert);
CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge);
diff --git a/source/blender/bmesh/intern/bmesh_mesh_conv.h b/source/blender/bmesh/intern/bmesh_mesh_conv.h
index ce286f6c662..0d235a128ab 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_conv.h
+++ b/source/blender/bmesh/intern/bmesh_mesh_conv.h
@@ -32,7 +32,10 @@
* \ingroup bmesh
*/
+#include "BLI_sys_types.h"
+
struct Mesh;
+typedef uint64_t CustomDataMask;
void BM_mesh_cd_validate(BMesh *bm);
void BM_mesh_cd_flag_ensure(BMesh *bm, struct Mesh *mesh, const char cd_flag);
@@ -42,6 +45,9 @@ char BM_mesh_cd_flag_from_bmesh(BMesh *bm);
void BM_mesh_bm_from_me(
BMesh *bm, struct Mesh *me,
const bool calc_face_normal, const bool set_key, int act_key_nr);
+void BM_mesh_bm_from_me_ex(BMesh *bm, struct Mesh *me, CustomDataMask mask,
+ const bool calc_face_normal, const bool set_key, int act_key_nr);
void BM_mesh_bm_to_me(BMesh *bm, struct Mesh *me, bool do_tessface);
+void BM_mesh_bm_to_me_ex(BMesh *bm, struct Mesh *me, CustomDataMask mask, bool do_tessface);
#endif /* __BMESH_MESH_CONV_H__ */
diff --git a/source/blender/bmesh/intern/bmesh_operators_private.h b/source/blender/bmesh/intern/bmesh_operators_private.h
index 5548ee7c361..c0896691fd3 100644
--- a/source/blender/bmesh/intern/bmesh_operators_private.h
+++ b/source/blender/bmesh/intern/bmesh_operators_private.h
@@ -54,6 +54,7 @@ void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op);
void bmo_create_monkey_exec(BMesh *bm, BMOperator *op);
void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op);
void bmo_create_vert_exec(BMesh *bm, BMOperator *op);
+//void bmo_create_strand_exec(BMesh *bm, BMOperator *op);
void bmo_delete_exec(BMesh *bm, BMOperator *op);
void bmo_dissolve_edges_exec(BMesh *bm, BMOperator *op);
void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op);
diff --git a/source/blender/bmesh/intern/bmesh_strands.c b/source/blender/bmesh/intern/bmesh_strands.c
new file mode 100644
index 00000000000..ae76c4761ad
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_strands.c
@@ -0,0 +1,145 @@
+/*
+ * ***** 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): Lukas Toenne.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/bmesh/intern/bmesh_strands.c
+ * \ingroup bmesh
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+#include "BLI_mempool.h"
+
+#include "bmesh.h"
+#include "bmesh_private.h"
+
+/*
+ * STRANDS OF MESH CALLBACKS
+ */
+
+void bmstranditer__strands_of_mesh_begin(struct BMIter__elem_of_mesh *iter)
+{
+ BLI_mempool_iternew(iter->pooliter.pool, &iter->pooliter);
+}
+
+void *bmstranditer__strands_of_mesh_step(struct BMIter__elem_of_mesh *iter)
+{
+ BMVert *v;
+
+ do {
+ v = BLI_mempool_iterstep(&iter->pooliter);
+ } while (v && !BM_strands_vert_is_root(v));
+
+ return v;
+}
+
+/*
+ * VERTS OF STRAND CALLBACKS
+ */
+
+/* BMIter__vert_of_strand is not included in the union in BMIter, just make sure it is big enough */
+BLI_STATIC_ASSERT(sizeof(BMIter__vert_of_strand) <= sizeof(BMIter), "BMIter must be at least as large as BMIter__vert_of_strand")
+
+void bmstranditer__verts_of_strand_begin(struct BMIter__vert_of_strand *iter)
+{
+ iter->e_next = iter->v_next->e;
+}
+
+void *bmstranditer__verts_of_strand_step(struct BMIter__vert_of_strand *iter)
+{
+ BMVert *v_curr = iter->v_next;
+
+ if (iter->e_next) {
+ BMEdge *e_first = iter->e_next;
+
+ /* select the other vertex of the current edge */
+ iter->v_next = (iter->v_next == iter->e_next->v1 ? iter->e_next->v2 : iter->e_next->v1);
+
+ /* select the next edge of the current vertex */
+ iter->e_next = bmesh_disk_edge_next(iter->e_next, iter->v_next);
+ if (iter->e_next == e_first) {
+ /* only one edge means the last segment, terminate */
+ iter->e_next = NULL;
+ }
+ }
+ else
+ iter->v_next = NULL; /* last vertex, terminate */
+
+ return v_curr;
+}
+
+/* ------------------------------------------------------------------------- */
+
+int BM_strands_count(BMesh *bm)
+{
+ BMVert *v;
+ BMIter iter;
+
+ int count = 0;
+ BM_ITER_STRANDS(v, &iter, bm, BM_STRANDS_OF_MESH) {
+ ++count;
+ }
+
+ return count;
+}
+
+int BM_strands_keys_count(BMVert *root)
+{
+ BMVert *v;
+ BMIter iter;
+
+ int count = 0;
+ BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) {
+ ++count;
+ }
+
+ return count;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Create a new strand */
+BMVert *BM_strands_create(BMesh *bm, int len, bool set_defaults)
+{
+ float co[3] = {0.0f, 0.0f, 0.0f};
+
+ BMVert *root, *v = NULL, *vprev;
+ int k;
+
+ for (k = 0; k < len; ++k) {
+ vprev = v;
+ v = BM_vert_create(bm, co, NULL, set_defaults ? BM_CREATE_NOP : BM_CREATE_SKIP_CD);
+
+ zero_v3(v->no);
+
+ /* root */
+ if (k == 0) {
+ root = v;
+ }
+ else {
+ /*BMEdge *e =*/ BM_edge_create(bm, vprev, v, NULL, set_defaults ? BM_CREATE_NOP : BM_CREATE_SKIP_CD);
+ }
+ }
+
+ return root;
+}
diff --git a/source/blender/bmesh/intern/bmesh_strands.h b/source/blender/bmesh/intern/bmesh_strands.h
new file mode 100644
index 00000000000..cd4267f0bb3
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_strands.h
@@ -0,0 +1,215 @@
+/*
+ * ***** 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): Lukas Toenne.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMESH_STRANDS_H__
+#define __BMESH_STRANDS_H__
+
+/** \file blender/bmesh/intern/bmesh_strands.h
+ * \ingroup bmesh
+ */
+
+#include "BLI_utildefines.h"
+
+#include "bmesh.h"
+#include "bmesh_queries.h"
+#include "bmesh_structure.h"
+
+/* True if v is the root of a strand */
+BLI_INLINE bool BM_strands_vert_is_root(BMVert *v)
+{
+ BMEdge *e_first = v->e;
+ BMEdge *e_next;
+
+ if (!e_first)
+ return true; /* single vertex is both root and tip */
+ e_next = bmesh_disk_edge_next(e_first, v);
+
+ /* with a single edge, the vertex is either first or last of the curve;
+ * first vertex is defined as the root
+ */
+ if (e_next == e_first) {
+ if (e_first->v1 == v)
+ return true;
+ }
+ return false;
+}
+
+/* True if v is the tip of a strand */
+BLI_INLINE bool BM_strands_vert_is_tip(BMVert *v)
+{
+ BMEdge *e_first = v->e;
+ BMEdge *e_next;
+
+ if (!e_first)
+ return true; /* single vertex is both root and tip */
+ e_next = bmesh_disk_edge_next(e_first, v);
+
+ /* with a single edge, the vertex is either first or last of the curve;
+ * last vertex is defined as the tip
+ */
+ if (e_next == e_first) {
+ if (e_first->v2 == v)
+ return true;
+ }
+ return false;
+}
+
+/* Next vertex on a strand */
+BLI_INLINE BMVert *BM_strands_vert_next(BMVert *v)
+{
+ BMEdge *e_first = v->e;
+ BMEdge *e_next;
+
+ /* one of the edges leads to the previous vertex */
+ if (e_first) {
+ if (e_first->v1 == v)
+ return e_first->v2;
+
+ e_next = bmesh_disk_edge_next(e_first, v);
+ if (e_next->v1 == v)
+ return e_next->v2;
+ }
+ return NULL;
+}
+
+/* Previous vertex on a strand */
+BLI_INLINE BMVert *BM_strands_vert_prev(BMVert *v)
+{
+ BMEdge *e_first = v->e;
+ BMEdge *e_next;
+
+ /* one of the edges leads to the previous vertex */
+ if (e_first) {
+ if (e_first->v2 == v)
+ return e_first->v1;
+
+ e_next = bmesh_disk_edge_next(e_first, v);
+ if (e_next->v2 == v)
+ return e_next->v1;
+ }
+ return NULL;
+}
+
+int BM_strands_count(BMesh *bm);
+int BM_strands_keys_count(BMVert *root);
+
+/* Create a new strand */
+struct BMVert *BM_strands_create(struct BMesh *bm, int len, bool set_defaults);
+
+/* ==== Iterators ==== */
+
+typedef enum BMStrandsIterType {
+ BM_STRANDS_OF_MESH,
+ BM_VERTS_OF_STRAND,
+} BMStrandsIterType;
+
+#define BM_ITER_STRANDS(ele, iter, bm, itype) \
+ for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, bm, itype, NULL); \
+ ele; \
+ BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter))
+
+#define BM_ITER_STRANDS_INDEX(ele, iter, bm, itype, indexvar) \
+ for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, bm, itype, NULL), indexvar = 0; \
+ ele; \
+ BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter), (indexvar)++)
+
+#define BM_ITER_STRANDS_ELEM(ele, iter, data, itype) \
+ for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, NULL, itype, data); \
+ ele; \
+ BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter))
+
+#define BM_ITER_STRANDS_ELEM_INDEX(ele, iter, data, itype, indexvar) \
+ for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_strand_iter_new(iter, NULL, itype, data), indexvar = 0; \
+ ele; \
+ BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter), (indexvar)++)
+
+typedef struct BMIter__vert_of_strand {
+ BMVert *v_next;
+ BMEdge *e_next;
+} BMIter__vert_of_strand;
+
+void bmstranditer__strands_of_mesh_begin(struct BMIter__elem_of_mesh *iter);
+void *bmstranditer__strands_of_mesh_step(struct BMIter__elem_of_mesh *iter);
+
+void bmstranditer__verts_of_strand_begin(struct BMIter__vert_of_strand *iter);
+void *bmstranditer__verts_of_strand_step(struct BMIter__vert_of_strand *iter);
+
+BLI_INLINE bool BM_strand_iter_init(BMIter *iter, BMesh *bm, const char itype, void *data)
+{
+ /* int argtype; */
+ iter->itype = itype;
+
+ /* inlining optimizes out this switch when called with the defined type */
+ switch ((BMStrandsIterType)itype) {
+ case BM_STRANDS_OF_MESH:
+ BLI_assert(bm != NULL);
+ BLI_assert(data == NULL);
+ iter->begin = (BMIter__begin_cb)bmstranditer__strands_of_mesh_begin;
+ iter->step = (BMIter__step_cb)bmstranditer__strands_of_mesh_step;
+ iter->data.elem_of_mesh.pooliter.pool = bm->vpool;
+ break;
+ case BM_VERTS_OF_STRAND: {
+ BMVert *root;
+
+ BLI_assert(data != NULL);
+ BLI_assert(((BMElem *)data)->head.htype == BM_VERT);
+ root = (BMVert *)data;
+ BLI_assert(BM_strands_vert_is_root(root));
+ iter->begin = (BMIter__begin_cb)bmstranditer__verts_of_strand_begin;
+ iter->step = (BMIter__step_cb)bmstranditer__verts_of_strand_step;
+ ((BMIter__vert_of_strand *)(&iter->data))->v_next = root;
+ break;
+ }
+ default:
+ /* fallback to regular bmesh iterator */
+ return BM_iter_init(iter, bm, itype, data);
+ break;
+ }
+
+ iter->begin(iter);
+
+ return true;
+}
+
+/**
+ * \brief Iterator New
+ *
+ * Takes a bmesh iterator structure and fills
+ * it with the appropriate function pointers based
+ * upon its type and then calls BMeshIter_step()
+ * to return the first element of the iterator.
+ *
+ */
+BLI_INLINE void *BM_strand_iter_new(BMIter *iter, BMesh *bm, const char itype, void *data)
+{
+ if (LIKELY(BM_strand_iter_init(iter, bm, itype, data))) {
+ return BM_iter_step(iter);
+ }
+ else {
+ return NULL;
+ }
+}
+
+#define BM_strand_iter_new(iter, bm, itype, data) \
+ (BM_ITER_CHECK_TYPE_DATA(data), BM_strand_iter_new(iter, bm, itype, data))
+
+#endif /* __BMESH_STRANDS_H__ */
diff --git a/source/blender/bmesh/intern/bmesh_strands_conv.c b/source/blender/bmesh/intern/bmesh_strands_conv.c
new file mode 100644
index 00000000000..50ea50c9299
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_strands_conv.c
@@ -0,0 +1,1314 @@
+/*
+ * ***** 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): Lukas Toenne.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/bmesh/intern/bmesh_strands_conv.c
+ * \ingroup bmesh
+ *
+ * BM mesh conversion functions.
+ */
+
+#include "DNA_cache_library_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_key_types.h"
+#include "DNA_strands_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "BKE_cache_library.h"
+#include "BKE_customdata.h"
+#include "BKE_key.h"
+#include "BKE_main.h"
+#include "BKE_mesh_sample.h"
+#include "BKE_strands.h"
+#include "BKE_particle.h"
+
+#include "bmesh.h"
+#include "intern/bmesh_private.h" /* for element checking */
+
+const char *CD_HAIR_SEGMENT_LENGTH = "HAIR_SEGMENT_LENGTH";
+const char *CD_HAIR_MASS = "HAIR_MASS";
+const char *CD_HAIR_WEIGHT = "HAIR_WEIGHT";
+const char *CD_HAIR_ROOT_LOCATION = "HAIR_ROOT_LOCATION";
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * Currently this is only used for Python scripts
+ * which may fail to keep matching UV/TexFace layers.
+ *
+ * \note This should only perform any changes in exceptional cases,
+ * if we need this to be faster we could inline #BM_data_layer_add and only
+ * call #update_data_blocks once at the end.
+ */
+void BM_strands_cd_validate(BMesh *UNUSED(bm))
+{
+}
+
+void BM_strands_cd_flag_ensure(BMesh *bm, const char cd_flag)
+{
+ const char cd_flag_all = BM_strands_cd_flag_from_bmesh(bm) | cd_flag;
+ BM_strands_cd_flag_apply(bm, cd_flag_all);
+}
+
+void BM_strands_cd_flag_apply(BMesh *bm, const char UNUSED(cd_flag))
+{
+ /* CustomData_bmesh_init_pool() must run first */
+ BLI_assert(bm->vdata.totlayer == 0 || bm->vdata.pool != NULL);
+ BLI_assert(bm->edata.totlayer == 0 || bm->edata.pool != NULL);
+
+ if (CustomData_get_named_layer_index(&bm->vdata, CD_PROP_FLT, CD_HAIR_MASS) < 0) {
+ BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_FLT, CD_HAIR_MASS);
+ }
+ if (CustomData_get_named_layer_index(&bm->vdata, CD_PROP_FLT, CD_HAIR_WEIGHT) < 0) {
+ BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_FLT, CD_HAIR_WEIGHT);
+ }
+ if (CustomData_get_named_layer_index(&bm->vdata, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION) < 0) {
+ BM_data_layer_add_named(bm, &bm->vdata, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION);
+ }
+ if (CustomData_get_named_layer_index(&bm->vdata, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH) < 0) {
+ BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH);
+ }
+}
+
+char BM_strands_cd_flag_from_bmesh(BMesh *UNUSED(bm))
+{
+ char cd_flag = 0;
+ return cd_flag;
+}
+
+/* ------------------------------------------------------------------------- */
+/* CacheLibrary */
+
+static KeyBlock *bm_set_shapekey_from_strands_key(BMesh *bm, Strands *strands, Key *key, int act_key_nr)
+{
+ int totvert = strands->totverts;
+ KeyBlock *actkey, *block;
+ int i, j;
+
+ if (!key) {
+ return NULL;
+ }
+
+ if (act_key_nr != 0)
+ actkey = BLI_findlink(&key->block, act_key_nr - 1);
+ else
+ actkey = NULL;
+
+ CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0);
+
+ if (actkey && actkey->totelem == totvert) {
+ bm->shapenr = act_key_nr;
+ }
+
+ for (i = 0, block = key->block.first; block; block = block->next, i++) {
+ CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY,
+ CD_ASSIGN, NULL, 0, block->name);
+
+ j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i);
+ bm->vdata.layers[j].uid = block->uid;
+ }
+
+ return actkey;
+}
+
+/* create vertex and edge data for BMesh based on strand data */
+static void bm_make_strands(BMesh *bm, Strands *strands, Key *key, struct DerivedMesh *UNUSED(emitter_dm), float mat[4][4], float (*keyco)[3], int cd_shape_keyindex_offset)
+{
+ KeyBlock *block;
+ StrandIterator it_strand;
+
+ int vindex, eindex;
+ BMVert *v = NULL, *v_prev;
+ BMEdge *e;
+
+ vindex = 0;
+ eindex = 0;
+ for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ StrandVertexIterator it_vert;
+
+ for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) {
+ float co[3];
+
+ copy_v3_v3(co, keyco ? keyco[vindex] : it_vert.vertex->co);
+ /* transform to duplicator local space */
+ mul_m4_v3(mat, co);
+
+ v_prev = v;
+ v = BM_vert_create(bm, co, NULL, BM_CREATE_SKIP_CD);
+ BM_elem_index_set(v, vindex); /* set_ok */
+
+ /* transfer flag */
+// v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT);
+
+ /* this is necessary for selection counts to work properly */
+// if (hkey->editflag & SELECT) {
+// BM_vert_select_set(bm, v, true);
+// }
+
+// normal_short_to_float_v3(v->no, mvert->no);
+
+ /* Copy Custom Data */
+// CustomData_to_bmesh_block(&me->vdata, &bm->vdata, vindex, &v->head.data, true);
+ CustomData_bmesh_set_default(&bm->vdata, &v->head.data);
+
+// BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_MASS, mass);
+ BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT, it_vert.vertex->weight);
+
+ /* root */
+ BM_elem_meshsample_data_named_set(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &it_strand.curve->msurf);
+
+ /* set shapekey data */
+ if (key) {
+ int k;
+
+ /* set shape key original index */
+ if (cd_shape_keyindex_offset != -1)
+ BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, vindex);
+
+ for (block = key->block.first, k = 0; block; block = block->next, k++) {
+ float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, k);
+
+ if (co) {
+ mul_v3_m4v3(co, mat, ((float *)block->data) + 3 * vindex);
+ }
+ }
+ }
+
+ vindex += 1;
+
+ if (it_vert.index > 0) {
+ e = BM_edge_create(bm, v_prev, v, NULL, BM_CREATE_SKIP_CD);
+ BM_elem_index_set(e, eindex); /* set_ok; one less edge than vertices for each particle */
+
+ /* transfer flags */
+// e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT);
+
+ /* this is necessary for selection counts to work properly */
+// if (medge->flag & SELECT) {
+// BM_edge_select_set(bm, e, true);
+// }
+
+ /* Copy Custom Data */
+// CustomData_to_bmesh_block(&me->edata, &bm->edata, eindex, &e->head.data, true);
+ CustomData_bmesh_set_default(&bm->edata, &e->head.data);
+
+ eindex += 1;
+ }
+ }
+
+ }
+
+ bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE); /* added in order, clear dirty flag */
+}
+
+/**
+ * \brief ParticleSystem -> BMesh
+ */
+void BM_strands_bm_from_strands(BMesh *bm, Strands *strands, float mat[4][4], Key *key, struct DerivedMesh *emitter_dm,
+ const bool set_key, int act_key_nr)
+{
+ KeyBlock *actkey;
+ float (*keyco)[3] = NULL;
+ int totvert, totedge;
+
+ int cd_shape_keyindex_offset;
+
+ /* free custom data */
+ /* this isnt needed in most cases but do just incase */
+ CustomData_free(&bm->vdata, bm->totvert);
+ CustomData_free(&bm->edata, bm->totedge);
+ CustomData_free(&bm->ldata, bm->totloop);
+ CustomData_free(&bm->pdata, bm->totface);
+
+ totvert = strands->totverts;
+ totedge = strands->totverts - strands->totcurves;
+
+ if (!strands || !totvert || !totedge) {
+ if (strands) { /*no verts? still copy customdata layout*/
+ CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT);
+ CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE);
+ CustomData_bmesh_init_pool(&bm->ldata, 0, BM_LOOP);
+ CustomData_bmesh_init_pool(&bm->pdata, 0, BM_FACE);
+ }
+ return; /* sanity check */
+ }
+
+ actkey = bm_set_shapekey_from_strands_key(bm, strands, key, act_key_nr);
+ if (actkey)
+ keyco = actkey->data;
+
+ CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT);
+ CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE);
+
+ BM_strands_cd_flag_apply(bm, 0);
+
+ cd_shape_keyindex_offset = key ? CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) : -1;
+
+ bm_make_strands(bm, strands, key, emitter_dm, mat, set_key ? keyco : NULL, cd_shape_keyindex_offset);
+
+#if 0 /* TODO */
+ if (me->mselect && me->totselect != 0) {
+
+ BMVert **vert_array = MEM_mallocN(sizeof(BMVert *) * bm->totvert, "VSelConv");
+ BMEdge **edge_array = MEM_mallocN(sizeof(BMEdge *) * bm->totedge, "ESelConv");
+ BMFace **face_array = MEM_mallocN(sizeof(BMFace *) * bm->totface, "FSelConv");
+ MSelect *msel;
+
+#pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT)
+ {
+#pragma omp section
+ { BM_iter_as_array(bm, BM_VERTS_OF_MESH, NULL, (void **)vert_array, bm->totvert); }
+#pragma omp section
+ { BM_iter_as_array(bm, BM_EDGES_OF_MESH, NULL, (void **)edge_array, bm->totedge); }
+#pragma omp section
+ { BM_iter_as_array(bm, BM_FACES_OF_MESH, NULL, (void **)face_array, bm->totface); }
+ }
+
+ for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) {
+ switch (msel->type) {
+ case ME_VSEL:
+ BM_select_history_store(bm, (BMElem *)vert_array[msel->index]);
+ break;
+ case ME_ESEL:
+ BM_select_history_store(bm, (BMElem *)edge_array[msel->index]);
+ break;
+ case ME_FSEL:
+ BM_select_history_store(bm, (BMElem *)face_array[msel->index]);
+ break;
+ }
+ }
+
+ MEM_freeN(vert_array);
+ MEM_freeN(edge_array);
+ MEM_freeN(face_array);
+ }
+ else {
+ me->totselect = 0;
+ if (me->mselect) {
+ MEM_freeN(me->mselect);
+ me->mselect = NULL;
+ }
+ }
+#endif
+}
+
+/* ------------------------------------------------------------------------- */
+
+#if 0
+/**
+ * \brief BMesh -> Mesh
+ */
+static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert)
+{
+ const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
+ BMVert **vertMap = NULL;
+ BMVert *eve;
+ int i = 0;
+ BMIter iter;
+
+ /* caller needs to ensure this */
+ BLI_assert(ototvert > 0);
+
+ vertMap = MEM_callocN(sizeof(*vertMap) * ototvert, "vertMap");
+ if (cd_shape_keyindex_offset != -1) {
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
+ if ((keyi != ORIGINDEX_NONE) && (keyi < ototvert)) {
+ vertMap[keyi] = eve;
+ }
+ }
+ }
+ else {
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ if (i < ototvert) {
+ vertMap[i] = eve;
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+ return vertMap;
+}
+
+BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
+{
+ /* this is a cheap way to set the edge draw, its not precise and will
+ * pick the first 2 faces an edge uses.
+ * The dot comparison is a little arbitrary, but set so that a 5 subd
+ * IcoSphere won't vanish but subd 6 will (as with pre-bmesh blender) */
+
+
+ if ( /* (med->flag & ME_EDGEDRAW) && */ /* assume to be true */
+ (e->l && (e->l != e->l->radial_next)) &&
+ (dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.9995f))
+ {
+ med->flag &= ~ME_EDGEDRAW;
+ }
+ else {
+ med->flag |= ME_EDGEDRAW;
+ }
+}
+#endif
+
+static void bm_strands_make_strand(BMesh *bm, BMVert *root, Strands *UNUSED(strands), float imat[4][4], Key *UNUSED(key),
+ struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *UNUSED(emitter_bvhtree),
+ StrandIterator *it_strand)
+{
+ int numverts = BM_strands_keys_count(root);
+
+ BMVert *v;
+ BMIter iter;
+ StrandVertexIterator it_vert;
+
+ it_strand->curve->numverts = numverts;
+ /* init root matrix, fully constructed below for non-degenerate strands */
+ unit_m3(it_strand->curve->root_matrix);
+
+ BKE_strand_vertex_iter_init(&it_vert, it_strand);
+ BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) {
+ BLI_assert(BKE_strand_vertex_iter_valid(&it_vert));
+
+ /* root */
+ if (it_vert.index == 0) {
+ float loc[3], nor[3], tang[3];
+ BM_elem_meshsample_data_named_get(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &it_strand->curve->msurf);
+ BKE_mesh_sample_eval(emitter_dm, &it_strand->curve->msurf, loc, nor, tang);
+
+ /* construct root matrix */
+ copy_v3_v3(it_strand->curve->root_matrix[2], nor);
+ copy_v3_v3(it_strand->curve->root_matrix[0], tang);
+ cross_v3_v3v3(it_strand->curve->root_matrix[1], it_strand->curve->root_matrix[2], it_strand->curve->root_matrix[0]);
+
+ /* transform from edit space (duplicator local space) back to the original object space */
+ mul_mat3_m4_v3(imat, it_strand->curve->root_matrix[0]);
+ mul_mat3_m4_v3(imat, it_strand->curve->root_matrix[1]);
+ mul_mat3_m4_v3(imat, it_strand->curve->root_matrix[2]);
+ }
+
+ /* transform from edit space (duplicator local space) back to the original object space */
+ mul_v3_m4v3(it_vert.vertex->co, imat, v->co);
+ it_vert.vertex->time = numverts > 0 ? (float)it_vert.index / (float)(numverts - 1) : 0.0f;
+
+ if (it_vert.index == 0) {
+ /* weight 1.0 is used for pinning hair roots in particles */
+ it_vert.vertex->weight = 1.0f;
+ }
+ else {
+ it_vert.vertex->weight = BM_elem_float_data_named_get(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT);
+ }
+
+ BKE_strand_vertex_iter_next(&it_vert);
+
+ BM_CHECK_ELEMENT(v);
+ }
+}
+
+/**
+ * returns customdata shapekey index from a keyblock or -1
+ * \note could split this out into a more generic function */
+static int bm_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey)
+{
+ int i;
+ int j = 0;
+
+ for (i = 0; i < bm->vdata.totlayer; i++) {
+ if (bm->vdata.layers[i].type == CD_SHAPEKEY) {
+ if (currkey->uid == bm->vdata.layers[i].uid) {
+ return j;
+ }
+ j++;
+ }
+ }
+ return -1;
+}
+
+/* go through and find any shapekey customdata layers
+ * that might not have corresponding KeyBlocks, and add them if
+ * necessary */
+static void bm_strands_add_missing_shapekeys(BMesh *bm, Key *key)
+{
+ KeyBlock *currkey;
+ int i;
+
+ for (i = 0; i < bm->vdata.totlayer; i++) {
+ const CustomDataLayer *layer = &bm->vdata.layers[i];
+ if (layer->type != CD_SHAPEKEY)
+ continue;
+
+ for (currkey = key->block.first; currkey; currkey = currkey->next) {
+ if (currkey->uid == layer->uid)
+ break;
+ }
+
+ if (!currkey) {
+ currkey = BKE_keyblock_add(key, layer->name);
+ currkey->uid = layer->uid;
+ }
+ }
+}
+
+/* returns offset of the edit against the active shape, so other shapes can compensate accordingly to avoid deformation */
+static void bm_strands_get_basiskey_offset(BMesh *bm, Strands *strands, Key *key, int cd_shape_keyindex_offset, float (**r_offset)[3])
+{
+ *r_offset = NULL;
+
+ /* only need offsets for relative shape keys */
+ if (key->type == KEY_RELATIVE) {
+
+ KeyBlock *actkey = BLI_findlink(&key->block, bm->shapenr - 1);
+ /* unlikely, but the active key may not be valid if the bmesh and the mesh are out of sync */
+ if (!actkey)
+ return;
+
+ /* only if active key is a base */
+ if (BKE_keyblock_is_basis(key, bm->shapenr - 1) && cd_shape_keyindex_offset >= 0) {
+ float (*fp)[3] = actkey->data;
+ float (*ofs)[3] = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data");
+ BMIter iter;
+ BMVert *eve;
+ StrandsVertex *svert;
+ int i;
+
+ svert = strands->verts;
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
+
+ if (keyi != ORIGINDEX_NONE) {
+ sub_v3_v3v3(ofs[i], svert->co, fp[keyi]);
+ }
+ else {
+ /* if there are new vertices in the mesh, we can't propagate the offset
+ * because it will only work for the existing vertices and not the new
+ * ones, creating a mess when doing e.g. subdivide + translate */
+ MEM_freeN(ofs);
+ ofs = NULL;
+ break;
+ }
+
+ svert++;
+ }
+
+ *r_offset = ofs;
+ }
+ }
+}
+
+static float *bm_strands_apply_keyblock(BMesh *bm, Strands *strands, StrandsVertex *oldverts, float imat[4][4], Key *key, int cd_shape_keyindex_offset,
+ KeyBlock *kb, KeyBlock *actkb, float (*oldkey)[3], float (*offset)[3])
+{
+ const bool apply_offset = (offset && (kb != actkb) && (bm->shapenr - 1 == kb->relative));
+ const int shape_layer_index = bm_shape_layer_index_from_kb(bm, kb);
+ const int cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, shape_layer_index);
+
+ float *newkey, *fp;
+ BMIter iter;
+ BMVert *eve;
+ StrandsVertex *svert;
+ int keyi;
+ float (*ofs_pt)[3] = offset;
+
+ fp = newkey = MEM_callocN(key->elemsize * bm->totvert, "currkey->data");
+
+ svert = strands->verts;
+ BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
+
+ if (kb == actkb) {
+ copy_v3_v3(fp, eve->co);
+
+ if (actkb != key->refkey) { /* important see bug [#30771] */
+ if (cd_shape_keyindex_offset != -1) {
+ if (oldverts) {
+ keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
+ if (keyi != ORIGINDEX_NONE && keyi < kb->totelem) { /* valid old vertex */
+ copy_v3_v3(svert->co, oldverts[keyi].co);
+ }
+ }
+ }
+ }
+ }
+ else if (shape_layer_index != -1) {
+ /* in most cases this runs */
+ copy_v3_v3(fp, BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset));
+ }
+ else if ((oldkey != NULL) &&
+ (cd_shape_keyindex_offset != -1) &&
+ ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) &&
+ (keyi < kb->totelem))
+ {
+ /* old method of reconstructing keys via vertice's original key indices,
+ * currently used if the new method above fails (which is theoretically
+ * possible in certain cases of undo) */
+ copy_v3_v3(fp, oldkey[keyi]);
+ }
+ else {
+ /* fail! fill in with dummy value */
+ copy_v3_v3(fp, svert->co);
+ }
+
+ /* propagate edited basis offsets to other shapes */
+ if (apply_offset) {
+ add_v3_v3(fp, *ofs_pt++);
+ }
+
+ /* transform from edit space (duplicator local space) back to the original object space */
+ mul_m4_v3(imat, fp);
+
+ fp += 3;
+ svert++;
+ }
+
+ return newkey;
+}
+
+static void bm_strands_apply_shapekeys(BMesh *bm, Strands *strands, StrandsVertex *oldverts, float imat[4][4], Key *key)
+{
+ const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
+ KeyBlock *actkb = BLI_findlink(&key->block, bm->shapenr - 1);
+ KeyBlock *kb;
+ float (*offset)[3] = NULL;
+
+ bm_strands_add_missing_shapekeys(bm, key);
+
+ if (oldverts)
+ bm_strands_get_basiskey_offset(bm, strands, key, cd_shape_keyindex_offset, &offset);
+
+ for (kb = key->block.first; kb; kb = kb->next) {
+ float *newkey;
+
+ newkey = bm_strands_apply_keyblock(bm, strands, oldverts, imat, key, cd_shape_keyindex_offset, kb, actkb, kb->data, offset);
+
+ kb->totelem = bm->totvert;
+ if (kb->data) {
+ MEM_freeN(kb->data);
+ }
+ kb->data = newkey;
+ }
+
+ if (offset)
+ MEM_freeN(offset);
+}
+
+Strands *BM_strands_bm_to_strands(BMesh *bm, Strands *strands, float mat[4][4], Key *key, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree)
+{
+ Strands *oldstrands;
+ int ntotcurves;
+ float imat[4][4];
+
+ BMVert *root;
+ BMIter iter;
+ StrandIterator it_strand;
+
+ ntotcurves = BM_strands_count(bm);
+
+ /* lets save the old strands just in case we are actually working on
+ * a key ... we now do processing of the keys at the end */
+ oldstrands = strands;
+
+ invert_m4_m4(imat, mat);
+
+ strands = BKE_strands_new(ntotcurves, bm->totvert);
+
+// strands->cd_flag = BM_strands_cd_flag_from_bmesh(bm);
+
+ BKE_strand_iter_init(&it_strand, strands);
+ BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) {
+ BLI_assert(BKE_strand_iter_valid(&it_strand));
+
+ bm_strands_make_strand(bm, root, strands, imat, key, emitter_dm, emitter_bvhtree, &it_strand);
+
+ BKE_strand_iter_next(&it_strand);
+ }
+ bm->elem_index_dirty &= ~BM_VERT;
+
+ BKE_strands_ensure_normals(strands);
+
+
+#if 0 // TODO
+ {
+ BMEditSelection *selected;
+ me->totselect = BLI_listbase_count(&(bm->selected));
+
+ if (me->mselect) MEM_freeN(me->mselect);
+
+ me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history");
+
+
+ for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) {
+ if (selected->htype == BM_VERT) {
+ me->mselect[i].type = ME_VSEL;
+
+ }
+ else if (selected->htype == BM_EDGE) {
+ me->mselect[i].type = ME_ESEL;
+
+ }
+ else if (selected->htype == BM_FACE) {
+ me->mselect[i].type = ME_FSEL;
+ }
+
+ me->mselect[i].index = BM_elem_index_get(selected->ele);
+ }
+ }
+#endif
+
+ if (key) {
+ bm_strands_apply_shapekeys(bm, strands, oldstrands ? oldstrands->verts : NULL, imat, key);
+ }
+
+ if (oldstrands) {
+ BKE_strands_free(oldstrands);
+ }
+
+ return strands;
+}
+
+/* ------------------------------------------------------------------------- */
+/* ParticleSystem */
+
+int BM_strands_count_psys_keys(ParticleSystem *psys)
+{
+ ParticleData *pa;
+ int p;
+ int totkeys = 0;
+
+ for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa)
+ totkeys += pa->totkey;
+
+ return totkeys;
+}
+
+#if 0
+static KeyBlock *bm_set_shapekey_from_psys(BMesh *bm, ParticleSystem *psys, int totvert, int act_key_nr)
+{
+ KeyBlock *actkey, *block;
+ int i, j;
+
+ if (!psys->key) {
+ return NULL;
+ }
+
+ if (act_key_nr != 0)
+ actkey = BLI_findlink(&psys->key->block, act_key_nr - 1);
+ else
+ actkey = NULL;
+
+ CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0);
+
+ /* check if we need to generate unique ids for the shapekeys.
+ * this also exists in the file reading code, but is here for
+ * a sanity check */
+ if (!psys->key->uidgen) {
+ fprintf(stderr,
+ "%s had to generate shape key uid's in a situation we shouldn't need to! "
+ "(bmesh internal error)\n",
+ __func__);
+
+ psys->key->uidgen = 1;
+ for (block = psys->key->block.first; block; block = block->next) {
+ block->uid = psys->key->uidgen++;
+ }
+ }
+
+ if (actkey && actkey->totelem == totvert) {
+ bm->shapenr = act_key_nr;
+ }
+
+ for (i = 0, block = psys->key->block.first; block; block = block->next, i++) {
+ CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY,
+ CD_ASSIGN, NULL, 0, block->name);
+
+ j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i);
+ bm->vdata.layers[j].uid = block->uid;
+ }
+
+ return actkey;
+}
+#endif
+
+/* create vertex and edge data for BMesh based on particle hair keys */
+static void bm_make_particles(BMesh *bm, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, float (*keyco)[3], int cd_shape_keyindex_offset)
+{
+// KeyBlock *block;
+ ParticleData *pa;
+ HairKey *hkey;
+ int p, k;
+
+ int vindex, eindex;
+ BMVert *v = NULL, *v_prev;
+ BMEdge *e;
+
+ float hairmat[4][4];
+
+ /* XXX currently all particles and keys have the same mass, this may change */
+ float mass = psys->part->mass;
+
+ vindex = 0;
+ eindex = 0;
+ for (p = 0, pa = psys->particles; p < psys->totpart; ++p, ++pa) {
+
+ /* hair keys are in a local "hair space", but edit data should be in object space */
+ psys_mat_hair_to_object(ob, emitter_dm, psys->part->from, pa, hairmat);
+
+ for (k = 0, hkey = pa->hair; k < pa->totkey; ++k, ++hkey) {
+ float co[3];
+
+ copy_v3_v3(co, keyco ? keyco[vindex] : hkey->co);
+ mul_m4_v3(hairmat, co);
+
+ v_prev = v;
+ v = BM_vert_create(bm, co, NULL, BM_CREATE_SKIP_CD);
+ BM_elem_index_set(v, vindex); /* set_ok */
+
+ /* transfer flag */
+// v->head.hflag = BM_vert_flag_from_mflag(mvert->flag & ~SELECT);
+
+ /* this is necessary for selection counts to work properly */
+// if (hkey->editflag & SELECT) {
+// BM_vert_select_set(bm, v, true);
+// }
+
+// normal_short_to_float_v3(v->no, mvert->no);
+
+ /* Copy Custom Data */
+// CustomData_to_bmesh_block(&me->vdata, &bm->vdata, vindex, &v->head.data, true);
+ CustomData_bmesh_set_default(&bm->vdata, &v->head.data);
+
+ BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_MASS, mass);
+ BM_elem_float_data_named_set(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT, hkey->weight);
+
+ /* root */
+ if (k == 0) {
+ MSurfaceSample root_loc;
+ if (BKE_mesh_sample_from_particle(&root_loc, psys, emitter_dm, pa)) {
+ BM_elem_meshsample_data_named_set(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &root_loc);
+ }
+ }
+
+#if 0
+ /* set shapekey data */
+ if (psys->key) {
+ /* set shape key original index */
+ if (cd_shape_keyindex_offset != -1) BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, vindex);
+
+ for (block = psys->key->block.first, j = 0; block; block = block->next, j++) {
+ float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, j);
+
+ if (co) {
+ copy_v3_v3(co, ((float *)block->data) + 3 * vindex);
+ }
+ }
+ }
+#else
+ (void)cd_shape_keyindex_offset;
+#endif
+
+ vindex += 1;
+
+ if (k > 0) {
+ e = BM_edge_create(bm, v_prev, v, NULL, BM_CREATE_SKIP_CD);
+ BM_elem_index_set(e, eindex); /* set_ok; one less edge than vertices for each particle */
+
+ /* transfer flags */
+// e->head.hflag = BM_edge_flag_from_mflag(medge->flag & ~SELECT);
+
+ /* this is necessary for selection counts to work properly */
+// if (medge->flag & SELECT) {
+// BM_edge_select_set(bm, e, true);
+// }
+
+ /* Copy Custom Data */
+// CustomData_to_bmesh_block(&me->edata, &bm->edata, eindex, &e->head.data, true);
+ CustomData_bmesh_set_default(&bm->edata, &e->head.data);
+
+ eindex += 1;
+ }
+
+ } /* hair keys */
+
+ } /* particles */
+
+ bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE); /* added in order, clear dirty flag */
+}
+
+/**
+ * \brief ParticleSystem -> BMesh
+ */
+void BM_strands_bm_from_psys(BMesh *bm, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm,
+ const bool set_key, int act_key_nr)
+{
+ // KeyBlock *actkey;
+ float (*keyco)[3] = NULL;
+ int totvert, totedge;
+
+ int cd_shape_keyindex_offset;
+
+ /* free custom data */
+ /* this isnt needed in most cases but do just incase */
+ CustomData_free(&bm->vdata, bm->totvert);
+ CustomData_free(&bm->edata, bm->totedge);
+ CustomData_free(&bm->ldata, bm->totloop);
+ CustomData_free(&bm->pdata, bm->totface);
+
+ totvert = BM_strands_count_psys_keys(psys);
+ totedge = totvert - psys->totpart;
+
+ if (!psys || !totvert || !totedge) {
+ if (psys) { /*no verts? still copy customdata layout*/
+ CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT);
+ CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE);
+ CustomData_bmesh_init_pool(&bm->ldata, 0, BM_LOOP);
+ CustomData_bmesh_init_pool(&bm->pdata, 0, BM_FACE);
+ }
+ return; /* sanity check */
+ }
+
+#if 0
+ actkey = bm_set_shapekey_from_psys(bm, psys, totvert, act_key_nr);
+ if (actkey)
+ keyco = actkey->data;
+#else
+ (void)act_key_nr;
+#endif
+
+ CustomData_bmesh_init_pool(&bm->vdata, totvert, BM_VERT);
+ CustomData_bmesh_init_pool(&bm->edata, totedge, BM_EDGE);
+
+ BM_strands_cd_flag_apply(bm, /*psys->cd_flag*/0);
+
+ cd_shape_keyindex_offset = /*psys->key ? CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) :*/ -1;
+
+ bm_make_particles(bm, ob, psys, emitter_dm, set_key ? keyco : NULL, cd_shape_keyindex_offset);
+
+
+#if 0 /* TODO */
+ if (me->mselect && me->totselect != 0) {
+
+ BMVert **vert_array = MEM_mallocN(sizeof(BMVert *) * bm->totvert, "VSelConv");
+ BMEdge **edge_array = MEM_mallocN(sizeof(BMEdge *) * bm->totedge, "ESelConv");
+ BMFace **face_array = MEM_mallocN(sizeof(BMFace *) * bm->totface, "FSelConv");
+ MSelect *msel;
+
+#pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT)
+ {
+#pragma omp section
+ { BM_iter_as_array(bm, BM_VERTS_OF_MESH, NULL, (void **)vert_array, bm->totvert); }
+#pragma omp section
+ { BM_iter_as_array(bm, BM_EDGES_OF_MESH, NULL, (void **)edge_array, bm->totedge); }
+#pragma omp section
+ { BM_iter_as_array(bm, BM_FACES_OF_MESH, NULL, (void **)face_array, bm->totface); }
+ }
+
+ for (i = 0, msel = me->mselect; i < me->totselect; i++, msel++) {
+ switch (msel->type) {
+ case ME_VSEL:
+ BM_select_history_store(bm, (BMElem *)vert_array[msel->index]);
+ break;
+ case ME_ESEL:
+ BM_select_history_store(bm, (BMElem *)edge_array[msel->index]);
+ break;
+ case ME_FSEL:
+ BM_select_history_store(bm, (BMElem *)face_array[msel->index]);
+ break;
+ }
+ }
+
+ MEM_freeN(vert_array);
+ MEM_freeN(edge_array);
+ MEM_freeN(face_array);
+ }
+ else {
+ me->totselect = 0;
+ if (me->mselect) {
+ MEM_freeN(me->mselect);
+ me->mselect = NULL;
+ }
+ }
+#endif
+}
+
+#if 0
+/**
+ * \brief BMesh -> Mesh
+ */
+static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert)
+{
+ const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
+ BMVert **vertMap = NULL;
+ BMVert *eve;
+ int i = 0;
+ BMIter iter;
+
+ /* caller needs to ensure this */
+ BLI_assert(ototvert > 0);
+
+ vertMap = MEM_callocN(sizeof(*vertMap) * ototvert, "vertMap");
+ if (cd_shape_keyindex_offset != -1) {
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
+ if ((keyi != ORIGINDEX_NONE) && (keyi < ototvert)) {
+ vertMap[keyi] = eve;
+ }
+ }
+ }
+ else {
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ if (i < ototvert) {
+ vertMap[i] = eve;
+ }
+ else {
+ break;
+ }
+ }
+ }
+
+ return vertMap;
+}
+
+/**
+ * returns customdata shapekey index from a keyblock or -1
+ * \note could split this out into a more generic function */
+static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey)
+{
+ int i;
+ int j = 0;
+
+ for (i = 0; i < bm->vdata.totlayer; i++) {
+ if (bm->vdata.layers[i].type == CD_SHAPEKEY) {
+ if (currkey->uid == bm->vdata.layers[i].uid) {
+ return j;
+ }
+ j++;
+ }
+ }
+ return -1;
+}
+
+BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
+{
+ /* this is a cheap way to set the edge draw, its not precise and will
+ * pick the first 2 faces an edge uses.
+ * The dot comparison is a little arbitrary, but set so that a 5 subd
+ * IcoSphere won't vanish but subd 6 will (as with pre-bmesh blender) */
+
+
+ if ( /* (med->flag & ME_EDGEDRAW) && */ /* assume to be true */
+ (e->l && (e->l != e->l->radial_next)) &&
+ (dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.9995f))
+ {
+ med->flag &= ~ME_EDGEDRAW;
+ }
+ else {
+ med->flag |= ME_EDGEDRAW;
+ }
+}
+#endif
+
+static void make_particle_hair(BMesh *bm, BMVert *root, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree, struct ParticleData *pa)
+{
+ int totkey = BM_strands_keys_count(root);
+ HairKey *hair;
+
+ BMVert *v;
+ BMIter iter;
+ HairKey *hkey;
+ int k;
+
+ float inv_hairmat[4][4];
+
+ pa->alive = PARS_ALIVE;
+ pa->flag = 0;
+
+ pa->time = 0.0f;
+ pa->lifetime = 100.0f;
+ pa->dietime = 100.0f;
+
+ pa->size = psys->part->size;
+
+ // TODO define other particle stuff ...
+
+ hair = MEM_callocN(totkey * sizeof(HairKey), "hair keys");
+
+ hkey = hair;
+ k = 0;
+ BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) {
+ /* root */
+ if (k == 0) {
+ MSurfaceSample root_loc;
+ BM_elem_meshsample_data_named_get(&bm->vdata, v, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &root_loc);
+ if (!BKE_mesh_sample_to_particle(&root_loc, psys, emitter_dm, emitter_bvhtree, pa)) {
+ pa->num = 0;
+ pa->num_dmcache = DMCACHE_NOTFOUND;
+ zero_v4(pa->fuv);
+ pa->foffset = 0.0f;
+ }
+
+ /* edit data is in object space, hair keys must be converted back into "hair space" */
+ psys_mat_hair_to_object(ob, emitter_dm, psys->part->from, pa, inv_hairmat);
+ invert_m4(inv_hairmat);
+ }
+
+ mul_v3_m4v3(hkey->co, inv_hairmat, v->co);
+ mul_v3_m4v3(hkey->world_co, ob->obmat, v->co);
+
+ hkey->time = totkey > 0 ? (float)k / (float)(totkey - 1) : 0.0f;
+ if (k == 0) {
+ /* weight 1.0 is used for pinning hair roots in particles */
+ hkey->weight = 1.0f;
+ }
+ else {
+ hkey->weight = BM_elem_float_data_named_get(&bm->vdata, v, CD_PROP_FLT, CD_HAIR_WEIGHT);
+ }
+
+ ++hkey;
+ ++k;
+
+ BM_CHECK_ELEMENT(v);
+ }
+
+ if (pa->hair)
+ MEM_freeN(pa->hair);
+
+ pa->hair = hair;
+ pa->totkey = totkey;
+}
+
+void BM_strands_bm_to_psys(BMesh *bm, Object *ob, ParticleSystem *psys, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree)
+{
+ ParticleData *particles, *oldparticles;
+ int ototpart, ntotpart;
+
+ BMVert *root;
+ BMIter iter;
+ ParticleData *pa;
+ int p;
+
+ ototpart = psys->totpart;
+
+ ntotpart = BM_strands_count(bm);
+
+ /* new particles block */
+ if (bm->totvert == 0) particles = NULL;
+ else particles = MEM_callocN(ntotpart * sizeof(ParticleData), "particles");
+
+ /* lets save the old particles just in case we are actually working on
+ * a key ... we now do processing of the keys at the end */
+ oldparticles = psys->particles;
+
+ psys->totpart = ntotpart;
+
+// psys->cd_flag = BM_strands_cd_flag_from_bmesh(bm);
+
+ pa = particles;
+ p = 0;
+ BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) {
+
+ make_particle_hair(bm, root, ob, psys, emitter_dm, emitter_bvhtree, pa);
+
+ ++pa;
+ ++p;
+ }
+ bm->elem_index_dirty &= ~BM_VERT;
+
+
+#if 0 // TODO
+ {
+ BMEditSelection *selected;
+ me->totselect = BLI_listbase_count(&(bm->selected));
+
+ if (me->mselect) MEM_freeN(me->mselect);
+
+ me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history");
+
+
+ for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) {
+ if (selected->htype == BM_VERT) {
+ me->mselect[i].type = ME_VSEL;
+
+ }
+ else if (selected->htype == BM_EDGE) {
+ me->mselect[i].type = ME_ESEL;
+
+ }
+ else if (selected->htype == BM_FACE) {
+ me->mselect[i].type = ME_FSEL;
+ }
+
+ me->mselect[i].index = BM_elem_index_get(selected->ele);
+ }
+ }
+#endif
+
+#if 0 // TODO
+ /* see comment below, this logic is in twice */
+
+ if (me->key) {
+ const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
+
+ KeyBlock *currkey;
+ KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1);
+
+ float (*ofs)[3] = NULL;
+
+ /* go through and find any shapekey customdata layers
+ * that might not have corresponding KeyBlocks, and add them if
+ * necessary */
+ j = 0;
+ for (i = 0; i < bm->vdata.totlayer; i++) {
+ if (bm->vdata.layers[i].type != CD_SHAPEKEY)
+ continue;
+
+ for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
+ if (currkey->uid == bm->vdata.layers[i].uid)
+ break;
+ }
+
+ if (!currkey) {
+ currkey = BKE_keyblock_add(me->key, bm->vdata.layers[i].name);
+ currkey->uid = bm->vdata.layers[i].uid;
+ }
+
+ j++;
+ }
+
+
+ /* editing the base key should update others */
+ if ((me->key->type == KEY_RELATIVE) && /* only need offsets for relative shape keys */
+ (actkey != NULL) && /* unlikely, but the active key may not be valid if the
+ * bmesh and the mesh are out of sync */
+ (oldverts != NULL)) /* not used here, but 'oldverts' is used later for applying 'ofs' */
+ {
+ const bool act_is_basis = BKE_keyblock_is_basis(me->key, bm->shapenr - 1);
+
+ /* active key is a base */
+ if (act_is_basis && (cd_shape_keyindex_offset != -1)) {
+ float (*fp)[3] = actkey->data;
+
+ ofs = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data");
+ mvert = me->mvert;
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
+ const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
+
+ if (keyi != ORIGINDEX_NONE) {
+ sub_v3_v3v3(ofs[i], mvert->co, fp[keyi]);
+ }
+ else {
+ /* if there are new vertices in the mesh, we can't propagate the offset
+ * because it will only work for the existing vertices and not the new
+ * ones, creating a mess when doing e.g. subdivide + translate */
+ MEM_freeN(ofs);
+ ofs = NULL;
+ break;
+ }
+
+ mvert++;
+ }
+ }
+ }
+
+ for (currkey = me->key->block.first; currkey; currkey = currkey->next) {
+ const bool apply_offset = (ofs && (currkey != actkey) && (bm->shapenr - 1 == currkey->relative));
+ int cd_shape_offset;
+ int keyi;
+ float (*ofs_pt)[3] = ofs;
+ float *newkey, (*oldkey)[3], *fp;
+
+ j = bm_to_mesh_shape_layer_index_from_kb(bm, currkey);
+ cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, j);
+
+
+ fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data");
+ oldkey = currkey->data;
+
+ mvert = me->mvert;
+ BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
+
+ if (currkey == actkey) {
+ copy_v3_v3(fp, eve->co);
+
+ if (actkey != me->key->refkey) { /* important see bug [#30771] */
+ if (cd_shape_keyindex_offset != -1) {
+ if (oldverts) {
+ keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
+ if (keyi != ORIGINDEX_NONE && keyi < currkey->totelem) { /* valid old vertex */
+ copy_v3_v3(mvert->co, oldverts[keyi].co);
+ }
+ }
+ }
+ }
+ }
+ else if (j != -1) {
+ /* in most cases this runs */
+ copy_v3_v3(fp, BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset));
+ }
+ else if ((oldkey != NULL) &&
+ (cd_shape_keyindex_offset != -1) &&
+ ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) &&
+ (keyi < currkey->totelem))
+ {
+ /* old method of reconstructing keys via vertice's original key indices,
+ * currently used if the new method above fails (which is theoretically
+ * possible in certain cases of undo) */
+ copy_v3_v3(fp, oldkey[keyi]);
+ }
+ else {
+ /* fail! fill in with dummy value */
+ copy_v3_v3(fp, mvert->co);
+ }
+
+ /* propagate edited basis offsets to other shapes */
+ if (apply_offset) {
+ add_v3_v3(fp, *ofs_pt++);
+ }
+
+ fp += 3;
+ mvert++;
+ }
+
+ currkey->totelem = bm->totvert;
+ if (currkey->data) {
+ MEM_freeN(currkey->data);
+ }
+ currkey->data = newkey;
+ }
+
+ if (ofs) MEM_freeN(ofs);
+ }
+#else
+ psys->particles = particles;
+#endif
+
+ if (oldparticles) {
+ ParticleData *pa;
+ int p;
+ for (p = 0, pa = oldparticles; p < ototpart; ++p, ++pa)
+ if (pa->hair)
+ MEM_freeN(pa->hair);
+ MEM_freeN(oldparticles);
+ }
+}
diff --git a/source/blender/bmesh/intern/bmesh_strands_conv.h b/source/blender/bmesh/intern/bmesh_strands_conv.h
new file mode 100644
index 00000000000..aa18b779b17
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_strands_conv.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) 2014 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BMESH_STRANDS_CONV_H__
+#define __BMESH_STRANDS_CONV_H__
+
+/** \file blender/bmesh/intern/bmesh_strands_conv.h
+ * \ingroup bmesh
+ */
+
+struct BMesh;
+struct Mesh;
+struct Object;
+struct ParticleSystem;
+struct DerivedMesh;
+struct BVHTreeFromMesh;
+struct Strands;
+struct Key;
+
+extern const char *CD_HAIR_SEGMENT_LENGTH;
+extern const char *CD_HAIR_MASS;
+extern const char *CD_HAIR_WEIGHT;
+extern const char *CD_HAIR_ROOT_LOCATION;
+
+void BM_strands_cd_validate(struct BMesh *bm);
+void BM_strands_cd_flag_ensure(struct BMesh *bm, const char cd_flag);
+void BM_strands_cd_flag_apply(struct BMesh *bm, const char cd_flag);
+char BM_strands_cd_flag_from_bmesh(struct BMesh *bm);
+
+void BM_strands_bm_from_strands(struct BMesh *bm, struct Strands *strands, float mat[4][4], struct Key *key, struct DerivedMesh *emitter_dm, const bool set_key, int act_key_nr);
+struct Strands *BM_strands_bm_to_strands(struct BMesh *bm, struct Strands *strands, float mat[4][4], struct Key *key, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree);
+
+int BM_strands_count_psys_keys(struct ParticleSystem *psys);
+void BM_strands_bm_from_psys(struct BMesh *bm, struct Object *ob, struct ParticleSystem *psys, struct DerivedMesh *emitter_dm,
+ const bool set_key, int act_key_nr);
+void BM_strands_bm_to_psys(struct BMesh *bm, struct Object *ob, struct ParticleSystem *psys, struct DerivedMesh *emitter_dm, struct BVHTreeFromMesh *emitter_bvhtree);
+
+#define BMALLOC_TEMPLATE_FROM_STRANDS(strands) { (CHECK_TYPE_INLINE(strands, Strands *), \
+ strands->totverts), (strands->totverts - strands->totcurves), 0, 0 }
+#define BMALLOC_TEMPLATE_FROM_PSYS(psys) { (CHECK_TYPE_INLINE(psys, ParticleSystem *), \
+ BM_strands_count_psys_keys(psys)), (BM_strands_count_psys_keys(psys) - (psys)->totpart), 0, 0 }
+
+#endif /* __BMESH_STRANDS_CONV_H__ */
diff --git a/source/blender/depsgraph/intern/depsgraph_build_relations.cc b/source/blender/depsgraph/intern/depsgraph_build_relations.cc
index 9bd448aa51c..fd19255598b 100644
--- a/source/blender/depsgraph/intern/depsgraph_build_relations.cc
+++ b/source/blender/depsgraph/intern/depsgraph_build_relations.cc
@@ -1084,7 +1084,7 @@ void DepsgraphRelationBuilder::build_particles(Scene *scene, Object *ob)
#endif
/* effectors */
- ListBase *effectors = pdInitEffectors(scene, ob, psys, part->effector_weights, false);
+ ListBase *effectors = pdInitEffectors_ex(scene, ob, psys, ob->lay, part->effector_weights, false);
if (effectors) {
for (EffectorCache *eff = (EffectorCache *)effectors->first; eff; eff = eff->next) {
diff --git a/source/blender/editors/CMakeLists.txt b/source/blender/editors/CMakeLists.txt
index 084006ce277..ec9ae775ad0 100644
--- a/source/blender/editors/CMakeLists.txt
+++ b/source/blender/editors/CMakeLists.txt
@@ -23,6 +23,7 @@ if(WITH_BLENDER)
add_subdirectory(armature)
add_subdirectory(curve)
add_subdirectory(gpencil)
+ add_subdirectory(hair)
add_subdirectory(interface)
add_subdirectory(io)
add_subdirectory(mask)
diff --git a/source/blender/editors/SConscript b/source/blender/editors/SConscript
index 1ea2bc0e4ef..0ef04739e9f 100644
--- a/source/blender/editors/SConscript
+++ b/source/blender/editors/SConscript
@@ -41,6 +41,7 @@ SConscript(['datafiles/SConscript',
'object/SConscript',
'curve/SConscript',
'gpencil/SConscript',
+ 'hair/SConscript',
'physics/SConscript',
'render/SConscript',
'sound/SConscript',
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index ebd05d8b16b..10af5b84509 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -865,7 +865,11 @@ static int acf_group_setting_flag(bAnimContext *ac, eAnimChannel_Settings settin
case ACHANNEL_SETTING_MUTE: /* muted */
return AGRP_MUTED;
-
+
+ case ACHANNEL_SETTING_MOD_OFF: /* muted */
+ *neg = 1;
+ return AGRP_MODIFIERS_OFF;
+
case ACHANNEL_SETTING_PROTECT: /* protected */
return AGRP_PROTECTED;
@@ -983,6 +987,10 @@ static int acf_fcurve_setting_flag(bAnimContext *UNUSED(ac), eAnimChannel_Settin
case ACHANNEL_SETTING_VISIBLE: /* visibility - graph editor */
return FCURVE_VISIBLE;
+ case ACHANNEL_SETTING_MOD_OFF:
+ *neg = 1;
+ return FCURVE_MOD_OFF;
+
default: /* unsupported */
return 0;
}
@@ -3977,6 +3985,7 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, const bAni
{
short ptrsize, butType;
bool negflag;
+ bool usetoggle = true;
int flag, icon;
void *ptr;
const char *tooltip;
@@ -3998,7 +4007,13 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, const bAni
else
tooltip = TIP_("Channels are visible in Graph Editor for editing");
break;
-
+
+ case ACHANNEL_SETTING_MOD_OFF: /* modifiers disabled */
+ icon = ICON_MODIFIER;
+ usetoggle = false;
+ tooltip = TIP_("F-Curve modifiers are disabled");
+ break;
+
case ACHANNEL_SETTING_EXPAND: /* expanded triangle */
//icon = ((enabled) ? ICON_TRIA_DOWN : ICON_TRIA_RIGHT);
icon = ICON_TRIA_RIGHT;
@@ -4059,11 +4074,18 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, const bAni
}
/* type of button */
- if (negflag)
- butType = UI_BTYPE_ICON_TOGGLE_N;
- else
- butType = UI_BTYPE_ICON_TOGGLE;
-
+ if (usetoggle) {
+ if (negflag)
+ butType = UI_BTYPE_ICON_TOGGLE_N;
+ else
+ butType = UI_BTYPE_ICON_TOGGLE;
+ }
+ else {
+ if (negflag)
+ butType = UI_BTYPE_TOGGLE_N;
+ else
+ butType = UI_BTYPE_TOGGLE;
+ }
/* draw button for setting */
if (ptr && flag) {
switch (ptrsize) {
@@ -4091,6 +4113,7 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, const bAni
case ACHANNEL_SETTING_PROTECT: /* General - protection flags */
case ACHANNEL_SETTING_MUTE: /* General - muting flags */
case ACHANNEL_SETTING_PINNED: /* NLA Actions - 'map/nomap' */
+ case ACHANNEL_SETTING_MOD_OFF:
UI_but_funcN_set(but, achannel_setting_flush_widget_cb, MEM_dupallocN(ale), SET_INT_IN_POINTER(setting));
break;
@@ -4258,7 +4281,13 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle
offset += ICON_WIDTH;
draw_setting_widget(ac, ale, acf, block, (int)v2d->cur.xmax - offset, ymid, ACHANNEL_SETTING_MUTE);
}
-
+
+ /* modifiers disable */
+ if (acf->has_setting(ac, ale, ACHANNEL_SETTING_MOD_OFF)) {
+ offset += ICON_WIDTH;
+ draw_setting_widget(ac, ale, acf, block, (int)v2d->cur.xmax - offset, ymid, ACHANNEL_SETTING_MOD_OFF);
+ }
+
/* ----------- */
/* pinned... */
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index ca8a35e4ed9..aed32305367 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -90,6 +90,7 @@
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_mask.h"
+#include "BKE_particle.h"
#include "BKE_sequencer.h"
#include "ED_anim_api.h"
@@ -2377,6 +2378,7 @@ static size_t animdata_filter_dopesheet_ob(bAnimContext *ac, ListBase *anim_data
BEGIN_ANIMFILTER_SUBCHANNELS(EXPANDED_OBJC(ob))
{
Key *key = BKE_key_from_object(ob);
+ ParticleSystem *psys;
/* object-level animation */
if ((ob->adt) && !(ads->filterflag & ADS_FILTER_NOOBJ)) {
@@ -2387,6 +2389,11 @@ static size_t animdata_filter_dopesheet_ob(bAnimContext *ac, ListBase *anim_data
if ((key && key->adt) && !(ads->filterflag & ADS_FILTER_NOSHAPEKEYS)) {
tmp_items += animdata_filter_ds_keyanim(ac, &tmp_data, ads, ob, key, filter_mode);
}
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ if ((psys->key && psys->key->adt) && !(ads->filterflag & ADS_FILTER_NOSHAPEKEYS)) {
+ tmp_items += animdata_filter_ds_keyanim(ac, &tmp_data, ads, ob, psys->key, filter_mode);
+ }
+ }
/* modifiers */
if ((ob->modifiers.first) && !(ads->filterflag & ADS_FILTER_NOMODIFIERS)) {
diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c
index 2de42933dc0..86b368fe0d2 100644
--- a/source/blender/editors/animation/anim_ops.c
+++ b/source/blender/editors/animation/anim_ops.c
@@ -60,6 +60,8 @@
#include "anim_intern.h"
+#include "MEM_guardedalloc.h"
+
/* ********************** frame change operator ***************************/
/* Check if the operator can be run from the current context */
@@ -90,10 +92,11 @@ static int change_frame_poll(bContext *C)
}
/* Set the new frame number */
-static void change_frame_apply(bContext *C, wmOperator *op)
+static void change_frame_apply(bContext *C, wmOperator *op, bool final)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
+ ARegion *ar_op = CTX_wm_region(C);
int frame = RNA_int_get(op->ptr, "frame");
bool do_snap = RNA_boolean_get(op->ptr, "snap");
@@ -107,8 +110,44 @@ static void change_frame_apply(bContext *C, wmOperator *op)
SUBFRA = 0.0f;
/* do updates */
- BKE_sound_seek_scene(bmain, scene);
- WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
+
+ if (final) {
+ BKE_sound_seek_scene(bmain, scene);
+ WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
+ }
+ else
+ {
+ bScreen *screen = CTX_wm_screen(C);
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *window;
+ ScrArea *sa;
+
+ BKE_sound_seek_scene(bmain, scene);
+
+ ED_update_for_newframe(bmain, scene, 1);
+
+ for (window = wm->windows.first; window; window = window->next) {
+ for (sa = window->screen->areabase.first; sa; sa = sa->next) {
+ ARegion *ar;
+ for (ar = sa->regionbase.first; ar; ar = ar->next) {
+ bool redraw = false;
+ if (ar == ar_op) {
+ redraw = true;
+ }
+ else if (ED_match_region_with_redraws(sa->spacetype, ar->regiontype, screen->redraws_flag)) {
+ redraw = true;
+ }
+
+ if (redraw) {
+ ED_region_tag_redraw(ar);
+ }
+ }
+
+ if (ED_match_area_with_refresh(sa->spacetype, SPACE_TIME))
+ ED_area_tag_refresh(sa);
+ }
+ }
+ }
}
/* ---- */
@@ -116,8 +155,7 @@ static void change_frame_apply(bContext *C, wmOperator *op)
/* Non-modal callback for running operator without user input */
static int change_frame_exec(bContext *C, wmOperator *op)
{
- change_frame_apply(C, op);
-
+ change_frame_apply(C, op, true);
return OPERATOR_FINISHED;
}
@@ -154,13 +192,35 @@ static void change_frame_seq_preview_begin(bContext *C, const wmEvent *event)
}
}
}
-static void change_frame_seq_preview_end(bContext *C)
+
+typedef struct ChangeFrameData {
+ wmTimer *timer;
+ double last_duration;
+ int frame;
+ int last_frame;
+} ChangeFrameData;
+
+static void change_frame_seq_preview_end(bContext *C, wmOperator *op)
{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win = CTX_wm_window(C);
+
if (ED_sequencer_special_preview_get() != NULL) {
Scene *scene = CTX_data_scene(C);
ED_sequencer_special_preview_clear();
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
}
+
+ if (op->customdata) {
+ ChangeFrameData *data = op->customdata;
+ WM_event_remove_timer(wm, win, data->timer);
+ MEM_freeN(data);
+ op->customdata = NULL;
+ /* add here too to take care of cancelling */
+ if (win->screen)
+ win->screen->scrubbing = false;
+ }
+
}
/* Modal Operator init */
@@ -170,11 +230,22 @@ static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event
* as user could click on a single frame (jump to frame) as well as
* click-dragging over a range (modal scrubbing).
*/
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win = CTX_wm_window(C);
+ Scene *scene = CTX_data_scene(C);
+ ChangeFrameData *data = MEM_callocN(sizeof(ChangeFrameData), "changeframedata");
+
+ data->timer = WM_event_add_timer(wm, win, TIMER, FRA2TIME(1.0));
+
RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
+ op->customdata = data;
change_frame_seq_preview_begin(C, event);
- change_frame_apply(C, op);
+ if (win->screen)
+ win->screen->scrubbing = true;
+
+ change_frame_apply(C, op, false);
/* add temp handler */
WM_event_add_modal_handler(C, op);
@@ -182,24 +253,43 @@ static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event
return OPERATOR_RUNNING_MODAL;
}
-static void change_frame_cancel(bContext *C, wmOperator *UNUSED(op))
+static void change_frame_cancel(bContext *C, wmOperator *op)
{
- change_frame_seq_preview_end(C);
+ change_frame_seq_preview_end(C, op);
}
/* Modal event handling of frame changing */
static int change_frame_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ ChangeFrameData *data = op->customdata;
+ wmWindow *win = CTX_wm_window(C);
+
int ret = OPERATOR_RUNNING_MODAL;
/* execute the events */
switch (event->type) {
case ESCKEY:
+ WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
+ if (win->screen)
+ win->screen->scrubbing = false;
+ BKE_sound_seek_scene(bmain, scene);
ret = OPERATOR_FINISHED;
break;
case MOUSEMOVE:
- RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
- change_frame_apply(C, op);
+ data->frame = frame_from_event(C, event);
+ break;
+
+ case TIMER:
+ if (data->timer->duration - data->last_duration > FRA2TIME(1)) {
+ if (data->frame != data->last_frame) {
+ RNA_int_set(op->ptr, "frame", data->frame);
+ change_frame_apply(C, op, false);
+ data->last_frame = data->frame;
+ data->last_duration = data->timer->duration;
+ }
+ }
break;
case LEFTMOUSE:
@@ -208,8 +298,14 @@ static int change_frame_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* we check for either mouse-button to end, as checking for ACTIONMOUSE (which is used to init
* the modal op) doesn't work for some reason
*/
- if (event->val == KM_RELEASE)
+ if (event->val == KM_RELEASE) {
+ if (win->screen)
+ win->screen->scrubbing = false;
+ data->frame = frame_from_event(C, event);
+ RNA_int_set(op->ptr, "frame", data->frame);
+ change_frame_apply(C, op, true);
ret = OPERATOR_FINISHED;
+ }
break;
case LEFTCTRLKEY:
@@ -224,7 +320,7 @@ static int change_frame_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
if (ret != OPERATOR_RUNNING_MODAL) {
- change_frame_seq_preview_end(C);
+ change_frame_seq_preview_end(C, op);
}
return ret;
@@ -247,7 +343,7 @@ static void ANIM_OT_change_frame(wmOperatorType *ot)
ot->poll = change_frame_poll;
/* flags */
- ot->flag = OPTYPE_BLOCKING | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR;
+ ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR;
/* rna */
ot->prop = RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME);
diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c
index b3dc0021f00..5d2c0077dee 100644
--- a/source/blender/editors/animation/keyframes_general.c
+++ b/source/blender/editors/animation/keyframes_general.c
@@ -179,17 +179,37 @@ void duplicate_fcurve_keys(FCurve *fcu)
/* **************************************************** */
/* Various Tools */
-/* Basic F-Curve 'cleanup' function that removes 'double points' and unnecessary keyframes on linear-segments only */
-void clean_fcurve(FCurve *fcu, float thresh)
+static void copy_bezt_ipo(BezTriple *bezdst, BezTriple *bezsrc)
{
+ bezdst->back = bezsrc->back;
+ bezdst->ipo = bezsrc->ipo;
+ bezdst->easing = bezsrc->easing;
+ bezdst->amplitude = bezsrc->amplitude;
+ bezdst->period = bezsrc->period;
+ bezdst->h1 = bezsrc->h1;
+ bezdst->h2 = bezsrc->h2;
+ bezdst->vec[0][0] = bezsrc->vec[0][0];
+ bezdst->vec[0][1] = bezsrc->vec[0][1];
+ bezdst->vec[2][0] = bezsrc->vec[2][0];
+ bezdst->vec[2][1] = bezsrc->vec[2][1];
+}
+
+/* Basic F-Curve 'cleanup' function that removes 'double points' and unnecessary keyframes on linear-segments only
+ * optionally clears up curve if one keyframe with default value remains */
+void clean_fcurve(struct bAnimContext *ac, bAnimListElem *ale, float thresh, bool cleardefault)
+{
+ FCurve *fcu = (FCurve *)ale->key_data;
BezTriple *old_bezts, *bezt, *beztn;
BezTriple *lastb;
int totCount, i;
/* check if any points */
- if ((fcu == NULL) || (fcu->bezt == NULL) || (fcu->totvert <= 1))
+ if ((fcu == NULL) || (fcu->bezt == NULL) || (fcu->totvert == 0) ||
+ (!cleardefault && fcu->totvert == 1))
+ {
return;
-
+ }
+
/* make a copy of the old BezTriples, and clear F-Curve */
old_bezts = fcu->bezt;
totCount = fcu->totvert;
@@ -227,7 +247,8 @@ void clean_fcurve(FCurve *fcu, float thresh)
cur[0] = bezt->vec[1][0]; cur[1] = bezt->vec[1][1];
if (!(bezt->f2 & SELECT)) {
- insert_vert_fcurve(fcu, cur[0], cur[1], 0);
+ insert_vert_fcurve(fcu, cur[0], cur[1], INSERTKEY_FAST);
+ copy_bezt_ipo((fcu->bezt + (fcu->totvert - 1)), bezt);
lastb = (fcu->bezt + (fcu->totvert - 1));
lastb->f1 = lastb->f2 = lastb->f3 = 0;
continue;
@@ -246,7 +267,8 @@ void clean_fcurve(FCurve *fcu, float thresh)
if (cur[1] > next[1]) {
if (IS_EQT(cur[1], prev[1], thresh) == 0) {
/* add new keyframe */
- insert_vert_fcurve(fcu, cur[0], cur[1], 0);
+ insert_vert_fcurve(fcu, cur[0], cur[1], INSERTKEY_FAST);
+ copy_bezt_ipo((fcu->bezt + (fcu->totvert - 1)), bezt);
}
}
}
@@ -254,7 +276,8 @@ void clean_fcurve(FCurve *fcu, float thresh)
/* only add if values are a considerable distance apart */
if (IS_EQT(cur[1], prev[1], thresh) == 0) {
/* add new keyframe */
- insert_vert_fcurve(fcu, cur[0], cur[1], 0);
+ insert_vert_fcurve(fcu, cur[0], cur[1], INSERTKEY_FAST);
+ copy_bezt_ipo((fcu->bezt + (fcu->totvert - 1)), bezt);
}
}
}
@@ -264,26 +287,60 @@ void clean_fcurve(FCurve *fcu, float thresh)
/* does current have same value as previous and next? */
if (IS_EQT(cur[1], prev[1], thresh) == 0) {
/* add new keyframe*/
- insert_vert_fcurve(fcu, cur[0], cur[1], 0);
+ insert_vert_fcurve(fcu, cur[0], cur[1], INSERTKEY_FAST);
+ copy_bezt_ipo((fcu->bezt + (fcu->totvert - 1)), bezt);
}
else if (IS_EQT(cur[1], next[1], thresh) == 0) {
/* add new keyframe */
- insert_vert_fcurve(fcu, cur[0], cur[1], 0);
+ insert_vert_fcurve(fcu, cur[0], cur[1], INSERTKEY_FAST);
+ copy_bezt_ipo((fcu->bezt + (fcu->totvert - 1)), bezt);
}
}
else {
/* add if value doesn't equal that of previous */
if (IS_EQT(cur[1], prev[1], thresh) == 0) {
/* add new keyframe */
- insert_vert_fcurve(fcu, cur[0], cur[1], 0);
+ insert_vert_fcurve(fcu, cur[0], cur[1], INSERTKEY_FAST);
+ copy_bezt_ipo((fcu->bezt + (fcu->totvert - 1)), bezt);
}
}
}
}
-
+
+ /* skip doing this, handles should be already sorted since we are copying in forward order only */
+ /* calchandles_fcurve(fcu); */
+
/* now free the memory used by the old BezTriples */
if (old_bezts)
MEM_freeN(old_bezts);
+
+ /* final step, if there is just one key in fcurve, check if it's
+ * the default value and if is, remove fcurve completely. */
+ if (cleardefault && fcu->totvert == 1) {
+ float default_value = 0.0f;
+ PointerRNA id_ptr, ptr;
+ PropertyRNA *prop;
+ RNA_id_pointer_create(ale->id, &id_ptr);
+
+ /* get property to read from, and get value as appropriate */
+ if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) {
+ if (RNA_property_type(prop) == PROP_FLOAT)
+ default_value = RNA_property_float_get_default_index(&ptr, prop, fcu->array_index);
+ }
+
+ if (fcu->bezt->vec[1][1] == default_value) {
+ clear_fcurve_keys(fcu);
+
+ /* check if curve is really unused and if it is, return signal for deletion */
+ if ((list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE) == 0) &&
+ (fcu->driver == NULL))
+ {
+ AnimData *adt = ale->adt;
+ ANIM_fcurve_delete_from_animdata(ac, adt, fcu);
+ ale->key_data = NULL;
+ }
+ }
+ }
}
/* ---------------- */
diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c
index 09284860ec4..6c441e38313 100644
--- a/source/blender/editors/armature/armature_utils.c
+++ b/source/blender/editors/armature/armature_utils.c
@@ -791,7 +791,7 @@ static void *get_armature_edit(bContext *C)
void undo_push_armature(bContext *C, const char *name)
{
// XXX solve getdata()
- undo_editmode_push(C, name, get_armature_edit, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL);
+ undo_editmode_push(C, name, CTX_data_edit_object, get_armature_edit, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL);
}
/* *************************************************************** */
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 1a10814bbf1..14a2483a699 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -7091,7 +7091,7 @@ static void *get_data(bContext *C)
/* and this is all the undo system needs to know */
void undo_push_curve(bContext *C, const char *name)
{
- undo_editmode_push(C, name, get_data, free_undoCurve, undoCurve_to_editCurve, editCurve_to_undoCurve, NULL);
+ undo_editmode_push(C, name, CTX_data_edit_object, get_data, free_undoCurve, undoCurve_to_editCurve, editCurve_to_undoCurve, NULL);
}
void ED_curve_beztcpy(EditNurb *editnurb, BezTriple *dst, BezTriple *src, int count)
diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c
index dd807828d9d..a4f5c250b0f 100644
--- a/source/blender/editors/curve/editfont.c
+++ b/source/blender/editors/curve/editfont.c
@@ -1879,7 +1879,7 @@ static void *get_undoFont(bContext *C)
/* and this is all the undo system needs to know */
void undo_push_font(bContext *C, const char *name)
{
- undo_editmode_push(C, name, get_undoFont, free_undoFont, undoFont_to_editFont, editFont_to_undoFont, NULL);
+ undo_editmode_push(C, name, CTX_data_edit_object, get_undoFont, free_undoFont, undoFont_to_editFont, editFont_to_undoFont, NULL);
}
/**
diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt
index 2a84ca7f297..2ac800ee6e0 100644
--- a/source/blender/editors/datafiles/CMakeLists.txt
+++ b/source/blender/editors/datafiles/CMakeLists.txt
@@ -78,6 +78,13 @@ if(WITH_BLENDER)
data_to_c_simple(../../../../release/datafiles/brushicons/fill.png SRC)
data_to_c_simple(../../../../release/datafiles/brushicons/flatten.png SRC)
data_to_c_simple(../../../../release/datafiles/brushicons/grab.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/hairadd.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/haircomb.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/haircut.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/hairlength.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/hairpuff.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/hairsmooth.png SRC)
+ data_to_c_simple(../../../../release/datafiles/brushicons/hairweight.png SRC)
data_to_c_simple(../../../../release/datafiles/brushicons/inflate.png SRC)
data_to_c_simple(../../../../release/datafiles/brushicons/layer.png SRC)
data_to_c_simple(../../../../release/datafiles/brushicons/lighten.png SRC)
diff --git a/source/blender/editors/datafiles/SConscript b/source/blender/editors/datafiles/SConscript
index 6bc8f21e384..e16d99ef15a 100644
--- a/source/blender/editors/datafiles/SConscript
+++ b/source/blender/editors/datafiles/SConscript
@@ -62,6 +62,13 @@ sources.extend((
os.path.join(env['DATA_SOURCES'], "fill.png.c"),
os.path.join(env['DATA_SOURCES'], "flatten.png.c"),
os.path.join(env['DATA_SOURCES'], "grab.png.c"),
+ os.path.join(env['DATA_SOURCES'], "hairadd.png.c"),
+ os.path.join(env['DATA_SOURCES'], "haircomb.png.c"),
+ os.path.join(env['DATA_SOURCES'], "haircut.png.c"),
+ os.path.join(env['DATA_SOURCES'], "hairlength.png.c"),
+ os.path.join(env['DATA_SOURCES'], "hairpuff.png.c"),
+ os.path.join(env['DATA_SOURCES'], "hairsmooth.png.c"),
+ os.path.join(env['DATA_SOURCES'], "hairweight.png.c"),
os.path.join(env['DATA_SOURCES'], "inflate.png.c"),
os.path.join(env['DATA_SOURCES'], "layer.png.c"),
os.path.join(env['DATA_SOURCES'], "lighten.png.c"),
diff --git a/source/blender/editors/hair/CMakeLists.txt b/source/blender/editors/hair/CMakeLists.txt
new file mode 100644
index 00000000000..2a72ebb9aa4
--- /dev/null
+++ b/source/blender/editors/hair/CMakeLists.txt
@@ -0,0 +1,56 @@
+# ***** 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): Jacques Beaurain.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ ../include
+ ../sculpt_paint
+ ../../blenfont
+ ../../blenkernel
+ ../../blenlib
+ ../../bmesh
+ ../../gpu
+ ../../makesdna
+ ../../makesrna
+ ../../windowmanager
+ ../../../../intern/guardedalloc
+ ../../../../intern/glew-mx
+)
+
+set(INC_SYS
+ ${GLEW_INCLUDE_PATH}
+)
+
+set(SRC
+ hair_cursor.c
+ hair_edit.c
+ hair_mirror.c
+ hair_object_cachelib.c
+ hair_object_particles.c
+ hair_ops.c
+ hair_select.c
+ hair_stroke.c
+ hair_undo.c
+
+ hair_intern.h
+)
+
+add_definitions(${GL_DEFINITIONS})
+
+blender_add_lib(bf_editor_hair "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/editors/hair/SConscript b/source/blender/editors/hair/SConscript
new file mode 100644
index 00000000000..d34bb48218d
--- /dev/null
+++ b/source/blender/editors/hair/SConscript
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+#
+# ***** 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): Nathan Letwory.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+Import ('env')
+
+sources = env.Glob('*.c')
+
+incs = [
+ '#/intern/guardedalloc',
+ env['BF_GLEW_INC'],
+ '#/intern/glew-mx',
+ '../include',
+ '../sculpt_paint',
+ '../../blenfont',
+ '../../blenkernel',
+ '../../blenlib',
+ '../../bmesh',
+ '../../gpu',
+ '../../makesdna',
+ '../../makesrna',
+ '../../windowmanager',
+ ]
+incs = ' '.join(incs)
+
+defs = ['BF_GL_DEFINITIONS']
+
+if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'):
+ incs += ' ' + env['BF_PTHREADS_INC']
+
+env.BlenderLib ( 'bf_editors_hair', sources, Split(incs), defs, libtype=['core'], priority=[344] )
diff --git a/source/blender/editors/hair/hair_cursor.c b/source/blender/editors/hair/hair_cursor.c
new file mode 100644
index 00000000000..dadd0cf114e
--- /dev/null
+++ b/source/blender/editors/hair/hair_cursor.c
@@ -0,0 +1,109 @@
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/hair/hair_cursor.c
+ * \ingroup edhair
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_context.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "ED_view3d.h"
+
+#include "hair_intern.h"
+
+static void hair_draw_cursor(bContext *C, int x, int y, void *UNUSED(customdata))
+{
+ Scene *scene = CTX_data_scene(C);
+ UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
+ HairEditSettings *settings = &scene->toolsettings->hair_edit;
+ Brush *brush = settings->brush;
+
+ float final_radius;
+ float translation[2];
+ float outline_alpha, *outline_col;
+
+ if (!brush)
+ return;
+
+ final_radius = BKE_brush_size_get(scene, brush);
+
+ /* set various defaults */
+ translation[0] = x;
+ translation[1] = y;
+ outline_alpha = 0.5;
+ outline_col = brush->add_col;
+
+ /* make lines pretty */
+ glEnable(GL_BLEND);
+ glEnable(GL_LINE_SMOOTH);
+
+ /* set brush color */
+ glColor4f(outline_col[0], outline_col[1], outline_col[2], outline_alpha);
+
+ /* draw brush outline */
+ glTranslatef(translation[0], translation[1], 0);
+
+ /* draw an inner brush */
+ if (ups->stroke_active && BKE_brush_use_size_pressure(scene, brush)) {
+ /* inner at full alpha */
+ glutil_draw_lined_arc(0.0, M_PI*2.0f, final_radius * ups->size_pressure_value, 40);
+ /* outer at half alpha */
+ glColor4f(outline_col[0], outline_col[1], outline_col[2], outline_alpha * 0.5f);
+ }
+ glutil_draw_lined_arc(0.0, M_PI*2.0f, final_radius, 40);
+ glTranslatef(-translation[0], -translation[1], 0);
+
+ /* restore GL state */
+ glDisable(GL_BLEND);
+ glDisable(GL_LINE_SMOOTH);
+}
+
+void hair_edit_cursor_start(bContext *C, int (*poll)(bContext *C))
+{
+ Scene *scene = CTX_data_scene(C);
+ HairEditSettings *settings = &scene->toolsettings->hair_edit;
+
+ if (!settings->paint_cursor)
+ settings->paint_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), poll, hair_draw_cursor, NULL);
+}
diff --git a/source/blender/editors/hair/hair_edit.c b/source/blender/editors/hair/hair_edit.c
new file mode 100644
index 00000000000..e4aad528f33
--- /dev/null
+++ b/source/blender/editors/hair/hair_edit.c
@@ -0,0 +1,459 @@
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/hair/hair_edit.c
+ * \ingroup edhair
+ */
+
+#include <stdlib.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_editstrands.h"
+#include "BKE_paint.h"
+
+#include "bmesh.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_view3d.h"
+
+#include "hair_intern.h"
+#include "paint_intern.h"
+
+int hair_edit_poll(bContext *C)
+{
+ Object *obact;
+
+ obact = CTX_data_active_object(C);
+ if ((obact && obact->mode & OB_MODE_HAIR_EDIT) && CTX_wm_region_view3d(C)) {
+ return true;
+ }
+
+ return false;
+}
+
+bool hair_use_mirror_x(Object *ob)
+{
+ if (ob->type == OB_MESH)
+ return ((Mesh *)ob->data)->editflag & ME_EDIT_MIRROR_X;
+ else
+ return false;
+}
+
+bool hair_use_mirror_topology(Object *ob)
+{
+ if (ob->type == OB_MESH)
+ return ((Mesh *)ob->data)->editflag & ME_EDIT_MIRROR_TOPO;
+ else
+ return false;
+}
+
+
+/* ==== BMesh utilities ==== */
+
+void hair_bm_min_max(BMEditStrands *edit, float min[3], float max[3])
+{
+ BMVert *v;
+ BMIter iter;
+
+ if (edit->bm->totvert > 0) {
+ INIT_MINMAX(min, max);
+
+ BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) {
+ minmax_v3v3_v3(min, max, v->co);
+ }
+ }
+ else {
+ zero_v3(min);
+ zero_v3(max);
+ }
+}
+
+
+/* ==== edit mode toggle ==== */
+
+int hair_edit_toggle_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+
+ if (ob == NULL)
+ return false;
+ if (CTX_data_edit_object(C))
+ return false;
+
+ return (ob->data && !((ID *)ob->data)->lib && ED_hair_object_has_hair_particle_data(ob)) ||
+ ED_hair_object_has_hair_cache_data(ob);
+}
+
+static void toggle_hair_cursor(bContext *C, bool enable)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ Scene *scene = CTX_data_scene(C);
+ HairEditSettings *settings = &scene->toolsettings->hair_edit;
+
+ if (enable) {
+ hair_edit_cursor_start(C, hair_edit_toggle_poll);
+ }
+ else {
+ if (settings->paint_cursor) {
+ WM_paint_cursor_end(wm, settings->paint_cursor);
+ settings->paint_cursor = NULL;
+ }
+ }
+}
+
+static int hair_edit_toggle_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ const int mode_flag = OB_MODE_HAIR_EDIT;
+ const bool is_mode_set = (ob->mode & mode_flag) != 0;
+
+ if (!is_mode_set) {
+ if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) {
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ if (!is_mode_set) {
+ if (ED_hair_object_init_particle_edit(scene, ob)) {
+ /* particle edit */
+ }
+ else if (ED_hair_object_init_cache_edit(ob)) {
+ /* strands cache edit */
+ }
+
+ ob->mode |= mode_flag;
+
+ toggle_hair_cursor(C, true);
+ WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_HAIR, NULL);
+ }
+ else {
+ if (ED_hair_object_apply_particle_edit(ob)) {
+ /* particle edit */
+ }
+ else if (ED_hair_object_apply_cache_edit(ob)) {
+ /* strands cache edit */
+ }
+ ob->mode &= ~mode_flag;
+
+ toggle_hair_cursor(C, false);
+ WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_HAIR, NULL);
+ }
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+void HAIR_OT_hair_edit_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Hair Edit Toggle";
+ ot->idname = "HAIR_OT_hair_edit_toggle";
+ ot->description = "Toggle hair edit mode";
+
+ /* api callbacks */
+ ot->exec = hair_edit_toggle_exec;
+ ot->poll = hair_edit_toggle_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+
+/* ==== brush stroke ==== */
+
+void hair_init_viewdata(bContext *C, HairViewData *viewdata)
+{
+ View3D *v3d;
+ bool has_zbuf;
+
+ view3d_set_viewcontext(C, &viewdata->vc);
+
+ v3d = viewdata->vc.v3d;
+ has_zbuf = (v3d->drawtype > OB_WIRE) && (v3d->flag & V3D_ZBUF_SELECT);
+
+ view3d_get_transformation(viewdata->vc.ar, viewdata->vc.rv3d, NULL, &viewdata->mats);
+
+ if (has_zbuf) {
+ if (v3d->flag & V3D_INVALID_BACKBUF) {
+ /* needed or else the draw matrix can be incorrect */
+ view3d_operator_needs_opengl(C);
+
+ ED_view3d_backbuf_validate(&viewdata->vc);
+ /* we may need to force an update here by setting the rv3d as dirty
+ * for now it seems ok, but take care!:
+ * rv3d->depths->dirty = 1; */
+ ED_view3d_depth_update(viewdata->vc.ar);
+ }
+ }
+}
+
+typedef struct HairStroke {
+ Scene *scene;
+ Object *ob;
+ BMEditStrands *edit;
+
+ bool first;
+ float lastmouse[2];
+ float zfac;
+
+ float smoothdir[2];
+} HairStroke;
+
+static int hair_stroke_init(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ BMEditStrands *edit = BKE_editstrands_from_object(ob);
+ ARegion *ar = CTX_wm_region(C);
+
+ HairStroke *stroke;
+ float min[3], max[3], center[3];
+
+ /* set the 'distance factor' for grabbing (used in comb etc) */
+ hair_bm_min_max(edit, min, max);
+ mid_v3_v3v3(center, min, max);
+
+ stroke = MEM_callocN(sizeof(HairStroke), "HairStroke");
+ stroke->first = true;
+ op->customdata = stroke;
+
+ stroke->scene = scene;
+ stroke->ob = ob;
+ stroke->edit = edit;
+
+ stroke->zfac = ED_view3d_calc_zfac(ar->regiondata, center, NULL);
+
+ return 1;
+}
+
+static bool hair_stroke_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
+{
+ HairStroke *stroke = op->customdata;
+ Scene *scene = stroke->scene;
+ Object *ob = stroke->ob;
+ BMEditStrands *edit = stroke->edit;
+ HairEditSettings *settings = &scene->toolsettings->hair_edit;
+ ARegion *ar = CTX_wm_region(C);
+ const float smoothfac = 0.9f; /* XXX should this be configurable? */
+
+ float mouse[2], mdelta[2], zvec[3], delta_max;
+ int totsteps, step;
+ HairToolData tool_data;
+ bool updated = false;
+
+ RNA_float_get_array(itemptr, "mouse", mouse);
+
+ if (stroke->first) {
+ copy_v2_v2(stroke->lastmouse, mouse);
+ zero_v2(stroke->smoothdir);
+ stroke->first = false;
+ }
+
+ if (!settings->brush)
+ return false;
+
+ sub_v2_v2v2(mdelta, mouse, stroke->lastmouse);
+ delta_max = max_ff(fabsf(mdelta[0]), fabsf(mdelta[1]));
+
+ totsteps = delta_max / (0.2f * BKE_brush_size_get(scene, settings->brush)) + 1;
+ mul_v2_fl(mdelta, 1.0f / (float)totsteps);
+
+ /* low-pass filter to smooth out jittery pixel increments in the direction */
+ interp_v2_v2v2(stroke->smoothdir, mdelta, stroke->smoothdir, smoothfac);
+
+ hair_init_viewdata(C, &tool_data.viewdata);
+ tool_data.scene = scene;
+ tool_data.ob = ob;
+ tool_data.edit = edit;
+ tool_data.settings = settings;
+
+ invert_m4_m4(tool_data.imat, ob->obmat);
+ copy_v2_v2(tool_data.mval, mouse);
+ tool_data.mdepth = stroke->zfac;
+
+ zvec[0] = 0.0f; zvec[1] = 0.0f; zvec[2] = stroke->zfac;
+ ED_view3d_win_to_3d(ar, zvec, mouse, tool_data.loc);
+ ED_view3d_win_to_delta(ar, stroke->smoothdir, tool_data.delta, stroke->zfac);
+ /* tools work in object space */
+ mul_m4_v3(tool_data.imat, tool_data.loc);
+ mul_mat3_m4_v3(tool_data.imat, tool_data.delta);
+
+ for (step = 0; step < totsteps; ++step) {
+ bool step_updated = hair_brush_step(&tool_data);
+
+ if (step_updated)
+ BKE_editstrands_solve_constraints(ob, edit, NULL);
+
+ updated |= step_updated;
+ }
+
+ copy_v2_v2(stroke->lastmouse, mouse);
+
+ return updated;
+}
+
+static void hair_stroke_exit(wmOperator *op)
+{
+ HairStroke *stroke = op->customdata;
+ MEM_freeN(stroke);
+}
+
+static int hair_stroke_exec(bContext *C, wmOperator *op)
+{
+ HairStroke *stroke = op->customdata;
+ Object *ob = stroke->ob;
+
+ bool updated = false;
+
+ if (!hair_stroke_init(C, op))
+ return OPERATOR_CANCELLED;
+
+ RNA_BEGIN (op->ptr, itemptr, "stroke")
+ {
+ updated |= hair_stroke_apply(C, op, &itemptr);
+ }
+ RNA_END;
+
+ if (updated) {
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
+ }
+
+ hair_stroke_exit(op);
+
+ return OPERATOR_FINISHED;
+}
+
+static void hair_stroke_apply_event(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ HairStroke *stroke = op->customdata;
+ Object *ob = stroke->ob;
+
+ PointerRNA itemptr;
+ float mouse[2];
+ bool updated = false;
+
+ mouse[0] = event->mval[0];
+ mouse[1] = event->mval[1];
+
+ /* fill in stroke */
+ RNA_collection_add(op->ptr, "stroke", &itemptr);
+
+ RNA_float_set_array(&itemptr, "mouse", mouse);
+
+ /* apply */
+ updated |= hair_stroke_apply(C, op, &itemptr);
+
+ if (updated) {
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
+ }
+ else {
+ /* even if nothing was changed, still trigger redraw
+ * for brush drawing during the modal operator
+ */
+ WM_event_add_notifier(C, NC_OBJECT|ND_DRAW, ob);
+ }
+}
+
+static int hair_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (!hair_stroke_init(C, op))
+ return OPERATOR_CANCELLED;
+
+ hair_stroke_apply_event(C, op, event);
+
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int hair_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ switch (event->type) {
+ case LEFTMOUSE:
+ case MIDDLEMOUSE:
+ case RIGHTMOUSE: // XXX hardcoded
+ hair_stroke_exit(op);
+ return OPERATOR_FINISHED;
+ case MOUSEMOVE:
+ hair_stroke_apply_event(C, op, event);
+ break;
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void hair_stroke_cancel(bContext *UNUSED(C), wmOperator *op)
+{
+ hair_stroke_exit(op);
+}
+
+void HAIR_OT_stroke(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Hair Stroke";
+ ot->idname = "HAIR_OT_stroke";
+ ot->description = "Use a stroke tool on hair strands";
+
+ /* api callbacks */
+ ot->exec = hair_stroke_exec;
+ ot->invoke = hair_stroke_invoke;
+ ot->modal = hair_stroke_modal;
+ ot->cancel = hair_stroke_cancel;
+ ot->poll = hair_edit_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
+
+ /* properties */
+ RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
+}
diff --git a/source/blender/editors/hair/hair_intern.h b/source/blender/editors/hair/hair_intern.h
new file mode 100644
index 00000000000..cc805136ace
--- /dev/null
+++ b/source/blender/editors/hair/hair_intern.h
@@ -0,0 +1,124 @@
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/hair/hair_intern.h
+ * \ingroup edhair
+ */
+
+#ifndef __HAIR_INTERN_H__
+#define __HAIR_INTERN_H__
+
+#include "BIF_glutil.h"
+
+#include "ED_view3d.h"
+
+struct ARegion;
+struct bContext;
+struct wmOperatorType;
+struct rcti;
+
+struct Object;
+struct Strands;
+struct DupliObjectData;
+struct StrandsKeyCacheModifier;
+struct DerivedMesh;
+
+/* hair_edit.c */
+bool hair_use_mirror_x(struct Object *ob);
+bool hair_use_mirror_topology(struct Object *ob);
+
+int hair_edit_toggle_poll(struct bContext *C);
+int hair_edit_poll(struct bContext *C);
+
+void HAIR_OT_hair_edit_toggle(struct wmOperatorType *ot);
+
+/* hair_select.c */
+void HAIR_OT_select_all(struct wmOperatorType *ot);
+void HAIR_OT_select_linked(struct wmOperatorType *ot);
+
+/* hair_stroke.c */
+void HAIR_OT_stroke(struct wmOperatorType *ot);
+
+/* hair_object_cachelib.c */
+bool ED_hair_object_has_hair_cache_data(struct Object *ob);
+bool ED_hair_object_init_cache_edit(struct Object *ob);
+bool ED_hair_object_apply_cache_edit(struct Object *ob);
+
+/* hair_object_particles.c */
+bool ED_hair_object_has_hair_particle_data(struct Object *ob);
+bool ED_hair_object_init_particle_edit(struct Scene *scene, struct Object *ob);
+bool ED_hair_object_apply_particle_edit(struct Object *ob);
+
+
+/* ==== Hair Brush ==== */
+
+typedef struct HairViewData {
+ ViewContext vc;
+ bglMats mats;
+} HairViewData;
+
+void hair_init_viewdata(struct bContext *C, struct HairViewData *viewdata);
+
+bool hair_test_depth(struct HairViewData *viewdata, const float co[3], const int screen_co[2]);
+bool hair_test_vertex_inside_circle(struct HairViewData *viewdata, const float mval[2], float radsq,
+ struct BMVert *v, float *r_dist);
+bool hair_test_edge_inside_circle(struct HairViewData *viewdata, const float mval[2], float radsq,
+ struct BMVert *v1, struct BMVert *v2, float *r_dist, float *r_lambda);
+bool hair_test_vertex_inside_rect(struct HairViewData *viewdata, struct rcti *rect, struct BMVert *v);
+bool hair_test_vertex_inside_lasso(struct HairViewData *viewdata, const int mcoords[][2], short moves, struct BMVert *v);
+
+typedef struct HairToolData {
+ /* context */
+ struct Scene *scene;
+ struct Object *ob;
+ struct BMEditStrands *edit;
+ struct HairEditSettings *settings;
+ HairViewData viewdata;
+
+ /* view space */
+ float mval[2]; /* mouse coordinates */
+ float mdepth; /* mouse z depth */
+
+ /* object space */
+ float imat[4][4]; /* obmat inverse */
+ float loc[3]; /* start location */
+ float delta[3]; /* stroke step */
+} HairToolData;
+
+bool hair_brush_step(struct HairToolData *data);
+
+/* ==== Cursor ==== */
+
+void hair_edit_cursor_start(struct bContext *C, int (*poll)(struct bContext *C));
+
+/* ==== BMesh utilities ==== */
+
+struct BMEditStrands;
+
+void hair_bm_min_max(struct BMEditStrands *edit, float min[3], float max[3]);
+
+#endif
diff --git a/source/blender/editors/hair/hair_mirror.c b/source/blender/editors/hair/hair_mirror.c
new file mode 100644
index 00000000000..7f5b14e4b37
--- /dev/null
+++ b/source/blender/editors/hair/hair_mirror.c
@@ -0,0 +1,210 @@
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/hair/hair_mirror.c
+ * \ingroup edhair
+ */
+
+#include <stdlib.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+
+#include "BKE_editmesh_bvh.h"
+#include "BKE_editstrands.h"
+
+#include "ED_physics.h"
+#include "ED_util.h"
+
+#include "bmesh.h"
+
+#include "hair_intern.h"
+
+/* TODO use_topology is not yet implemented for strands.
+ * Native strand topology is not very useful for this.
+ * Instead, the topology of the scalp mesh should be used for finding mirrored strand roots,
+ * then the arc- or parametric length of a vertex from the root to find mirrored verts.
+ */
+
+#define BM_SEARCH_MAXDIST_MIRR 0.00002f
+#define BM_CD_LAYER_ID "__mirror_index"
+/**
+ * \param edit Edit strands.
+ * \param use_self Allow a vertex to point to its self (middle verts).
+ * \param use_select Restrict to selected verts.
+ * \param use_topology Use topology mirror.
+ * \param maxdist Distance for close point test.
+ * \param r_index Optional array to write into, as an alternative to a customdata layer (length of total verts).
+ */
+void ED_strands_mirror_cache_begin_ex(BMEditStrands *edit, const int axis, const bool use_self, const bool use_select,
+ /* extra args */
+ const bool UNUSED(use_topology), float maxdist, int *r_index)
+{
+ BMesh *bm = edit->bm;
+ BMIter iter;
+ BMVert *v;
+ int cd_vmirr_offset;
+ int i;
+
+ /* one or the other is used depending if topo is enabled */
+ struct BMBVHTree *tree = NULL;
+
+ BM_mesh_elem_table_ensure(bm, BM_VERT);
+
+ if (r_index == NULL) {
+ const char *layer_id = BM_CD_LAYER_ID;
+ edit->mirror_cdlayer = CustomData_get_named_layer_index(&bm->vdata, CD_PROP_INT, layer_id);
+ if (edit->mirror_cdlayer == -1) {
+ BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_INT, layer_id);
+ edit->mirror_cdlayer = CustomData_get_named_layer_index(&bm->vdata, CD_PROP_INT, layer_id);
+ }
+
+ cd_vmirr_offset = CustomData_get_n_offset(&bm->vdata, CD_PROP_INT,
+ edit->mirror_cdlayer - CustomData_get_layer_index(&bm->vdata, CD_PROP_INT));
+
+ bm->vdata.layers[edit->mirror_cdlayer].flag |= CD_FLAG_TEMPORARY;
+ }
+
+ BM_mesh_elem_index_ensure(bm, BM_VERT);
+
+ tree = BKE_bmbvh_new(edit->bm, NULL, 0, 0, NULL, false);
+
+#define VERT_INTPTR(_v, _i) r_index ? &r_index[_i] : BM_ELEM_CD_GET_VOID_P(_v, cd_vmirr_offset);
+
+ BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
+ BLI_assert(BM_elem_index_get(v) == i);
+
+ /* temporary for testing, check for selection */
+ if (use_select && !BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ /* do nothing */
+ }
+ else {
+ BMVert *v_mirr;
+ float co[3];
+ int *idx = VERT_INTPTR(v, i);
+
+ copy_v3_v3(co, v->co);
+ co[axis] *= -1.0f;
+ v_mirr = BKE_bmbvh_find_vert_closest(tree, co, maxdist);
+
+ if (v_mirr && (use_self || (v_mirr != v))) {
+ const int i_mirr = BM_elem_index_get(v_mirr);
+ *idx = i_mirr;
+ idx = VERT_INTPTR(v_mirr, i_mirr);
+ *idx = i;
+ }
+ else {
+ *idx = -1;
+ }
+ }
+
+ }
+
+#undef VERT_INTPTR
+
+ BKE_bmbvh_free(tree);
+}
+
+void ED_strands_mirror_cache_begin(BMEditStrands *edit, const int axis,
+ const bool use_self, const bool use_select,
+ const bool use_topology)
+{
+ ED_strands_mirror_cache_begin_ex(edit, axis,
+ use_self, use_select,
+ /* extra args */
+ use_topology, BM_SEARCH_MAXDIST_MIRR, NULL);
+}
+
+BMVert *ED_strands_mirror_get(BMEditStrands *edit, BMVert *v)
+{
+ const int *mirr = CustomData_bmesh_get_layer_n(&edit->bm->vdata, v->head.data, edit->mirror_cdlayer);
+
+ BLI_assert(edit->mirror_cdlayer != -1); /* invalid use */
+
+ if (mirr && *mirr >= 0 && *mirr < edit->bm->totvert) {
+ if (!edit->bm->vtable) {
+ printf("err: should only be called between "
+ "ED_strands_mirror_cache_begin and ED_strands_mirror_cache_end\n");
+ return NULL;
+ }
+
+ return edit->bm->vtable[*mirr];
+ }
+
+ return NULL;
+}
+
+BMEdge *ED_strands_mirror_get_edge(BMEditStrands *edit, BMEdge *e)
+{
+ BMVert *v1_mirr = ED_strands_mirror_get(edit, e->v1);
+ if (v1_mirr) {
+ BMVert *v2_mirr = ED_strands_mirror_get(edit, e->v2);
+ if (v2_mirr) {
+ return BM_edge_exists(v1_mirr, v2_mirr);
+ }
+ }
+
+ return NULL;
+}
+
+void ED_strands_mirror_cache_clear(BMEditStrands *edit, BMVert *v)
+{
+ int *mirr = CustomData_bmesh_get_layer_n(&edit->bm->vdata, v->head.data, edit->mirror_cdlayer);
+
+ BLI_assert(edit->mirror_cdlayer != -1); /* invalid use */
+
+ if (mirr) {
+ *mirr = -1;
+ }
+}
+
+void ED_strands_mirror_cache_end(BMEditStrands *edit)
+{
+ edit->mirror_cdlayer = -1;
+}
+
+void ED_strands_mirror_apply(BMEditStrands *edit, const int sel_from, const int sel_to)
+{
+ BMIter iter;
+ BMVert *v;
+
+ BLI_assert((edit->bm->vtable != NULL) && ((edit->bm->elem_table_dirty & BM_VERT) == 0));
+
+ BM_ITER_MESH (v, &iter, edit->bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT) == sel_from) {
+ BMVert *mirr = ED_strands_mirror_get(edit, v);
+ if (mirr) {
+ if (BM_elem_flag_test(mirr, BM_ELEM_SELECT) == sel_to) {
+ copy_v3_v3(mirr->co, v->co);
+ mirr->co[0] *= -1.0f;
+ }
+ }
+ }
+ }
+}
diff --git a/source/blender/editors/hair/hair_object_cachelib.c b/source/blender/editors/hair/hair_object_cachelib.c
new file mode 100644
index 00000000000..576d8cd2c9d
--- /dev/null
+++ b/source/blender/editors/hair/hair_object_cachelib.c
@@ -0,0 +1,104 @@
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/hair/hair_object_cachelib.c
+ * \ingroup edhair
+ */
+
+#include <stdlib.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+
+#include "DNA_cache_library_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_strands_types.h"
+
+#include "BKE_anim.h"
+#include "BKE_cache_library.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_editstrands.h"
+#include "BKE_strands.h"
+
+#include "bmesh.h"
+
+#include "hair_intern.h"
+
+bool ED_hair_object_has_hair_cache_data(Object *ob)
+{
+ return BKE_cache_modifier_strands_key_get(ob, NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
+bool ED_hair_object_init_cache_edit(Object *ob)
+{
+ StrandsKeyCacheModifier *skmd;
+ DerivedMesh *dm;
+ Strands *strands;
+ float mat[4][4];
+
+ if (!BKE_cache_modifier_strands_key_get(ob, &skmd, &dm, &strands, NULL, NULL, mat))
+ return false;
+
+ if (!skmd->edit) {
+ BMesh *bm = BKE_cache_strands_to_bmesh(strands, skmd->key, mat, skmd->shapenr, dm);
+
+ skmd->edit = BKE_editstrands_create(bm, dm, mat);
+ }
+
+ return true;
+}
+
+bool ED_hair_object_apply_cache_edit(Object *ob)
+{
+ StrandsKeyCacheModifier *skmd;
+ DerivedMesh *dm;
+ Strands *strands;
+ DupliObjectData *dobdata;
+ const char *name;
+ float mat[4][4];
+
+ if (!BKE_cache_modifier_strands_key_get(ob, &skmd, &dm, &strands, &dobdata, &name, mat))
+ return false;
+
+ if (skmd->edit) {
+ Strands *nstrands;
+
+ nstrands = BKE_cache_strands_from_bmesh(skmd->edit, skmd->key, mat, dm);
+
+ BKE_dupli_object_data_add_strands(dobdata, name, nstrands);
+
+ BKE_editstrands_free(skmd->edit);
+ MEM_freeN(skmd->edit);
+ skmd->edit = NULL;
+ }
+
+ return true;
+}
diff --git a/source/blender/editors/hair/hair_object_particles.c b/source/blender/editors/hair/hair_object_particles.c
new file mode 100644
index 00000000000..a6d05327482
--- /dev/null
+++ b/source/blender/editors/hair/hair_object_particles.c
@@ -0,0 +1,101 @@
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/hair/hair_object_particles.c
+ * \ingroup edhair
+ */
+
+#include <stdlib.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_cdderivedmesh.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_editstrands.h"
+#include "BKE_particle.h"
+
+#include "bmesh.h"
+
+#include "hair_intern.h"
+
+bool ED_hair_object_has_hair_particle_data(Object *ob)
+{
+ ParticleSystem *psys = psys_get_current(ob);
+ if (psys && psys->part->type == PART_HAIR)
+ return true;
+
+ return false;
+}
+
+bool ED_hair_object_init_particle_edit(Scene *scene, Object *ob)
+{
+ ParticleSystem *psys = psys_get_current(ob);
+ BMesh *bm;
+ DerivedMesh *dm;
+
+ if (psys && psys->part->type == PART_HAIR) {
+ if (!psys->hairedit) {
+ bm = BKE_particles_to_bmesh(ob, psys);
+
+ if (ob->type == OB_MESH || ob->derivedFinal)
+ dm = ob->derivedFinal ? ob->derivedFinal : mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
+ else
+ dm = NULL;
+
+ psys->hairedit = BKE_editstrands_create(bm, dm, NULL);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool ED_hair_object_apply_particle_edit(Object *ob)
+{
+ ParticleSystem *psys = psys_get_current(ob);
+ if (psys && psys->part->type == PART_HAIR) {
+ if (psys->hairedit) {
+ BKE_particles_from_bmesh(ob, psys);
+ psys->flag |= PSYS_EDITED;
+
+ BKE_editstrands_free(psys->hairedit);
+ MEM_freeN(psys->hairedit);
+ psys->hairedit = NULL;
+ }
+
+ return true;
+ }
+
+ return false;
+}
diff --git a/source/blender/editors/hair/hair_ops.c b/source/blender/editors/hair/hair_ops.c
new file mode 100644
index 00000000000..bb3b027cf20
--- /dev/null
+++ b/source/blender/editors/hair/hair_ops.c
@@ -0,0 +1,143 @@
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/hair/hair_ops.c
+ * \ingroup edhair
+ */
+
+#include "BLI_utildefines.h"
+
+#include "DNA_object_types.h"
+
+#include "BKE_context.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_physics.h"
+
+#include "hair_intern.h"
+#include "paint_intern.h"
+
+void ED_operatortypes_hair(void)
+{
+ WM_operatortype_append(HAIR_OT_hair_edit_toggle);
+
+ WM_operatortype_append(HAIR_OT_select_all);
+ WM_operatortype_append(HAIR_OT_select_linked);
+
+ WM_operatortype_append(HAIR_OT_stroke);
+}
+
+static int hair_poll(bContext *C)
+{
+ if (hair_edit_toggle_poll(C))
+ if (CTX_data_active_object(C)->mode & OB_MODE_HAIR_EDIT)
+ return true;
+
+ return false;
+}
+
+static void ed_keymap_hair_brush_switch(wmKeyMap *keymap, const char *mode)
+{
+ wmKeyMapItem *kmi;
+ int i;
+ /* index 0-9 (zero key is tenth), shift key for index 10-19 */
+ for (i = 0; i < 20; i++) {
+ kmi = WM_keymap_add_item(keymap, "BRUSH_OT_active_index_set",
+ ZEROKEY + ((i + 1) % 10), KM_PRESS, i < 10 ? 0 : KM_SHIFT, 0);
+ RNA_string_set(kmi->ptr, "mode", mode);
+ RNA_int_set(kmi->ptr, "index", i);
+ }
+}
+
+static void ed_keymap_hair_brush_size(wmKeyMap *keymap, const char *UNUSED(path))
+{
+ wmKeyMapItem *kmi;
+
+ kmi = WM_keymap_add_item(keymap, "BRUSH_OT_scale_size", LEFTBRACKETKEY, KM_PRESS, 0, 0);
+ RNA_float_set(kmi->ptr, "scalar", 0.9);
+
+ kmi = WM_keymap_add_item(keymap, "BRUSH_OT_scale_size", RIGHTBRACKETKEY, KM_PRESS, 0, 0);
+ RNA_float_set(kmi->ptr, "scalar", 10.0 / 9.0); // 1.1111....
+}
+
+static void ed_keymap_hair_brush_radial_control(wmKeyMap *keymap, const char *settings, RCFlags flags)
+{
+ wmKeyMapItem *kmi;
+ /* only size needs to follow zoom, strength shows fixed size circle */
+ int flags_nozoom = flags & (~RC_ZOOM);
+ int flags_noradial_secondary = flags & (~(RC_SECONDARY_ROTATION | RC_ZOOM));
+
+ kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0);
+ set_brush_rc_props(kmi->ptr, settings, "size", "use_unified_size", flags);
+
+ kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0);
+ set_brush_rc_props(kmi->ptr, settings, "strength", "use_unified_strength", flags_nozoom);
+
+ if (flags & RC_WEIGHT) {
+ kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", WKEY, KM_PRESS, 0, 0);
+ set_brush_rc_props(kmi->ptr, settings, "weight", "use_unified_weight", flags_nozoom);
+ }
+
+ if (flags & RC_ROTATION) {
+ kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0);
+ set_brush_rc_props(kmi->ptr, settings, "texture_slot.angle", NULL, flags_noradial_secondary);
+ }
+
+ if (flags & RC_SECONDARY_ROTATION) {
+ kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL | KM_ALT, 0);
+ set_brush_rc_props(kmi->ptr, settings, "mask_texture_slot.angle", NULL, flags_nozoom);
+ }
+}
+
+void ED_keymap_hair(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap;
+ wmKeyMapItem *kmi;
+
+ keymap = WM_keymap_find(keyconf, "Hair", 0, 0);
+ keymap->poll = hair_poll;
+
+ kmi = WM_keymap_add_item(keymap, "HAIR_OT_select_all", AKEY, KM_PRESS, 0, 0);
+ RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE);
+ kmi = WM_keymap_add_item(keymap, "HAIR_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0);
+ RNA_enum_set(kmi->ptr, "action", SEL_INVERT);
+
+ kmi = WM_keymap_add_item(keymap, "HAIR_OT_select_linked", LKEY, KM_PRESS, 0, 0);
+ RNA_boolean_set(kmi->ptr, "deselect", false);
+ kmi = WM_keymap_add_item(keymap, "HAIR_OT_select_linked", LKEY, KM_PRESS, KM_SHIFT, 0);
+ RNA_boolean_set(kmi->ptr, "deselect", true);
+
+ kmi = WM_keymap_add_item(keymap, "HAIR_OT_stroke", LEFTMOUSE, KM_PRESS, 0, 0);
+
+ ed_keymap_hair_brush_switch(keymap, "hair_edit");
+ ed_keymap_hair_brush_size(keymap, "tool_settings.hair_edit.brush.size");
+ ed_keymap_hair_brush_radial_control(keymap, "hair_edit", 0);
+}
diff --git a/source/blender/editors/hair/hair_select.c b/source/blender/editors/hair/hair_select.c
new file mode 100644
index 00000000000..986e669f3a7
--- /dev/null
+++ b/source/blender/editors/hair/hair_select.c
@@ -0,0 +1,502 @@
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/hair/hair_select.c
+ * \ingroup edhair
+ */
+
+#include <stdlib.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+#include "BLI_rect.h"
+
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_editstrands.h"
+
+#include "bmesh.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_physics.h"
+#include "ED_view3d.h"
+
+#include "hair_intern.h"
+
+BLI_INLINE bool apply_select_action_flag(BMVert *v, int action)
+{
+ bool cursel = BM_elem_flag_test_bool(v, BM_ELEM_SELECT);
+ bool newsel;
+
+ switch (action) {
+ case SEL_SELECT:
+ newsel = true;
+ break;
+ case SEL_DESELECT:
+ newsel = false;
+ break;
+ case SEL_INVERT:
+ newsel = !cursel;
+ break;
+ case SEL_TOGGLE:
+ /* toggle case should be converted to SELECT or DESELECT based on global state */
+ BLI_assert(false);
+ break;
+ }
+
+ if (newsel != cursel) {
+ BM_elem_flag_set(v, BM_ELEM_SELECT, newsel);
+ return true;
+ }
+ else
+ return false;
+}
+
+/* poll function */
+typedef bool (*PollVertexCb)(void *userdata, struct BMVert *v);
+/* distance metric function */
+typedef bool (*DistanceVertexCb)(void *userdata, struct BMVert *v, float *dist);
+typedef void (*ActionVertexCb)(void *userdata, struct BMVert *v, int action);
+
+static int hair_select_verts_filter(BMEditStrands *edit, HairEditSelectMode select_mode, int action, PollVertexCb cb, void *userdata)
+{
+ BMesh *bm = edit->bm;
+
+ BMVert *v;
+ BMIter iter;
+ int tot = 0;
+
+ bm->selectmode = BM_VERT;
+
+ switch (select_mode) {
+ case HAIR_SELECT_STRAND:
+ break;
+ case HAIR_SELECT_VERTEX:
+ BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) {
+ if (!cb(userdata, v))
+ continue;
+
+ if (apply_select_action_flag(v, action))
+ ++tot;
+ }
+ break;
+ case HAIR_SELECT_TIP:
+ BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) {
+ if (!BM_strands_vert_is_tip(v))
+ continue;
+ if (!cb(userdata, v))
+ continue;
+
+ if (apply_select_action_flag(v, action))
+ ++tot;
+ }
+ break;
+ }
+
+ BM_mesh_select_mode_flush(bm);
+
+ return tot;
+}
+
+static bool hair_select_verts_closest(BMEditStrands *edit, HairEditSelectMode select_mode, int action, DistanceVertexCb cb, ActionVertexCb action_cb, void *userdata)
+{
+ BMesh *bm = edit->bm;
+
+ BMVert *v;
+ BMIter iter;
+
+ float dist;
+ BMVert *closest_v = NULL;
+ float closest_dist = FLT_MAX;
+
+ bm->selectmode = BM_VERT;
+
+ switch (select_mode) {
+ case HAIR_SELECT_STRAND:
+ break;
+ case HAIR_SELECT_VERTEX:
+ BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) {
+ if (!cb(userdata, v, &dist))
+ continue;
+
+ if (dist < closest_dist) {
+ closest_v = v;
+ closest_dist = dist;
+ }
+ }
+ break;
+ case HAIR_SELECT_TIP:
+ BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) {
+ if (!BM_strands_vert_is_tip(v))
+ continue;
+ if (!cb(userdata, v, &dist))
+ continue;
+
+ if (dist < closest_dist) {
+ closest_v = v;
+ closest_dist = dist;
+ }
+ }
+ break;
+ }
+
+ if (closest_v) {
+ action_cb(userdata, closest_v, action);
+
+ BM_mesh_select_mode_flush(bm);
+ return true;
+ }
+ else
+ return false;
+}
+
+static void hair_deselect_all(BMEditStrands *edit)
+{
+ BMVert *v;
+ BMIter iter;
+
+ BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) {
+ BM_elem_flag_set(v, BM_ELEM_SELECT, false);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+/************************ select/deselect all operator ************************/
+
+static bool poll_vertex_all(void *UNUSED(userdata), struct BMVert *UNUSED(v))
+{
+ return true;
+}
+
+static int select_all_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ BMEditStrands *edit = BKE_editstrands_from_object(ob);
+ HairEditSettings *settings = &scene->toolsettings->hair_edit;
+ int action = RNA_enum_get(op->ptr, "action");
+
+ if (!edit)
+ return 0;
+
+ /* toggle action depends on current global selection state */
+ if (action == SEL_TOGGLE) {
+ if (edit->bm->totvertsel == 0)
+ action = SEL_SELECT;
+ else
+ action = SEL_DESELECT;
+ }
+
+ hair_select_verts_filter(edit, settings->select_mode, action, poll_vertex_all, NULL);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW | NA_SELECTED, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void HAIR_OT_select_all(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select/Deselect All";
+ ot->idname = "HAIR_OT_select_all";
+ ot->description = "Select/Deselect all hair vertices";
+
+ /* api callbacks */
+ ot->exec = select_all_exec;
+ ot->poll = hair_edit_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ WM_operator_properties_select_all(ot);
+}
+
+/************************ mouse select operator ************************/
+
+typedef struct DistanceVertexCirleData {
+ HairViewData viewdata;
+ float mval[2];
+ float radsq;
+} DistanceVertexCirleData;
+
+static bool distance_vertex_circle(void *userdata, struct BMVert *v, float *dist)
+{
+ DistanceVertexCirleData *data = userdata;
+
+ return hair_test_vertex_inside_circle(&data->viewdata, data->mval, data->radsq, v, dist);
+}
+
+static void closest_vertex_select(void *UNUSED(userdata), struct BMVert *v, int action)
+{
+ apply_select_action_flag(v, action);
+}
+
+int ED_hair_mouse_select(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ BMEditStrands *edit = BKE_editstrands_from_object(ob);
+ HairEditSettings *settings = &scene->toolsettings->hair_edit;
+ float select_radius = ED_view3d_select_dist_px();
+
+ DistanceVertexCirleData data;
+ int action;
+
+ if (!extend && !deselect && !toggle) {
+ hair_deselect_all(edit);
+ }
+
+ hair_init_viewdata(C, &data.viewdata);
+ data.mval[0] = mval[0];
+ data.mval[1] = mval[1];
+ data.radsq = select_radius * select_radius;
+
+ if (extend)
+ action = SEL_SELECT;
+ else if (deselect)
+ action = SEL_DESELECT;
+ else
+ action = SEL_INVERT;
+
+ hair_select_verts_closest(edit, settings->select_mode, action, distance_vertex_circle, closest_vertex_select, &data);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW | NA_SELECTED, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+/************************ select linked operator ************************/
+
+static void linked_vertices_select(void *UNUSED(userdata), struct BMVert *v, int action)
+{
+ BMVert *lv;
+
+ apply_select_action_flag(v, action);
+
+ for (lv = BM_strands_vert_prev(v); lv; lv = BM_strands_vert_prev(lv))
+ apply_select_action_flag(lv, action);
+ for (lv = BM_strands_vert_next(v); lv; lv = BM_strands_vert_next(lv))
+ apply_select_action_flag(lv, action);
+}
+
+static int select_linked_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ BMEditStrands *edit = BKE_editstrands_from_object(ob);
+ HairEditSettings *settings = &scene->toolsettings->hair_edit;
+ float select_radius = ED_view3d_select_dist_px();
+
+ DistanceVertexCirleData data;
+ int location[2];
+ int action;
+
+ RNA_int_get_array(op->ptr, "location", location);
+
+ hair_init_viewdata(C, &data.viewdata);
+ data.mval[0] = location[0];
+ data.mval[1] = location[1];
+ data.radsq = select_radius * select_radius;
+
+ action = RNA_boolean_get(op->ptr, "deselect") ? SEL_DESELECT : SEL_SELECT;
+
+ hair_select_verts_closest(edit, settings->select_mode, action, distance_vertex_circle, linked_vertices_select, &data);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW | NA_SELECTED, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+static int select_linked_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ RNA_int_set_array(op->ptr, "location", event->mval);
+ return select_linked_exec(C, op);
+}
+
+void HAIR_OT_select_linked(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Linked";
+ ot->idname = "HAIR_OT_select_linked";
+ ot->description = "Select connected vertices";
+
+ /* api callbacks */
+ ot->exec = select_linked_exec;
+ ot->invoke = select_linked_invoke;
+ ot->poll = hair_edit_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect linked keys rather than selecting them");
+ RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384);
+}
+
+/************************ border select operator ************************/
+
+typedef struct PollVertexRectData {
+ HairViewData viewdata;
+ rcti rect;
+} PollVertexRectData;
+
+static bool poll_vertex_inside_rect(void *userdata, struct BMVert *v)
+{
+ PollVertexRectData *data = userdata;
+
+ return hair_test_vertex_inside_rect(&data->viewdata, &data->rect, v);
+}
+
+int ED_hair_border_select(bContext *C, rcti *rect, bool select, bool extend)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ BMEditStrands *edit = BKE_editstrands_from_object(ob);
+ HairEditSettings *settings = &scene->toolsettings->hair_edit;
+
+ PollVertexRectData data;
+ int action;
+
+ if (!extend && select)
+ hair_deselect_all(edit);
+
+ hair_init_viewdata(C, &data.viewdata);
+ data.rect = *rect;
+
+ if (extend)
+ action = SEL_SELECT;
+ else if (select)
+ action = SEL_INVERT;
+ else
+ action = SEL_DESELECT;
+
+ hair_select_verts_filter(edit, settings->select_mode, action, poll_vertex_inside_rect, &data);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW | NA_SELECTED, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+/************************ circle select operator ************************/
+
+typedef struct PollVertexCirleData {
+ HairViewData viewdata;
+ float mval[2];
+ float radsq;
+} PollVertexCirleData;
+
+static bool poll_vertex_inside_circle(void *userdata, struct BMVert *v)
+{
+ PollVertexCirleData *data = userdata;
+ float dist;
+
+ return hair_test_vertex_inside_circle(&data->viewdata, data->mval, data->radsq, v, &dist);
+}
+
+int ED_hair_circle_select(bContext *C, bool select, const int mval[2], float radius)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ BMEditStrands *edit = BKE_editstrands_from_object(ob);
+ HairEditSettings *settings = &scene->toolsettings->hair_edit;
+ int action = select ? SEL_SELECT : SEL_DESELECT;
+
+ PollVertexCirleData data;
+ int tot;
+
+ if (!edit)
+ return 0;
+
+ hair_init_viewdata(C, &data.viewdata);
+ data.mval[0] = mval[0];
+ data.mval[1] = mval[1];
+ data.radsq = radius * radius;
+
+ tot = hair_select_verts_filter(edit, settings->select_mode, action, poll_vertex_inside_circle, &data);
+
+ return tot;
+}
+
+/************************ lasso select operator ************************/
+
+typedef struct PollVertexLassoData {
+ HairViewData viewdata;
+ const int (*mcoords)[2];
+ short moves;
+} PollVertexLassoData;
+
+static bool poll_vertex_inside_lasso(void *userdata, struct BMVert *v)
+{
+ PollVertexLassoData *data = userdata;
+
+ return hair_test_vertex_inside_lasso(&data->viewdata, data->mcoords, data->moves, v);
+}
+
+int ED_hair_lasso_select(bContext *C, const int mcoords[][2], const short moves, bool extend, bool select)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ BMEditStrands *edit = BKE_editstrands_from_object(ob);
+ HairEditSettings *settings = &scene->toolsettings->hair_edit;
+
+ PollVertexLassoData data;
+ int action;
+
+ if (!extend && select)
+ hair_deselect_all(edit);
+
+ hair_init_viewdata(C, &data.viewdata);
+ data.mcoords = mcoords;
+ data.moves = moves;
+
+ if (extend)
+ action = SEL_SELECT;
+ else if (select)
+ action = SEL_INVERT;
+ else
+ action = SEL_DESELECT;
+
+ hair_select_verts_filter(edit, settings->select_mode, action, poll_vertex_inside_lasso, &data);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW | NA_SELECTED, ob);
+
+ return OPERATOR_FINISHED;
+}
diff --git a/source/blender/editors/hair/hair_stroke.c b/source/blender/editors/hair/hair_stroke.c
new file mode 100644
index 00000000000..aeb460990db
--- /dev/null
+++ b/source/blender/editors/hair/hair_stroke.c
@@ -0,0 +1,498 @@
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/hair/hair_edit.c
+ * \ingroup edhair
+ */
+
+#include <stdlib.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_lasso.h"
+#include "BLI_math.h"
+#include "BLI_rect.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_editstrands.h"
+#include "BKE_effect.h"
+#include "BKE_mesh_sample.h"
+
+#include "bmesh.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "ED_physics.h"
+#include "ED_view3d.h"
+
+#include "hair_intern.h"
+
+bool hair_test_depth(HairViewData *viewdata, const float co[3], const int screen_co[2])
+{
+ View3D *v3d = viewdata->vc.v3d;
+ ViewDepths *vd = viewdata->vc.rv3d->depths;
+ const bool has_zbuf = (v3d->drawtype > OB_WIRE) && (v3d->flag & V3D_ZBUF_SELECT);
+
+ double ux, uy, uz;
+ float depth;
+
+ /* nothing to do */
+ if (!has_zbuf)
+ return true;
+
+ gluProject(co[0], co[1], co[2],
+ viewdata->mats.modelview, viewdata->mats.projection, viewdata->mats.viewport,
+ &ux, &uy, &uz);
+
+ /* check if screen_co is within bounds because brush_cut uses out of screen coords */
+ if (screen_co[0] >= 0 && screen_co[0] < vd->w && screen_co[1] >= 0 && screen_co[1] < vd->h) {
+ BLI_assert(vd && vd->depths);
+ /* we know its not clipped */
+ depth = vd->depths[screen_co[1] * vd->w + screen_co[0]];
+
+ return ((float)uz - 0.00001f <= depth);
+ }
+
+ return false;
+}
+
+bool hair_test_vertex_inside_circle(HairViewData *viewdata, const float mval[2], float radsq, BMVert *v, float *r_dist)
+{
+ float (*obmat)[4] = viewdata->vc.obact->obmat;
+ float co_world[3];
+ float dx, dy, distsq;
+ int screen_co[2];
+
+ mul_v3_m4v3(co_world, obmat, v->co);
+
+ /* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */
+ if (ED_view3d_project_int_global(viewdata->vc.ar, co_world, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK)
+ return false;
+
+ dx = mval[0] - (float)screen_co[0];
+ dy = mval[1] - (float)screen_co[1];
+ distsq = dx * dx + dy * dy;
+
+ if (distsq > radsq)
+ return false;
+
+ if (hair_test_depth(viewdata, v->co, screen_co)) {
+ *r_dist = sqrtf(distsq);
+ return true;
+ }
+ else
+ return false;
+}
+
+bool hair_test_edge_inside_circle(HairViewData *viewdata, const float mval[2], float radsq, BMVert *v1, BMVert *v2, float *r_dist, float *r_lambda)
+{
+ float (*obmat)[4] = viewdata->vc.obact->obmat;
+ float world_co1[3], world_co2[3];
+ float dx, dy, distsq;
+ int screen_co1[2], screen_co2[2], screen_cp[2];
+ float lambda, world_cp[3], screen_cpf[2], screen_co1f[2], screen_co2f[2];
+
+ mul_v3_m4v3(world_co1, obmat, v1->co);
+ mul_v3_m4v3(world_co2, obmat, v2->co);
+
+ /* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */
+ if (ED_view3d_project_int_global(viewdata->vc.ar, world_co1, screen_co1, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK)
+ return false;
+ if (ED_view3d_project_int_global(viewdata->vc.ar, world_co2, screen_co2, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK)
+ return false;
+
+ screen_co1f[0] = screen_co1[0];
+ screen_co1f[1] = screen_co1[1];
+ screen_co2f[0] = screen_co2[0];
+ screen_co2f[1] = screen_co2[1];
+ lambda = closest_to_line_v2(screen_cpf, mval, screen_co1f, screen_co2f);
+ if (lambda < 0.0f || lambda > 1.0f) {
+ CLAMP(lambda, 0.0f, 1.0f);
+ interp_v2_v2v2(screen_cpf, screen_co1f, screen_co2f, lambda);
+ }
+
+ dx = mval[0] - screen_cpf[0];
+ dy = mval[1] - screen_cpf[1];
+ distsq = dx * dx + dy * dy;
+
+ if (distsq > radsq)
+ return false;
+
+ interp_v3_v3v3(world_cp, world_co1, world_co2, lambda);
+
+ screen_cp[0] = screen_cpf[0];
+ screen_cp[1] = screen_cpf[1];
+ if (hair_test_depth(viewdata, world_cp, screen_cp)) {
+ *r_dist = sqrtf(distsq);
+ *r_lambda = lambda;
+ return true;
+ }
+ else
+ return false;
+}
+
+bool hair_test_vertex_inside_rect(HairViewData *viewdata, rcti *rect, BMVert *v)
+{
+ float (*obmat)[4] = viewdata->vc.obact->obmat;
+ float co_world[3];
+ int screen_co[2];
+
+ mul_v3_m4v3(co_world, obmat, v->co);
+
+ /* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */
+ if (ED_view3d_project_int_global(viewdata->vc.ar, co_world, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK)
+ return false;
+
+ if (!BLI_rcti_isect_pt_v(rect, screen_co))
+ return false;
+
+ if (hair_test_depth(viewdata, v->co, screen_co))
+ return true;
+ else
+ return false;
+}
+
+bool hair_test_vertex_inside_lasso(HairViewData *viewdata, const int mcoords[][2], short moves, BMVert *v)
+{
+ float (*obmat)[4] = viewdata->vc.obact->obmat;
+ float co_world[3];
+ int screen_co[2];
+
+ mul_v3_m4v3(co_world, obmat, v->co);
+
+ /* TODO, should this check V3D_PROJ_TEST_CLIP_BB too? */
+ if (ED_view3d_project_int_global(viewdata->vc.ar, co_world, screen_co, V3D_PROJ_TEST_CLIP_WIN) != V3D_PROJ_RET_OK)
+ return false;
+
+ if (!BLI_lasso_is_point_inside(mcoords, moves, screen_co[0], screen_co[1], IS_CLIPPED))
+ return false;
+
+ if (hair_test_depth(viewdata, v->co, screen_co))
+ return true;
+ else
+ return false;
+}
+
+/* ------------------------------------------------------------------------- */
+
+typedef void (*VertexToolCb)(HairToolData *data, void *userdata, BMVert *v, float factor);
+
+/* apply tool directly to each vertex inside the filter area */
+static int UNUSED_FUNCTION(hair_tool_apply_vertex)(HairToolData *data, VertexToolCb cb, void *userdata)
+{
+ Scene *scene = data->scene;
+ Brush *brush = data->settings->brush;
+ const float rad = BKE_brush_size_get(scene, brush);
+ const float radsq = rad*rad;
+ const float threshold = 0.0f; /* XXX could be useful, is it needed? */
+ const bool use_mirror = hair_use_mirror_x(data->ob);
+
+ BMVert *v, *v_mirr;
+ BMIter iter;
+ int tot = 0;
+ float dist, factor;
+
+ if (use_mirror)
+ ED_strands_mirror_cache_begin(data->edit, 0, false, false, hair_use_mirror_topology(data->ob));
+
+ BM_ITER_MESH(v, &iter, data->edit->bm, BM_VERTS_OF_MESH) {
+ if (!hair_test_vertex_inside_circle(&data->viewdata, data->mval, radsq, v, &dist))
+ continue;
+
+ factor = 1.0f - dist / rad;
+ if (factor > threshold) {
+ cb(data, userdata, v, factor);
+ ++tot;
+
+ if (use_mirror) {
+ v_mirr = ED_strands_mirror_get(data->edit, v);
+ if (v_mirr)
+ cb(data, userdata, v_mirr, factor);
+ }
+ }
+ }
+
+ /* apply mirror */
+ if (use_mirror)
+ ED_strands_mirror_cache_end(data->edit);
+
+ return tot;
+}
+
+/* ------------------------------------------------------------------------- */
+
+typedef void (*EdgeToolCb)(HairToolData *data, void *userdata, BMVert *v1, BMVert *v2, float factor, float edge_param);
+
+static int hair_tool_apply_strand_edges(HairToolData *data, EdgeToolCb cb, void *userdata, BMVert *root)
+{
+ Scene *scene = data->scene;
+ Brush *brush = data->settings->brush;
+ const float rad = BKE_brush_size_get(scene, brush);
+ const float radsq = rad*rad;
+ const float threshold = 0.0f; /* XXX could be useful, is it needed? */
+ const bool use_mirror = hair_use_mirror_x(data->ob);
+
+ BMVert *v, *vprev, *v_mirr, *vprev_mirr;
+ BMIter iter;
+ int k;
+ int tot = 0;
+
+ BM_ITER_STRANDS_ELEM_INDEX(v, &iter, root, BM_VERTS_OF_STRAND, k) {
+ if (k > 0) {
+ float dist, lambda;
+
+ if (hair_test_edge_inside_circle(&data->viewdata, data->mval, radsq, vprev, v, &dist, &lambda)) {
+ float factor = 1.0f - dist / rad;
+ if (factor > threshold) {
+ cb(data, userdata, vprev, v, factor, lambda);
+ ++tot;
+
+ if (use_mirror) {
+ v_mirr = ED_strands_mirror_get(data->edit, v);
+ if (vprev_mirr && v_mirr)
+ cb(data, userdata, vprev_mirr, v_mirr, factor, lambda);
+ }
+ }
+ }
+ }
+
+ vprev = v;
+ vprev_mirr = v_mirr;
+ }
+
+ return tot;
+}
+
+/* apply tool to vertices of edges inside the filter area,
+ * using the closest edge point for weighting
+ */
+static int hair_tool_apply_edge(HairToolData *data, EdgeToolCb cb, void *userdata)
+{
+ BMVert *root;
+ BMIter iter;
+ int tot = 0;
+
+ if (hair_use_mirror_x(data->ob))
+ ED_strands_mirror_cache_begin(data->edit, 0, false, false, hair_use_mirror_topology(data->ob));
+
+ BM_ITER_STRANDS(root, &iter, data->edit->bm, BM_STRANDS_OF_MESH) {
+ tot += hair_tool_apply_strand_edges(data, cb, userdata, root);
+ }
+
+ /* apply mirror */
+ if (hair_use_mirror_x(data->ob))
+ ED_strands_mirror_cache_end(data->edit);
+
+ return tot;
+}
+
+/* ------------------------------------------------------------------------- */
+
+typedef struct CombData {
+ float power;
+} CombData;
+
+static void UNUSED_FUNCTION(hair_vertex_comb)(HairToolData *data, void *userdata, BMVert *v, float factor)
+{
+ CombData *combdata = userdata;
+
+ float combfactor = powf(factor, combdata->power);
+
+ madd_v3_v3fl(v->co, data->delta, combfactor);
+}
+
+/* Edge-based combing tool:
+ * Unlike the vertex tool (which simply displaces vertices), the edge tool
+ * adjusts edge orientations to follow the stroke direction.
+ */
+static void hair_edge_comb(HairToolData *data, void *userdata, BMVert *v1, BMVert *v2, float factor, float UNUSED(edge_param))
+{
+ CombData *combdata = userdata;
+ float strokedir[3], edge[3], edgedir[3], strokelen, edgelen;
+ float edge_proj[3];
+
+ float combfactor = powf(factor, combdata->power);
+ float effect;
+
+ strokelen = normalize_v3_v3(strokedir, data->delta);
+
+ sub_v3_v3v3(edge, v2->co, v1->co);
+ edgelen = normalize_v3_v3(edgedir, edge);
+ if (edgelen == 0.0f)
+ return;
+
+ /* This factor prevents sudden changes in direction with small stroke lengths.
+ * The arctan maps the 0..inf range of the length ratio to 0..1 smoothly.
+ */
+ effect = atan(strokelen / edgelen * 4.0f / (0.5f*M_PI));
+
+ mul_v3_v3fl(edge_proj, strokedir, edgelen);
+
+ interp_v3_v3v3(edge, edge, edge_proj, combfactor * effect);
+
+ add_v3_v3v3(v2->co, v1->co, edge);
+}
+
+
+BLI_INLINE void construct_m4_loc_nor_tan(float mat[4][4], const float loc[3], const float nor[3], const float tang[3])
+{
+ float cotang[3];
+
+ cross_v3_v3v3(cotang, nor, tang);
+
+ copy_v3_v3(mat[0], tang);
+ copy_v3_v3(mat[1], cotang);
+ copy_v3_v3(mat[2], nor);
+ copy_v3_v3(mat[3], loc);
+ mat[0][3] = 0.0f;
+ mat[1][3] = 0.0f;
+ mat[2][3] = 0.0f;
+ mat[3][3] = 1.0f;
+}
+
+static void grow_hair(BMEditStrands *edit, MSurfaceSample *sample)
+{
+ DerivedMesh *dm = edit->root_dm;
+ const float len = 1.5f;
+
+ float root_mat[4][4];
+ BMVert *root, *v;
+ BMIter iter;
+ int i;
+
+ {
+ float co[3], nor[3], tang[3];
+ BKE_mesh_sample_eval(dm, sample, co, nor, tang);
+ construct_m4_loc_nor_tan(root_mat, co, nor, tang);
+ }
+
+ root = BM_strands_create(edit->bm, 5, true);
+
+ BM_elem_meshsample_data_named_set(&edit->bm->vdata, root, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, sample);
+
+ BM_ITER_STRANDS_ELEM_INDEX(v, &iter, root, BM_VERTS_OF_STRAND, i) {
+ float co[3];
+
+ co[0] = co[1] = 0.0f;
+ co[2] = len * (float)i / (float)(len - 1);
+
+ mul_m4_v3(root_mat, co);
+
+ copy_v3_v3(v->co, co);
+ }
+
+ BM_mesh_elem_index_ensure(edit->bm, BM_ALL);
+}
+
+static bool hair_add_ray_cb(void *vdata, float ray_start[3], float ray_end[3])
+{
+ HairToolData *data = vdata;
+ ViewContext *vc = &data->viewdata.vc;
+
+ ED_view3d_win_to_segment(vc->ar, vc->v3d, data->mval, ray_start, ray_end, true);
+
+ mul_m4_v3(data->imat, ray_start);
+ mul_m4_v3(data->imat, ray_end);
+
+ return true;
+}
+
+static bool hair_get_surface_sample(HairToolData *data, MSurfaceSample *sample)
+{
+ DerivedMesh *dm = data->edit->root_dm;
+
+ MSurfaceSampleStorage dst;
+ int tot;
+
+ BKE_mesh_sample_storage_single(&dst, sample);
+ tot = BKE_mesh_sample_generate_raycast(&dst, dm, hair_add_ray_cb, data, 1);
+ BKE_mesh_sample_storage_release(&dst);
+
+ return tot > 0;
+}
+
+static bool hair_add(HairToolData *data)
+{
+ MSurfaceSample sample;
+
+ if (!hair_get_surface_sample(data, &sample))
+ return false;
+
+ grow_hair(data->edit, &sample);
+
+ return true;
+}
+
+
+bool hair_brush_step(HairToolData *data)
+{
+ Brush *brush = data->settings->brush;
+ BrushHairTool hair_tool = brush->hair_tool;
+ BMEditStrands *edit = data->edit;
+ int tot = 0;
+
+ switch (hair_tool) {
+ case HAIR_TOOL_COMB: {
+ CombData combdata;
+ combdata.power = (brush->alpha - 0.5f) * 2.0f;
+ if (combdata.power < 0.0f)
+ combdata.power = 1.0f - 9.0f * combdata.power;
+ else
+ combdata.power = 1.0f - combdata.power;
+
+ /*tot = hair_tool_apply_vertex(data, hair_vertex_comb, &combdata);*/ /* UNUSED */
+ tot = hair_tool_apply_edge(data, hair_edge_comb, &combdata);
+ break;
+ }
+ case HAIR_TOOL_CUT:
+ break;
+ case HAIR_TOOL_LENGTH:
+ break;
+ case HAIR_TOOL_PUFF:
+ break;
+ case HAIR_TOOL_ADD:
+ if (hair_add(data))
+ edit->flag |= BM_STRANDS_DIRTY_SEGLEN;
+ break;
+ case HAIR_TOOL_SMOOTH:
+ break;
+ case HAIR_TOOL_WEIGHT:
+ break;
+ }
+
+ return tot > 0;
+}
diff --git a/source/blender/editors/hair/hair_undo.c b/source/blender/editors/hair/hair_undo.c
new file mode 100644
index 00000000000..a58b9c042f8
--- /dev/null
+++ b/source/blender/editors/hair/hair_undo.c
@@ -0,0 +1,190 @@
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/hair/hair_undo.c
+ * \ingroup edhair
+ */
+
+#include <stdlib.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_utildefines.h"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BKE_context.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_editstrands.h"
+#include "BKE_key.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_sample.h"
+
+#include "ED_physics.h"
+#include "ED_util.h"
+
+#include "bmesh.h"
+
+#include "hair_intern.h"
+
+static void *strands_get_edit(bContext *C)
+{
+ Object *obact = CTX_data_active_object(C);
+ const int mode_flag = OB_MODE_HAIR_EDIT;
+ const bool is_mode_set = ((obact->mode & mode_flag) != 0);
+
+ if (obact && is_mode_set) {
+ BMEditStrands *edit = BKE_editstrands_from_object(obact);
+ return edit;
+ }
+ return NULL;
+}
+
+typedef struct UndoStrands {
+ Mesh me; /* Mesh supports all the customdata we need, easiest way to implement undo storage */
+ int selectmode;
+
+ /** \note
+ * this isn't a prefect solution, if you edit keys and change shapes this works well (fixing [#32442]),
+ * but editing shape keys, going into object mode, removing or changing their order,
+ * then go back into editmode and undo will give issues - where the old index will be out of sync
+ * with the new object index.
+ *
+ * There are a few ways this could be made to work but for now its a known limitation with mixing
+ * object and editmode operations - Campbell */
+ int shapenr;
+} UndoStrands;
+
+/* undo simply makes copies of a bmesh */
+static void *strands_edit_to_undo(void *editv, void *UNUSED(obdata))
+{
+ BMEditStrands *edit = editv;
+// Mesh *obme = obdata;
+
+ UndoStrands *undo = MEM_callocN(sizeof(UndoStrands), "undo Strands");
+
+ /* make sure shape keys work */
+// um->me.key = obme->key ? BKE_key_copy_nolib(obme->key) : NULL;
+
+ /* BM_mesh_validate(em->bm); */ /* for troubleshooting */
+
+ BM_mesh_bm_to_me_ex(edit->bm, &undo->me, CD_MASK_STRANDS, false);
+
+ undo->selectmode = edit->bm->selectmode;
+ undo->shapenr = edit->bm->shapenr;
+
+ return undo;
+}
+
+static void strands_undo_to_edit(void *undov, void *editv, void *UNUSED(obdata))
+{
+ UndoStrands *undo = undov;
+ BMEditStrands *edit = editv, *edit_tmp;
+ Object *ob = edit->ob;
+ DerivedMesh *dm = edit->root_dm;
+ BMesh *bm;
+// Key *key = ((Mesh *) obdata)->key;
+
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(&undo->me);
+
+ edit->bm->shapenr = undo->shapenr;
+
+ bm = BM_mesh_create(&allocsize);
+ BM_mesh_bm_from_me_ex(bm, &undo->me, CD_MASK_STRANDS_BMESH, false, false, undo->shapenr);
+
+ /* note: have to create the new edit before freeing the old one,
+ * because it owns the root_dm and we have to copy it before
+ * it gets released when freeing the old edit.
+ */
+ edit_tmp = BKE_editstrands_create(bm, dm, NULL);
+ BKE_editstrands_free(edit);
+ *edit = *edit_tmp;
+
+ bm->selectmode = undo->selectmode;
+ edit->ob = ob;
+
+#if 0
+ /* T35170: Restore the active key on the RealMesh. Otherwise 'fake' offset propagation happens
+ * if the active is a basis for any other. */
+ if (key && (key->type == KEY_RELATIVE)) {
+ /* Since we can't add, remove or reorder keyblocks in editmode, it's safe to assume
+ * shapenr from restored bmesh and keyblock indices are in sync. */
+ const int kb_act_idx = ob->shapenr - 1;
+
+ /* If it is, let's patch the current mesh key block to its restored value.
+ * Else, the offsets won't be computed and it won't matter. */
+ if (BKE_keyblock_is_basis(key, kb_act_idx)) {
+ KeyBlock *kb_act = BLI_findlink(&key->block, kb_act_idx);
+
+ if (kb_act->totelem != undo->me.totvert) {
+ /* The current mesh has some extra/missing verts compared to the undo, adjust. */
+ MEM_SAFE_FREE(kb_act->data);
+ kb_act->data = MEM_mallocN((size_t)(key->elemsize * bm->totvert), __func__);
+ kb_act->totelem = undo->me.totvert;
+ }
+
+ BKE_keyblock_update_from_mesh(&undo->me, kb_act);
+ }
+ }
+#endif
+
+ ob->shapenr = undo->shapenr;
+
+ MEM_freeN(edit_tmp);
+}
+
+static void strands_free_undo(void *undov)
+{
+ UndoStrands *undo = undov;
+
+ if (undo->me.key) {
+ BKE_key_free(undo->me.key);
+ MEM_freeN(undo->me.key);
+ }
+
+ BKE_mesh_free(&undo->me, false);
+ MEM_freeN(undo);
+}
+
+/* and this is all the undo system needs to know */
+void undo_push_strands(bContext *C, const char *name)
+{
+ /* edit->ob gets out of date and crashes on mesh undo,
+ * this is an easy way to ensure its OK
+ * though we could investigate the matter further. */
+ Object *obact = CTX_data_active_object(C);
+ BMEditStrands *edit = BKE_editstrands_from_object(obact);
+ if (edit) {
+ edit->ob = obact;
+
+ undo_editmode_push(C, name, CTX_data_active_object, strands_get_edit, strands_free_undo, strands_undo_to_edit, strands_edit_to_undo, NULL);
+ }
+}
diff --git a/source/blender/editors/include/BIF_glutil.h b/source/blender/editors/include/BIF_glutil.h
index b904417cfcb..7292e8b171f 100644
--- a/source/blender/editors/include/BIF_glutil.h
+++ b/source/blender/editors/include/BIF_glutil.h
@@ -89,6 +89,9 @@ void glutil_draw_lined_arc(float start, float angle, float radius, int nsegments
*/
void glutil_draw_filled_arc(float start, float angle, float radius, int nsegments);
+
+void glutil_draw_filled_arc_part(float start, float angle, float radius, float radius_inn, int nsegments);
+
/**
* Returns an integer value as obtained by glGetIntegerv.
* The param must cause only one value to be gotten from GL.
@@ -155,7 +158,8 @@ void glaDrawPixelsTex(float x, float y, int img_w, int img_h, int format, int ty
* only RGBA
* needs glaDefine2DArea to be set.
*/
-void glaDrawPixelsAuto(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect);
+void glaDrawPixelsAuto(float x, float y, int img_w, int img_h, int format,
+ int type, int zoomfilter, float alpha, void *rect);
void glaDrawPixelsTexScaled(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect, float scaleX, float scaleY);
@@ -217,12 +221,12 @@ void bgl_get_mats(bglMats *mats);
/* **** Color management helper functions for GLSL display/transform ***** */
/* Draw imbuf on a screen, preferably using GLSL display transform */
-void glaDrawImBuf_glsl(struct ImBuf *ibuf, float x, float y, int zoomfilter,
+void glaDrawImBuf_glsl(struct ImBuf *ibuf, float x, float y, int zoomfilter, float alpha,
struct ColorManagedViewSettings *view_settings,
struct ColorManagedDisplaySettings *display_settings);
/* Draw imbuf on a screen, preferably using GLSL display transform */
-void glaDrawImBuf_glsl_ctx(const struct bContext *C, struct ImBuf *ibuf, float x, float y, int zoomfilter);
+void glaDrawImBuf_glsl_ctx(const struct bContext *C, struct ImBuf *ibuf, float x, float y, int zoomfilter, float alpha);
void glaDrawBorderCorners(const struct rcti *border, float zoomx, float zoomy);
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index 0f70bf3c745..dfa667d095a 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -412,7 +412,8 @@ typedef enum eAnimChannel_Settings {
ACHANNEL_SETTING_EXPAND = 3,
ACHANNEL_SETTING_VISIBLE = 4, /* only for Graph Editor */
ACHANNEL_SETTING_SOLO = 5, /* only for NLA Tracks */
- ACHANNEL_SETTING_PINNED = 6 /* only for NLA Actions */
+ ACHANNEL_SETTING_PINNED = 6, /* only for NLA Actions */
+ ACHANNEL_SETTING_MOD_OFF = 7
} eAnimChannel_Settings;
diff --git a/source/blender/editors/include/ED_datafiles.h b/source/blender/editors/include/ED_datafiles.h
index 661ab58b98c..5e2c6bb8c44 100644
--- a/source/blender/editors/include/ED_datafiles.h
+++ b/source/blender/editors/include/ED_datafiles.h
@@ -165,6 +165,27 @@ extern char datatoc_twist_png[];
extern int datatoc_vertexdraw_png_size;
extern char datatoc_vertexdraw_png[];
+extern int datatoc_haircomb_png_size;
+extern char datatoc_haircomb_png[];
+
+extern int datatoc_haircut_png_size;
+extern char datatoc_haircut_png[];
+
+extern int datatoc_hairlength_png_size;
+extern char datatoc_hairlength_png[];
+
+extern int datatoc_hairpuff_png_size;
+extern char datatoc_hairpuff_png[];
+
+extern int datatoc_hairadd_png_size;
+extern char datatoc_hairadd_png[];
+
+extern int datatoc_hairsmooth_png_size;
+extern char datatoc_hairsmooth_png[];
+
+extern int datatoc_hairweight_png_size;
+extern char datatoc_hairweight_png[];
+
/* Matcap files */
extern int datatoc_mc01_jpg_size;
diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h
index adf82acb399..92375cbbbcf 100644
--- a/source/blender/editors/include/ED_keyframes_edit.h
+++ b/source/blender/editors/include/ED_keyframes_edit.h
@@ -261,7 +261,7 @@ bool delete_fcurve_keys(struct FCurve *fcu);
void clear_fcurve_keys(struct FCurve *fcu);
void duplicate_fcurve_keys(struct FCurve *fcu);
-void clean_fcurve(struct FCurve *fcu, float thresh);
+void clean_fcurve(struct bAnimContext *ac, struct bAnimListElem *ale, float thresh, bool cleardefault);
void smooth_fcurve(struct FCurve *fcu);
void sample_fcurve(struct FCurve *fcu);
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index 8e19ec839d8..d391ca43134 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -39,6 +39,7 @@ struct ID;
struct View3D;
struct ARegion;
struct bContext;
+struct bFaceMap;
struct wmOperator;
struct wmKeyConfig;
struct ReportList;
@@ -259,6 +260,10 @@ float ED_vgroup_vert_weight(struct Object *ob, struct bDeformGrou
void ED_vgroup_vert_active_mirror(struct Object *ob, int def_nr);
+/* object_fmap.c */
+void ED_fmap_face_add(struct Object *ob, struct bFaceMap *fmap, int facenum);
+void ED_fmap_face_remove(struct Object *ob, struct bFaceMap *fmap, int facenum);
+
/* mesh_data.c */
// void ED_mesh_geometry_add(struct Mesh *mesh, struct ReportList *reports, int verts, int edges, int faces);
void ED_mesh_polys_add(struct Mesh *mesh, struct ReportList *reports, int count);
diff --git a/source/blender/editors/include/ED_particle.h b/source/blender/editors/include/ED_particle.h
index 6cb8c0cfb19..26a8563bf52 100644
--- a/source/blender/editors/include/ED_particle.h
+++ b/source/blender/editors/include/ED_particle.h
@@ -42,6 +42,8 @@ struct Scene;
/* particle edit mode */
void PE_free_ptcache_edit(struct PTCacheEdit *edit);
int PE_start_edit(struct PTCacheEdit *edit);
+bool PE_shapekey_load(struct Object *ob, struct ParticleSystem *psys);
+bool PE_shapekey_apply(struct Object *ob, struct ParticleSystem *psys);
/* access */
struct PTCacheEdit *PE_get_current(struct Scene *scene, struct Object *ob);
diff --git a/source/blender/editors/include/ED_physics.h b/source/blender/editors/include/ED_physics.h
index 584e9a92bb6..efa852797e1 100644
--- a/source/blender/editors/include/ED_physics.h
+++ b/source/blender/editors/include/ED_physics.h
@@ -35,9 +35,12 @@
struct bContext;
struct ReportList;
struct wmKeyConfig;
+struct ViewContext;
+struct rcti;
struct Scene;
struct Object;
+struct BMEditStrands;
/* particle_edit.c */
int PE_poll(struct bContext *C);
@@ -56,5 +59,28 @@ void ED_rigidbody_constraint_remove(struct Scene *scene, struct Object *ob);
void ED_operatortypes_physics(void);
void ED_keymap_physics(struct wmKeyConfig *keyconf);
+/* hair edit */
+void undo_push_strands(struct bContext *C, const char *name);
+
+void ED_strands_mirror_cache_begin_ex(struct BMEditStrands *edit, const int axis,
+ const bool use_self, const bool use_select,
+ const bool use_topology, float maxdist, int *r_index);
+void ED_strands_mirror_cache_begin(struct BMEditStrands *edit, const int axis,
+ const bool use_self, const bool use_select,
+ const bool use_topology);
+void ED_strands_mirror_apply(struct BMEditStrands *edit, const int sel_from, const int sel_to);
+struct BMVert *ED_strands_mirror_get(struct BMEditStrands *edit, struct BMVert *v);
+struct BMEdge *ED_strands_mirror_get_edge(struct BMEditStrands *edit, struct BMEdge *e);
+void ED_strands_mirror_cache_clear(struct BMEditStrands *edit, struct BMVert *v);
+void ED_strands_mirror_cache_end(struct BMEditStrands *edit);
+
+int ED_hair_mouse_select(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
+int ED_hair_border_select(struct bContext *C, struct rcti *rect, bool select, bool extend);
+int ED_hair_circle_select(struct bContext *C, bool select, const int mval[2], float radius);
+int ED_hair_lasso_select(struct bContext *C, const int mcoords[][2], short moves, bool extend, bool select);
+
+void ED_operatortypes_hair(void);
+void ED_keymap_hair(struct wmKeyConfig *keyconf);
+
#endif /* __ED_PHYSICS_H__ */
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index 79b1751fcb9..d462b9e7ad3 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -68,9 +68,15 @@ void ED_region_toggle_hidden(struct bContext *C, struct ARegion *ar);
void ED_region_info_draw(struct ARegion *ar, const char *text, int block, float fill_color[4]);
void ED_region_image_metadata_draw(int x, int y, struct ImBuf *ibuf, rctf frame, float zoomx, float zoomy);
void ED_region_grid_draw(struct ARegion *ar, float zoomx, float zoomy);
+void ED_region_draw_backdrop_view3d(const struct bContext *C, struct Object *camera, const float alpha,
+ const float width, const float height, const float x, const float y,
+ const float zoomx, const float zoomy, const bool draw_background);
float ED_region_blend_factor(struct ARegion *ar);
void ED_region_visible_rect(struct ARegion *ar, struct rcti *rect);
+int ED_match_area_with_refresh(int spacetype, int refresh);
+int ED_match_region_with_redraws(int spacetype, int regiontype, int redraws);
+
/* spaces */
void ED_spacetypes_keymap(struct wmKeyConfig *keyconf);
@@ -121,6 +127,7 @@ void ED_update_for_newframe(struct Main *bmain, struct Scene *scene, int mute
void ED_refresh_viewport_fps(struct bContext *C);
int ED_screen_animation_play(struct bContext *C, int sync, int mode);
bScreen *ED_screen_animation_playing(const struct wmWindowManager *wm);
+bScreen *ED_screen_animation_no_scrub(const struct wmWindowManager *wm);
/* screen keymaps */
void ED_operatortypes_screen(void);
diff --git a/source/blender/editors/include/ED_space_api.h b/source/blender/editors/include/ED_space_api.h
index d268c578cf2..fc812e01659 100644
--- a/source/blender/editors/include/ED_space_api.h
+++ b/source/blender/editors/include/ED_space_api.h
@@ -36,6 +36,7 @@ struct bContext;
void ED_spacetypes_init(void);
void ED_spacemacros_init(void);
+void ED_spacedropwidgets_init(void);
/* the pluginnable API for export to editors */
diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h
index 732e67a341d..8c82b2fb99b 100644
--- a/source/blender/editors/include/ED_transform.h
+++ b/source/blender/editors/include/ED_transform.h
@@ -35,14 +35,18 @@
/* ******************* Registration Function ********************** */
struct ARegion;
+struct EnumPropertyItem;
struct ListBase;
struct Object;
struct View3D;
struct bContext;
+struct uiLayout;
struct wmEvent;
struct wmKeyConfig;
struct wmKeyMap;
struct wmOperatorType;
+struct wmWindowManager;
+struct PointerRNA;
void transform_keymap_for_space(struct wmKeyConfig *keyconf, struct wmKeyMap *keymap, int spaceid);
void transform_operatortypes(void);
@@ -107,9 +111,12 @@ enum TfmMode {
bool calculateTransformCenter(struct bContext *C, int centerMode, float cent3d[3], float cent2d[2]);
struct TransInfo;
+struct ScrArea;
struct Base;
struct Scene;
struct Object;
+struct wmWidget;
+struct wmWidgetGroup;
/* UNUSED */
// int BIF_snappingSupported(struct Object *obedit);
@@ -151,9 +158,32 @@ void Transform_Properties(struct wmOperatorType *ot, int flags);
/* view3d manipulators */
-int BIF_do_manipulator(struct bContext *C, const struct wmEvent *event, struct wmOperator *op);
-void BIF_draw_manipulator(const struct bContext *C);
+/*
+typedef struct ManipulatorGroup {
+ struct wmWidget *translate_x;
+ struct wmWidget *translate_y;
+ struct wmWidget *translate_z;
+
+ struct wmWidget *rotate_x;
+ struct wmWidget *rotate_y;
+ struct wmWidget *rotate_z;
+} ManipulatorGroup;
+
+int WIDGET_manipulator_handler(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget, struct wmOperator *ptr);
+int WIDGET_manipulator_handler_trans(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget, struct PointerRNA *ptr);
+int WIDGET_manipulator_handler_rot(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget, struct PointerRNA *ptr);
+
+void WIDGET_manipulator_render_3d_intersect(const struct bContext *C, struct wmWidget *widget, int selectionbase);
+void WIDGET_manipulator_draw(struct wmWidget *widget, const struct bContext *C);
+bool WIDGETGROUP_manipulator_poll(struct wmWidgetGroup *wgroup, const struct bContext *C);
+void WIDGETGROUP_manipulator_update(struct wmWidgetGroup *wgroup, const struct bContext *C);
+void WIDGETGROUP_manipulator_free(struct wmWidgetGroup *wgroup);
+void WIDGETGROUP_manipulator_create(struct wmWidgetGroup *wgroup);
+*/
+
+void BIF_draw_manipulator(const struct bContext *C);
+int BIF_do_manipulator(struct bContext *C, const struct wmEvent *event, struct wmOperator *op);
/* Snapping */
diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h
index 496ce7f2c60..c46780a975a 100644
--- a/source/blender/editors/include/ED_util.h
+++ b/source/blender/editors/include/ED_util.h
@@ -32,6 +32,8 @@
#define __ED_UTIL_H__
struct bContext;
+struct PackedFile;
+struct SpaceLink;
struct wmOperator;
struct wmOperatorType;
@@ -66,6 +68,7 @@ bool ED_undo_is_valid(const struct bContext *C, const char *undoname);
/* undo_editmode.c */
void undo_editmode_push(struct bContext *C, const char *name,
+ struct Object *(*get_object)(const struct bContext * C),
void * (*getdata)(struct bContext *C),
void (*freedata)(void *),
void (*to_editmode)(void *, void *, void *),
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index be4204e7cb7..b84d4099d46 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -41,6 +41,7 @@ struct Base;
struct BezTriple;
struct BoundBox;
struct EditBone;
+struct wmEvent;
struct ImBuf;
struct MVert;
struct Main;
@@ -62,6 +63,9 @@ struct wmOperator;
struct wmOperatorType;
struct wmWindow;
struct wmWindowManager;
+struct wmWidget;
+struct wmWidgetGroup;
+struct wmWidgetGroupType;
struct GPUFX;
struct GPUOffScreen;
struct GPUFXSettings;
@@ -319,6 +323,8 @@ void ED_view3d_check_mats_rv3d(struct RegionView3D *rv3d);
#endif
int ED_view3d_scene_layer_set(int lay, const int *values, int *active);
+void ED_draw_object_facemap(struct Scene *scene, struct Object *ob, int facemap);
+
bool ED_view3d_context_activate(struct bContext *C);
void ED_view3d_draw_offscreen_init(struct Scene *scene, struct View3D *v3d);
void ED_view3d_draw_offscreen(
@@ -387,6 +393,9 @@ void ED_view3d_operator_properties_viewmat_set(struct bContext *C, struct wmOper
void ED_view3d_operator_properties_viewmat_get(struct wmOperator *op, int *winx, int *winy, float persmat[4][4]);
#endif
+int WIDGETGROUP_lamp_poll(const struct bContext *C, struct wmWidgetGroupType *wgrouptype);
+void WIDGETGROUP_lamp_draw(const struct bContext *C, struct wmWidgetGroup *wgroup);
+
/* render */
void ED_view3d_stop_render_preview(struct wmWindowManager *wm, struct ARegion *ar);
void ED_view3d_shade_update(struct Main *bmain, struct Scene *scene, struct View3D *v3d, struct ScrArea *sa);
diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h
index bfb7a3420c9..35d5f264ae7 100644
--- a/source/blender/editors/include/UI_icons.h
+++ b/source/blender/editors/include/UI_icons.h
@@ -979,6 +979,13 @@ DEF_ICON(BRUSH_TEXMASK)
DEF_ICON(BRUSH_THUMB)
DEF_ICON(BRUSH_ROTATE)
DEF_ICON(BRUSH_VERTEXDRAW)
+DEF_ICON(BRUSH_HAIR_COMB)
+DEF_ICON(BRUSH_HAIR_CUT)
+DEF_ICON(BRUSH_HAIR_LENGTH)
+DEF_ICON(BRUSH_HAIR_PUFF)
+DEF_ICON(BRUSH_HAIR_ADD)
+DEF_ICON(BRUSH_HAIR_SMOOTH)
+DEF_ICON(BRUSH_HAIR_WEIGHT)
/* Matcaps */
DEF_ICON(MATCAP_01)
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index b5e1b9f08b1..9300c2a7480 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -68,9 +68,12 @@ struct ImBuf;
struct bNodeTree;
struct bNode;
struct bNodeSocket;
+struct CacheLibrary;
struct wmDropBox;
struct wmDrag;
struct wmEvent;
+struct wmWidget;
+struct wmWidgetGroup;
typedef struct uiBut uiBut;
typedef struct uiBlock uiBlock;
@@ -179,6 +182,7 @@ enum {
UI_BUT_TIP_FORCE = (1 << 28), /* force show tooltips when holding option/alt if U's USER_TOOLTIPS is off */
UI_BUT_TEXTEDIT_UPDATE = (1 << 29), /* when widget is in textedit mode, update value on each char stroke */
UI_BUT_SEARCH_UNLINK = (1 << 30), /* show unlink for search button */
+ UI_BUT_MENU_ROOT = (1 << 31), /* but is root of a menu */
};
#define UI_PANEL_WIDTH 340
@@ -917,6 +921,8 @@ 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);
+uiLayout *uiTemplateCacheLibraryItem(uiLayout *layout, struct bContext *C, struct CacheLibrary *cachelib,
+ struct Object *ob, int type, int index, int enabled);
/* 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_draw.c b/source/blender/editors/interface/interface_draw.c
index 2c13b199bba..fc78948c507 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -436,7 +436,7 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(ar), uiBut *but, uiWidgetColors *UNUSED(w
float facy = (float)h / (float)ibuf->y;
glPixelZoom(facx, facy);
}
- glaDrawPixelsAuto((float)rect->xmin, (float)rect->ymin, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, ibuf->rect);
+ glaDrawPixelsAuto((float)rect->xmin, (float)rect->ymin, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, 1.0f, ibuf->rect);
glPixelZoom(1.0f, 1.0f);
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 58cf6b900b9..f333a58742a 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -3515,6 +3515,8 @@ static void ui_block_open_begin(bContext *C, uiBut *but, uiHandleButtonData *dat
data->menu->popup = but->block->handle->popup;
}
+ but->flag |= UI_BUT_MENU_ROOT;
+
#ifdef USE_ALLSELECT
{
wmWindow *win = CTX_wm_window(C);
@@ -6461,6 +6463,33 @@ void ui_panel_menu(bContext *C, ARegion *ar, Panel *pa)
UI_popup_menu_end(C, pup);
}
+static void ui_menu_scripting(bContext *UNUSED(C), uiLayout *layout, void *arg)
+{
+ uiBut *but = arg;
+
+ if (!ui_block_is_menu(but->block)) {
+ uiItemFullO(layout, "UI_OT_editsource", NULL, ICON_NONE, NULL, WM_OP_INVOKE_DEFAULT, 0);
+ }
+
+ uiItemBooleanO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Data Path"),
+ ICON_NONE, "UI_OT_copy_data_path_button", "full", 0);
+ uiItemBooleanO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Full Data Path"),
+ ICON_NONE, "UI_OT_copy_data_path_button", "full", 1);
+
+ if (but->rnapoin.data && but->rnaprop) {
+ PointerRNA ptr_props;
+ char buf[512];
+
+ BLI_snprintf(buf, sizeof(buf), "%s.%s",
+ RNA_struct_identifier(but->rnapoin.type), RNA_property_identifier(but->rnaprop));
+
+ WM_operator_properties_create(&ptr_props, "WM_OT_doc_view");
+ RNA_string_set(&ptr_props, "doc_id", buf);
+ uiItemFullO(layout, "WM_OT_doc_view", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Online Python Reference"),
+ ICON_NONE, ptr_props.data, WM_OP_EXEC_DEFAULT, 0);
+ }
+}
+
static bool ui_but_menu(bContext *C, uiBut *but)
{
uiPopupMenu *pup;
@@ -6475,7 +6504,9 @@ static bool ui_but_menu(bContext *C, uiBut *but)
if (but->type == UI_BTYPE_IMAGE) {
return false;
}
-
+
+ but->flag |= UI_BUT_MENU_ROOT;
+
UI_but_tooltip_timer_remove(C, but);
/* highly unlikely getting the label ever fails */
@@ -6645,15 +6676,16 @@ static bool ui_but_menu(bContext *C, uiBut *but)
uiItemO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Unset"),
ICON_NONE, "UI_OT_unset_property_button");
}
-
- uiItemO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Data Path"),
- ICON_NONE, "UI_OT_copy_data_path_button");
uiItemO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Copy To Selected"),
ICON_NONE, "UI_OT_copy_to_selected_button");
uiItemS(layout);
}
+ /* Scripting submenu */
+ uiItemMenuF(layout, IFACE_("Scripting"), ICON_NONE, ui_menu_scripting, but);
+ uiItemS(layout);
+
/* Operator buttons */
if (but->optype) {
uiBlock *block = uiLayoutGetBlock(layout);
@@ -6702,17 +6734,12 @@ static bool ui_but_menu(bContext *C, uiBut *but)
{ /* Docs */
char buf[512];
- PointerRNA ptr_props;
+ // PointerRNA ptr_props;
if (UI_but_online_manual_id(but, buf, sizeof(buf))) {
uiItemO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Online Manual"),
ICON_NONE, "WM_OT_doc_view_manual_ui_context");
- WM_operator_properties_create(&ptr_props, "WM_OT_doc_view");
- RNA_string_set(&ptr_props, "doc_id", buf);
- uiItemFullO(layout, "WM_OT_doc_view", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Online Python Reference"),
- ICON_NONE, ptr_props.data, WM_OP_EXEC_DEFAULT, 0);
-
/* XXX inactive option, not for public! */
#if 0
WM_operator_properties_create(&ptr_props, "WM_OT_doc_edit");
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index db1eacf57dc..5c4da1f5e53 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -515,6 +515,13 @@ static void init_brush_icons(void)
INIT_BRUSH_ICON(ICON_BRUSH_THUMB, thumb);
INIT_BRUSH_ICON(ICON_BRUSH_ROTATE, twist);
INIT_BRUSH_ICON(ICON_BRUSH_VERTEXDRAW, vertexdraw);
+ INIT_BRUSH_ICON(ICON_BRUSH_HAIR_COMB, haircomb);
+ INIT_BRUSH_ICON(ICON_BRUSH_HAIR_CUT, haircut);
+ INIT_BRUSH_ICON(ICON_BRUSH_HAIR_LENGTH, hairlength);
+ INIT_BRUSH_ICON(ICON_BRUSH_HAIR_PUFF, hairpuff);
+ INIT_BRUSH_ICON(ICON_BRUSH_HAIR_ADD, hairadd);
+ INIT_BRUSH_ICON(ICON_BRUSH_HAIR_SMOOTH, hairsmooth);
+ INIT_BRUSH_ICON(ICON_BRUSH_HAIR_WEIGHT, hairweight);
#undef INIT_BRUSH_ICON
}
@@ -1308,6 +1315,8 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id)
mode = OB_MODE_VERTEX_PAINT;
else if (ob->mode & OB_MODE_TEXTURE_PAINT)
mode = OB_MODE_TEXTURE_PAINT;
+ else if (ob->mode & OB_MODE_HAIR_EDIT)
+ mode = OB_MODE_HAIR_EDIT;
}
else if ((sima = CTX_wm_space_image(C)) &&
(sima->mode == SI_MODE_PAINT))
@@ -1328,6 +1337,10 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id)
items = brush_image_tool_items;
tool = br->imagepaint_tool;
}
+ else if (mode == OB_MODE_HAIR_EDIT) {
+ items = brush_hair_tool_items;
+ tool = br->hair_tool;
+ }
if (!items || !RNA_enum_icon_from_value(items, tool, &id->icon_id))
id->icon_id = 0;
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 9461547a164..dd1937500ec 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -633,6 +633,7 @@ extern int ui_but_menu_direction(uiBut *but);
extern void ui_but_text_password_hide(char password_str[UI_MAX_DRAW_STR], uiBut *but, const bool restore);
extern uiBut *ui_but_find_select_in_enum(uiBut *but, int direction);
extern uiBut *ui_but_find_active_in_region(struct ARegion *ar);
+extern uiBut *ui_but_find_menu_root(struct bContext *C);
bool ui_but_is_editable(const uiBut *but);
bool ui_but_is_editable_as_text(const uiBut *but);
void ui_but_pie_dir_visual(RadialDirection dir, float vec[2]);
@@ -713,4 +714,8 @@ void UI_OT_eyedropper_color(struct wmOperatorType *ot);
void UI_OT_eyedropper_id(struct wmOperatorType *ot);
void UI_OT_eyedropper_depth(struct wmOperatorType *ot);
+
+/* interface_generic_widgets.c */
+void UI_OT_lamp_position(struct wmOperatorType *ot);
+
#endif /* __INTERFACE_INTERN_H__ */
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 23b20591275..88da91acc36 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -94,15 +94,15 @@ static void UI_OT_reset_default_theme(wmOperatorType *ot)
static int copy_data_path_button_poll(bContext *C)
{
- PointerRNA ptr;
- PropertyRNA *prop;
- char *path;
- int index;
+ uiBut *but = ui_but_find_menu_root(C);
- UI_context_active_but_prop_get(C, &ptr, &prop, &index);
+ /* fallback to find but->active */
+ if (but == NULL) {
+ but = UI_context_active_but_get(C);
+ }
- if (ptr.id.data && ptr.data && prop) {
- path = RNA_path_from_ID_to_property(&ptr, prop);
+ if (but && but->rnapoin.data) {
+ char *path = RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop);
if (path) {
MEM_freeN(path);
@@ -113,19 +113,20 @@ static int copy_data_path_button_poll(bContext *C)
return 0;
}
-static int copy_data_path_button_exec(bContext *C, wmOperator *UNUSED(op))
+static int copy_data_path_button_exec(bContext *C, wmOperator *op)
{
- PointerRNA ptr;
- PropertyRNA *prop;
- char *path;
- int index;
+ uiBut *but = ui_but_find_menu_root(C);
- /* try to create driver using property retrieved from UI */
- UI_context_active_but_prop_get(C, &ptr, &prop, &index);
+ /* fallback to find but->active */
+ if (but == NULL) {
+ but = UI_context_active_but_get(C);
+ }
+
+ if (but && but->rnapoin.data) {
+ const bool full = RNA_boolean_get(op->ptr, "full");
+ char *path = full ? RNA_path_full_property_py(&but->rnapoin, but->rnaprop, -1) :
+ RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop);
- if (ptr.id.data && ptr.data && prop) {
- path = RNA_path_from_ID_to_property(&ptr, prop);
-
if (path) {
WM_clipboard_text_set(path, false);
MEM_freeN(path);
@@ -149,6 +150,9 @@ static void UI_OT_copy_data_path_button(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "full", 0, "Full", "Copy the full RNA data path for this property to the clipboard");
}
/* Reset to Default Values Button Operator ------------------------ */
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index c3b58e2d1c1..5fcfbd253a2 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -31,6 +31,7 @@
#include "MEM_guardedalloc.h"
+#include "DNA_cache_library_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
#include "DNA_object_types.h"
@@ -49,6 +50,7 @@
#include "BLF_api.h"
#include "BLF_translation.h"
+#include "BKE_cache_library.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
@@ -73,6 +75,7 @@
#include "ED_util.h"
#include "RNA_access.h"
+#include "RNA_enum_types.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -357,6 +360,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_CL: return N_("Browse Cache Library Data to be linked");
}
}
return N_("Browse ID data to be linked");
@@ -3703,3 +3707,51 @@ void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float *color)
UI_block_align_end(block);
}
+
+/************************* Cache Library Item **************************/
+
+static int cache_item_indent(int type)
+{
+ switch (type) {
+ case CACHE_TYPE_OBJECT:
+ return 0;
+
+ default:
+ return 1;
+ }
+}
+
+uiLayout *uiTemplateCacheLibraryItem(uiLayout *layout, bContext *UNUSED(C), CacheLibrary *UNUSED(cachelib),
+ Object *ob, int datatype, int index, int enabled)
+{
+ uiLayout *split, *row, *col;
+ int i;
+ char name[2*MAX_NAME];
+ int icon = ICON_NONE;
+
+ row = uiLayoutRow(layout, false);
+ uiLayoutSetEnabled(row, enabled);
+ uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_LEFT);
+
+ split = uiLayoutSplit(row, 0.0f, false);
+
+ for (i = 0; i < cache_item_indent(datatype); ++i)
+ uiItemL(split, "", ICON_NONE);
+
+ col = uiLayoutColumn(split, false);
+
+ BKE_cache_item_name(ob, datatype, index, name);
+ RNA_enum_icon_from_value(cache_library_data_type_items, datatype, &icon);
+ uiItemL(split, name, icon);
+
+ /* display read result */
+ split = uiLayoutSplit(row, 0.9f, false);
+ // XXX TODO store cache read results in a hash table and display them here
+// if (item && (item->flag & CACHE_ITEM_ENABLED))
+// RNA_enum_icon_from_value(cache_library_read_result_items, item->read_result, &icon);
+// else
+// icon = ICON_NONE;
+// uiItemL(split, NULL, icon);
+
+ return col;
+}
diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c
index 0823238fcb1..576e17727dd 100644
--- a/source/blender/editors/interface/interface_utils.c
+++ b/source/blender/editors/interface/interface_utils.c
@@ -43,6 +43,7 @@
#include "BLF_translation.h"
+#include "BKE_context.h"
#include "BKE_report.h"
#include "MEM_guardedalloc.h"
@@ -313,6 +314,25 @@ int UI_calc_float_precision(int prec, double value)
return prec;
}
+uiBut *ui_but_find_menu_root(struct bContext *C) {
+ ScrArea *sa = CTX_wm_area(C);
+ ARegion *ar;
+ uiBlock *block;
+ uiBut *but;
+
+ for (ar = sa->regionbase.first; ar; ar = ar->next) {
+ for (block = ar->uiblocks.first; block; block = block->next) {
+ for (but = block->buttons.first; but; but = but->next) {
+ if (but->flag & UI_BUT_MENU_ROOT) {
+ return but;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
bool UI_but_online_manual_id(const uiBut *but, char *r_str, size_t maxlength)
{
if (but->rnapoin.id.data && but->rnapoin.data && but->rnaprop) {
diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c
index 5f0c3ff6993..fdfab59cb8e 100644
--- a/source/blender/editors/interface/view2d.c
+++ b/source/blender/editors/interface/view2d.c
@@ -1061,7 +1061,7 @@ static void view2d_map_cur_using_mask(View2D *v2d, rctf *curmasked)
}
}
-/* Set view matrices to use 'cur' rect as viewing frame for View2D drawing */
+/* Set view matrices to use 'cur' rect as viewing frame for View2D drawing, return y/x aspect ratio */
void UI_view2d_view_ortho(View2D *v2d)
{
rctf curmasked;
diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt
index f331dea5c5c..f8e4738ab38 100644
--- a/source/blender/editors/io/CMakeLists.txt
+++ b/source/blender/editors/io/CMakeLists.txt
@@ -26,8 +26,10 @@ set(INC
../../bmesh
../../makesdna
../../makesrna
+ ../../pointcache
../../windowmanager
../../collada
+ ../../../../intern/guardedalloc
)
set(INC_SYS
@@ -35,9 +37,12 @@ set(INC_SYS
)
set(SRC
+ io_cache_library.c
+ io_cache_shapekey.c
io_collada.c
io_ops.c
+ io_cache_library.h
io_collada.h
io_ops.h
)
diff --git a/source/blender/editors/io/SConscript b/source/blender/editors/io/SConscript
index 0facb24e2c3..d1826784547 100644
--- a/source/blender/editors/io/SConscript
+++ b/source/blender/editors/io/SConscript
@@ -36,10 +36,13 @@ incs = [
'../../blenfont',
'../../blenkernel',
'../../blenlib',
+ '../../bmesh',
'../../collada',
+ '../../makesdna',
'../../makesrna',
+ '../../pointcache',
'../../windowmanager',
- '../../bmesh../../makesdna',
+ '../../../../intern/guardedalloc',
]
if env['WITH_BF_COLLADA']:
diff --git a/source/blender/editors/io/io_cache_library.c b/source/blender/editors/io/io_cache_library.c
new file mode 100644
index 00000000000..cb6b77592e3
--- /dev/null
+++ b/source/blender/editors/io/io_cache_library.c
@@ -0,0 +1,1005 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/io/io_cache_library.c
+ * \ingroup editor/io
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLF_translation.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_fileops.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_path_util.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_cache_library_types.h"
+#include "DNA_group_types.h"
+#include "DNA_listBase.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_force.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+
+#include "BKE_anim.h"
+#include "BKE_blender.h"
+#include "BKE_depsgraph.h"
+#include "BKE_cache_library.h"
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+#include "BKE_screen.h"
+
+#include "ED_screen.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_cache_library.h"
+
+#include "PTC_api.h"
+
+static int ED_cache_library_active_object_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if (!(ob && (ob->transflag & OB_DUPLIGROUP) && ob->dup_group && ob->cache_library))
+ return false;
+
+ return true;
+}
+
+static int ED_cache_modifier_poll(bContext *C)
+{
+ if (!ED_cache_library_active_object_poll(C))
+ return false;
+ if (!CTX_data_pointer_get_type(C, "cache_modifier", &RNA_CacheLibraryModifier).data)
+ return false;
+
+ return true;
+}
+
+/********************** new cache library operator *********************/
+
+static int new_cachelib_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_active_object(C);
+ CacheLibrary *cachelib = ob->cache_library;
+ Main *bmain = CTX_data_main(C);
+ PointerRNA ptr, idptr;
+ PropertyRNA *prop;
+
+ /* add or copy material */
+ if (cachelib) {
+ cachelib = BKE_cache_library_copy(cachelib);
+ }
+ else {
+ cachelib = BKE_cache_library_add(bmain, DATA_("CacheLibrary"));
+ }
+
+ /* enable fake user by default */
+ cachelib->id.flag |= LIB_FAKEUSER;
+
+ /* hook into UI */
+ UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
+
+ if (prop) {
+ /* when creating new ID blocks, use is already 1, but RNA
+ * pointer se also increases user, so this compensates it */
+ cachelib->id.us--;
+
+ RNA_id_pointer_create(&cachelib->id, &idptr);
+ RNA_property_pointer_set(&ptr, prop, idptr);
+ RNA_property_update(C, &ptr, prop);
+ }
+
+ WM_event_add_notifier(C, NC_SCENE, cachelib);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHELIBRARY_OT_new(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "New Cache Library";
+ ot->idname = "CACHELIBRARY_OT_new";
+ ot->description = "Add a new cache library";
+
+ /* api callbacks */
+ ot->poll = ED_operator_object_active;
+ ot->exec = new_cachelib_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+}
+
+/********************** delete cache library operator *********************/
+
+static int cache_library_delete_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Main *bmain = CTX_data_main(C);
+ Object *ob = CTX_data_active_object(C);
+ CacheLibrary *cachelib = ob->cache_library;
+
+ BKE_cache_library_unlink(cachelib);
+ BKE_libblock_free(bmain, cachelib);
+
+ WM_event_add_notifier(C, NC_SCENE, cachelib);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHELIBRARY_OT_delete(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Delete Cache Library";
+ ot->idname = "CACHELIBRARY_OT_delete";
+ ot->description = "Delete a cache library data block";
+
+ /* api callbacks */
+ ot->exec = cache_library_delete_exec;
+ ot->invoke = WM_operator_confirm;
+ ot->poll = ED_cache_library_active_object_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO;
+}
+
+/********************** bake cache operator *********************/
+
+typedef enum eCacheLibraryBake_EvalMode {
+ CACHELIBRARY_BAKE_PREVIEW = (1 << 0), /* evaluate data with preview settings */
+ CACHELIBRARY_BAKE_RENDER = (1 << 1), /* evaluate data with render settings */
+} eCacheLibraryBake_EvalMode;
+
+static int cache_library_bake_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+
+ if (!ob || !(ob->transflag & OB_DUPLIGROUP) || !ob->dup_group || !ob->cache_library)
+ return false;
+ /* disable when the result is not displayed, just to avoid confusing situations */
+ if (ob->cache_library->display_mode != CACHE_LIBRARY_DISPLAY_RESULT)
+ return false;
+
+ return true;
+}
+
+typedef struct CacheLibraryBakeJob {
+ short *stop, *do_update;
+ float *progress;
+
+ struct Main *bmain;
+ struct Scene *scene;
+ struct CacheLibrary *cachelib;
+ int lay;
+ float mat[4][4];
+ struct Group *group;
+
+ eCacheLibraryBake_EvalMode eval_mode;
+ EvaluationContext eval_ctx;
+
+ struct PTCWriterArchive *archive;
+ struct PTCWriter *writer;
+
+ int start_frame, end_frame;
+ int origfra; /* original frame to reset scene after export */
+ float origframelen; /* original frame length to reset scene after export */
+} CacheLibraryBakeJob;
+
+static bool cache_library_bake_stop(CacheLibraryBakeJob *data)
+{
+ return (*data->stop) || G.is_break;
+}
+
+static void cache_library_bake_set_progress(CacheLibraryBakeJob *data, float progress)
+{
+ *data->do_update = 1;
+ *data->progress = progress;
+}
+
+static void cache_library_bake_set_particle_baking(Main *bmain, bool baking)
+{
+ /* XXX would be nicer to just loop over scene->base here,
+ * but this does not catch all objects included in dupli groups ...
+ */
+ Object *ob;
+ for (ob = bmain->object.first; ob; ob = ob->id.next) {
+ ParticleSystem *psys;
+
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ if (baking)
+ psys->pointcache->flag |= PTCACHE_BAKING;
+ else
+ psys->pointcache->flag &= ~PTCACHE_BAKING;
+ }
+ }
+}
+
+static void cache_library_bake_do(CacheLibraryBakeJob *data, bool use_render)
+{
+ Scene *scene = data->scene;
+ int frame, frame_prev, start_frame, end_frame;
+ CacheProcessData process_data;
+
+ if (cache_library_bake_stop(data))
+ return;
+
+ /* === prepare === */
+
+ process_data.lay = data->lay;
+ copy_m4_m4(process_data.mat, data->mat);
+ process_data.dupcache = BKE_dupli_cache_new();
+
+ switch (data->cachelib->source_mode) {
+ case CACHE_LIBRARY_SOURCE_SCENE:
+ data->writer = PTC_writer_dupligroup(data->group->id.name, &data->eval_ctx, scene, data->group, data->cachelib);
+ break;
+ case CACHE_LIBRARY_SOURCE_CACHE:
+ data->writer = PTC_writer_duplicache(data->group->id.name, data->group, process_data.dupcache, data->cachelib->data_types, G.debug & G_DEBUG_SIMDATA);
+ break;
+ }
+ if (!data->writer) {
+ BKE_dupli_cache_free(process_data.dupcache);
+ return;
+ }
+
+ data->cachelib->flag |= CACHE_LIBRARY_BAKING;
+
+ PTC_writer_init(data->writer, data->archive);
+
+ start_frame = data->start_frame;
+ end_frame = data->end_frame;
+
+ /* === frame loop === */
+
+ cache_library_bake_set_progress(data, 0.0f);
+ for (frame = frame_prev = start_frame; frame <= end_frame; frame_prev = frame++) {
+
+ const bool init_strands = (frame == start_frame);
+
+ printf("Bake Cache '%s' | Frame %d\n", data->group->id.name+2, frame);
+
+ /* XXX Ugly, but necessary to avoid particle caching of paths when not needed.
+ * This takes a lot of time, but is only needed in the first frame.
+ */
+ cache_library_bake_set_particle_baking(data->bmain, !init_strands);
+
+ scene->r.cfra = frame;
+ BKE_scene_update_group_for_newframe(&data->eval_ctx, data->bmain, scene, data->group, scene->lay);
+
+ switch (data->cachelib->source_mode) {
+ case CACHE_LIBRARY_SOURCE_SCENE:
+ BKE_dupli_cache_from_group(scene, data->group, data->cachelib, process_data.dupcache, &data->eval_ctx, init_strands);
+ break;
+ case CACHE_LIBRARY_SOURCE_CACHE:
+ BKE_cache_read_dupli_cache(data->cachelib, process_data.dupcache, scene, data->group, frame, use_render, false);
+ break;
+ }
+
+ BKE_cache_process_dupli_cache(data->cachelib, &process_data, scene, data->group, frame_prev, frame, true, false, true);
+
+ PTC_write_sample(data->writer);
+
+ cache_library_bake_set_progress(data, (float)(frame - start_frame + 1) / (float)(end_frame - start_frame + 1));
+ if (cache_library_bake_stop(data))
+ break;
+ }
+
+ /* === cleanup === */
+
+ if (data->writer) {
+ PTC_writer_free(data->writer);
+ data->writer = NULL;
+ }
+
+ data->cachelib->flag &= ~CACHE_LIBRARY_BAKING;
+ cache_library_bake_set_particle_baking(data->bmain, false);
+
+ BKE_dupli_cache_free(process_data.dupcache);
+}
+
+/* Warning! Deletes existing files if possible, operator should show confirm dialog! */
+static bool cache_library_bake_ensure_file_target(const char *filename)
+{
+ if (BLI_exists(filename)) {
+ if (BLI_is_dir(filename)) {
+ return false;
+ }
+ else if (BLI_is_file(filename)) {
+ if (BLI_file_is_writable(filename)) {
+ /* returns 0 on success */
+ return (BLI_delete(filename, false, false) == 0);
+ }
+ else {
+ return false;
+ }
+ }
+ else {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void cache_library_bake_start(void *customdata, short *stop, short *do_update, float *progress)
+{
+ CacheLibraryBakeJob *data = (CacheLibraryBakeJob *)customdata;
+ const bool do_preview = data->eval_mode & CACHELIBRARY_BAKE_PREVIEW;
+ const bool do_render = data->eval_mode & CACHELIBRARY_BAKE_RENDER;
+ const PTCArchiveResolution archive_res = (do_preview ? PTC_RESOLUTION_PREVIEW : 0) | (do_render ? PTC_RESOLUTION_RENDER : 0);
+ Scene *scene = data->scene;
+ char filename[FILE_MAX];
+ char app_name[MAX_NAME];
+ IDProperty *metadata;
+
+ data->stop = stop;
+ data->do_update = do_update;
+ data->progress = progress;
+
+ data->origfra = scene->r.cfra;
+ data->origframelen = scene->r.framelen;
+ scene->r.framelen = 1.0f;
+
+ BKE_cache_archive_output_path(data->cachelib, filename, sizeof(filename));
+ BLI_snprintf(app_name, sizeof(app_name), "Blender %s", versionstr);
+
+ metadata = BKE_cache_library_get_output_metadata(data->cachelib, false);
+
+ data->archive = PTC_open_writer_archive(FPS, data->start_frame, filename, archive_res, app_name, data->cachelib->description, NULL, metadata);
+
+ if (data->archive) {
+
+ G.is_break = false;
+
+ if (do_preview) {
+ data->eval_ctx.mode = DAG_EVAL_VIEWPORT;
+ PTC_writer_archive_use_render(data->archive, false);
+ cache_library_bake_do(data, false);
+ }
+
+ if (do_render) {
+ data->eval_ctx.mode = DAG_EVAL_RENDER;
+ PTC_writer_archive_use_render(data->archive, true);
+ cache_library_bake_do(data, true);
+ }
+
+ }
+
+ *do_update = true;
+ *stop = 0;
+}
+
+static void cache_library_bake_end(void *customdata)
+{
+ CacheLibraryBakeJob *data = (CacheLibraryBakeJob *)customdata;
+ Scene *scene = data->scene;
+
+ G.is_rendering = false;
+ BKE_spacedata_draw_locks(false);
+
+ if (data->writer)
+ PTC_writer_free(data->writer);
+ if (data->archive)
+ PTC_close_writer_archive(data->archive);
+
+ /* reset scene frame */
+ scene->r.cfra = data->origfra;
+ scene->r.framelen = data->origframelen;
+ BKE_scene_update_for_newframe(&data->eval_ctx, data->bmain, scene, scene->lay);
+}
+
+static void cache_library_bake_init(CacheLibraryBakeJob *data, bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ CacheLibrary *cachelib = ob->cache_library;
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+
+ /* make sure we can write */
+ char filename[FILE_MAX];
+ BKE_cache_archive_output_path(cachelib, filename, sizeof(filename));
+ cache_library_bake_ensure_file_target(filename);
+
+ /* XXX annoying hack: needed to prevent data corruption when changing
+ * scene frame in separate threads
+ */
+ G.is_rendering = true;
+
+ BKE_spacedata_draw_locks(true);
+
+ /* setup data */
+ data->bmain = bmain;
+ data->scene = scene;
+ data->cachelib = cachelib;
+ data->lay = ob->lay;
+ copy_m4_m4(data->mat, ob->obmat);
+ data->group = ob->dup_group;
+
+ data->eval_mode = RNA_enum_get(op->ptr, "eval_mode");
+
+ if (RNA_struct_property_is_set(op->ptr, "start_frame"))
+ data->start_frame = RNA_int_get(op->ptr, "start_frame");
+ else
+ data->start_frame = scene->r.sfra;
+ if (RNA_struct_property_is_set(op->ptr, "end_frame"))
+ data->end_frame = RNA_int_get(op->ptr, "end_frame");
+ else
+ data->end_frame = scene->r.efra;
+}
+
+static void cache_library_bake_freejob(void *customdata)
+{
+ CacheLibraryBakeJob *data= (CacheLibraryBakeJob *)customdata;
+ MEM_freeN(data);
+}
+
+static int cache_library_bake_exec(bContext *C, wmOperator *op)
+{
+ const bool use_job = RNA_boolean_get(op->ptr, "use_job");
+
+ if (use_job) {
+ /* when running through invoke, run as a job */
+ CacheLibraryBakeJob *data;
+ wmJob *wm_job;
+
+ /* XXX set WM_JOB_EXCL_RENDER to prevent conflicts with render jobs,
+ * since we need to set G.is_rendering
+ */
+ wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), CTX_data_scene(C), "Cache Library Bake",
+ WM_JOB_PROGRESS | WM_JOB_EXCL_RENDER, WM_JOB_TYPE_CACHELIBRARY_BAKE);
+
+ /* setup data */
+ data = MEM_callocN(sizeof(CacheLibraryBakeJob), "Cache Library Bake Job");
+ cache_library_bake_init(data, C, op);
+
+ WM_jobs_customdata_set(wm_job, data, cache_library_bake_freejob);
+ WM_jobs_timer(wm_job, 0.1, NC_SCENE|ND_FRAME, NC_SCENE|ND_FRAME);
+ WM_jobs_callbacks(wm_job, cache_library_bake_start, NULL, NULL, cache_library_bake_end);
+
+ WM_jobs_start(CTX_wm_manager(C), wm_job);
+ WM_cursor_wait(0);
+
+ /* add modal handler for ESC */
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+ }
+ else {
+ /* in direct execution mode we run this operator blocking instead of using a job */
+ CacheLibraryBakeJob data;
+ short stop = false, do_update = false;
+ float progress = 0.0f;
+
+ cache_library_bake_init(&data, C, op);
+
+ cache_library_bake_start(&data, &stop, &do_update, &progress);
+ cache_library_bake_end(&data);
+
+ return OPERATOR_FINISHED;
+ }
+}
+
+static int cache_library_bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ Object *ob = CTX_data_active_object(C);
+ CacheLibrary *cachelib = ob->cache_library;
+
+ char filename[FILE_MAX];
+
+ if (!cachelib)
+ return OPERATOR_CANCELLED;
+
+ /* make sure we run a job when exec is called after confirm popup */
+ RNA_boolean_set(op->ptr, "use_job", true);
+
+ BKE_cache_archive_output_path(cachelib, filename, sizeof(filename));
+
+ if (!BKE_cache_archive_path_test(cachelib, cachelib->output_filepath)) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot create file path for cache library %200s", cachelib->id.name+2);
+ return OPERATOR_CANCELLED;
+ }
+
+ if (BLI_exists(filename)) {
+ if (BLI_is_dir(filename)) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cache Library target is a directory: %200s", filename);
+ return OPERATOR_CANCELLED;
+ }
+ else if (BLI_is_file(filename)) {
+ if (BLI_file_is_writable(filename)) {
+ return WM_operator_confirm_message(C, op, "Overwrite?");
+ }
+ else {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot overwrite Cache Library target: %200s", filename);
+ return OPERATOR_CANCELLED;
+ }
+
+ }
+ else {
+ BKE_reportf(op->reports, RPT_ERROR, "Invalid Cache Library target: %200s", filename);
+ return OPERATOR_CANCELLED;
+ }
+ }
+ else {
+ return cache_library_bake_exec(C, op);
+ }
+}
+
+/* catch esc */
+static int cache_library_bake_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+{
+ /* no running job, remove handler and pass through */
+ if (!WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_CACHELIBRARY_BAKE))
+ return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
+
+ /* running bake */
+ switch (event->type) {
+ case ESCKEY:
+ return OPERATOR_RUNNING_MODAL;
+ }
+ return OPERATOR_PASS_THROUGH;
+}
+
+void CACHELIBRARY_OT_bake(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ static EnumPropertyItem eval_mode_items[] = {
+ {CACHELIBRARY_BAKE_PREVIEW, "PREVIEW", ICON_RESTRICT_VIEW_OFF, "Preview", "Evaluate data with preview settings"},
+ {CACHELIBRARY_BAKE_RENDER, "RENDER", ICON_RESTRICT_RENDER_OFF, "Render", "Evaluate data with render settings"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ /* identifiers */
+ ot->name = "Bake";
+ ot->description = "Bake cache library";
+ ot->idname = "CACHELIBRARY_OT_bake";
+
+ /* api callbacks */
+ ot->invoke = cache_library_bake_invoke;
+ ot->exec = cache_library_bake_exec;
+ ot->modal = cache_library_bake_modal;
+ ot->poll = cache_library_bake_poll;
+
+ /* flags */
+ /* no undo for this operator, cannot restore old cache files anyway */
+ ot->flag = OPTYPE_REGISTER;
+
+ prop = RNA_def_boolean(ot->srna, "use_job", false, "Use Job", "Run operator as a job");
+ /* This is in internal property set by the invoke function.
+ * It allows the exec function to be called from both the confirm popup
+ * as well as a direct exec call for running a blocking operator in background mode.
+ */
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+
+ prop = RNA_def_enum(ot->srna, "eval_mode", eval_mode_items, CACHELIBRARY_BAKE_RENDER, "Evaluation Mode", "Mode to use when evaluating data");
+ RNA_def_property_flag(prop, PROP_ENUM_FLAG);
+
+ RNA_def_int(ot->srna, "start_frame", 0, INT_MIN, INT_MAX, "Start Frame", "First frame to be cached", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "end_frame", 0, INT_MIN, INT_MAX, "End Frame", "Last frame to be cached", INT_MIN, INT_MAX);
+}
+
+/* ========================================================================= */
+
+static void parse_whitespace(const char **s)
+{
+ const char *c = *s;
+ while (*c && isspace(*c))
+ ++c;
+ *s = c;
+}
+
+static bool parse_literal(const char **s, const char *lit)
+{
+ const char *c = *s;
+
+ if (lit[0] == '\0')
+ return true;
+
+ parse_whitespace(&c);
+
+ while (*c && *lit) {
+ if (*c != *lit)
+ return false;
+ ++c;
+ ++lit;
+ }
+ *s = c;
+
+ return !(*lit);
+}
+
+static bool parse_int(const char **s, int *i)
+{
+ char *end;
+ bool found;
+ // does whitespace skipping internally
+ *i = (int)strtol(*s, &end, 10);
+ found = (end != *s);
+ *s = end;
+ return found;
+}
+
+static void parse_error_log(const char *msg, const char *s, int pos)
+{
+ printf("%s:\n", msg);
+ printf("%s\n", s);
+ printf("%*s^\n", pos, "");
+}
+
+static bool parse_range(const char **s, int *start, int *end)
+{
+ const char *full = *s;
+ if (!parse_int(s, start)) {
+ parse_error_log("Invalid range format, expected int\n", full, *s - full);
+ return false;
+ }
+ if (!parse_literal(s, "-")) {
+ parse_error_log("Invalid range format, expected '-'\n", full, *s - full);
+ return false;
+ }
+ if (!parse_int(s, end)) {
+ parse_error_log("Invalid range format, expected int\n", full, *s - full);
+ return false;
+ }
+
+ return true;
+}
+
+static bool parse_slices(const char *s, ListBase *slices)
+{
+ CacheSlice *slice;
+ int start, end;
+ do {
+ if (!parse_range(&s, &start, &end))
+ return false;
+
+ slice = MEM_callocN(sizeof(CacheSlice), "cache slice");
+ slice->start = start;
+ slice->end = end;
+ BLI_addtail(slices, slice);
+
+ if (!parse_literal(&s, ","))
+ break;
+ } while (*s);
+
+ return true;
+}
+
+static int cache_library_archive_slice_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ CacheLibrary *cachelib = ob->cache_library;
+ Scene *scene = CTX_data_scene(C);
+
+ char *frames = RNA_string_get_alloc(op->ptr, "frames", NULL, 0);
+ ListBase slices = {0};
+
+ char input_filepath[FILE_MAX], input_filename[FILE_MAX];
+ char output_filepath[FILE_MAX], output_filename[FILE_MAX];
+ struct PTCReaderArchive *input_archive;
+ struct PTCWriterArchive *output_archive;
+ PTCArchiveResolution archive_res;
+ CacheArchiveInfo info;
+ IDProperty *metadata;
+ int start_frame = 0, end_frame = 0;
+
+ if (!parse_slices(frames, &slices))
+ return OPERATOR_CANCELLED;
+
+ RNA_string_get(op->ptr, "input_filepath", input_filepath);
+ if (input_filepath[0] == '\0')
+ return OPERATOR_CANCELLED;
+ RNA_string_get(op->ptr, "output_filepath", output_filepath);
+ if (output_filepath[0] == '\0')
+ return OPERATOR_CANCELLED;
+
+ BKE_cache_archive_path_ex(input_filepath, cachelib->id.lib, NULL, input_filename, sizeof(input_filename));
+ BKE_cache_archive_path_ex(output_filepath, cachelib->id.lib, NULL, output_filename, sizeof(output_filename));
+
+ /* make sure we can write */
+ cache_library_bake_ensure_file_target(output_filename);
+
+ input_archive = PTC_open_reader_archive(scene, input_filename);
+ if (!input_archive) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot open cache file at '%s'", input_filepath);
+ return OPERATOR_CANCELLED;
+ }
+
+ archive_res = PTC_reader_archive_get_resolutions(input_archive);
+ {
+ IDPropertyTemplate val;
+ val.i = 0;
+ metadata = IDP_New(IDP_GROUP, &val, "cache input metadata");
+ }
+ PTC_get_archive_info(input_archive, &info, metadata);
+ PTC_reader_archive_get_frame_range(input_archive, &start_frame, &end_frame);
+
+ output_archive = PTC_open_writer_archive(FPS, start_frame, output_filename, archive_res, info.app_name, info.description, NULL, metadata);
+
+ IDP_FreeProperty(metadata);
+ MEM_freeN(metadata);
+
+ if (!output_archive) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot write to cache file at '%s'", output_filepath);
+ return OPERATOR_CANCELLED;
+ }
+
+ PTC_archive_slice(input_archive, output_archive, &slices);
+
+ PTC_close_reader_archive(input_archive);
+ PTC_close_writer_archive(output_archive);
+
+ return OPERATOR_FINISHED;
+}
+
+static int cache_library_archive_slice_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ return WM_operator_props_popup_confirm(C, op, event);
+}
+
+void CACHELIBRARY_OT_archive_slice(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Archive Slice";
+ ot->description = "Copy a range of frames to a new cache archive";
+ ot->idname = "CACHELIBRARY_OT_archive_slice";
+
+ /* api callbacks */
+ ot->exec = cache_library_archive_slice_exec;
+ ot->invoke = cache_library_archive_slice_invoke;
+ ot->poll = ED_cache_library_active_object_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ prop = RNA_def_boolean(ot->srna, "use_job", false, "Use Job", "Run operator as a job");
+ /* This is in internal property set by the invoke function.
+ * It allows the exec function to be called from both the confirm popup
+ * as well as a direct exec call for running a blocking operator in background mode.
+ */
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+
+ prop = RNA_def_string(ot->srna, "input_filepath", NULL, FILE_MAX, "Input File Path", "Path to the source cache archive");
+ RNA_def_property_subtype(prop, PROP_FILEPATH);
+ prop = RNA_def_string(ot->srna, "output_filepath", NULL, FILE_MAX, "Output File Path", "Path to the target cache archive");
+ RNA_def_property_subtype(prop, PROP_FILEPATH);
+ RNA_def_string(ot->srna, "frames", NULL, 0, "Frames", "Frame ranges <start>..<end>[, <start>..<end>]");
+}
+
+/* ========================================================================= */
+
+#if 0
+static void ui_item_nlabel(uiLayout *layout, const char *s, size_t len)
+{
+ char buf[256];
+
+ BLI_strncpy(buf, s, sizeof(buf)-1);
+ buf[min_ii(len, sizeof(buf)-1)] = '\0';
+
+ uiItemL(layout, buf, ICON_NONE);
+}
+
+static void archive_info_labels(uiLayout *layout, const char *info)
+{
+ const char delim[] = {'\n', '\0'};
+ const char *cur = info;
+ size_t linelen;
+ char *sep, *suf;
+
+ linelen = BLI_str_partition(cur, delim, &sep, &suf);
+ while (sep) {
+ ui_item_nlabel(layout, cur, linelen);
+ cur = suf;
+
+ linelen = BLI_str_partition(cur, delim, &sep, &suf);
+ }
+ ui_item_nlabel(layout, cur, linelen);
+}
+
+static uiBlock *archive_info_popup_create(bContext *C, ARegion *ar, void *arg)
+{
+ const char *info = arg;
+ uiBlock *block;
+ uiLayout *layout;
+
+ block = UI_block_begin(C, ar, "_popup", UI_EMBOSS);
+ UI_block_flag_disable(block, UI_BLOCK_LOOP);
+ UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT);
+
+ layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, UI_UNIT_X * 20, UI_UNIT_Y, 0, UI_style_get());
+
+ archive_info_labels(layout, info);
+
+ UI_block_bounds_set_centered(block, 0);
+ UI_block_direction_set(block, UI_DIR_DOWN);
+
+ return block;
+}
+#endif
+
+static void print_stream(void *UNUSED(userdata), const char *s)
+{
+ printf("%s", s);
+}
+
+static int cache_library_archive_info_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ CacheLibrary *cachelib = ob->cache_library;
+ Scene *scene = CTX_data_scene(C);
+
+ const bool use_cache_info = RNA_boolean_get(op->ptr, "use_cache_info");
+ const bool calc_bytes_size = RNA_boolean_get(op->ptr, "calc_bytes_size");
+ const bool use_stdout = RNA_boolean_get(op->ptr, "use_stdout");
+ const bool use_popup = RNA_boolean_get(op->ptr, "use_popup");
+ const bool use_clipboard = RNA_boolean_get(op->ptr, "use_clipboard");
+
+ char filepath[FILE_MAX], filename[FILE_MAX];
+ struct PTCReaderArchive *archive;
+
+ RNA_string_get(op->ptr, "filepath", filepath);
+ if (filepath[0] == '\0')
+ return OPERATOR_CANCELLED;
+
+ BKE_cache_archive_path_ex(filepath, cachelib->id.lib, NULL, filename, sizeof(filename));
+ archive = PTC_open_reader_archive(scene, filename);
+ if (!archive) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot open cache file at '%s'", filepath);
+ return OPERATOR_CANCELLED;
+ }
+
+ if (use_cache_info) {
+ if (cachelib->archive_info)
+ BKE_cache_archive_info_clear(cachelib->archive_info);
+ else
+ cachelib->archive_info = BKE_cache_archive_info_new();
+
+ BLI_strncpy(cachelib->archive_info->filepath, filename, sizeof(cachelib->archive_info->filepath));
+
+ PTC_get_archive_info_nodes(archive, cachelib->archive_info, calc_bytes_size);
+ }
+
+ if (use_stdout) {
+ PTC_get_archive_info_stream(archive, print_stream, NULL);
+ }
+
+ if (use_popup) {
+// UI_popup_block_invoke(C, archive_info_popup_create, info);
+ }
+
+ if (use_clipboard) {
+// WM_clipboard_text_set(info, false);
+ }
+
+ PTC_close_reader_archive(archive);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHELIBRARY_OT_archive_info(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Archive Info";
+ ot->description = "Get archive details from a cache library archive";
+ ot->idname = "CACHELIBRARY_OT_archive_info";
+
+ /* api callbacks */
+ ot->exec = cache_library_archive_info_exec;
+ ot->poll = ED_cache_library_active_object_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_string(ot->srna, "filepath", NULL, FILE_MAX, "File Path", "Path to the cache archive");
+ RNA_def_boolean(ot->srna, "use_cache_info", false, "Use Cache Library Info", "Store info in the cache library");
+ RNA_def_boolean(ot->srna, "calc_bytes_size", false, "Calculate Size", "Calculate overall size of nodes in bytes (can take a while)");
+ RNA_def_boolean(ot->srna, "use_stdout", false, "Use stdout", "Print info in standard output");
+ RNA_def_boolean(ot->srna, "use_popup", false, "Show Popup", "Display archive info in a popup");
+ RNA_def_boolean(ot->srna, "use_clipboard", false, "Copy to Clipboard", "Copy archive info to the clipboard");
+}
+
+/* ------------------------------------------------------------------------- */
+/* Cache Modifiers */
+
+static int cache_library_add_modifier_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ CacheLibrary *cachelib = ob->cache_library;
+
+ eCacheModifier_Type type = RNA_enum_get(op->ptr, "type");
+ if (type == eCacheModifierType_None) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BKE_cache_modifier_add(cachelib, NULL, type);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHELIBRARY_OT_add_modifier(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Cache Modifier";
+ ot->description = "Add a cache modifier";
+ ot->idname = "CACHELIBRARY_OT_add_modifier";
+
+ /* api callbacks */
+ ot->exec = cache_library_add_modifier_exec;
+ ot->poll = ED_cache_library_active_object_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_enum(ot->srna, "type", cache_modifier_type_items, eCacheModifierType_None, "Type", "Type of modifier to add");
+}
+
+static int cache_library_remove_modifier_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA md_ptr = CTX_data_pointer_get_type(C, "cache_modifier", &RNA_CacheLibraryModifier);
+ CacheModifier *md = md_ptr.data;
+ CacheLibrary *cachelib = md_ptr.id.data;
+
+ if (!md)
+ return OPERATOR_CANCELLED;
+
+ BKE_cache_modifier_remove(cachelib, md);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHELIBRARY_OT_remove_modifier(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Cache Modifier";
+ ot->description = "Remove a cache modifier";
+ ot->idname = "CACHELIBRARY_OT_remove_modifier";
+
+ /* api callbacks */
+ ot->exec = cache_library_remove_modifier_exec;
+ ot->poll = ED_cache_modifier_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
diff --git a/source/blender/editors/io/io_cache_library.h b/source/blender/editors/io/io_cache_library.h
new file mode 100644
index 00000000000..78959e6900c
--- /dev/null
+++ b/source/blender/editors/io/io_cache_library.h
@@ -0,0 +1,56 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/io/io_cache_library.h
+ * \ingroup editor/io
+ */
+
+#ifndef __IO_CACHE_LIBRARY_H__
+#define __IO_CACHE_LIBRARY_H__
+
+struct wmOperatorType;
+
+void CACHELIBRARY_OT_new(struct wmOperatorType *ot);
+void CACHELIBRARY_OT_delete(struct wmOperatorType *ot);
+
+void CACHELIBRARY_OT_bake(struct wmOperatorType *ot);
+
+void CACHELIBRARY_OT_archive_info(struct wmOperatorType *ot);
+void CACHELIBRARY_OT_archive_slice(struct wmOperatorType *ot);
+
+/* ------------------------------------------------------------------------- */
+/* Cache Modifiers */
+
+void CACHELIBRARY_OT_add_modifier(struct wmOperatorType *ot);
+void CACHELIBRARY_OT_remove_modifier(struct wmOperatorType *ot);
+
+/* cache_shapekey.c */
+void CACHELIBRARY_OT_shape_key_add(struct wmOperatorType *ot);
+void CACHELIBRARY_OT_shape_key_remove(struct wmOperatorType *ot);
+void CACHELIBRARY_OT_shape_key_clear(struct wmOperatorType *ot);
+void CACHELIBRARY_OT_shape_key_retime(struct wmOperatorType *ot);
+void CACHELIBRARY_OT_shape_key_move(struct wmOperatorType *ot);
+
+#endif
diff --git a/source/blender/editors/io/io_cache_shapekey.c b/source/blender/editors/io/io_cache_shapekey.c
new file mode 100644
index 00000000000..e28a16b8e55
--- /dev/null
+++ b/source/blender/editors/io/io_cache_shapekey.c
@@ -0,0 +1,417 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/io/io_cache_shapekey.c
+ * \ingroup editor/io
+ */
+
+
+#include <math.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_cache_library_types.h"
+#include "DNA_key_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_strands_types.h"
+
+#include "BKE_cache_library.h"
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_key.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "io_cache_library.h"
+
+/*********************** add shape key ***********************/
+
+static void ED_cache_shape_key_add(bContext *C, StrandsKeyCacheModifier *skmd, Strands *strands, const bool from_mix)
+{
+ KeyBlock *kb;
+ if ((kb = BKE_cache_modifier_strands_key_insert_key(skmd, strands, NULL, from_mix))) {
+ Key *key = skmd->key;
+ /* for absolute shape keys, new keys may not be added last */
+ skmd->shapenr = BLI_findindex(&key->block, kb) + 1;
+
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
+ }
+}
+
+/*********************** remove shape key ***********************/
+
+static void keyblock_free(StrandsKeyCacheModifier *skmd, KeyBlock *kb)
+{
+ Key *key = skmd->key;
+
+ BLI_remlink(&key->block, kb);
+ key->totkey--;
+
+ if (kb->data) MEM_freeN(kb->data);
+ MEM_freeN(kb);
+}
+
+static bool ED_cache_shape_key_remove_all(StrandsKeyCacheModifier *skmd, Strands *UNUSED(strands))
+{
+ Key *key = skmd->key;
+ KeyBlock *kb, *kb_next;
+
+ if (key == NULL)
+ return false;
+
+ for (kb = key->block.first; kb; kb = kb_next) {
+ kb_next = kb->next;
+
+ keyblock_free(skmd, kb);
+ }
+
+ key->refkey = NULL;
+ skmd->shapenr = 0;
+
+ return true;
+}
+
+static bool ED_cache_shape_key_remove(StrandsKeyCacheModifier *skmd, Strands *strands)
+{
+ Key *key = skmd->key;
+ KeyBlock *kb, *rkb;
+
+ if (key == NULL)
+ return false;
+
+ kb = BLI_findlink(&key->block, skmd->shapenr - 1);
+ if (kb) {
+ for (rkb = key->block.first; rkb; rkb = rkb->next) {
+ if (rkb->relative == skmd->shapenr - 1) {
+ /* remap to the 'Basis' */
+ rkb->relative = 0;
+ }
+ else if (rkb->relative >= skmd->shapenr) {
+ /* Fix positional shift of the keys when kb is deleted from the list */
+ rkb->relative -= 1;
+ }
+ }
+
+ keyblock_free(skmd, kb);
+
+ if (key->refkey == kb) {
+ key->refkey = key->block.first;
+
+ if (key->refkey) {
+ /* apply new basis key on original data */
+ BKE_keyblock_convert_to_strands(key->refkey, strands, skmd->flag & eStrandsKeyCacheModifier_Flag_UseMotionState);
+ }
+ }
+
+ if (skmd->shapenr > 1) {
+ skmd->shapenr--;
+ }
+ }
+
+ return true;
+}
+
+/********************** shape key operators *********************/
+
+static bool shape_key_get_context(bContext *C, CacheLibrary **r_cachelib, StrandsKeyCacheModifier **r_skmd, Strands **r_strands)
+{
+ CacheLibrary *cachelib = CTX_data_pointer_get_type(C, "cache_library", &RNA_CacheLibrary).data;
+ CacheModifier *md = CTX_data_pointer_get_type(C, "cache_modifier", &RNA_CacheLibraryModifier).data;
+ StrandsKeyCacheModifier *skmd;
+ Object *ob = CTX_data_active_object(C);
+ Strands *strands;
+
+ if (!(cachelib && !cachelib->id.lib && md && md->type == eCacheModifierType_StrandsKey))
+ return false;
+ skmd = (StrandsKeyCacheModifier *)md;
+
+ if (!(ob && ob->dup_cache && (ob->transflag & OB_DUPLIGROUP) && ob->dup_group))
+ return false;
+ if (!BKE_cache_modifier_find_strands(ob->dup_cache, skmd->object, skmd->hair_system, NULL, &strands, NULL, NULL))
+ return false;
+
+ if (r_cachelib) *r_cachelib = cachelib;
+ if (r_skmd) *r_skmd = skmd;
+ if (r_strands) *r_strands = strands;
+ return true;
+}
+
+static int shape_key_poll(bContext *C)
+{
+ return shape_key_get_context(C, NULL, NULL, NULL);
+}
+
+static int shape_key_exists_poll(bContext *C)
+{
+ StrandsKeyCacheModifier *skmd;
+
+ if (!shape_key_get_context(C, NULL, &skmd, NULL))
+ return false;
+
+ return (skmd->key && skmd->shapenr >= 0 && skmd->shapenr < skmd->key->totkey);
+}
+
+static int shape_key_move_poll(bContext *C)
+{
+ StrandsKeyCacheModifier *skmd;
+
+ if (!shape_key_get_context(C, NULL, &skmd, NULL))
+ return false;
+
+ return (skmd->key != NULL && skmd->key->totkey > 1);
+}
+
+static int shape_key_add_exec(bContext *C, wmOperator *op)
+{
+ const bool from_mix = RNA_boolean_get(op->ptr, "from_mix");
+ CacheLibrary *cachelib;
+ StrandsKeyCacheModifier *skmd;
+ Strands *strands;
+
+ shape_key_get_context(C, &cachelib, &skmd, &strands);
+
+ ED_cache_shape_key_add(C, skmd, strands, from_mix);
+
+ DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHELIBRARY_OT_shape_key_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Shape Key";
+ ot->idname = "CACHELIBRARY_OT_shape_key_add";
+ ot->description = "Add shape key to the object";
+
+ /* api callbacks */
+ ot->poll = shape_key_poll;
+ ot->exec = shape_key_add_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "from_mix", true, "From Mix", "Create the new shape key from the existing mix of keys");
+}
+
+static int shape_key_remove_exec(bContext *C, wmOperator *op)
+{
+ CacheLibrary *cachelib;
+ StrandsKeyCacheModifier *skmd;
+ Strands *strands;
+ bool changed = false;
+
+ shape_key_get_context(C, &cachelib, &skmd, &strands);
+
+ if (RNA_boolean_get(op->ptr, "all")) {
+ changed = ED_cache_shape_key_remove_all(skmd, strands);
+ }
+ else {
+ changed = ED_cache_shape_key_remove(skmd, strands);
+ }
+
+ if (changed) {
+ DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
+
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
+}
+
+void CACHELIBRARY_OT_shape_key_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Shape Key";
+ ot->idname = "CACHELIBRARY_OT_shape_key_remove";
+ ot->description = "Remove shape key from the object";
+
+ /* api callbacks */
+ ot->poll = shape_key_exists_poll;
+ ot->exec = shape_key_remove_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "all", 0, "All", "Remove all shape keys");
+}
+
+static int shape_key_clear_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ CacheLibrary *cachelib;
+ StrandsKeyCacheModifier *skmd;
+ Strands *strands;
+ KeyBlock *kb;
+
+ shape_key_get_context(C, &cachelib, &skmd, &strands);
+
+ for (kb = skmd->key->block.first; kb; kb = kb->next)
+ kb->curval = 0.0f;
+
+ DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHELIBRARY_OT_shape_key_clear(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Clear Shape Keys";
+ ot->description = "Clear weights for all shape keys";
+ ot->idname = "CACHELIBRARY_OT_shape_key_clear";
+
+ /* api callbacks */
+ ot->poll = shape_key_poll;
+ ot->exec = shape_key_clear_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* starting point and step size could be optional */
+static int shape_key_retime_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ CacheLibrary *cachelib;
+ StrandsKeyCacheModifier *skmd;
+ Strands *strands;
+ KeyBlock *kb;
+ float cfra = 0.0f;
+
+ shape_key_get_context(C, &cachelib, &skmd, &strands);
+
+ for (kb = skmd->key->block.first; kb; kb = kb->next)
+ kb->pos = (cfra += 0.1f);
+
+ DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHELIBRARY_OT_shape_key_retime(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Re-Time Shape Keys";
+ ot->description = "Resets the timing for absolute shape keys";
+ ot->idname = "CACHELIBRARY_OT_shape_key_retime";
+
+ /* api callbacks */
+ ot->poll = shape_key_poll;
+ ot->exec = shape_key_retime_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+
+enum {
+ KB_MOVE_TOP = -2,
+ KB_MOVE_UP = -1,
+ KB_MOVE_DOWN = 1,
+ KB_MOVE_BOTTOM = 2,
+};
+
+static int shape_key_move_exec(bContext *C, wmOperator *op)
+{
+ const int type = RNA_enum_get(op->ptr, "type");
+ CacheLibrary *cachelib;
+ StrandsKeyCacheModifier *skmd;
+ Strands *strands;
+ Key *key;
+ int totkey, act_index, new_index;
+
+ shape_key_get_context(C, &cachelib, &skmd, &strands);
+ key = skmd->key;
+ totkey = key->totkey;
+ act_index = skmd->shapenr - 1;
+
+ switch (type) {
+ case KB_MOVE_TOP:
+ /* Replace the ref key only if we're at the top already (only for relative keys) */
+ new_index = (ELEM(act_index, 0, 1) || key->type == KEY_NORMAL) ? 0 : 1;
+ break;
+ case KB_MOVE_BOTTOM:
+ new_index = totkey - 1;
+ break;
+ case KB_MOVE_UP:
+ case KB_MOVE_DOWN:
+ default:
+ new_index = (totkey + act_index + type) % totkey;
+ break;
+ }
+
+ if (!BKE_keyblock_move_ex(key, &skmd->shapenr, act_index, new_index)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void CACHELIBRARY_OT_shape_key_move(wmOperatorType *ot)
+{
+ static EnumPropertyItem slot_move[] = {
+ {KB_MOVE_TOP, "TOP", 0, "Top", "Top of the list"},
+ {KB_MOVE_UP, "UP", 0, "Up", ""},
+ {KB_MOVE_DOWN, "DOWN", 0, "Down", ""},
+ {KB_MOVE_BOTTOM, "BOTTOM", 0, "Bottom", "Bottom of the list"},
+ { 0, NULL, 0, NULL, NULL }
+ };
+
+ /* identifiers */
+ ot->name = "Move Shape Key";
+ ot->idname = "CACHELIBRARY_OT_shape_key_move";
+ ot->description = "Move the active shape key up/down in the list";
+
+ /* api callbacks */
+ ot->poll = shape_key_move_poll;
+ ot->exec = shape_key_move_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
+}
+
diff --git a/source/blender/editors/io/io_ops.c b/source/blender/editors/io/io_ops.c
index a70a51a60be..49b1553ee78 100644
--- a/source/blender/editors/io/io_ops.c
+++ b/source/blender/editors/io/io_ops.c
@@ -30,13 +30,30 @@
#include "io_ops.h" /* own include */
+#include "io_cache_library.h"
#ifdef WITH_COLLADA
# include "io_collada.h"
-# include "WM_api.h"
#endif
+#include "WM_api.h"
+
void ED_operatortypes_io(void)
{
+ WM_operatortype_append(CACHELIBRARY_OT_new);
+ WM_operatortype_append(CACHELIBRARY_OT_delete);
+ WM_operatortype_append(CACHELIBRARY_OT_bake);
+ WM_operatortype_append(CACHELIBRARY_OT_archive_info);
+ WM_operatortype_append(CACHELIBRARY_OT_archive_slice);
+
+ WM_operatortype_append(CACHELIBRARY_OT_add_modifier);
+ WM_operatortype_append(CACHELIBRARY_OT_remove_modifier);
+
+ WM_operatortype_append(CACHELIBRARY_OT_shape_key_add);
+ WM_operatortype_append(CACHELIBRARY_OT_shape_key_remove);
+ WM_operatortype_append(CACHELIBRARY_OT_shape_key_clear);
+ WM_operatortype_append(CACHELIBRARY_OT_shape_key_retime);
+ WM_operatortype_append(CACHELIBRARY_OT_shape_key_move);
+
#ifdef WITH_COLLADA
/* Collada operators: */
WM_operatortype_append(WM_OT_collada_export);
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index d521b2c01e5..574ada51570 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -609,7 +609,7 @@ void undo_push_mesh(bContext *C, const char *name)
BMEditMesh *em = BKE_editmesh_from_object(obedit);
em->ob = obedit;
- undo_editmode_push(C, name, getEditMesh, free_undo, undoMesh_to_editbtMesh, editbtMesh_to_undoMesh, NULL);
+ undo_editmode_push(C, name, CTX_data_edit_object, getEditMesh, free_undo, undoMesh_to_editbtMesh, editbtMesh_to_undoMesh, NULL);
}
/**
diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c
index 42eda235276..ccb01f60869 100644
--- a/source/blender/editors/metaball/mball_edit.c
+++ b/source/blender/editors/metaball/mball_edit.c
@@ -741,5 +741,5 @@ static void *get_data(bContext *C)
/* this is undo system for MetaBalls */
void undo_push_mball(bContext *C, const char *name)
{
- undo_editmode_push(C, name, get_data, free_undoMball, undoMball_to_editMball, editMball_to_undoMball, NULL);
+ undo_editmode_push(C, name, CTX_data_edit_object, get_data, free_undoMball, undoMball_to_editMball, editMball_to_undoMball, NULL);
}
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 79437bb05b9..15a5ded91f8 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -46,8 +46,10 @@ set(SRC
object_bake_api.c
object_constraint.c
object_edit.c
+ object_fmap.c
object_group.c
object_hook.c
+ object_lamp.c
object_lattice.c
object_lod.c
object_modifier.c
@@ -78,4 +80,11 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
+if(WITH_OPENVDB)
+ add_definitions(-DWITH_OPENVDB)
+ list(APPEND INC
+ ../../../../intern/openvdb
+ )
+endif()
+
blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/editors/object/SConscript b/source/blender/editors/object/SConscript
index db30fae1f84..852a854e9d0 100644
--- a/source/blender/editors/object/SConscript
+++ b/source/blender/editors/object/SConscript
@@ -63,4 +63,8 @@ if env['WITH_BF_GAMEENGINE']:
if env['WITH_BF_INTERNATIONAL']:
defs.append('WITH_INTERNATIONAL')
+if env['WITH_BF_OPENVDB']:
+ defs.append('WITH_OPENVDB')
+ incs += ' #/intern/openvdb'
+
env.BlenderLib ( 'bf_editors_object', sources, Split(incs), defs, libtype=['core'], priority=[35] )
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index 3499a3cc364..e469b575c17 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -1521,6 +1521,7 @@ static EnumPropertyItem *object_mode_set_itemsf(bContext *C, PointerRNA *UNUSED(
(input->value == OB_MODE_PARTICLE_EDIT && use_mode_particle_edit) ||
(ELEM(input->value, OB_MODE_SCULPT, OB_MODE_VERTEX_PAINT,
OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT) && (ob->type == OB_MESH)) ||
+ (input->value == OB_MODE_HAIR_EDIT) || /* XXX always on, testing for strand data is a bit involved */
(input->value == OB_MODE_OBJECT))
{
RNA_enum_item_add(&item, &totitem, input);
@@ -1554,6 +1555,8 @@ static const char *object_mode_op_string(int mode)
return "PAINT_OT_texture_paint_toggle";
if (mode == OB_MODE_PARTICLE_EDIT)
return "PARTICLE_OT_particle_edit_toggle";
+ if (mode == OB_MODE_HAIR_EDIT)
+ return "HAIR_OT_hair_edit_toggle";
if (mode == OB_MODE_POSE)
return "OBJECT_OT_posemode_toggle";
return NULL;
@@ -1567,6 +1570,9 @@ static bool object_mode_compat_test(Object *ob, ObjectMode mode)
if (ob) {
if (mode == OB_MODE_OBJECT)
return true;
+ /* XXX this is not nice, but testing for cached strands data + shape keys is a bit complicated */
+ if (mode == OB_MODE_HAIR_EDIT)
+ return true;
switch (ob->type) {
case OB_MESH:
diff --git a/source/blender/editors/object/object_fmap.c b/source/blender/editors/object/object_fmap.c
new file mode 100644
index 00000000000..cef2b068fde
--- /dev/null
+++ b/source/blender/editors/object/object_fmap.c
@@ -0,0 +1,495 @@
+/*
+ * ***** 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) 2008 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+
+#include "DNA_object_types.h"
+#include "DNA_mesh_types.h"
+
+#include "WM_types.h"
+#include "WM_api.h"
+
+#include "BKE_context.h"
+#include "BKE_customdata.h"
+#include "BKE_facemap.h"
+#include "BKE_editmesh.h"
+#include "BKE_object.h"
+#include "BKE_object_deform.h"
+
+#include "BKE_depsgraph.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BLI_listbase.h"
+
+#include "BLF_translation.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "ED_mesh.h"
+#include "ED_object.h"
+
+#include "RNA_define.h"
+#include "RNA_access.h"
+
+#include <string.h>
+
+#include "object_intern.h"
+
+/* called while not in editmode */
+void ED_fmap_face_add(Object *ob, bFaceMap *fmap, int facenum)
+{
+ int fmap_nr;
+ if (GS(((ID *)ob->data)->name) != ID_ME)
+ return;
+
+ /* get the face map number, exit if it can't be found */
+ fmap_nr = BLI_findindex(&ob->fmaps, fmap);
+
+ if (fmap_nr != -1) {
+ int *facemap;
+ Mesh *me = ob->data;
+
+ /* if there's is no facemap layer then create one */
+ if ((facemap = CustomData_get_layer(&me->pdata, CD_FACEMAP)) == NULL)
+ facemap = CustomData_add_layer(&me->pdata, CD_FACEMAP, CD_DEFAULT, NULL, me->totpoly);
+
+ facemap[facenum] = fmap_nr;
+ }
+}
+
+/* called while not in editmode */
+void ED_fmap_face_remove(Object *ob, bFaceMap *fmap, int facenum)
+{
+ int fmap_nr;
+ if (GS(((ID *)ob->data)->name) != ID_ME)
+ return;
+
+ /* get the face map number, exit if it can't be found */
+ fmap_nr = BLI_findindex(&ob->fmaps, fmap);
+
+ if (fmap_nr != -1) {
+ int *facemap;
+ Mesh *me = ob->data;
+
+ /* if there's is no facemap layer then create one */
+ if ((facemap = CustomData_get_layer(&me->pdata, CD_FACEMAP)) == NULL)
+ return;
+
+ facemap[facenum] = -1;
+ }
+}
+
+static void object_fmap_swap_edit_mode(Object *ob, int num1, int num2)
+{
+ if (ob->type == OB_MESH) {
+ Mesh *me = ob->data;
+
+ if (me->edit_btmesh) {
+ BMEditMesh *em = me->edit_btmesh;
+ const int cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP);
+
+ if (cd_fmap_offset != -1) {
+ BMFace *efa;
+ BMIter iter;
+ int *map;
+
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
+
+ if (map) {
+ if (num1 != -1) {
+ if (*map == num1)
+ *map = num2;
+ else if (*map == num2)
+ *map = num1;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static void object_fmap_swap_object_mode(Object *ob, int num1, int num2)
+{
+ if (ob->type == OB_MESH) {
+ Mesh *me = ob->data;
+
+ if (CustomData_has_layer(&me->pdata, CD_FACEMAP)) {
+ int *map = CustomData_get_layer(&me->pdata, CD_FACEMAP);
+ int i;
+
+ if (map) {
+ for (i = 0; i < me->totpoly; i++) {
+ if (num1 != -1) {
+ if (map[i] == num1)
+ map[i] = num2;
+ else if (map[i]== num2)
+ map[i] = num1;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void object_facemap_swap(Object *ob, int num1, int num2)
+{
+ if (BKE_object_is_in_editmode(ob))
+ object_fmap_swap_edit_mode(ob, num1, num2);
+ else
+ object_fmap_swap_object_mode(ob, num1, num2);
+}
+
+static int face_map_supported_poll(bContext *C)
+{
+ Object *ob = ED_object_context(C);
+ ID *data = (ob) ? ob->data : NULL;
+ return (ob && !ob->id.lib && ob->type == OB_MESH && data && !data->lib);
+}
+
+static int face_map_supported_edit_mode_poll(bContext *C)
+{
+ Object *ob = ED_object_context(C);
+ ID *data = (ob) ? ob->data : NULL;
+ return (ob && !ob->id.lib && ob->type == OB_MESH && data && !data->lib && ob->mode == OB_MODE_EDIT);
+}
+
+static int face_map_add_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = ED_object_context(C);
+
+ BKE_object_facemap_add(ob);
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_face_map_add(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Face Map";
+ ot->idname = "OBJECT_OT_face_map_add";
+ ot->description = "Add a new face map to the active object";
+
+ /* api callbacks */
+ ot->poll = face_map_supported_poll;
+ ot->exec = face_map_add_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int face_map_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = ED_object_context(C);
+ bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
+
+ if (fmap) {
+ BKE_object_facemap_remove(ob, fmap);
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+ }
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_face_map_remove(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Face Map";
+ ot->idname = "OBJECT_OT_face_map_remove";
+ ot->description = "Remove a face map from the active object";
+
+ /* api callbacks */
+ ot->poll = face_map_supported_poll;
+ ot->exec = face_map_remove_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int face_map_assign_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = ED_object_context(C);
+ bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
+
+ if (fmap) {
+ Mesh *me = ob->data;
+ BMEditMesh *em = me->edit_btmesh;
+ BMFace *efa;
+ BMIter iter;
+ int *map;
+ int cd_fmap_offset;
+
+ if (!CustomData_has_layer(&em->bm->pdata, CD_FACEMAP))
+ BM_data_layer_add(em->bm, &em->bm->pdata, CD_FACEMAP);
+
+ cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP);
+
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
+
+ if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ *map = ob->actfmap - 1;
+ }
+ }
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+ }
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_face_map_assign(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Assign Face Map";
+ ot->idname = "OBJECT_OT_face_map_assign";
+ ot->description = "Assign faces to a face map";
+
+ /* api callbacks */
+ ot->poll = face_map_supported_edit_mode_poll;
+ ot->exec = face_map_assign_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int face_map_remove_from_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = ED_object_context(C);
+ bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
+
+ if (fmap) {
+ Mesh *me = ob->data;
+ BMEditMesh *em = me->edit_btmesh;
+ BMFace *efa;
+ BMIter iter;
+ int *map;
+ int cd_fmap_offset;
+ int mapindex = ob->actfmap - 1;
+
+ if (!CustomData_has_layer(&em->bm->pdata, CD_FACEMAP))
+ return OPERATOR_CANCELLED;
+
+ cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP);
+
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
+
+ if (BM_elem_flag_test(efa, BM_ELEM_SELECT) && *map == mapindex) {
+ *map = -1;
+ }
+ }
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+ }
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_face_map_remove_from(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove From Face Map";
+ ot->idname = "OBJECT_OT_face_map_remove_from";
+ ot->description = "Remove faces from a face map";
+
+ /* api callbacks */
+ ot->poll = face_map_supported_edit_mode_poll;
+ ot->exec = face_map_remove_from_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static void fmap_select(Object *ob, bool select)
+{
+ Mesh *me = ob->data;
+ BMEditMesh *em = me->edit_btmesh;
+ BMFace *efa;
+ BMIter iter;
+ int *map;
+ int cd_fmap_offset;
+ int mapindex = ob->actfmap - 1;
+
+ if (!CustomData_has_layer(&em->bm->pdata, CD_FACEMAP))
+ BM_data_layer_add(em->bm, &em->bm->pdata, CD_FACEMAP);
+
+ cd_fmap_offset = CustomData_get_offset(&em->bm->pdata, CD_FACEMAP);
+
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ map = BM_ELEM_CD_GET_VOID_P(efa, cd_fmap_offset);
+
+ if (*map == mapindex) {
+ BM_face_select_set(em->bm, efa, select);
+ }
+ }
+}
+
+static int face_map_select_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = ED_object_context(C);
+ bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
+
+ if (fmap) {
+ fmap_select(ob, true);
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+ }
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_face_map_select(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Face Map Faces";
+ ot->idname = "OBJECT_OT_face_map_select";
+ ot->description = "Select faces belonging to a face map";
+
+ /* api callbacks */
+ ot->poll = face_map_supported_edit_mode_poll;
+ ot->exec = face_map_select_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int face_map_deselect_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = ED_object_context(C);
+ bFaceMap *fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
+
+ if (fmap) {
+ fmap_select(ob, false);
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+ }
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_face_map_deselect(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Deselect Face Map Faces";
+ ot->idname = "OBJECT_OT_face_map_deselect";
+ ot->description = "Deselect faces belonging to a face map";
+
+ /* api callbacks */
+ ot->poll = face_map_supported_edit_mode_poll;
+ ot->exec = face_map_deselect_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+
+static int face_map_move_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = ED_object_context(C);
+ bFaceMap *fmap;
+ int dir = RNA_enum_get(op->ptr, "direction");
+ int pos1, pos2 = -1, count;
+
+ fmap = BLI_findlink(&ob->fmaps, ob->actfmap - 1);
+ if (!fmap) {
+ return OPERATOR_CANCELLED;
+ }
+
+ count = BLI_listbase_count(&ob->fmaps);
+ pos1 = BLI_findindex(&ob->fmaps, fmap);
+
+ if (dir == 1) { /*up*/
+ void *prev = fmap->prev;
+
+ if (prev) {
+ pos2 = pos1 - 1;
+ }
+ else {
+ pos2 = count - 1;
+ }
+
+ BLI_remlink(&ob->fmaps, fmap);
+ BLI_insertlinkbefore(&ob->fmaps, prev, fmap);
+ }
+ else { /*down*/
+ void *next = fmap->next;
+
+ if (next) {
+ pos2 = pos1 + 1;
+ }
+ else {
+ pos2 = 0;
+ }
+
+ BLI_remlink(&ob->fmaps, fmap);
+ BLI_insertlinkafter(&ob->fmaps, next, fmap);
+ }
+
+ /* iterate through mesh and substitute the indices as necessary */
+ object_facemap_swap(ob, pos2, pos1);
+
+ ob->actfmap = pos2 +1;
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_GEOM | ND_VERTEX_GROUP, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+
+void OBJECT_OT_face_map_move(wmOperatorType *ot)
+{
+ static EnumPropertyItem fmap_slot_move[] = {
+ {1, "UP", 0, "Up", ""},
+ {-1, "DOWN", 0, "Down", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ /* identifiers */
+ ot->name = "Move Face Map";
+ ot->idname = "OBJECT_OT_face_map_move";
+ ot->description = "Move the active face map up/down in the list";
+
+ /* api callbacks */
+ ot->poll = face_map_supported_poll;
+ ot->exec = face_map_move_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_enum(ot->srna, "direction", fmap_slot_move, 0, "Direction", "Direction to move, UP or DOWN");
+}
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 6344e04ef1b..ac0e7997022 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -184,6 +184,11 @@ void OBJECT_OT_skin_loose_mark_clear(struct wmOperatorType *ot);
void OBJECT_OT_skin_radii_equalize(struct wmOperatorType *ot);
void OBJECT_OT_skin_armature_create(struct wmOperatorType *ot);
void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot);
+void OBJECT_OT_smoke_vdb_export(struct wmOperatorType *ot);
+void OBJECT_OT_smoke_vdb_transform_update(struct wmOperatorType *ot);
+void OBJECT_OT_openvdb_cache_add(struct wmOperatorType *ot);
+void OBJECT_OT_openvdb_cache_remove(struct wmOperatorType *ot);
+void OBJECT_OT_openvdb_cache_move(struct wmOperatorType *ot);
/* object_constraint.c */
void OBJECT_OT_constraint_add(struct wmOperatorType *ot);
@@ -244,6 +249,15 @@ void OBJECT_OT_vertex_weight_set_active(struct wmOperatorType *ot);
void OBJECT_OT_vertex_weight_normalize_active_vertex(struct wmOperatorType *ot);
void OBJECT_OT_vertex_weight_copy(struct wmOperatorType *ot);
+/* object_fmap.c */
+void OBJECT_OT_face_map_add(struct wmOperatorType *ot);
+void OBJECT_OT_face_map_remove(struct wmOperatorType *ot);
+void OBJECT_OT_face_map_assign(struct wmOperatorType *ot);
+void OBJECT_OT_face_map_remove_from(struct wmOperatorType *ot);
+void OBJECT_OT_face_map_select(struct wmOperatorType *ot);
+void OBJECT_OT_face_map_deselect(struct wmOperatorType *ot);
+void OBJECT_OT_face_map_move(struct wmOperatorType *ot);
+
/* object_warp.c */
void TRANSFORM_OT_vertex_warp(struct wmOperatorType *ot);
@@ -277,5 +291,8 @@ void TRANSFORM_OT_vertex_random(struct wmOperatorType *ot);
void OBJECT_OT_data_transfer(struct wmOperatorType *ot);
void OBJECT_OT_datalayout_transfer(struct wmOperatorType *ot);
+/* object_lamp.c */
+void LAMP_OT_lamp_position(struct wmOperatorType *ot);
+
#endif /* __OBJECT_INTERN_H__ */
diff --git a/source/blender/editors/object/object_lamp.c b/source/blender/editors/object/object_lamp.c
new file mode 100644
index 00000000000..f60c8f3044e
--- /dev/null
+++ b/source/blender/editors/object/object_lamp.c
@@ -0,0 +1,235 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+
+/** \file blender/editors/object/object_lamp.c
+ * \ingroup edobj
+ */
+
+#include "WM_types.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_lamp_types.h"
+#include "DNA_object_types.h"
+#include "DNA_lamp_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_windowmanager_types.h"
+#include "DNA_userdef_types.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math_matrix.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_object.h"
+
+#include "ED_view3d.h"
+#include "ED_screen.h"
+
+#include "WM_types.h"
+#include "WM_api.h"
+
+#include "BIF_glutil.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "UI_interface.h"
+#include "object_intern.h"
+
+#include "RNA_define.h"
+#include "RNA_access.h"
+
+typedef struct LampPositionData {
+ int pos[2];
+ float quat[4];
+ float lvec[3];
+} LampPositionData;
+
+/* Modal Operator init */
+static int lamp_position_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Object *ob = CTX_data_active_object(C);
+ LampPositionData *data;
+ data = op->customdata = MEM_mallocN(sizeof (LampPositionData), "lamp_position_data");
+
+ copy_v2_v2_int(data->pos, event->mval);
+
+ mat4_to_quat(data->quat, ob->obmat);
+ copy_v3_v3(data->lvec, ob->obmat[2]);
+ negate_v3(data->lvec);
+ normalize_v3(data->lvec);
+
+ WM_event_add_modal_handler(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* Repeat operator */
+static int lamp_position_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+
+ LampPositionData *data = op->customdata;
+
+ switch (event->type) {
+ case MOUSEMOVE:
+ {
+ Object *ob = CTX_data_active_object(C);
+ Lamp *la = ob->data;
+ Scene *scene = CTX_data_scene(C);
+ ARegion *ar = CTX_wm_region(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ float world_pos[3];
+ int flag = v3d->flag2;
+
+ v3d->flag2 |= V3D_RENDER_OVERRIDE;
+
+ view3d_operator_needs_opengl(C);
+ if (ED_view3d_autodist(scene, ar, v3d, event->mval, world_pos, true, NULL)) {
+ float axis[3];
+
+ /* restore the floag here */
+ v3d->flag2 = flag;
+
+ sub_v3_v3(world_pos, ob->obmat[3]);
+ la->dist = normalize_v3(world_pos);
+
+ cross_v3_v3v3(axis, data->lvec, world_pos);
+ if (normalize_v3(axis) > 0.0001) {
+ float mat[4][4];
+ float quat[4], qfinal[4];
+ float angle = saacos(dot_v3v3(world_pos, data->lvec));
+
+ /* transform the initial rotation quaternion to the new position and set the matrix to the lamp */
+ axis_angle_to_quat(quat, axis, angle);
+ mul_qt_qtqt(qfinal, quat, data->quat);
+ quat_to_mat4(mat, qfinal);
+ copy_v3_v3(mat[3], ob->obmat[3]);
+
+ BKE_object_apply_mat4(ob, mat, true, false);
+ }
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_OB);
+
+ ED_region_tag_redraw(ar);
+ }
+
+ v3d->flag2 = flag;
+
+ break;
+ }
+
+ case LEFTMOUSE:
+ if (event->val == KM_RELEASE) {
+ MEM_freeN(op->customdata);
+ return OPERATOR_FINISHED;
+ }
+
+ case EVT_WIDGET_UPDATE:
+ {
+ ARegion *ar = CTX_wm_region(C);
+ Object *ob = CTX_data_active_object(C);
+ Lamp *la = ob->data;
+ float value[3], len;
+
+ RNA_float_get_array(op->ptr, "value", value);
+
+ sub_v3_v3(value, ob->obmat[3]);
+
+ len = len_v3(value);
+
+ la->spotsize = len * 0.1f;
+ DAG_id_tag_update(&ob->id, OB_RECALC_OB);
+
+ ED_region_tag_redraw(ar);
+ break;
+ }
+
+ case EVT_WIDGET_RELEASED:
+ {
+ MEM_freeN(op->customdata);
+ return OPERATOR_FINISHED;
+ }
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int lamp_position_poll(bContext *C)
+{
+ return CTX_wm_region_view3d(C) != NULL;
+}
+
+
+void LAMP_OT_lamp_position(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Lamp Position";
+ ot->idname = "UI_OT_lamp_position";
+ ot->description = "Sample a color from the Blender Window to store in a property";
+
+ /* api callbacks */
+ ot->invoke = lamp_position_invoke;
+ ot->modal = lamp_position_modal;
+ ot->poll = lamp_position_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_BLOCKING | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_float_vector_xyz(ot->srna, "value", 3, NULL, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX);
+}
+
+int WIDGETGROUP_lamp_poll(const struct bContext *C, struct wmWidgetGroupType *UNUSED(wgrouptype))
+{
+ Object *ob = CTX_data_active_object(C);
+
+ if (ob && ob->type == OB_LAMP) {
+ Lamp *la = ob->data;
+ return (la->type == LA_SPOT);
+ }
+ return false;
+}
+
+void WIDGETGROUP_lamp_draw(const struct bContext *C, struct wmWidgetGroup *wgroup)
+{
+ float color_lamp[4] = {0.5f, 0.5f, 1.0f, 1.0f};
+ Object *ob = CTX_data_active_object(C);
+ Lamp *la = ob->data;
+ wmWidget *widget;
+ PointerRNA ptr;
+ float dir[3];
+
+ widget = WIDGET_arrow_new(wgroup, WIDGET_ARROW_STYLE_INVERTED);
+
+ WIDGET_arrow_set_color(widget, color_lamp);
+
+ RNA_pointer_create(&la->id, &RNA_Lamp, la, &ptr);
+ WM_widget_set_origin(widget, ob->obmat[3]);
+ WM_widget_property(widget, ARROW_SLOT_OFFSET_WORLD_SPACE, &ptr, "spot_size");
+ negate_v3_v3(dir, ob->obmat[2]);
+ WIDGET_arrow_set_direction(widget, dir);
+}
diff --git a/source/blender/editors/object/object_lattice.c b/source/blender/editors/object/object_lattice.c
index 76d9facf701..0c1c1b75698 100644
--- a/source/blender/editors/object/object_lattice.c
+++ b/source/blender/editors/object/object_lattice.c
@@ -967,6 +967,6 @@ static void *get_editlatt(bContext *C)
/* and this is all the undo system needs to know */
void undo_push_lattice(bContext *C, const char *name)
{
- undo_editmode_push(C, name, get_editlatt, free_undoLatt, undoLatt_to_editLatt, editLatt_to_undoLatt, validate_undoLatt);
+ undo_editmode_push(C, name, CTX_data_edit_object, get_editlatt, free_undoLatt, undoLatt_to_editLatt, editLatt_to_undoLatt, validate_undoLatt);
}
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index ce9693793a4..eb751aabba8 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -42,8 +42,10 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_force.h"
#include "DNA_scene_types.h"
+#include "DNA_smoke_types.h"
#include "BLI_bitmap.h"
+#include "BLI_fileops.h"
#include "BLI_math.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
@@ -72,6 +74,9 @@
#include "BKE_ocean.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
+#include "BKE_pointcache.h"
+#include "BKE_screen.h"
+#include "BKE_smoke.h"
#include "BKE_softbody.h"
#include "BKE_editmesh.h"
@@ -89,6 +94,10 @@
#include "object_intern.h"
+#ifdef WITH_OPENVDB
+# include "openvdb_capi.h"
+#endif
+
static void modifier_skin_customdata_delete(struct Object *ob);
/******************************** API ****************************/
@@ -2295,3 +2304,409 @@ void OBJECT_OT_laplaciandeform_bind(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_modifier_properties(ot);
}
+
+/************************ OpenVDB smoke convertor operator *********************/
+
+static int openvdb_cache_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ SmokeModifierData *smd = NULL;
+
+ if (!ob) {
+ return false;
+ }
+
+ smd = (SmokeModifierData *)modifiers_findByType(ob, eModifierType_Smoke);
+
+ if (!smd) {
+ return false;
+ }
+
+ return true;
+}
+
+typedef struct SmokeExportJob {
+ /* from wmJob */
+ void *owner;
+ short *stop, *do_update;
+ float *progress;
+ struct SmokeModifierData *smd;
+ struct Scene *scene;
+ struct Object *ob;
+ struct DerivedMesh *dm;
+} SmokeExportJob;
+
+static void smoke_export_free(void *customdata)
+{
+ SmokeExportJob *sej = customdata;
+ MEM_freeN(sej);
+}
+
+/* called by smoke export, only to check job 'stop' value */
+static int smoke_export_breakjob(void *customdata)
+{
+ UNUSED_VARS(customdata);
+ return G.is_break;
+}
+
+/* called by smokeModifier_OpenVDB_export, wmJob sends notifier */
+static void smoke_export_update(void *customdata, float progress, int *cancel)
+{
+ SmokeExportJob *sej = customdata;
+
+ if (smoke_export_breakjob(sej)) {
+ *cancel = 1;
+ }
+
+ *(sej->do_update) = true;
+ *(sej->progress) = progress;
+}
+
+static void smoke_export_startjob(void *customdata, short *stop, short *do_update, float *progress)
+{
+ SmokeExportJob *sej = customdata;
+
+ sej->stop = stop;
+ sej->do_update = do_update;
+ sej->progress = progress;
+
+ G.is_break = false;
+
+ /* XXX annoying hack: needed to prevent data corruption when changing
+ * scene frame in separate threads
+ */
+ G.is_rendering = true;
+ BKE_spacedata_draw_locks(true);
+
+ smokeModifier_OpenVDB_export(sej->smd, sej->scene, sej->ob, sej->dm,
+ smoke_export_update, (void *)sej);
+
+ *do_update = true;
+ *stop = 0;
+}
+
+static void smoke_export_endjob(void *customdata)
+{
+ SmokeExportJob *sej = customdata;
+ Object *ob = sej->ob;
+
+ G.is_rendering = false;
+ BKE_spacedata_draw_locks(false);
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
+}
+
+static int smoke_vdb_export_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ SmokeModifierData *smd = (SmokeModifierData *)modifiers_findByType(ob, eModifierType_Smoke);
+ Scene *scene = CTX_data_scene(C);
+ wmJob *wm_job;
+ SmokeExportJob *sej;
+
+ if (!smd) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* setup job */
+ wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "OpenVDB Export",
+ WM_JOB_PROGRESS, WM_JOB_TYPE_SMOKE_EXPORT);
+
+ sej = MEM_callocN(sizeof(SmokeExportJob), "smoke export job");
+ sej->smd = smd;
+ sej->scene = scene;
+ sej->ob = ob;
+ sej->dm = ob->derivedDeform;
+
+ WM_jobs_customdata_set(wm_job, sej, smoke_export_free);
+ WM_jobs_timer(wm_job, 0.1, NC_OBJECT | ND_MODIFIER, NC_OBJECT | ND_MODIFIER);
+ WM_jobs_callbacks(wm_job, smoke_export_startjob, NULL, NULL, smoke_export_endjob);
+
+ WM_jobs_start(CTX_wm_manager(C), wm_job);
+
+ return OPERATOR_FINISHED;
+
+ UNUSED_VARS(op);
+}
+
+static int smoke_vdb_export_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Object *ob = CTX_data_active_object(C);
+ SmokeModifierData *smd = (SmokeModifierData *)modifiers_findByType(ob, eModifierType_Smoke);
+ SmokeDomainSettings *sds = smd->domain;
+ OpenVDBCache *cache = BKE_openvdb_get_current_cache(sds);
+ const char *relbase = modifier_path_relbase(ob);
+ char filename[FILE_MAX];
+
+ if (!cache)
+ return OPERATOR_CANCELLED;
+
+ BKE_openvdb_cache_filename(filename, cache->path, cache->name, relbase, cache->startframe);
+
+ if (BLI_exists(filename)) {
+ if (BLI_is_file(filename)) {
+ if (BLI_file_is_writable(filename)) {
+ return WM_operator_confirm_message(C, op, "Cache target already exists! Overwrite?");
+ }
+ else {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot overwrite cache target: %200s", filename);
+ return OPERATOR_CANCELLED;
+ }
+ }
+ else {
+ BKE_reportf(op->reports, RPT_ERROR, "Invalid cache target: %200s", filename);
+ return OPERATOR_CANCELLED;
+ }
+ }
+ else {
+ return smoke_vdb_export_exec(C, op);
+ }
+
+ UNUSED_VARS(event);
+}
+
+void OBJECT_OT_smoke_vdb_export(wmOperatorType *ot)
+{
+ ot->name = "Export to OpenVDB";
+ ot->description = "Export simulation to the OpenVDB representation";
+ ot->idname = "OBJECT_OT_smoke_vdb_export";
+
+ ot->invoke = smoke_vdb_export_invoke;
+ ot->poll = openvdb_cache_poll;
+ ot->exec = smoke_vdb_export_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+}
+
+static void smoke_transform_startjob(void *customdata, short *stop, short *do_update, float *progress)
+{
+ SmokeExportJob *sej = customdata;
+
+ sej->stop = stop;
+ sej->do_update = do_update;
+ sej->progress = progress;
+
+ G.is_break = false;
+
+ smokeModifier_OpenVDB_update_transform(sej->smd, sej->scene, sej->ob,
+ smoke_export_update, (void *)sej);
+
+ *do_update = true;
+ *stop = 0;
+}
+
+static int smoke_transform_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ SmokeModifierData *smd = (SmokeModifierData *)modifiers_findByType(ob, eModifierType_Smoke);
+ Scene *scene = CTX_data_scene(C);
+ wmJob *wm_job;
+ SmokeExportJob *sej;
+
+ if (!smd) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* setup job */
+ wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "OpenVDB Export",
+ WM_JOB_PROGRESS, WM_JOB_TYPE_SMOKE_EXPORT);
+
+ sej = MEM_callocN(sizeof(SmokeExportJob), "smoke export job");
+ sej->smd = smd;
+ sej->scene = scene;
+ sej->ob = ob;
+ sej->dm = ob->derivedDeform;
+
+ WM_jobs_customdata_set(wm_job, sej, smoke_export_free);
+ WM_jobs_timer(wm_job, 0.1, NC_OBJECT | ND_MODIFIER, NC_OBJECT | ND_MODIFIER);
+ WM_jobs_callbacks(wm_job, smoke_transform_startjob, NULL, NULL, smoke_export_endjob);
+
+ WM_jobs_start(CTX_wm_manager(C), wm_job);
+
+ return OPERATOR_FINISHED;
+
+ UNUSED_VARS(op);
+}
+
+void OBJECT_OT_smoke_vdb_transform_update(wmOperatorType *ot)
+{
+ ot->name = "Update Transformation";
+ ot->description = "Update transformation matrices for all grids in every file for this cache";
+ ot->idname = "OBJECT_OT_smoke_vdb_transform_update";
+
+ ot->poll = openvdb_cache_poll;
+ ot->exec = smoke_transform_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+}
+
+/* ************************* OpenVDB cache operators ************************* */
+
+static OpenVDBCache *openvdb_cache_new(void)
+{
+ OpenVDBCache *cache = NULL;
+
+ cache = MEM_callocN(sizeof(OpenVDBCache), "OpenVDBCache");
+ cache->reader = NULL;
+ cache->writer = NULL;
+ cache->startframe = 1;
+ cache->endframe = 250;
+ cache->compression = VDB_COMPRESSION_ZIP;
+
+ BLI_strncpy(cache->name, "openvdb_smoke_export", sizeof(cache->name));
+
+ return cache;
+}
+
+static int openvdb_cache_add_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ SmokeModifierData *smd = (SmokeModifierData *)modifiers_findByType(ob, eModifierType_Smoke);
+ SmokeDomainSettings *sds = smd->domain;
+ OpenVDBCache *cache, *cache_new;
+
+ if (!smd) {
+ return OPERATOR_CANCELLED;
+ }
+
+ cache = BKE_openvdb_get_current_cache(sds);
+
+ if (cache) {
+ cache->flags &= ~VDB_CACHE_CURRENT;
+ }
+
+ cache_new = openvdb_cache_new();
+ cache_new->flags |= VDB_CACHE_CURRENT;
+
+ BLI_addtail(&sds->vdb_caches, cache_new);
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, ob);
+
+ return OPERATOR_FINISHED;
+
+ UNUSED_VARS(op);
+}
+
+void OBJECT_OT_openvdb_cache_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add an OpenVDB cache";
+ ot->description = "Add an OpenVDB cache";
+ ot->idname = "OBJECT_OT_openvdb_cache_add";
+
+ /* api callbacks */
+ ot->poll = openvdb_cache_poll;
+ ot->exec = openvdb_cache_add_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int openvdb_cache_remove_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ SmokeModifierData *smd = (SmokeModifierData *)modifiers_findByType(ob, eModifierType_Smoke);
+ SmokeDomainSettings *sds = smd->domain;
+ OpenVDBCache *cache, *cache_prev = NULL, *cache_next = NULL;
+
+ if (!smd) {
+ return OPERATOR_CANCELLED;
+ }
+
+ cache = BKE_openvdb_get_current_cache(sds);
+
+ if (cache) {
+ cache_prev = cache->prev;
+ cache_next = cache->next;
+ BLI_remlink(&sds->vdb_caches, cache);
+ MEM_freeN(cache);
+ }
+
+ if (cache_next) {
+ cache_next->flags |= VDB_CACHE_CURRENT;
+ }
+ else if (cache_prev) {
+ cache_prev->flags |= VDB_CACHE_CURRENT;
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, ob);
+
+ return OPERATOR_FINISHED;
+
+ UNUSED_VARS(op);
+}
+
+void OBJECT_OT_openvdb_cache_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Cache";
+ ot->description = "Remove the currently selected cache";
+ ot->idname = "OBJECT_OT_openvdb_cache_remove";
+
+ /* api callbacks */
+ ot->poll = openvdb_cache_poll;
+ ot->exec = openvdb_cache_remove_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+enum {
+ VDB_CACHE_MOVE_UP = 0,
+ VDB_CACHE_MOVE_DOWN,
+};
+
+static int openvdb_cache_move_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ SmokeModifierData *smd = (SmokeModifierData *)modifiers_findByType(ob, eModifierType_Smoke);
+ SmokeDomainSettings *sds = smd->domain;
+ OpenVDBCache *cache;
+ int direction = RNA_enum_get(op->ptr, "direction");
+
+ if (!smd) {
+ return OPERATOR_CANCELLED;
+ }
+
+ cache = BKE_openvdb_get_current_cache(sds);
+
+ if (direction == VDB_CACHE_MOVE_UP) {
+ if (cache->prev) {
+ BLI_remlink(&sds->vdb_caches, cache);
+ BLI_insertlinkbefore(&sds->vdb_caches, cache->prev, cache);
+ }
+ }
+ else if (direction == VDB_CACHE_MOVE_DOWN) {
+ if (cache->next) {
+ BLI_remlink(&sds->vdb_caches, cache);
+ BLI_insertlinkafter(&sds->vdb_caches, cache->next, cache);
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_openvdb_cache_move(wmOperatorType *ot)
+{
+ static EnumPropertyItem cache_move[] = {
+ {VDB_CACHE_MOVE_UP, "UP", 0, "Up", ""},
+ {VDB_CACHE_MOVE_DOWN, "DOWN", 0, "Down", ""},
+ { 0, NULL, 0, NULL, NULL }
+ };
+
+ ot->name = "Move Cache";
+ ot->description = "Move cache up or down the list";
+ ot->idname = "OBJECT_OT_openvdb_cache_move";
+
+ ot->poll = openvdb_cache_poll;
+ ot->exec = openvdb_cache_move_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ ot->prop = RNA_def_enum(ot->srna, "direction", cache_move, VDB_CACHE_MOVE_UP, "Direction", "");
+}
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 22cac8638d9..47dfc3c9b7d 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -143,6 +143,9 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_skin_loose_mark_clear);
WM_operatortype_append(OBJECT_OT_skin_radii_equalize);
WM_operatortype_append(OBJECT_OT_skin_armature_create);
+ WM_operatortype_append(OBJECT_OT_openvdb_cache_add);
+ WM_operatortype_append(OBJECT_OT_openvdb_cache_remove);
+ WM_operatortype_append(OBJECT_OT_openvdb_cache_move);
WM_operatortype_append(OBJECT_OT_correctivesmooth_bind);
WM_operatortype_append(OBJECT_OT_meshdeform_bind);
@@ -200,6 +203,14 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_vertex_weight_normalize_active_vertex);
WM_operatortype_append(OBJECT_OT_vertex_weight_copy);
+ WM_operatortype_append(OBJECT_OT_face_map_add);
+ WM_operatortype_append(OBJECT_OT_face_map_remove);
+ WM_operatortype_append(OBJECT_OT_face_map_assign);
+ WM_operatortype_append(OBJECT_OT_face_map_remove_from);
+ WM_operatortype_append(OBJECT_OT_face_map_select);
+ WM_operatortype_append(OBJECT_OT_face_map_deselect);
+ WM_operatortype_append(OBJECT_OT_face_map_move);
+
WM_operatortype_append(TRANSFORM_OT_vertex_warp);
WM_operatortype_append(OBJECT_OT_game_property_new);
@@ -245,6 +256,8 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_drop_named_material);
WM_operatortype_append(OBJECT_OT_unlink_data);
WM_operatortype_append(OBJECT_OT_laplaciandeform_bind);
+ WM_operatortype_append(OBJECT_OT_smoke_vdb_export);
+ WM_operatortype_append(OBJECT_OT_smoke_vdb_transform_update);
WM_operatortype_append(OBJECT_OT_lod_add);
WM_operatortype_append(OBJECT_OT_lod_remove);
@@ -253,6 +266,8 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_data_transfer);
WM_operatortype_append(OBJECT_OT_datalayout_transfer);
+
+ WM_operatortype_append(LAMP_OT_lamp_position);
}
void ED_operatormacros_object(void)
diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c
index ed71af71ac9..1b17e4b66f0 100644
--- a/source/blender/editors/object/object_shapekey.c
+++ b/source/blender/editors/object/object_shapekey.c
@@ -48,10 +48,13 @@
#include "DNA_lattice_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_scene_types.h"
#include "DNA_object_types.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
+#include "BKE_editmesh.h"
#include "BKE_key.h"
#include "BKE_library.h"
#include "BKE_main.h"
@@ -498,4 +501,3 @@ void OBJECT_OT_shape_key_move(wmOperatorType *ot)
RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
}
-
diff --git a/source/blender/editors/physics/CMakeLists.txt b/source/blender/editors/physics/CMakeLists.txt
index 40d555226f3..f291af4239c 100644
--- a/source/blender/editors/physics/CMakeLists.txt
+++ b/source/blender/editors/physics/CMakeLists.txt
@@ -41,6 +41,7 @@ set(SRC
particle_boids.c
particle_edit.c
particle_object.c
+ particle_shapekey.c
physics_fluid.c
physics_ops.c
physics_pointcache.c
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index f25679986a5..cf02d2773c0 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -37,6 +37,7 @@
#include "MEM_guardedalloc.h"
+#include "DNA_key_types.h"
#include "DNA_scene_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -56,6 +57,7 @@
#include "BKE_depsgraph.h"
#include "BKE_DerivedMesh.h"
#include "BKE_global.h"
+#include "BKE_key.h"
#include "BKE_object.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
@@ -1201,6 +1203,9 @@ void update_world_cos(Object *ob, PTCacheEdit *edit)
mul_m4_v3(hairmat, key->world_co);
}
}
+
+ /* apply hair changes to the active shape key */
+ PE_shapekey_apply(ob, psys);
}
static void update_velocities(PTCacheEdit *edit)
{
@@ -3701,6 +3706,7 @@ typedef struct BrushEdit {
int first;
int lastmouse[2];
float zfac;
+ bool done;
/* optional cached view settings to avoid setting on every mousemove */
PEData data;
@@ -3726,6 +3732,7 @@ static int brush_edit_init(bContext *C, wmOperator *op)
bedit= MEM_callocN(sizeof(BrushEdit), "BrushEdit");
bedit->first= 1;
+ bedit->done = false;
op->customdata= bedit;
bedit->scene= scene;
@@ -3881,13 +3888,16 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
}
case PE_BRUSH_ADD:
{
- if (edit->psys && edit->psys->part->from==PART_FROM_FACE) {
+ bool done = (brush->flag & PE_BRUSH_DATA_ADD_SINGLE) && bedit->done;
+ if (!done && edit->psys && edit->psys->part->from==PART_FROM_FACE) {
data.mval= mval;
added= brush_add(&data, brush->count);
if (pset->flag & PE_KEEP_LENGTHS)
recalc_lengths(edit);
+
+ bedit->done = true;
}
else
added= 0;
@@ -4091,10 +4101,11 @@ static bool shape_cut_test_point(PEData *data, ParticleCacheKey *key)
BVHTreeFromMesh *shape_bvh = &data->shape_bvh;
const float dir[3] = {1.0f, 0.0f, 0.0f};
PointInsideBVH userdata;
-
userdata.bvhdata = data->shape_bvh;
userdata.num_hits = 0;
+ userdata.bvhdata = data->shape_bvh;
+ userdata.num_hits = 0;
BLI_bvhtree_ray_cast_all(shape_bvh->tree, key->co, dir, 0.0f, point_inside_bvh_cb, &userdata);
/* for any point inside a watertight mesh the number of hits is uneven */
@@ -4560,6 +4571,44 @@ int PE_minmax(Scene *scene, float min[3], float max[3])
/************************ particle edit toggle operator ************************/
+bool PE_shapekey_load(Object *ob, ParticleSystem *psys)
+{
+ const int mode_flag = OB_MODE_PARTICLE_EDIT;
+ const bool is_mode_set = (ob->mode & mode_flag) != 0;
+
+ if (!is_mode_set)
+ return false;
+
+ if (psys->edit) {
+ /* set the active shape key */
+ KeyBlock *actkb = BKE_keyblock_from_particles(psys);
+
+ if (actkb)
+ BKE_keyblock_convert_to_hair_keys(actkb, ob, psys);
+ }
+
+ return true;
+}
+
+bool PE_shapekey_apply(struct Object *ob, struct ParticleSystem *psys)
+{
+ const int mode_flag = OB_MODE_PARTICLE_EDIT;
+ const bool is_mode_set = (ob->mode & mode_flag) != 0;
+
+ if (!is_mode_set)
+ return false;
+
+ if (psys->edit) {
+ /* define the active shape key */
+ KeyBlock *actkb = BKE_keyblock_from_particles(psys);
+
+ if (actkb)
+ BKE_keyblock_convert_from_hair_keys(ob, psys, actkb);
+ }
+
+ return true;
+}
+
/* initialize needed data for bake edit */
void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys)
{
@@ -4702,11 +4751,18 @@ static int particle_edit_toggle_exec(bContext *C, wmOperator *op)
PTCacheEdit *edit;
ob->mode |= mode_flag;
edit= PE_create_current(scene, ob);
-
+
/* mesh may have changed since last entering editmode.
* note, this may have run before if the edit data was just created, so could avoid this and speed up a little */
- if (edit && edit->psys)
+ if (edit && edit->psys) {
+ /* set the active shape key */
+ KeyBlock *actkb = BKE_keyblock_from_particles(edit->psys);
+
+ if (actkb)
+ BKE_keyblock_convert_to_hair_keys(actkb, ob, edit->psys);
+
recalc_emitter_field(ob, edit->psys);
+ }
toggle_particle_cursor(C, 1);
WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_PARTICLE, NULL);
@@ -4793,3 +4849,89 @@ void PARTICLE_OT_edited_clear(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
}
+/************************ Propagate Shape Key *************************/
+
+static int PE_count_keys(PTCacheEdit *edit)
+{
+ int totkey = 0, p;
+ for (p = 0; p < edit->totpoint; ++p) {
+ totkey += edit->points[p].totkey;
+ }
+ return totkey;
+}
+
+static void shape_propagate(PTCacheEdit *edit, int totkey, KeyBlock *kb, wmOperator *UNUSED(op))
+{
+ PTCacheEditPoint *point;
+ float *fp;
+ int i, k;
+
+ if (kb->totelem != totkey)
+ return;
+
+ fp = kb->data;
+ point = edit->points;
+ for (i = 0; i < edit->totpoint; ++i, ++point) {
+ PTCacheEditKey *key = point->keys;
+ const bool use_point = !(point->flag & PEP_HIDE);
+
+ for (k = 0; k < point->totkey; ++k, ++key) {
+ const bool use_key = (key->flag & PEK_SELECT) && !(key->flag & PEK_HIDE);
+
+ if (use_point && use_key) {
+ copy_v3_v3(fp, key->co);
+
+ point->flag |= PEP_EDIT_RECALC;
+ }
+
+ fp += 3;
+ }
+ }
+}
+
+static int particle_shape_propagate_to_all_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ ParticleEditSettings *pset = PE_settings(scene);
+ Object *ob = ED_object_context(C);
+ ParticleSystem *psys = psys_get_current(ob);
+ PTCacheEdit *edit = psys->edit;
+ Key *key = psys->key;
+ KeyBlock *kb;
+ const int totkey = PE_count_keys(edit);
+
+ if (!key)
+ return OPERATOR_CANCELLED;
+
+ /* we might need world space coordinates, update to be sure */
+ update_world_cos(ob, edit);
+
+ for (kb = key->block.first; kb; kb = kb->next)
+ shape_propagate(edit, totkey, kb, op);
+
+ update_world_cos(ob, edit);
+ PE_update_object(scene, ob, 1);
+
+ if (!(pset->flag & PE_KEEP_LENGTHS))
+ recalc_lengths(edit);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+
+void PARTICLE_OT_shape_propagate_to_all(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Shape Propagate";
+ ot->description = "Apply selected vertex locations to all other shape keys";
+ ot->idname = "PARTICLE_OT_shape_propagate_to_all";
+
+ /* api callbacks */
+ ot->exec = particle_shape_propagate_to_all_exec;
+ ot->poll = PE_hair_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
diff --git a/source/blender/editors/physics/particle_shapekey.c b/source/blender/editors/physics/particle_shapekey.c
new file mode 100644
index 00000000000..3de3d56911a
--- /dev/null
+++ b/source/blender/editors/physics/particle_shapekey.c
@@ -0,0 +1,416 @@
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/physics/particle_key.c
+ * \ingroup edphys
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_key_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_key.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+#include "BKE_particle.h"
+
+#include "BLI_sys_types.h" // for intptr_t support
+
+#include "ED_object.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "physics_intern.h"
+
+/*********************** add shape key ***********************/
+
+static void ED_particles_shape_key_add(bContext *C, Scene *scene, Object *ob, ParticleSystem *psys, const bool from_mix)
+{
+ KeyBlock *kb;
+ if ((kb = BKE_psys_insert_shape_key(scene, ob, psys, NULL, from_mix))) {
+ Key *key = psys->key;
+ /* for absolute shape keys, new keys may not be added last */
+ psys->shapenr = BLI_findindex(&key->block, kb) + 1;
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+ }
+}
+
+/*********************** remove shape key ***********************/
+
+static bool ED_particles_shape_key_remove_all(Main *bmain, Object *UNUSED(ob), ParticleSystem *psys)
+{
+ Key *key = psys->key;
+ if (key == NULL)
+ return false;
+
+ psys->key = NULL;
+
+ BKE_libblock_free_us(bmain, key);
+
+ return true;
+}
+
+static bool ED_particles_shape_key_remove(Main *bmain, Object *ob, ParticleSystem *psys)
+{
+ KeyBlock *kb, *rkb;
+ Key *key = psys->key;
+ if (key == NULL)
+ return false;
+
+ kb = BLI_findlink(&key->block, psys->shapenr - 1);
+
+ if (kb) {
+ for (rkb = key->block.first; rkb; rkb = rkb->next)
+ if (rkb->relative == psys->shapenr - 1)
+ rkb->relative = 0;
+
+ BLI_remlink(&key->block, kb);
+ key->totkey--;
+ if (key->refkey == kb) {
+ key->refkey = key->block.first;
+
+ if (key->refkey) {
+ /* apply new basis key on original data */
+ BKE_keyblock_convert_to_hair_keys(key->refkey, ob, psys);
+ }
+ }
+
+ if (kb->data) MEM_freeN(kb->data);
+ MEM_freeN(kb);
+
+ if (psys->shapenr > 1) {
+ psys->shapenr--;
+ }
+ }
+
+ if (key->totkey == 0) {
+ psys->key = NULL;
+
+ BKE_libblock_free_us(bmain, key);
+ }
+
+ return true;
+}
+
+/********************** shape key operators *********************/
+
+static int shape_key_mode_poll(bContext *C)
+{
+ Object *ob = ED_object_context(C);
+ ParticleSystem *psys = psys_get_current(ob);
+ return (ob && !ob->id.lib && psys && ob->mode != OB_MODE_PARTICLE_EDIT);
+}
+
+static int shape_key_mode_exists_poll(bContext *C)
+{
+ Object *ob = ED_object_context(C);
+ ParticleSystem *psys = psys_get_current(ob);
+
+ /* same as shape_key_mode_poll */
+ return (ob && !ob->id.lib && psys && ob->mode != OB_MODE_PARTICLE_EDIT) &&
+ /* check a keyblock exists */
+ (BKE_keyblock_from_particles(psys) != NULL);
+}
+
+static int shape_key_poll(bContext *C)
+{
+ Object *ob = ED_object_context(C);
+ ParticleSystem *psys = psys_get_current(ob);
+
+ return (ob && !ob->id.lib && psys);
+}
+
+static int shape_key_add_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = ED_object_context(C);
+ ParticleSystem *psys = psys_get_current(ob);
+ const bool from_mix = RNA_boolean_get(op->ptr, "from_mix");
+
+ ED_particles_shape_key_add(C, scene, ob, psys, from_mix);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_shape_key_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Shape Key";
+ ot->idname = "PARTICLE_OT_shape_key_add";
+ ot->description = "Add shape key to the object";
+
+ /* api callbacks */
+ ot->poll = shape_key_mode_poll;
+ ot->exec = shape_key_add_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "from_mix", true, "From Mix", "Create the new shape key from the existing mix of keys");
+}
+
+static int shape_key_remove_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Object *ob = ED_object_context(C);
+ ParticleSystem *psys = psys_get_current(ob);
+ bool changed = false;
+
+ if (RNA_boolean_get(op->ptr, "all")) {
+ changed = ED_particles_shape_key_remove_all(bmain, ob, psys);
+ }
+ else {
+ changed = ED_particles_shape_key_remove(bmain, ob, psys);
+ }
+
+ if (changed) {
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
+}
+
+void PARTICLE_OT_shape_key_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Shape Key";
+ ot->idname = "PARTICLE_OT_shape_key_remove";
+ ot->description = "Remove shape key from the object";
+
+ /* api callbacks */
+ ot->poll = shape_key_mode_poll;
+ ot->poll = shape_key_mode_exists_poll;
+ ot->exec = shape_key_remove_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "all", 0, "All", "Remove all shape keys");
+}
+
+static int shape_key_clear_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = ED_object_context(C);
+ ParticleSystem *psys = psys_get_current(ob);
+ Key *key = psys->key;
+ KeyBlock *kb = BKE_keyblock_from_particles(psys);
+
+ if (!key || !kb)
+ return OPERATOR_CANCELLED;
+
+ for (kb = key->block.first; kb; kb = kb->next)
+ kb->curval = 0.0f;
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_shape_key_clear(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Clear Shape Keys";
+ ot->description = "Clear weights for all shape keys";
+ ot->idname = "PARTICLE_OT_shape_key_clear";
+
+ /* api callbacks */
+ ot->poll = shape_key_poll;
+ ot->exec = shape_key_clear_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* starting point and step size could be optional */
+static int shape_key_retime_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = ED_object_context(C);
+ ParticleSystem *psys = psys_get_current(ob);
+ Key *key = psys->key;
+ KeyBlock *kb = BKE_keyblock_from_particles(psys);
+ float cfra = 0.0f;
+
+ if (!key || !kb)
+ return OPERATOR_CANCELLED;
+
+ for (kb = key->block.first; kb; kb = kb->next)
+ kb->pos = (cfra += 0.1f);
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_shape_key_retime(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Re-Time Shape Keys";
+ ot->description = "Resets the timing for absolute shape keys";
+ ot->idname = "PARTICLE_OT_shape_key_retime";
+
+ /* api callbacks */
+ ot->poll = shape_key_poll;
+ ot->exec = shape_key_retime_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int shape_key_move_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = ED_object_context(C);
+ ParticleSystem *psys = psys_get_current(ob);
+ Key *key = psys->key;
+
+ if (!key) {
+ return OPERATOR_CANCELLED;
+ }
+
+ {
+ KeyBlock *kb, *kb_other, *kb_iter;
+ const int type = RNA_enum_get(op->ptr, "type");
+ const int shape_tot = key->totkey;
+ const int shapenr_act = psys->shapenr - 1;
+ const int shapenr_swap = (shape_tot + shapenr_act + type) % shape_tot;
+
+ kb = BLI_findlink(&key->block, shapenr_act);
+ if (!kb || shape_tot == 1) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (type == -1) {
+ /* move back */
+ kb_other = kb->prev;
+ BLI_remlink(&key->block, kb);
+ BLI_insertlinkbefore(&key->block, kb_other, kb);
+ }
+ else {
+ /* move next */
+ kb_other = kb->next;
+ BLI_remlink(&key->block, kb);
+ BLI_insertlinkafter(&key->block, kb_other, kb);
+ }
+
+ psys->shapenr = shapenr_swap + 1;
+
+ /* for relative shape keys */
+ if (kb_other) {
+ for (kb_iter = key->block.first; kb_iter; kb_iter = kb_iter->next) {
+ if (kb_iter->relative == shapenr_act) {
+ kb_iter->relative = shapenr_swap;
+ }
+ else if (kb_iter->relative == shapenr_swap) {
+ kb_iter->relative = shapenr_act;
+ }
+ }
+ }
+ /* First key became last, or vice-versa, we have to change all keys' relative value. */
+ else {
+ for (kb_iter = key->block.first; kb_iter; kb_iter = kb_iter->next) {
+ if (kb_iter->relative == shapenr_act) {
+ kb_iter->relative = shapenr_swap;
+ }
+ else {
+ kb_iter->relative += type;
+ }
+ }
+ }
+
+ /* for absolute shape keys */
+ if (kb_other) {
+ SWAP(float, kb_other->pos, kb->pos);
+ }
+ /* First key became last, or vice-versa, we have to change all keys' pos value. */
+ else {
+ float pos = kb->pos;
+ if (type == -1) {
+ for (kb_iter = key->block.first; kb_iter; kb_iter = kb_iter->next) {
+ SWAP(float, kb_iter->pos, pos);
+ }
+ }
+ else {
+ for (kb_iter = key->block.last; kb_iter; kb_iter = kb_iter->prev) {
+ SWAP(float, kb_iter->pos, pos);
+ }
+ }
+ }
+
+ /* First key is refkey, matches interface and BKE_key_sort */
+ key->refkey = key->block.first;
+ }
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_shape_key_move(wmOperatorType *ot)
+{
+ static EnumPropertyItem slot_move[] = {
+ {-1, "UP", 0, "Up", ""},
+ {1, "DOWN", 0, "Down", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ /* identifiers */
+ ot->name = "Move Shape Key";
+ ot->idname = "PARTICLE_OT_shape_key_move";
+ ot->description = "Move the active shape key up/down in the list";
+
+ /* api callbacks */
+ ot->poll = shape_key_mode_poll;
+ ot->exec = shape_key_move_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", "");
+}
diff --git a/source/blender/editors/physics/physics_intern.h b/source/blender/editors/physics/physics_intern.h
index 666ed2397d2..1d68a429c06 100644
--- a/source/blender/editors/physics/physics_intern.h
+++ b/source/blender/editors/physics/physics_intern.h
@@ -61,6 +61,8 @@ void PARTICLE_OT_shape_cut(struct wmOperatorType *ot);
void PARTICLE_OT_particle_edit_toggle(struct wmOperatorType *ot);
void PARTICLE_OT_edited_clear(struct wmOperatorType *ot);
+void PARTICLE_OT_shape_propagate_to_all(struct wmOperatorType *ot);
+
/* particle_object.c */
void OBJECT_OT_particle_system_add(struct wmOperatorType *ot);
void OBJECT_OT_particle_system_remove(struct wmOperatorType *ot);
@@ -79,6 +81,13 @@ void PARTICLE_OT_dupliob_remove(struct wmOperatorType *ot);
void PARTICLE_OT_dupliob_move_up(struct wmOperatorType *ot);
void PARTICLE_OT_dupliob_move_down(struct wmOperatorType *ot);
+/* particle_shapekey.c */
+void PARTICLE_OT_shape_key_add(struct wmOperatorType *ot);
+void PARTICLE_OT_shape_key_remove(struct wmOperatorType *ot);
+void PARTICLE_OT_shape_key_clear(struct wmOperatorType *ot);
+void PARTICLE_OT_shape_key_retime(struct wmOperatorType *ot);
+void PARTICLE_OT_shape_key_move(struct wmOperatorType *ot);
+
/* particle_boids.c */
void BOID_OT_rule_add(struct wmOperatorType *ot);
void BOID_OT_rule_del(struct wmOperatorType *ot);
diff --git a/source/blender/editors/physics/physics_ops.c b/source/blender/editors/physics/physics_ops.c
index c765bff796e..4d78e745606 100644
--- a/source/blender/editors/physics/physics_ops.c
+++ b/source/blender/editors/physics/physics_ops.c
@@ -69,6 +69,8 @@ static void operatortypes_particle(void)
WM_operatortype_append(PARTICLE_OT_particle_edit_toggle);
WM_operatortype_append(PARTICLE_OT_edited_clear);
+ WM_operatortype_append(PARTICLE_OT_shape_propagate_to_all);
+
WM_operatortype_append(OBJECT_OT_particle_system_add);
WM_operatortype_append(OBJECT_OT_particle_system_remove);
@@ -87,6 +89,12 @@ static void operatortypes_particle(void)
WM_operatortype_append(PARTICLE_OT_dupliob_move_up);
WM_operatortype_append(PARTICLE_OT_dupliob_move_down);
+ WM_operatortype_append(PARTICLE_OT_shape_key_add);
+ WM_operatortype_append(PARTICLE_OT_shape_key_remove);
+ WM_operatortype_append(PARTICLE_OT_shape_key_clear);
+ WM_operatortype_append(PARTICLE_OT_shape_key_retime);
+ WM_operatortype_append(PARTICLE_OT_shape_key_move);
+
WM_operatortype_append(RIGIDBODY_OT_object_add);
WM_operatortype_append(RIGIDBODY_OT_object_remove);
diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c
index bf2fcb73249..74aedcba4aa 100644
--- a/source/blender/editors/render/render_internal.c
+++ b/source/blender/editors/render/render_internal.c
@@ -1554,8 +1554,8 @@ void render_view3d_draw(RenderEngine *engine, const bContext *C)
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glPixelZoom(scale_x, scale_y);
glaDrawPixelsAuto(xof, yof, rres.rectx, rres.recty,
- GL_RGBA, GL_UNSIGNED_BYTE,
- GL_NEAREST, display_buffer);
+ GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST,
+ 1.0f, display_buffer);
glPixelZoom(1.0f, 1.0f);
glDisable(GL_BLEND);
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index c718dfa9229..7da5f41ff2c 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -360,6 +360,7 @@ static void screen_opengl_render_doit(OGLRender *oglrender, RenderResult *rr)
}
else
is_persp = true;
+
BKE_camera_to_gpu_dof(camera, &fx_settings);
}
else {
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index 1b8680804a5..0da78eee86b 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -34,6 +34,7 @@
#include "MEM_guardedalloc.h"
+#include "DNA_object_types.h"
#include "DNA_userdef_types.h"
#include "BLI_blenlib.h"
@@ -44,6 +45,8 @@
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_scene.h"
#include "BKE_screen.h"
#include "RNA_access.h"
@@ -56,6 +59,7 @@
#include "ED_screen.h"
#include "ED_screen_types.h"
#include "ED_space_api.h"
+#include "ED_view3d.h"
#include "BIF_gl.h"
#include "BIF_glutil.h"
@@ -69,6 +73,9 @@
#include "UI_resources.h"
#include "UI_view2d.h"
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
#include "screen_intern.h"
extern void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3); /* xxx temp */
@@ -546,6 +553,100 @@ void ED_region_do_draw(bContext *C, ARegion *ar)
* maybe silly, but let's try for now
* to keep these tags protected
* ********************************** */
+int ED_match_area_with_refresh(int spacetype, int refresh)
+{
+ switch (spacetype) {
+ case SPACE_TIME:
+ if (refresh & SPACE_TIME)
+ return 1;
+ break;
+ }
+
+ return 0;
+}
+
+int ED_match_region_with_redraws(int spacetype, int regiontype, int redraws)
+{
+ if (regiontype == RGN_TYPE_WINDOW) {
+
+ switch (spacetype) {
+ case SPACE_VIEW3D:
+ if (redraws & TIME_ALL_3D_WIN)
+ return 1;
+ break;
+ case SPACE_IPO:
+ case SPACE_ACTION:
+ case SPACE_NLA:
+ if (redraws & TIME_ALL_ANIM_WIN)
+ return 1;
+ break;
+ case SPACE_TIME:
+ /* if only 1 window or 3d windows, we do timeline too */
+ if (redraws & (TIME_ALL_ANIM_WIN | TIME_REGION | TIME_ALL_3D_WIN))
+ return 1;
+ break;
+ case SPACE_BUTS:
+ if (redraws & TIME_ALL_BUTS_WIN)
+ return 1;
+ break;
+ case SPACE_SEQ:
+ if (redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN))
+ return 1;
+ break;
+ case SPACE_NODE:
+ if (redraws & (TIME_NODES))
+ return 1;
+ break;
+ case SPACE_IMAGE:
+ if (redraws & TIME_ALL_IMAGE_WIN)
+ return 1;
+ break;
+ case SPACE_CLIP:
+ if (redraws & TIME_CLIPS)
+ return 1;
+ break;
+
+ }
+ }
+ else if (regiontype == RGN_TYPE_CHANNELS) {
+ switch (spacetype) {
+ case SPACE_IPO:
+ case SPACE_ACTION:
+ case SPACE_NLA:
+ if (redraws & TIME_ALL_ANIM_WIN)
+ return 1;
+ break;
+ }
+ }
+ else if (regiontype == RGN_TYPE_UI) {
+ if (spacetype == SPACE_CLIP) {
+ /* Track Preview button is on Properties Editor in SpaceClip,
+ * and it's very common case when users want it be refreshing
+ * during playback, so asking people to enable special option
+ * for this is a bit tricky, so add exception here for refreshing
+ * Properties Editor for SpaceClip always */
+ return 1;
+ }
+
+ if (redraws & TIME_ALL_BUTS_WIN)
+ return 1;
+ }
+ else if (regiontype == RGN_TYPE_HEADER) {
+ if (spacetype == SPACE_TIME)
+ return 1;
+ }
+ else if (regiontype == RGN_TYPE_PREVIEW) {
+ switch (spacetype) {
+ case SPACE_SEQ:
+ if (redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN))
+ return 1;
+ break;
+ case SPACE_CLIP:
+ return 1;
+ }
+ }
+ return 0;
+}
void ED_region_tag_redraw(ARegion *ar)
{
@@ -1512,8 +1613,9 @@ void region_toggle_hidden(bContext *C, ARegion *ar, const bool do_fade)
region_blend_start(C, sa, ar);
}
else {
- if (ar->flag & RGN_FLAG_HIDDEN)
+ if (ar->flag & RGN_FLAG_HIDDEN) {
WM_event_remove_handlers(C, &ar->handlers);
+ }
ED_area_initialize(CTX_wm_manager(C), CTX_wm_window(C), sa);
ED_area_tag_redraw(sa);
@@ -2214,7 +2316,7 @@ void ED_region_image_metadata_draw(int x, int y, ImBuf *ibuf, rctf frame, float
glTranslatef(x, y, 0.0f);
glScalef(zoomx, zoomy, 1.0f);
- BLF_size(blf_mono_font, style->widgetlabel.points * 1.5f, U.dpi);
+ BLF_size(blf_mono_font, style->widgetlabel.points, U.dpi);
/* *** upper box*** */
@@ -2323,6 +2425,47 @@ void ED_region_grid_draw(ARegion *ar, float zoomx, float zoomy)
glEnd();
}
+/* uses the viewplane from the given camera and draws it as a backdrop */
+void ED_region_draw_backdrop_view3d(const bContext *C, struct Object *camera, const float alpha,
+ const float width, const float height, const float x, const float y,
+ const float zoomx, const float zoomy, const bool draw_background)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ char err_out[256] = "unknown";
+ struct ImBuf *ibuf;
+
+ BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, scene, scene->lay);
+ ibuf = ED_view3d_draw_offscreen_imbuf_simple(scene, camera, width, height, IB_rect,
+ OB_SOLID, false, false, false,
+ R_ADDSKY, NULL, err_out);
+
+ if (ibuf == NULL)
+ return;
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glPushMatrix();
+ glScalef(zoomx, zoomy, 0.0f);
+ glTranslatef(x, y, 0.0f);
+
+ /* draw background */
+ if (draw_background) {
+ char col[4];
+
+ UI_GetThemeColorType4ubv(TH_HIGH_GRAD, SPACE_VIEW3D, col);
+ glColor4ub(UNPACK3(col), alpha * 255);
+ glRectf(0, 0, width, height);
+ }
+ /* draw the imbuf itself */
+ glaDrawImBuf_glsl_ctx(C, ibuf, 0.0f, 0.0f, GL_NEAREST, alpha);
+
+ glPopMatrix();
+ glDisable(GL_BLEND);
+
+ IMB_freeImBuf(ibuf);
+}
+
/* If the area has overlapping regions, it returns visible rect for Region *ar */
/* rect gets returned in local region coordinates */
void ED_region_visible_rect(ARegion *ar, rcti *rect)
diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c
index fd65d81baad..ca0fd17c5a7 100644
--- a/source/blender/editors/screen/glutil.c
+++ b/source/blender/editors/screen/glutil.c
@@ -422,6 +422,24 @@ void glutil_draw_filled_arc(float start, float angle, float radius, int nsegment
glEnd();
}
+void glutil_draw_filled_arc_part(float start, float angle, float radius, float radius_inn, int nsegments)
+{
+ int i;
+
+ glBegin(GL_QUAD_STRIP);
+ glVertex2f(cosf(start) * radius_inn, sinf(start) * radius_inn);
+ glVertex2f(cosf(start) * radius, sinf(start) * radius);
+ for (i = 0; i < nsegments; i++) {
+ float t = (float) i / (nsegments - 1);
+ float cur = start + t * angle;
+
+ glVertex2f(cosf(cur) * radius_inn, sinf(cur) * radius_inn);
+ glVertex2f(cosf(cur) * radius, sinf(cur) * radius);
+ }
+ glEnd();
+}
+
+
void glutil_draw_lined_arc(float start, float angle, float radius, int nsegments)
{
int i;
@@ -713,10 +731,11 @@ void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, int row_w, int fo
}
/* uses either DrawPixelsSafe or DrawPixelsTex, based on user defined maximum */
-void glaDrawPixelsAuto(float x, float y, int img_w, int img_h, int format, int type, int zoomfilter, void *rect)
+void glaDrawPixelsAuto(float x, float y, int img_w, int img_h, int format,
+ int type, int zoomfilter, float alpha, void *rect)
{
if (U.image_draw_method != IMAGE_DRAW_METHOD_DRAWPIXELS) {
- glColor4f(1.0, 1.0, 1.0, 1.0);
+ glColor4f(1.0, 1.0, 1.0, alpha);
glaDrawPixelsTex(x, y, img_w, img_h, format, type, zoomfilter, rect);
}
else {
@@ -1057,7 +1076,7 @@ void bglFlush(void)
/* **** Color management helper functions for GLSL display/transform ***** */
/* Draw given image buffer on a screen using GLSL for display transform */
-void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter,
+void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter, float alpha,
ColorManagedViewSettings *view_settings,
ColorManagedDisplaySettings *display_settings)
{
@@ -1097,7 +1116,7 @@ void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter,
if (ok) {
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
- glColor4f(1.0, 1.0, 1.0, 1.0);
+ glColor4f(1.0, 1.0, 1.0, alpha);
if (ibuf->rect_float) {
int format = 0;
@@ -1135,20 +1154,20 @@ void glaDrawImBuf_glsl(ImBuf *ibuf, float x, float y, int zoomfilter,
if (display_buffer)
glaDrawPixelsAuto(x, y, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE,
- zoomfilter, display_buffer);
+ zoomfilter, alpha, display_buffer);
IMB_display_buffer_release(cache_handle);
}
}
-void glaDrawImBuf_glsl_ctx(const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter)
+void glaDrawImBuf_glsl_ctx(const bContext *C, ImBuf *ibuf, float x, float y, int zoomfilter, float alpha)
{
ColorManagedViewSettings *view_settings;
ColorManagedDisplaySettings *display_settings;
IMB_colormanagement_display_settings_from_ctx(C, &view_settings, &display_settings);
- glaDrawImBuf_glsl(ibuf, x, y, zoomfilter, view_settings, display_settings);
+ glaDrawImBuf_glsl(ibuf, x, y, zoomfilter, alpha, view_settings, display_settings);
}
void cpack(unsigned int x)
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index 87c0ce398e5..9b40b8b4464 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -80,7 +80,7 @@ const char *screen_context_dir[] = {
"visible_pose_bones", "selected_pose_bones", "active_bone", "active_pose_bone",
"active_base", "active_object", "object", "edit_object",
"sculpt_object", "vertex_paint_object", "weight_paint_object",
- "image_paint_object", "particle_edit_object",
+ "image_paint_object", "particle_edit_object", "hair_edit_object",
"sequences", "selected_sequences", "selected_editable_sequences", /* sequencer */
"gpencil_data", "gpencil_data_owner", /* grease pencil data */
"visible_gpencil_layers", "editable_gpencil_layers", "editable_gpencil_strokes",
@@ -378,6 +378,12 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
return 1;
}
+ else if (CTX_data_equals(member, "hair_edit_object")) {
+ if (obact && (obact->mode & OB_MODE_HAIR_EDIT))
+ CTX_data_id_pointer_set(result, &obact->id);
+
+ return 1;
+ }
else if (CTX_data_equals(member, "sequences")) {
Editing *ed = BKE_sequencer_editing_get(scene, false);
if (ed) {
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 0c40c833c0d..a30b840a136 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -1079,6 +1079,8 @@ static void region_cursor_set(wmWindow *win, int swinid, int swin_changed)
for (; ar; ar = ar->next) {
if (ar->swinid == swinid) {
if (swin_changed || (ar->type && ar->type->event_cursor)) {
+ if (WM_widgetmap_cursor_set(ar->widgetmaps.first, win))
+ return;
if (ar->type && ar->type->cursor)
ar->type->cursor(win, sa, ar);
else
@@ -2202,7 +2204,7 @@ bool ED_screen_stereo3d_required(bScreen *screen)
return true;
}
- if (sseq->draw_flag & SEQ_DRAW_BACKDROP) {
+ if (sseq->draw_flag & SEQ_DRAW_OVERDROP) {
return true;
}
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 571dab6a65e..62b2e4ba799 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -3327,101 +3327,6 @@ static void SCREEN_OT_header_toolbox(wmOperatorType *ot)
/* ****************** anim player, with timer ***************** */
-static int match_area_with_refresh(int spacetype, int refresh)
-{
- switch (spacetype) {
- case SPACE_TIME:
- if (refresh & SPACE_TIME)
- return 1;
- break;
- }
-
- return 0;
-}
-
-static int match_region_with_redraws(int spacetype, int regiontype, int redraws)
-{
- if (regiontype == RGN_TYPE_WINDOW) {
-
- switch (spacetype) {
- case SPACE_VIEW3D:
- if (redraws & TIME_ALL_3D_WIN)
- return 1;
- break;
- case SPACE_IPO:
- case SPACE_ACTION:
- case SPACE_NLA:
- if (redraws & TIME_ALL_ANIM_WIN)
- return 1;
- break;
- case SPACE_TIME:
- /* if only 1 window or 3d windows, we do timeline too */
- if (redraws & (TIME_ALL_ANIM_WIN | TIME_REGION | TIME_ALL_3D_WIN))
- return 1;
- break;
- case SPACE_BUTS:
- if (redraws & TIME_ALL_BUTS_WIN)
- return 1;
- break;
- case SPACE_SEQ:
- if (redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN))
- return 1;
- break;
- case SPACE_NODE:
- if (redraws & (TIME_NODES))
- return 1;
- break;
- case SPACE_IMAGE:
- if (redraws & TIME_ALL_IMAGE_WIN)
- return 1;
- break;
- case SPACE_CLIP:
- if (redraws & TIME_CLIPS)
- return 1;
- break;
-
- }
- }
- else if (regiontype == RGN_TYPE_CHANNELS) {
- switch (spacetype) {
- case SPACE_IPO:
- case SPACE_ACTION:
- case SPACE_NLA:
- if (redraws & TIME_ALL_ANIM_WIN)
- return 1;
- break;
- }
- }
- else if (regiontype == RGN_TYPE_UI) {
- if (spacetype == SPACE_CLIP) {
- /* Track Preview button is on Properties Editor in SpaceClip,
- * and it's very common case when users want it be refreshing
- * during playback, so asking people to enable special option
- * for this is a bit tricky, so add exception here for refreshing
- * Properties Editor for SpaceClip always */
- return 1;
- }
-
- if (redraws & TIME_ALL_BUTS_WIN)
- return 1;
- }
- else if (regiontype == RGN_TYPE_HEADER) {
- if (spacetype == SPACE_TIME)
- return 1;
- }
- else if (regiontype == RGN_TYPE_PREVIEW) {
- switch (spacetype) {
- case SPACE_SEQ:
- if (redraws & (TIME_SEQ | TIME_ALL_ANIM_WIN))
- return 1;
- break;
- case SPACE_CLIP:
- return 1;
- }
- }
- return 0;
-}
-
//#define PROFILE_AUDIO_SYNCH
static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
@@ -3453,13 +3358,7 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv
(sad->flag & ANIMPLAY_FLAG_REVERSE) == false &&
finite(time = BKE_sound_sync_scene(scene)))
{
- double newfra = (double)time * FPS;
-
- /* give some space here to avoid jumps */
- if (newfra + 0.5 > scene->r.cfra && newfra - 0.5 < scene->r.cfra)
- scene->r.cfra++;
- else
- scene->r.cfra = newfra + 0.5;
+ scene->r.cfra = (double)time * FPS;
#ifdef PROFILE_AUDIO_SYNCH
newfra_int = scene->r.cfra;
@@ -3556,7 +3455,7 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv
if (ar == sad->ar) {
redraw = true;
}
- else if (match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws)) {
+ else if (ED_match_region_with_redraws(sa->spacetype, ar->regiontype, sad->redraws)) {
redraw = true;
}
@@ -3582,7 +3481,7 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv
}
}
- if (match_area_with_refresh(sa->spacetype, sad->refresh))
+ if (ED_match_area_with_refresh(sa->spacetype, sad->refresh))
ED_area_tag_refresh(sa);
}
}
@@ -3626,6 +3525,19 @@ bScreen *ED_screen_animation_playing(const wmWindowManager *wm)
wmWindow *win;
for (win = wm->windows.first; win; win = win->next) {
+ if (win->screen->animtimer || win->screen->scrubbing) {
+ return win->screen;
+ }
+ }
+
+ return NULL;
+}
+
+bScreen *ED_screen_animation_no_scrub(const wmWindowManager *wm)
+{
+ wmWindow *win;
+
+ for (win = wm->windows.first; win; win = win->next) {
if (win->screen->animtimer) {
return win->screen;
}
@@ -3634,6 +3546,7 @@ bScreen *ED_screen_animation_playing(const wmWindowManager *wm)
return NULL;
}
+
/* toggle operator */
int ED_screen_animation_play(bContext *C, int sync, int mode)
{
@@ -4081,8 +3994,9 @@ void region_blend_start(bContext *C, ScrArea *sa, ARegion *ar)
/* blend in, reinitialize regions because it got unhidden */
if (rgi->hidden == 0)
ED_area_initialize(wm, win, sa);
- else
+ else {
WM_event_remove_handlers(C, &ar->handlers);
+ }
if (ar->next) {
if (ar->next->alignment & RGN_SPLIT_PREV) {
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index eebd49895ef..b95951a4d7a 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -44,6 +44,7 @@
#include "ED_screen.h"
#include "ED_image.h"
#include "UI_resources.h"
+#include "UI_interface.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -60,19 +61,39 @@
#include <stddef.h>
/* Brush operators */
+
static int brush_add_exec(bContext *C, wmOperator *UNUSED(op))
{
/*int type = RNA_enum_get(op->ptr, "type");*/
- Paint *paint = BKE_paint_get_active_from_context(C);
- Brush *br = BKE_paint_brush(paint);
Main *bmain = CTX_data_main(C);
-
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ Paint *paint = NULL;
+ HairEditSettings *hair_edit = NULL;
+ Brush *br = NULL;
+
+ /* get active brush context */
+ if (ob->mode == OB_MODE_HAIR_EDIT) {
+ hair_edit = &scene->toolsettings->hair_edit;
+ br = hair_edit->brush;
+ }
+ else {
+ paint = BKE_paint_get_active_from_context(C);
+ br = BKE_paint_brush(paint);
+ }
+
if (br)
br = BKE_brush_copy(br);
else
br = BKE_brush_add(bmain, "Brush");
- BKE_paint_brush_set(paint, br);
+ /* set new brush pointer in the context */
+ if (ob->mode == OB_MODE_HAIR_EDIT) {
+ hair_edit->brush = br;
+ }
+ else {
+ BKE_paint_brush_set(paint, br);
+ }
return OPERATOR_FINISHED;
}
@@ -95,11 +116,20 @@ static void BRUSH_OT_add(wmOperatorType *ot)
static int brush_scale_size_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- Paint *paint = BKE_paint_get_active_from_context(C);
- Brush *brush = BKE_paint_brush(paint);
- // Object *ob = CTX_data_active_object(C);
+ Object *ob = CTX_data_active_object(C);
+ Brush *brush;
float scalar = RNA_float_get(op->ptr, "scalar");
+ /* get active brush context */
+ if (ob->mode == OB_MODE_HAIR_EDIT) {
+ HairEditSettings *hair_edit = &scene->toolsettings->hair_edit;
+ brush = hair_edit->brush;
+ }
+ else {
+ Paint *paint = BKE_paint_get_active_from_context(C);
+ brush = BKE_paint_brush(paint);
+ }
+
if (brush) {
// pixel radius
{
@@ -467,7 +497,7 @@ static int brush_select_exec(bContext *C, wmOperator *op)
Object *ob = CTX_data_active_object(C);
if (ob) {
/* select current paint mode */
- paint_mode = ob->mode & OB_MODE_ALL_PAINT;
+ paint_mode = ob->mode & OB_MODE_ALL_BRUSH;
}
else {
return OPERATOR_CANCELLED;
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index 90c38426164..a28b4c1a4f2 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -898,7 +898,7 @@ void ACTION_OT_delete(wmOperatorType *ot)
/* ******************** Clean Keyframes Operator ************************* */
-static void clean_action_keys(bAnimContext *ac, float thresh)
+static void clean_action_keys(bAnimContext *ac, float thresh, bool clean_chan)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
@@ -910,7 +910,7 @@ static void clean_action_keys(bAnimContext *ac, float thresh)
/* loop through filtered data and clean curves */
for (ale = anim_data.first; ale; ale = ale->next) {
- clean_fcurve((FCurve *)ale->key_data, thresh);
+ clean_fcurve(ac, ale, thresh, clean_chan);
ale->update |= ANIM_UPDATE_DEFAULT;
}
@@ -925,6 +925,7 @@ static int actkeys_clean_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
float thresh;
+ bool clean_chan;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0)
@@ -937,9 +938,10 @@ static int actkeys_clean_exec(bContext *C, wmOperator *op)
/* get cleaning threshold */
thresh = RNA_float_get(op->ptr, "threshold");
+ clean_chan = RNA_boolean_get(op->ptr, "channels");
/* clean keyframes */
- clean_action_keys(&ac, thresh);
+ clean_action_keys(&ac, thresh, clean_chan);
/* set notifier that keyframes have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
@@ -964,6 +966,7 @@ void ACTION_OT_clean(wmOperatorType *ot)
/* properties */
ot->prop = RNA_def_float(ot->srna, "threshold", 0.001f, 0.0f, FLT_MAX, "Threshold", "", 0.0f, 1000.0f);
+ RNA_def_boolean(ot->srna, "channels", false, "Channels", "");
}
/* ******************** Sample Keyframes Operator *********************** */
diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index 01f0d1ae54f..d827761f473 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -103,6 +103,7 @@ void ED_spacetypes_init(void)
ED_operatortypes_anim();
ED_operatortypes_animchannels();
ED_operatortypes_gpencil();
+ ED_operatortypes_hair();
ED_operatortypes_object();
ED_operatortypes_mesh();
ED_operatortypes_sculpt();
@@ -135,9 +136,6 @@ void ED_spacetypes_init(void)
void ED_spacemacros_init(void)
{
- const ListBase *spacetypes;
- SpaceType *type;
-
/* Macros's must go last since they reference other operators.
* We need to have them go after python operators too */
ED_operatormacros_armature();
@@ -154,15 +152,24 @@ void ED_spacemacros_init(void)
ED_operatormacros_sequencer();
ED_operatormacros_paint();
ED_operatormacros_gpencil();
+}
+
+void ED_spacedropwidgets_init(void)
+{
+ const ListBase *spacetypes;
+ SpaceType *type;
/* register dropboxes (can use macros) */
spacetypes = BKE_spacetypes_list();
for (type = spacetypes->first; type; type = type->next) {
if (type->dropboxes)
type->dropboxes();
+ if (type->widgets)
+ type->widgets();
}
}
+
/* called in wm.c */
/* keymap definitions are registered only once per WM initialize, usually on file read,
* using the keymap the actual areas/regions add the handlers */
@@ -176,6 +183,7 @@ void ED_spacetypes_keymap(wmKeyConfig *keyconf)
ED_keymap_anim(keyconf);
ED_keymap_animchannels(keyconf);
ED_keymap_gpencil(keyconf);
+ ED_keymap_hair(keyconf);
ED_keymap_object(keyconf); /* defines lattice also */
ED_keymap_mesh(keyconf);
ED_keymap_uvedit(keyconf);
diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c
index 76954ede522..e6c05bbdda2 100644
--- a/source/blender/editors/space_clip/clip_draw.c
+++ b/source/blender/editors/space_clip/clip_draw.c
@@ -309,7 +309,8 @@ static void draw_movieclip_buffer(const bContext *C, SpaceClip *sc, ARegion *ar,
/* set zoom */
glPixelZoom(zoomx * width / ibuf->x, zoomy * height / ibuf->y);
- glaDrawImBuf_glsl_ctx(C, ibuf, x, y, filter);
+ glaDrawImBuf_glsl_ctx(C, ibuf, x, y, filter, 1.0f);
+
/* reset zoom */
glPixelZoom(1.0f, 1.0f);
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
index 1d398177d9f..232b2fe7e1e 100644
--- a/source/blender/editors/space_clip/clip_ops.c
+++ b/source/blender/editors/space_clip/clip_ops.c
@@ -999,7 +999,7 @@ void CLIP_OT_change_frame(wmOperatorType *ot)
ot->poll = change_frame_poll;
/* flags */
- ot->flag = OPTYPE_BLOCKING | OPTYPE_UNDO;
+ ot->flag = OPTYPE_BLOCKING;
/* rna */
RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME);
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index 6347ce7c4e8..9f66a22fbc1 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -35,6 +35,7 @@
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "BLI_fileops_types.h"
+#include "BLI_math.h"
#ifdef WIN32
# include "BLI_winstuff.h"
@@ -64,6 +65,7 @@
#include "UI_resources.h"
#include "UI_view2d.h"
+#include "WM_api.h"
#include "WM_types.h"
#include "filelist.h"
@@ -254,8 +256,13 @@ static int get_file_icon(struct direntry *file)
return ICON_FILE_BLEND;
else if (file->flags & FILE_TYPE_BLENDER_BACKUP)
return ICON_FILE_BACKUP;
- else if (file->flags & FILE_TYPE_IMAGE)
- return ICON_FILE_IMAGE;
+ else if (file->flags & FILE_TYPE_IMAGE) {
+ if (file->selflag & FILE_SEL_COLLAPSED) {
+ return ICON_FILE_MOVIE;
+ }
+ else
+ return ICON_FILE_IMAGE;
+ }
else if (file->flags & FILE_TYPE_MOVIE)
return ICON_FILE_MOVIE;
else if (file->flags & FILE_TYPE_PYSCRIPT)
@@ -330,7 +337,7 @@ void file_calc_previews(const bContext *C, ARegion *ar)
UI_view2d_totRect_set(v2d, sfile->layout->width, sfile->layout->height);
}
-static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int sy, ImBuf *imb, FileLayout *layout, bool is_icon, bool drag)
+static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int sy, ImBuf *imb, FileLayout *layout, bool is_icon, bool drag, bool play)
{
uiBut *but;
float fx, fy;
@@ -406,6 +413,24 @@ static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int
UI_but_drag_set_image(but, file->path, get_file_icon(file), imb, scale);
}
+ if (play && !is_icon) {
+ float r = MIN2(ey, ex) / 6.0f;
+ float yr = sqrt(3) / 2 * r;
+ float xr = r / 2;
+ glPushMatrix();
+ glTranslatef(xco + ex / 2.0f, yco + ey / 2.0f, 0.0);
+ glColor4f(1.0f, 1.0f, 1.0f, 0.8);
+ glutil_draw_filled_arc(0.0f, 2.0f * M_PI, ey / 4.0f, 32);
+ glColor4f(0.3f, 0.3f, 1.0f, 0.8f);
+ glutil_draw_filled_arc_part(0.0f, 2.0f * M_PI, ey / 4.0f, ey / 5.0f, 32);
+ glBegin(GL_TRIANGLES);
+ glVertex2f(-xr, yr);
+ glVertex2f(-xr, -yr);
+ glVertex2f(r, 0.0f);
+ glEnd();
+ glPopMatrix();
+ }
+
glDisable(GL_BLEND);
}
@@ -503,6 +528,7 @@ void file_draw_list(const bContext *C, ARegion *ar)
bool is_icon;
short align;
bool do_drag;
+ bool do_play;
int column_space = 0.6f * UI_UNIT_X;
numfiles = filelist_numfiles(files);
@@ -560,14 +586,24 @@ void file_draw_list(const bContext *C, ARegion *ar)
do_drag = !(FILENAME_IS_CURRPAR(file->relname));
if (FILE_IMGDISPLAY == params->display) {
- is_icon = 0;
+ is_icon = false;
imb = filelist_getimage(files, i);
if (!imb) {
imb = filelist_geticon(files, i);
- is_icon = 1;
+ is_icon = true;
}
- file_draw_preview(block, file, sx, sy, imb, layout, is_icon, do_drag);
+ do_play = (file->selflag & FILE_SEL_COLLAPSED) && !(file->selflag & FILE_SEL_PLAYING);
+
+ file_draw_preview(block, file, sx, sy, imb, layout, is_icon, do_drag, do_play);
+
+ if ((file->selflag & FILE_SEL_COLLAPSED) && (file->selflag & FILE_SEL_PLAYING)) {
+ /* refresh to keep movie playing */
+ file->collapsed_info.curfra++;
+ file->collapsed_info.curfra %= file->collapsed_info.totfiles;
+
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+ }
}
else {
file_draw_icon(block, file->path, sx, sy - (UI_UNIT_Y / 6), get_file_icon(file), ICON_DEFAULT_WIDTH_SCALE, ICON_DEFAULT_HEIGHT_SCALE, do_drag);
@@ -605,43 +641,71 @@ void file_draw_list(const bContext *C, ARegion *ar)
if (!(file->selflag & FILE_SEL_EDITING)) {
int tpos = (FILE_IMGDISPLAY == params->display) ? sy - layout->tile_h + layout->textheight : sy;
- file_draw_string(sx + 1, tpos, file->relname, (float)textwidth, textheight, align);
+ if (file->selflag & FILE_SEL_COLLAPSED) {
+ char fname[PATH_MAX];
+ char finalname[PATH_MAX];
+ char ext[PATH_MAX];
+ CollapsedEntry *collapsed = &file->collapsed_info;
+ BLI_strncpy(fname, file->relname, sizeof(fname));
+ BLI_path_frame_strip(fname, false, ext);
+ BLI_snprintf(finalname, sizeof(finalname), "%s%.*d-%.*d%s",
+ fname, collapsed->numdigits, collapsed->minframe, collapsed->numdigits, collapsed->maxframe, ext);
+ file_draw_string(sx + 1, tpos, finalname, (float)textwidth, textheight, align);
+ }
+ else
+ file_draw_string(sx + 1, tpos, file->relname, (float)textwidth, textheight, align);
}
if (params->display == FILE_SHORTDISPLAY) {
sx += (int)layout->column_widths[COLUMN_NAME] + column_space;
if (!(file->type & S_IFDIR)) {
- file_draw_string(sx, sy, file->size, layout->column_widths[COLUMN_SIZE], layout->tile_h, align);
+ if (file->selflag & FILE_SEL_COLLAPSED) {
+ CollapsedEntry *collapsed = &file->collapsed_info;
+ char sizestr[16];
+ BLI_file_size_string(collapsed->totalsize, sizestr, sizeof(sizestr));
+ file_draw_string(sx, sy, sizestr, layout->column_widths[COLUMN_SIZE], layout->tile_h, align);
+ }
+ else
+ file_draw_string(sx, sy, file->size, layout->column_widths[COLUMN_SIZE], layout->tile_h, align);
sx += (int)layout->column_widths[COLUMN_SIZE] + column_space;
}
}
else if (params->display == FILE_LONGDISPLAY) {
sx += (int)layout->column_widths[COLUMN_NAME] + column_space;
+ /* for collapsed files it doesn't make sense to display all info */
+ if (file->selflag & FILE_SEL_COLLAPSED) {
+ char sizestr[16];
+ CollapsedEntry *collapsed = &file->collapsed_info;
+ BLI_file_size_string(collapsed->totalsize, sizestr, sizeof(sizestr));
+ file_draw_string(sx, sy, sizestr, layout->column_widths[COLUMN_SIZE], layout->tile_h, align);
+ }
+ else {
#ifndef WIN32
- /* rwx rwx rwx */
- file_draw_string(sx, sy, file->mode1, layout->column_widths[COLUMN_MODE1], layout->tile_h, align);
- sx += layout->column_widths[COLUMN_MODE1] + column_space;
+ /* rwx rwx rwx */
+ file_draw_string(sx, sy, file->mode1, layout->column_widths[COLUMN_MODE1], layout->tile_h, align);
+ sx += layout->column_widths[COLUMN_MODE1] + column_space;
- file_draw_string(sx, sy, file->mode2, layout->column_widths[COLUMN_MODE2], layout->tile_h, align);
- sx += layout->column_widths[COLUMN_MODE2] + column_space;
+ file_draw_string(sx, sy, file->mode2, layout->column_widths[COLUMN_MODE2], layout->tile_h, align);
+ sx += layout->column_widths[COLUMN_MODE2] + column_space;
- file_draw_string(sx, sy, file->mode3, layout->column_widths[COLUMN_MODE3], layout->tile_h, align);
- sx += layout->column_widths[COLUMN_MODE3] + column_space;
+ file_draw_string(sx, sy, file->mode3, layout->column_widths[COLUMN_MODE3], layout->tile_h, align);
+ sx += layout->column_widths[COLUMN_MODE3] + column_space;
- file_draw_string(sx, sy, file->owner, layout->column_widths[COLUMN_OWNER], layout->tile_h, align);
- sx += layout->column_widths[COLUMN_OWNER] + column_space;
+ file_draw_string(sx, sy, file->owner, layout->column_widths[COLUMN_OWNER], layout->tile_h, align);
+ sx += layout->column_widths[COLUMN_OWNER] + column_space;
#endif
- file_draw_string(sx, sy, file->date, layout->column_widths[COLUMN_DATE], layout->tile_h, align);
- sx += (int)layout->column_widths[COLUMN_DATE] + column_space;
+ file_draw_string(sx, sy, file->date, layout->column_widths[COLUMN_DATE], layout->tile_h, align);
+ sx += (int)layout->column_widths[COLUMN_DATE] + column_space;
- file_draw_string(sx, sy, file->time, layout->column_widths[COLUMN_TIME], layout->tile_h, align);
- sx += (int)layout->column_widths[COLUMN_TIME] + column_space;
+ file_draw_string(sx, sy, file->time, layout->column_widths[COLUMN_TIME], layout->tile_h, align);
+ sx += (int)layout->column_widths[COLUMN_TIME] + column_space;
- if (!(file->type & S_IFDIR)) {
- file_draw_string(sx, sy, file->size, layout->column_widths[COLUMN_SIZE], layout->tile_h, align);
- sx += (int)layout->column_widths[COLUMN_SIZE] + column_space;
+ if (!(file->type & S_IFDIR)) {
+ file_draw_string(sx, sy, file->size, layout->column_widths[COLUMN_SIZE], layout->tile_h, align);
+ sx += (int)layout->column_widths[COLUMN_SIZE] + column_space;
+ }
}
}
}
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 8c9233e3ce5..5a7c4fdd8eb 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -242,7 +242,7 @@ static FileSelect file_select(bContext *C, const rcti *rect, FileSelType select,
/* flag the files as selected in the filelist */
filelist_select(sfile->files, &sel, select, FILE_SEL_SELECTED, check_type);
-
+
/* Don't act on multiple selected files */
if (sel.first != sel.last) select = 0;
@@ -326,6 +326,7 @@ static int file_border_select_modal(bContext *C, wmOperator *op, const wmEvent *
if (FILENAME_IS_CURRPAR(file->relname)) {
file->selflag &= ~FILE_SEL_HIGHLIGHTED;
+ file->collapsed_info.curfra = 0;
}
/* make sure highlight_file is no readonly file */
@@ -422,7 +423,15 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
}
else {
/* single select, deselect all selected first */
- if (!extend) file_deselect_all(sfile, FILE_SEL_SELECTED);
+ if (!(file->type & S_IFDIR) && (file->selflag & FILE_SEL_COLLAPSED)&& (file->selflag & FILE_SEL_SELECTED)) {
+ if (file->selflag & FILE_SEL_PLAYING) {
+ file->collapsed_info.curfra = 0;
+ }
+ file->selflag ^= FILE_SEL_PLAYING;
+ }
+ if (!extend) {
+ file_deselect_all(sfile, FILE_SEL_SELECTED);
+ }
}
}
}
@@ -1072,6 +1081,17 @@ int file_cancel_exec(bContext *C, wmOperator *UNUSED(unused))
SpaceFile *sfile = CTX_wm_space_file(C);
wmOperator *op = sfile->op;
+ if (op) {
+ PropertyRNA *prop;
+ if ((prop = RNA_struct_find_property(op->ptr, "collapse_images")) &&
+ (sfile->params->flag & FILE_COLLAPSE_IMAGES_TMP))
+ {
+ /* turn off collapsed flag if evoked from operator */
+ sfile->params->flag &= ~FILE_COLLAPSE_IMAGES_TMP;
+ sfile->params->flag &= ~FILE_COLLAPSE_IMAGES;
+ }
+ }
+
sfile->op = NULL;
WM_event_fileselect_event(wm, op, EVT_FILESELECT_CANCEL);
@@ -1137,9 +1157,26 @@ void file_sfile_to_operator(wmOperator *op, SpaceFile *sfile, char *filepath)
for (i = 0; i < numfiles; i++) {
if (filelist_is_selected(sfile->files, i, CHECK_FILES)) {
struct direntry *file = filelist_file(sfile->files, i);
- RNA_property_collection_add(op->ptr, prop, &itemptr);
- RNA_string_set(&itemptr, "name", file->relname);
- num_files++;
+
+ if (file->selflag & FILE_SEL_COLLAPSED) {
+ int j = 0;
+ CollapsedEntry *collapsed = &file->collapsed_info;
+
+ for (; j < collapsed->totfiles; j++) {
+ struct direntry *file_tmp = collapsed->darray[j];
+ RNA_property_collection_add(op->ptr, prop, &itemptr);
+ RNA_string_set(&itemptr, "name", file_tmp->relname);
+ num_files++;
+ }
+
+ MEM_freeN(collapsed->darray);
+ collapsed->darray = NULL;
+ }
+ else {
+ RNA_property_collection_add(op->ptr, prop, &itemptr);
+ RNA_string_set(&itemptr, "name", file->relname);
+ num_files++;
+ }
}
}
/* make sure the file specified in the filename button is added even if no files selected */
@@ -1169,7 +1206,13 @@ void file_sfile_to_operator(wmOperator *op, SpaceFile *sfile, char *filepath)
}
}
-
+ if ((prop = RNA_struct_find_property(op->ptr, "collapse_images")) &&
+ (sfile->params->flag & FILE_COLLAPSE_IMAGES_TMP))
+ {
+ /* turn off collapsed flag if evoked from operator */
+ sfile->params->flag &= ~FILE_COLLAPSE_IMAGES_TMP;
+ sfile->params->flag &= ~FILE_COLLAPSE_IMAGES;
+ }
}
}
@@ -1942,9 +1985,11 @@ static int file_rename_exec(bContext *C, wmOperator *UNUSED(op))
int numfiles = filelist_numfiles(sfile->files);
if ( (0 <= idx) && (idx < numfiles) ) {
struct direntry *file = filelist_file(sfile->files, idx);
- filelist_select_file(sfile->files, idx, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL);
- BLI_strncpy(sfile->params->renameedit, file->relname, FILE_MAXFILE);
- sfile->params->renamefile[0] = '\0';
+ if (!(file->selflag & FILE_SEL_COLLAPSED)) {
+ filelist_select_file(sfile->files, idx, FILE_SEL_ADD, FILE_SEL_EDITING, CHECK_ALL);
+ BLI_strncpy(sfile->params->renameedit, file->relname, FILE_MAXFILE);
+ sfile->params->renamefile[0] = '\0';
+ }
}
ED_area_tag_redraw(sa);
}
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 4048c9fc1a1..a0bc71b4a1f 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -35,6 +35,7 @@
#include <stdlib.h>
#include <math.h>
#include <string.h>
+#include <ctype.h>
#ifndef WIN32
# include <unistd.h>
@@ -49,6 +50,7 @@
#include "BLI_fnmatch.h"
#include "BLI_linklist.h"
#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
#ifdef WIN32
# include "BLI_winstuff.h"
@@ -208,9 +210,11 @@ typedef struct FileImage {
typedef struct FileListFilter {
bool hide_dot;
bool hide_parent;
+ bool collapse_ima_seq;
unsigned int filter;
char filter_glob[64];
- char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */
+ char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */
+ GHash *unique_image_list; /* hash that stores unique filename */
} FileListFilter;
typedef struct FileList {
@@ -464,6 +468,55 @@ static bool is_filtered_file(struct direntry *file, const char *UNUSED(root), Fi
}
}
+ if (is_filtered && (file->flags & FILE_TYPE_IMAGE) && filter->collapse_ima_seq) {
+ if (file->relname) {
+ struct direntry *ofile;
+ int frame, numdigits;
+
+ if (BLI_path_frame_get(file->relname, &frame, &numdigits)) {
+ char filename[PATH_MAX];
+
+ BLI_strncpy(filename, file->relname, sizeof(filename));
+ BLI_path_frame_strip(filename, false, NULL);
+
+ if ((ofile = BLI_ghash_lookup(filter->unique_image_list, filename)))
+ {
+ CollapsedEntry *collapsed = &ofile->collapsed_info;
+ is_filtered = false;
+ ofile->selflag |= FILE_SEL_COLLAPSED;
+ file->selflag |= FILE_SEL_COLLAPSED;
+ file->frame = frame;
+ BLI_addhead(&collapsed->list, BLI_genericNodeN(file));
+ collapsed->totalsize += file->realsize;
+ collapsed->maxframe = MAX2(frame, collapsed->maxframe);
+ collapsed->minframe = MIN2(frame, collapsed->minframe);
+ file->collapsed_info.numdigits = MAX2(numdigits, file->collapsed_info.numdigits);
+ collapsed->totfiles++;
+ }
+ else {
+ if (file->collapsed_info.darray) {
+ MEM_freeN(file->collapsed_info.darray);
+ file->collapsed_info.darray = NULL;
+ }
+ BLI_ghash_insert(filter->unique_image_list, BLI_strdup(filename), file);
+ file->frame = frame;
+ file->collapsed_info.totalsize = file->realsize;
+ file->collapsed_info.maxframe = file->collapsed_info.minframe = frame;
+ file->collapsed_info.numdigits = numdigits;
+ file->collapsed_info.totfiles = 1;
+ }
+ }
+ }
+ }
+ else {
+ if (file->collapsed_info.darray) {
+ MEM_freeN(file->collapsed_info.darray);
+ file->collapsed_info.darray = NULL;
+ }
+ /* may have been set in a previous filtering iteration, so always clear */
+ file->selflag &= ~FILE_SEL_COLLAPSED;
+ }
+
return is_filtered;
}
@@ -500,6 +553,17 @@ static void filelist_filter_clear(FileList *filelist)
filelist->numfiltered = 0;
}
+static int compareFrame(const void *pa, const void *pb)
+{
+ struct direntry *a = *((struct direntry **)pa);
+ struct direntry *b = *((struct direntry **)pb);
+ if (a->frame < b->frame)
+ return -1;
+ if (a->frame > b->frame)
+ return 1;
+ return 0;
+}
+
void filelist_filter(FileList *filelist)
{
int num_filtered = 0;
@@ -517,6 +581,11 @@ void filelist_filter(FileList *filelist)
fidx_tmp = MEM_mallocN(sizeof(*fidx_tmp) * (size_t)filelist->numfiles, __func__);
+ /* */
+ if (filelist->filter_data.collapse_ima_seq) {
+ filelist->filter_data.unique_image_list = BLI_ghash_str_new("image_seq_hash");
+ }
+
/* Filter remap & count how many files are left after filter in a single loop. */
for (i = 0; i < filelist->numfiles; ++i) {
struct direntry *file = &filelist->filelist[i];
@@ -526,6 +595,33 @@ void filelist_filter(FileList *filelist)
}
}
+ if (filelist->filter_data.unique_image_list) {
+ BLI_ghash_free(filelist->filter_data.unique_image_list, MEM_freeN, NULL);
+ filelist->filter_data.unique_image_list = NULL;
+ }
+
+ /* extra step, need to sort the file list according to frame */
+ if (filelist->filter_data.collapse_ima_seq) {
+ for (i = 0; i < num_filtered; i++) {
+ struct direntry *file = &filelist->filelist[fidx_tmp[i]];
+ if (file->selflag & FILE_SEL_COLLAPSED) {
+ LinkData *fdata = file->collapsed_info.list.first;
+ int j = 1;
+ file->collapsed_info.darray =
+ MEM_mallocN(sizeof(struct direntry *) * file->collapsed_info.totfiles, "collapsed files");
+ file->collapsed_info.darray[0] = file;
+
+ for (; fdata; fdata = fdata->next, j++) {
+ file->collapsed_info.darray[j] = fdata->data;
+ }
+ qsort(file->collapsed_info.darray, file->collapsed_info.totfiles, sizeof(struct direntry *), compareFrame);
+ if (file->collapsed_info.list.first) {
+ BLI_freelistN(&file->collapsed_info.list);
+ }
+ }
+ }
+ }
+
/* Note: maybe we could even accept filelist->fidx to be filelist->numfiles -len allocated? */
filelist->fidx = MEM_mallocN(sizeof(*filelist->fidx) * (size_t)num_filtered, __func__);
memcpy(filelist->fidx, fidx_tmp, sizeof(*filelist->fidx) * (size_t)num_filtered);
@@ -535,17 +631,20 @@ void filelist_filter(FileList *filelist)
}
void filelist_setfilter_options(FileList *filelist, const bool hide_dot, const bool hide_parent,
+ const bool collapse_ima_seq,
const unsigned int filter,
const char *filter_glob, const char *filter_search)
{
if ((filelist->filter_data.hide_dot != hide_dot) ||
(filelist->filter_data.hide_parent != hide_parent) ||
+ (filelist->filter_data.collapse_ima_seq != collapse_ima_seq) ||
(filelist->filter_data.filter != filter) ||
!STREQ(filelist->filter_data.filter_glob, filter_glob) ||
(BLI_strcmp_ignore_pad(filelist->filter_data.filter_search, filter_search, '*') != 0))
{
filelist->filter_data.hide_dot = hide_dot;
filelist->filter_data.hide_parent = hide_parent;
+ filelist->filter_data.collapse_ima_seq = collapse_ima_seq;
filelist->filter_data.filter = filter;
BLI_strncpy(filelist->filter_data.filter_glob, filter_glob, sizeof(filelist->filter_data.filter_glob));
@@ -611,6 +710,7 @@ ImBuf *filelist_getimage(struct FileList *filelist, const int index)
{
ImBuf *ibuf = NULL;
int fidx = 0;
+ struct direntry *file;
BLI_assert(G.background == false);
@@ -618,8 +718,15 @@ ImBuf *filelist_getimage(struct FileList *filelist, const int index)
return NULL;
}
fidx = filelist->fidx[index];
- ibuf = filelist->filelist[fidx].image;
+ file = filelist->filelist + fidx;
+ if (file->selflag & FILE_SEL_COLLAPSED) {
+ int curfra = file->collapsed_info.curfra;
+ ibuf = file->collapsed_info.darray[curfra]->image;
+ }
+ else {
+ ibuf = filelist->filelist[fidx].image;
+ }
return ibuf;
}
@@ -670,7 +777,10 @@ ImBuf *filelist_geticon(struct FileList *filelist, const int index)
ibuf = gSpecialFileImages[SPECIAL_IMG_TEXTFILE];
}
else if (file->flags & FILE_TYPE_IMAGE) {
- ibuf = gSpecialFileImages[SPECIAL_IMG_LOADING];
+ if (file->selflag & FILE_SEL_COLLAPSED)
+ ibuf = gSpecialFileImages[SPECIAL_IMG_MOVIEFILE];
+ else
+ ibuf = gSpecialFileImages[SPECIAL_IMG_LOADING];
}
else if (file->flags & FILE_TYPE_BLENDER_BACKUP) {
ibuf = gSpecialFileImages[SPECIAL_IMG_BACKUP];
@@ -1189,29 +1299,30 @@ static void filelist_from_main(struct FileList *filelist)
filelist->filelist[0].relname = BLI_strdup(FILENAME_PARENT);
filelist->filelist[1].relname = BLI_strdup("Scene");
- filelist->filelist[2].relname = BLI_strdup("Object");
- filelist->filelist[3].relname = BLI_strdup("Mesh");
- filelist->filelist[4].relname = BLI_strdup("Curve");
- filelist->filelist[5].relname = BLI_strdup("Metaball");
- filelist->filelist[6].relname = BLI_strdup("Material");
- filelist->filelist[7].relname = BLI_strdup("Texture");
- filelist->filelist[8].relname = BLI_strdup("Image");
- filelist->filelist[9].relname = BLI_strdup("Ika");
- filelist->filelist[10].relname = BLI_strdup("Wave");
- filelist->filelist[11].relname = BLI_strdup("Lattice");
- filelist->filelist[12].relname = BLI_strdup("Lamp");
- filelist->filelist[13].relname = BLI_strdup("Camera");
- filelist->filelist[14].relname = BLI_strdup("Ipo");
- filelist->filelist[15].relname = BLI_strdup("World");
- filelist->filelist[16].relname = BLI_strdup("Screen");
- filelist->filelist[17].relname = BLI_strdup("VFont");
- filelist->filelist[18].relname = BLI_strdup("Text");
- filelist->filelist[19].relname = BLI_strdup("Armature");
- filelist->filelist[20].relname = BLI_strdup("Action");
- filelist->filelist[21].relname = BLI_strdup("NodeTree");
- filelist->filelist[22].relname = BLI_strdup("Speaker");
+ filelist->filelist[2].relname = BLI_strdup("CacheLibrary");
+ filelist->filelist[3].relname = BLI_strdup("Object");
+ filelist->filelist[4].relname = BLI_strdup("Mesh");
+ filelist->filelist[5].relname = BLI_strdup("Curve");
+ filelist->filelist[6].relname = BLI_strdup("Metaball");
+ filelist->filelist[7].relname = BLI_strdup("Material");
+ filelist->filelist[8].relname = BLI_strdup("Texture");
+ filelist->filelist[9].relname = BLI_strdup("Image");
+ filelist->filelist[10].relname = BLI_strdup("Ika");
+ filelist->filelist[11].relname = BLI_strdup("Wave");
+ filelist->filelist[12].relname = BLI_strdup("Lattice");
+ filelist->filelist[13].relname = BLI_strdup("Lamp");
+ filelist->filelist[14].relname = BLI_strdup("Camera");
+ filelist->filelist[15].relname = BLI_strdup("Ipo");
+ filelist->filelist[16].relname = BLI_strdup("World");
+ filelist->filelist[17].relname = BLI_strdup("Screen");
+ filelist->filelist[18].relname = BLI_strdup("VFont");
+ filelist->filelist[19].relname = BLI_strdup("Text");
+ filelist->filelist[20].relname = BLI_strdup("Armature");
+ filelist->filelist[21].relname = BLI_strdup("Action");
+ filelist->filelist[22].relname = BLI_strdup("NodeTree");
+ filelist->filelist[23].relname = BLI_strdup("Speaker");
#ifdef WITH_FREESTYLE
- filelist->filelist[23].relname = BLI_strdup("FreestyleLineStyle");
+ filelist->filelist[24].relname = BLI_strdup("FreestyleLineStyle");
#endif
}
else {
diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h
index 44e0a5169fa..c6c9ed8dcc9 100644
--- a/source/blender/editors/space_file/filelist.h
+++ b/source/blender/editors/space_file/filelist.h
@@ -69,6 +69,7 @@ bool filelist_need_sorting(struct FileList *filelist);
void filelist_sort(struct FileList *filelist);
void filelist_setfilter_options(struct FileList *filelist, const bool hide_dot, const bool hide_parent,
+ const bool collapse_ima_seq,
const unsigned int filter,
const char *filter_glob, const char *filter_search);
void filelist_filter(struct FileList *filelist);
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 66eb79abae2..8b5249dea08 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -230,6 +230,15 @@ short ED_fileselect_set_params(SpaceFile *sfile)
}
}
+ if (params->filter & FILE_TYPE_IMAGE) {
+ if ((prop = RNA_struct_find_property(op->ptr, "collapse_images"))) {
+ if (!(params->flag & FILE_COLLAPSE_IMAGES)) {
+ params->flag |= FILE_COLLAPSE_IMAGES_TMP;
+ params->flag |= FILE_COLLAPSE_IMAGES;
+ }
+ }
+ }
+
if (is_relative_path) {
if ((prop = RNA_struct_find_property(op->ptr, "relative_path"))) {
if (!RNA_property_is_set_ex(op->ptr, prop, false)) {
@@ -448,7 +457,20 @@ static void column_widths(struct FileList *files, struct FileLayout *layout)
struct direntry *file = filelist_file(files, i);
if (file) {
float len;
- len = file_string_width(file->relname);
+ if (file->selflag & FILE_SEL_COLLAPSED) {
+ char fname[PATH_MAX];
+ char finalname[PATH_MAX];
+ char ext[PATH_MAX];
+ CollapsedEntry *collapsed = &file->collapsed_info;
+ BLI_strncpy(fname, file->relname, sizeof(fname));
+ BLI_path_frame_strip(fname, false, ext);
+ BLI_snprintf(finalname, sizeof(finalname), "%s%.*d-%.*d%s",
+ fname, collapsed->numdigits, collapsed->minframe, collapsed->numdigits, collapsed->maxframe, ext);
+ len = file_string_width(finalname);
+ }
+ else {
+ len = file_string_width(file->relname);
+ }
if (len > layout->column_widths[COLUMN_NAME]) layout->column_widths[COLUMN_NAME] = len;
len = file_string_width(file->date);
if (len > layout->column_widths[COLUMN_DATE]) layout->column_widths[COLUMN_DATE] = len;
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index c7e0e4ad4e9..c662d759397 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -215,8 +215,9 @@ static void file_refresh(const bContext *C, ScrArea *sa)
params->highlight_file = -1; /* added this so it opens nicer (ton) */
}
filelist_setsorting(sfile->files, params->sort);
- filelist_setfilter_options(sfile->files, params->flag & FILE_HIDE_DOT,
+ filelist_setfilter_options(sfile->files, (params->flag & FILE_HIDE_DOT) != 0,
false, /* TODO hide_parent, should be controllable? */
+ (params->flag & FILE_COLLAPSE_IMAGES) != 0,
params->flag & FILE_FILTER ? params->filter : 0,
params->filter_glob,
params->filter_search);
@@ -254,7 +255,7 @@ static void file_refresh(const bContext *C, ScrArea *sa)
int idx = filelist_find(sfile->files, params->renamefile);
if (idx >= 0) {
struct direntry *file = filelist_file(sfile->files, idx);
- if (file) {
+ if (file && !(file->selflag & FILE_SEL_COLLAPSED)) {
file->selflag |= FILE_SEL_EDITING;
}
}
diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c
index e0b62722d57..c1e3d855e7d 100644
--- a/source/blender/editors/space_graph/graph_buttons.c
+++ b/source/blender/editors/space_graph/graph_buttons.c
@@ -139,6 +139,13 @@ static void graph_panel_view(const bContext *C, Panel *pa)
row = uiLayoutSplit(sub, 0.7f, true);
uiItemR(row, &spaceptr, "cursor_position_y", 0, IFACE_("Cursor Y"), ICON_NONE);
uiItemEnumO(row, "GRAPH_OT_snap", IFACE_("To Keys"), 0, "type", GRAPHKEYS_SNAP_VALUE);
+
+ col = uiLayoutColumn(pa->layout, false);
+ uiItemR(col, &spaceptr, "show_backdrop", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(pa->layout, false);
+ uiLayoutSetActive(col, RNA_boolean_get(&spaceptr, "show_backdrop"));
+ uiItemR(col, &spaceptr, "backdrop_camera", 0, "Camera", ICON_NONE);
+ uiItemR(col, &spaceptr, "backdrop_opacity", 0, "Opacity", ICON_NONE);
}
/* ******************* active F-Curve ************** */
@@ -883,6 +890,7 @@ static void graph_panel_modifiers(const bContext *C, Panel *pa)
FModifier *fcm;
uiLayout *col, *row;
uiBlock *block;
+ bool active;
if (!graph_panel_context(C, &ale, &fcu))
return;
@@ -907,9 +915,11 @@ static void graph_panel_modifiers(const bContext *C, Panel *pa)
uiItemO(row, "", ICON_PASTEDOWN, "GRAPH_OT_fmodifier_paste");
}
+ active = !(fcu->flag & FCURVE_MOD_OFF);
/* draw each modifier */
for (fcm = fcu->modifiers.first; fcm; fcm = fcm->next) {
col = uiLayoutColumn(pa->layout, true);
+ uiLayoutSetActive(col, active);
ANIM_uiTemplate_fmodifier_draw(col, ale->id, &fcu->modifiers, fcm);
}
@@ -917,6 +927,21 @@ static void graph_panel_modifiers(const bContext *C, Panel *pa)
MEM_freeN(ale);
}
+/* ******************* Others ************************ */
+
+/* Graph Editor Backdrop Settings */
+static void UNUSED_FUNCTION(graph_panel_backdrop)(const bContext *C, Panel *UNUSED(pa))
+{
+ bScreen *sc = CTX_wm_screen(C);
+ SpaceIpo *sipo = CTX_wm_space_graph(C);
+ PointerRNA spaceptr;
+ // uiLayout *col;
+
+ /* get RNA pointers for use when creating the UI elements */
+ RNA_pointer_create(&sc->id, &RNA_SpaceGraphEditor, sipo, &spaceptr);
+
+}
+
/* ******************* general ******************************** */
void graph_buttons_register(ARegionType *art)
@@ -928,7 +953,6 @@ void graph_buttons_register(ARegionType *art)
strcpy(pt->label, N_("View Properties"));
strcpy(pt->translation_context, BLF_I18NCONTEXT_DEFAULT_BPYRNA);
pt->draw = graph_panel_view;
- pt->flag |= PNL_DEFAULT_CLOSED;
BLI_addtail(&art->paneltypes, pt);
pt = MEM_callocN(sizeof(PanelType), "spacetype graph panel properties");
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index 3de3ecebc44..f55a9dc45f3 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -59,6 +59,7 @@
#include "BKE_nla.h"
#include "BKE_context.h"
#include "BKE_report.h"
+#include "BKE_screen.h"
#include "UI_view2d.h"
@@ -982,7 +983,7 @@ void GRAPH_OT_delete(wmOperatorType *ot)
/* ******************** Clean Keyframes Operator ************************* */
-static void clean_graph_keys(bAnimContext *ac, float thresh)
+static void clean_graph_keys(bAnimContext *ac, float thresh, bool clean_chan)
{
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
@@ -994,7 +995,7 @@ static void clean_graph_keys(bAnimContext *ac, float thresh)
/* loop through filtered data and clean curves */
for (ale = anim_data.first; ale; ale = ale->next) {
- clean_fcurve((FCurve *)ale->key_data, thresh);
+ clean_fcurve(ac, ale, thresh, clean_chan);
ale->update |= ANIM_UPDATE_DEFAULT;
}
@@ -1009,6 +1010,7 @@ static int graphkeys_clean_exec(bContext *C, wmOperator *op)
{
bAnimContext ac;
float thresh;
+ bool clean_chan;
/* get editor data */
if (ANIM_animdata_get_context(C, &ac) == 0)
@@ -1016,9 +1018,9 @@ static int graphkeys_clean_exec(bContext *C, wmOperator *op)
/* get cleaning threshold */
thresh = RNA_float_get(op->ptr, "threshold");
-
+ clean_chan = RNA_boolean_get(op->ptr, "channels");
/* clean keyframes */
- clean_graph_keys(&ac, thresh);
+ clean_graph_keys(&ac, thresh, clean_chan);
/* set notifier that keyframes have changed */
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
@@ -1043,6 +1045,7 @@ void GRAPH_OT_clean(wmOperatorType *ot)
/* properties */
ot->prop = RNA_def_float(ot->srna, "threshold", 0.001f, 0.0f, FLT_MAX, "Threshold", "", 0.0f, 1000.0f);
+ RNA_def_boolean(ot->srna, "channels", false, "Channels", "");
}
/* ******************** Bake F-Curve Operator *********************** */
@@ -2491,3 +2494,154 @@ void GRAPH_OT_fmodifier_paste(wmOperatorType *ot)
}
/* ************************************************************************** */
+
+typedef struct BackDropTransformData {
+ float init_offset[2];
+ float init_zoom;
+ short event_type;
+ wmWidgetGroupType *cagetype;
+} BackDropTransformData;
+
+static int graph_widget_backdrop_transform_poll(bContext *C)
+{
+ SpaceIpo *sipo = CTX_wm_space_graph(C);
+ ARegion *ar = CTX_wm_region(C);
+
+ return ((sipo != NULL) &&
+ (ar->type->regionid == RGN_TYPE_WINDOW) &&
+ (sipo->flag & SIPO_DRAW_BACKDROP) &&
+ (sipo->backdrop_camera));
+}
+
+static void widgetgroup_backdrop_draw(const struct bContext *C, struct wmWidgetGroup *wgroup)
+{
+ ARegion *ar = CTX_wm_region(C);
+ wmOperator *op = wgroup->type->op;
+ Scene *scene = CTX_data_scene(C);
+ int width = (scene->r.size * scene->r.xsch) / 150.0f;
+ int height = (scene->r.size * scene->r.ysch) / 150.0f;
+ float origin[3];
+
+ wmWidget *cage = WIDGET_rect_transform_new(wgroup, WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM |
+ WIDGET_RECT_TRANSFORM_STYLE_TRANSLATE, width, height);
+ WM_widget_property(cage, RECT_TRANSFORM_SLOT_OFFSET, op->ptr, "offset");
+ WM_widget_property(cage, RECT_TRANSFORM_SLOT_SCALE, op->ptr, "scale");
+
+ origin[0] = BLI_rcti_size_x(&ar->winrct) / 2.0f;
+ origin[1] = BLI_rcti_size_y(&ar->winrct) / 2.0f;
+
+ WM_widget_set_origin(cage, origin);
+}
+
+static int graph_widget_backdrop_transform_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ SpaceIpo *sipo = CTX_wm_space_graph(C);
+ /* no poll, lives always for the duration of the operator */
+ wmWidgetGroupType *cagetype = WM_widgetgrouptype_new(NULL, widgetgroup_backdrop_draw, CTX_data_main(C), "Graph_Canvas", SPACE_IPO, RGN_TYPE_WINDOW, false);
+ struct wmEventHandler *handler = WM_event_add_modal_handler(C, op);
+ BackDropTransformData *data = MEM_mallocN(sizeof(BackDropTransformData), "overdrop transform data");
+ WM_modal_handler_attach_widgetgroup(C, handler, cagetype, op);
+
+ RNA_float_set_array(op->ptr, "offset", sipo->backdrop_offset);
+ RNA_float_set(op->ptr, "scale", sipo->backdrop_zoom);
+
+ copy_v2_v2(data->init_offset, sipo->backdrop_offset);
+ data->init_zoom = sipo->backdrop_zoom;
+ data->cagetype = cagetype;
+ data->event_type = event->type;
+
+ op->customdata = data;
+
+ ED_area_headerprint(sa, "Drag to place, and scale, Space/Enter/Caller key to confirm, R to recenter, RClick/Esc to cancel");
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void graph_widget_backdrop_transform_finish(bContext *C, BackDropTransformData *data)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ ED_area_headerprint(sa, NULL);
+ WM_widgetgrouptype_unregister(C, CTX_data_main(C), data->cagetype);
+ MEM_freeN(data);
+}
+
+static void graph_widget_backdrop_transform_cancel(struct bContext *C, struct wmOperator *op)
+{
+ BackDropTransformData *data = op->customdata;
+ graph_widget_backdrop_transform_finish(C, data);
+}
+
+static int graph_widget_backdrop_transform_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ BackDropTransformData *data = op->customdata;
+
+ if (event->type == data->event_type && event->val == KM_PRESS) {
+ graph_widget_backdrop_transform_finish(C, data);
+ return OPERATOR_FINISHED;
+ }
+
+ switch (event->type) {
+ case EVT_WIDGET_UPDATE: {
+ SpaceIpo *sipo = CTX_wm_space_graph(C);
+ RNA_float_get_array(op->ptr, "offset", sipo->backdrop_offset);
+ sipo->backdrop_zoom = RNA_float_get(op->ptr, "scale");
+ break;
+ }
+ case RKEY:
+ {
+ SpaceIpo *sipo = CTX_wm_space_graph(C);
+ ARegion *ar = CTX_wm_region(C);
+ float zero[2] = {0.0f};
+ RNA_float_set_array(op->ptr, "offset", zero);
+ RNA_float_set(op->ptr, "scale", 1.0f);
+ copy_v2_v2(sipo->backdrop_offset, zero);
+ sipo->backdrop_zoom = 1.0f;
+ ED_region_tag_redraw(ar);
+ /* add a mousemove to refresh the widget */
+ WM_event_add_mousemove(C);
+ break;
+ }
+ case RETKEY:
+ case PADENTER:
+ case SPACEKEY:
+ {
+ graph_widget_backdrop_transform_finish(C, data);
+ return OPERATOR_FINISHED;
+ }
+ case ESCKEY:
+ case RIGHTMOUSE:
+ {
+ SpaceIpo *sipo = CTX_wm_space_graph(C);
+ copy_v2_v2(sipo->backdrop_offset, data->init_offset);
+ sipo->backdrop_zoom = data->init_zoom;
+
+ graph_widget_backdrop_transform_finish(C, data);
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void GRAPH_OT_widget_backdrop_transform(struct wmOperatorType *ot)
+{
+ float default_offset[2] = {0.0f, 0.0f};
+
+ /* identifiers */
+ ot->name = "Transform Backdrop";
+ ot->idname = "GRAPH_OT_widget_backdrop_transform";
+ ot->description = "";
+
+ /* api callbacks */
+ ot->invoke = graph_widget_backdrop_transform_invoke;
+ ot->modal = graph_widget_backdrop_transform_modal;
+ ot->poll = graph_widget_backdrop_transform_poll;
+ ot->cancel = graph_widget_backdrop_transform_cancel;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_float_array(ot->srna, "offset", 2, default_offset, FLT_MIN, FLT_MAX, "Offset", "Offset of the backdrop", FLT_MIN, FLT_MAX);
+ RNA_def_float(ot->srna, "scale", 1.0f, 0.0f, FLT_MAX, "Scale", "Scale of the backdrop", 0.0f, FLT_MAX);
+}
diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h
index a478a86a5e2..35267cbf999 100644
--- a/source/blender/editors/space_graph/graph_intern.h
+++ b/source/blender/editors/space_graph/graph_intern.h
@@ -117,6 +117,8 @@ void GRAPH_OT_frame_jump(struct wmOperatorType *ot);
void GRAPH_OT_snap(struct wmOperatorType *ot);
void GRAPH_OT_mirror(struct wmOperatorType *ot);
+void GRAPH_OT_widget_backdrop_transform(struct wmOperatorType *ot);
+
/* defines for snap keyframes
* NOTE: keep in sync with eEditKeyframes_Snap (in ED_keyframes_edit.h)
*/
diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c
index f4cce29bda8..9b5fa3b744a 100644
--- a/source/blender/editors/space_graph/graph_ops.c
+++ b/source/blender/editors/space_graph/graph_ops.c
@@ -139,12 +139,16 @@ static void graphview_cursor_setprops(bContext *C, wmOperator *op, const wmEvent
/* Modal Operator init */
static int graphview_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
+ wmWindow *win = CTX_wm_window(C);
/* Change to frame that mouse is over before adding modal handler,
* as user could click on a single frame (jump to frame) as well as
* click-dragging over a range (modal scrubbing).
*/
graphview_cursor_setprops(C, op, event);
-
+
+ if (win->screen)
+ win->screen->scrubbing = true;
+
/* apply these changes first */
graphview_cursor_apply(C, op);
@@ -156,9 +160,13 @@ static int graphview_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *e
/* Modal event handling of cursor changing */
static int graphview_cursor_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
+ wmWindow *win = CTX_wm_window(C);
+
/* execute the events */
switch (event->type) {
case ESCKEY:
+ if (win->screen)
+ win->screen->scrubbing = false;
return OPERATOR_FINISHED;
case MOUSEMOVE:
@@ -173,8 +181,12 @@ static int graphview_cursor_modal(bContext *C, wmOperator *op, const wmEvent *ev
/* we check for either mouse-button to end, as checking for ACTIONMOUSE (which is used to init
* the modal op) doesn't work for some reason
*/
- if (event->val == KM_RELEASE)
+ if (event->val == KM_RELEASE) {
+ if (win->screen)
+ win->screen->scrubbing = false;
+
return OPERATOR_FINISHED;
+ }
break;
}
@@ -429,6 +441,8 @@ void graphedit_operatortypes(void)
WM_operatortype_append(GRAPH_OT_fmodifier_add);
WM_operatortype_append(GRAPH_OT_fmodifier_copy);
WM_operatortype_append(GRAPH_OT_fmodifier_paste);
+
+ WM_operatortype_append(GRAPH_OT_widget_backdrop_transform);
}
void ED_operatormacros_graph(void)
@@ -667,5 +681,7 @@ void graphedit_keymap(wmKeyConfig *keyconf)
/* keyframes */
keymap = WM_keymap_find(keyconf, "Graph Editor", SPACE_IPO, 0);
graphedit_keymap_keyframes(keyconf, keymap);
+
+ WM_keymap_add_item(keymap, "GRAPH_OT_widget_backdrop_transform", WKEY, KM_PRESS, 0, 0);
}
diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c
index ad6f3ff5c7e..4fd6276a28f 100644
--- a/source/blender/editors/space_graph/space_graph.c
+++ b/source/blender/editors/space_graph/space_graph.c
@@ -103,6 +103,10 @@ static SpaceLink *graph_new(const bContext *C)
sipo->autosnap = SACTSNAP_FRAME;
+ sipo->backdrop_camera = scene->camera;
+ sipo->backdrop_zoom = 1.0f;
+ sipo->backdrop_opacity = 0.7f;
+
/* allocate DopeSheet data for Graph Editor */
sipo->ads = MEM_callocN(sizeof(bDopeSheet), "GraphEdit DopeSheet");
sipo->ads->source = (ID *)scene;
@@ -219,18 +223,25 @@ static void graph_main_area_init(wmWindowManager *wm, ARegion *ar)
WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
keymap = WM_keymap_find(wm->defaultconf, "Graph Editor Generic", SPACE_IPO, 0);
WM_event_add_keymap_handler(&ar->handlers, keymap);
+
+ /* widgets */
+ if (BLI_listbase_is_empty(&ar->widgetmaps)) {
+ BLI_addhead(&ar->widgetmaps, WM_widgetmap_from_type("Graph_Canvas", SPACE_IPO, RGN_TYPE_WINDOW, false));
+ }
}
static void graph_main_area_draw(const bContext *C, ARegion *ar)
{
/* draw entirely, view changes should be handled here */
SpaceIpo *sipo = CTX_wm_space_graph(C);
+ Scene *scene = CTX_data_scene(C);
bAnimContext ac;
View2D *v2d = &ar->v2d;
View2DGrid *grid;
View2DScrollers *scrollers;
float col[3];
short unitx = 0, unity = V2D_UNIT_VALUES, flag = 0;
+ const bool draw_backdrop = ((sipo->flag & SIPO_DRAW_BACKDROP) && (sipo->backdrop_camera != NULL));
/* clear and setup matrix */
UI_GetThemeColor3fv(TH_BACK, col);
@@ -241,11 +252,26 @@ static void graph_main_area_draw(const bContext *C, ARegion *ar)
/* grid */
unitx = (sipo->flag & SIPO_DRAWTIME) ? V2D_UNIT_SECONDS : V2D_UNIT_FRAMESCALE;
- grid = UI_view2d_grid_calc(CTX_data_scene(C), v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP, ar->winx, ar->winy);
+ grid = UI_view2d_grid_calc(scene, v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP, ar->winx, ar->winy);
UI_view2d_grid_draw(v2d, grid, V2D_GRIDLINES_ALL);
ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW);
-
+
+ if (draw_backdrop) {
+ int width = (scene->r.size * scene->r.xsch) / 150 * sipo->backdrop_zoom;
+ int height = (scene->r.size * scene->r.ysch) / 150 * sipo->backdrop_zoom;
+ float xofs = (BLI_rcti_size_x(&ar->winrct) - width) / 2.0f + sipo->backdrop_offset[0];
+ float yofs = (BLI_rcti_size_y(&ar->winrct) - height) / 2.0f + sipo->backdrop_offset[1];
+
+ /* reset view matrix */
+ UI_view2d_view_restore(C);
+
+ ED_region_draw_backdrop_view3d(C, sipo->backdrop_camera, sipo->backdrop_opacity,
+ width, height, xofs, yofs, 1.0f, 1.0f, true);
+
+ UI_view2d_view_ortho(v2d);
+ }
+
/* draw data */
if (ANIM_animdata_get_context(C, &ac)) {
/* draw ghost curves */
@@ -306,6 +332,10 @@ static void graph_main_area_draw(const bContext *C, ARegion *ar)
/* reset view matrix */
UI_view2d_view_restore(C);
+ /* finally draw any widgets here */
+ WM_widgets_update(C, ar->widgetmaps.first);
+ WM_widgets_draw(C, ar->widgetmaps.first, false);
+
/* scrollers */
// FIXME: args for scrollers depend on the type of data being shown...
scrollers = UI_view2d_scrollers_calc(C, v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP);
@@ -611,6 +641,14 @@ static void graph_refresh(const bContext *C, ScrArea *sa)
}
}
+/* ************************************* */
+
+static void graph_widgets(void)
+{
+ /* create the widgetmap for the area here */
+ WM_widgetmaptype_find("Graph_Canvas", SPACE_IPO, RGN_TYPE_WINDOW, false, true);
+}
+
/* only called once, from space/spacetypes.c */
void ED_spacetype_ipo(void)
{
@@ -628,6 +666,7 @@ void ED_spacetype_ipo(void)
st->keymap = graphedit_keymap;
st->listener = graph_listener;
st->refresh = graph_refresh;
+ st->widgets = graph_widgets;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index 36419ccd10d..34e32c150d6 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -513,7 +513,7 @@ static void draw_image_buffer(const bContext *C, SpaceImage *sima, ARegion *ar,
fdrawcheckerboard(x, y, x + ibuf->x * zoomx, y + ibuf->y * zoomy);
}
- glaDrawImBuf_glsl_ctx(C, ibuf, x, y, GL_NEAREST);
+ glaDrawImBuf_glsl_ctx(C, ibuf, x, y, GL_NEAREST, 1.0f);
if (sima->flag & SI_USE_ALPHA)
glDisable(GL_BLEND);
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index 7f35884cb65..b23834b7746 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -937,6 +937,27 @@ static void node_shader_buts_tex_voronoi(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "coloring", 0, "", ICON_NONE);
}
+static void node_shader_buts_tex_pointdensity(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ bNode *node = ptr->data;
+ NodeShaderTexPointDensity *shader_point_density = node->storage;
+
+ uiItemR(layout, ptr, "point_source", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "object", 0, NULL, ICON_NONE);
+
+ if (node->id && shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) {
+ PointerRNA dataptr;
+ RNA_id_pointer_create((ID *)node->id, &dataptr);
+ uiItemPointerR(layout, ptr, "particle_system", &dataptr, "particle_systems", NULL, ICON_NONE);
+ }
+
+ uiItemR(layout, ptr, "space", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "radius", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "interpolation", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "resolution", 0, NULL, ICON_NONE);
+ uiItemR(layout, ptr, "color_source", 0, NULL, ICON_NONE);
+}
+
static void node_shader_buts_tex_coord(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "object", 0, NULL, 0);
@@ -962,6 +983,22 @@ static void node_shader_buts_uvmap(uiLayout *layout, bContext *C, PointerRNA *pt
}
}
+static void node_shader_buts_openvdb(uiLayout *layout, bContext *C, PointerRNA *ptr)
+{
+ PointerRNA scene = CTX_data_pointer_get(C, "scene");
+ if (scene.data) {
+ PointerRNA cscene = RNA_pointer_get(&scene, "cycles");
+ if (cscene.data && RNA_enum_get(&cscene, "device") == 1)
+ uiItemL(layout, IFACE_("OpenVDB is not supported on GPU"), ICON_NONE);
+ }
+
+ uiItemR(layout, ptr, "filename", 0, "", 0);
+ uiItemR(layout, ptr, "sampling", 0, "", 0);
+ uiItemR(layout, ptr, "source", 0, "", 0);
+
+ UNUSED_VARS(C);
+}
+
static void node_shader_buts_uvalongstroke(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiItemR(layout, ptr, "use_tips", 0, NULL, 0);
@@ -1170,6 +1207,9 @@ static void node_shader_set_butfunc(bNodeType *ntype)
case SH_NODE_TEX_VORONOI:
ntype->draw_buttons = node_shader_buts_tex_voronoi;
break;
+ case SH_NODE_TEX_POINTDENSITY:
+ ntype->draw_buttons = node_shader_buts_tex_pointdensity;
+ break;
case SH_NODE_TEX_COORD:
ntype->draw_buttons = node_shader_buts_tex_coord;
break;
@@ -1212,6 +1252,9 @@ static void node_shader_set_butfunc(bNodeType *ntype)
case SH_NODE_OUTPUT_LINESTYLE:
ntype->draw_buttons = node_buts_output_linestyle;
break;
+ case SH_NODE_OPENVDB:
+ ntype->draw_buttons = node_shader_buts_openvdb;
+ break;
}
}
@@ -2173,8 +2216,8 @@ static void node_composit_backdrop_viewer(SpaceNode *snode, ImBuf *backdrop, bNo
if (node->custom1 == 0) {
const float backdropWidth = backdrop->x;
const float backdropHeight = backdrop->y;
- const float cx = x + snode->zoom * backdropWidth * node->custom3;
- const float cy = y + snode->zoom * backdropHeight * node->custom4;
+ const float cx = x + snode->backdrop_zoom * backdropWidth * node->custom3;
+ const float cy = y + snode->backdrop_zoom * backdropHeight * node->custom4;
glColor3f(1.0, 1.0, 1.0);
@@ -2205,17 +2248,17 @@ static void node_composit_backdrop_boxmask(SpaceNode *snode, ImBuf *backdrop, bN
glColor3f(1.0, 1.0, 1.0);
- cx = x + snode->zoom * backdropWidth * boxmask->x;
- cy = y + snode->zoom * backdropHeight * boxmask->y;
+ cx = x + snode->backdrop_zoom * backdropWidth * boxmask->x;
+ cy = y + snode->backdrop_zoom * backdropHeight * boxmask->y;
- x1 = cx - (cosine * halveBoxWidth + sine * halveBoxHeight) * snode->zoom;
- x2 = cx - (cosine * -halveBoxWidth + sine * halveBoxHeight) * snode->zoom;
- x3 = cx - (cosine * -halveBoxWidth + sine * -halveBoxHeight) * snode->zoom;
- x4 = cx - (cosine * halveBoxWidth + sine * -halveBoxHeight) * snode->zoom;
- y1 = cy - (-sine * halveBoxWidth + cosine * halveBoxHeight) * snode->zoom;
- y2 = cy - (-sine * -halveBoxWidth + cosine * halveBoxHeight) * snode->zoom;
- y3 = cy - (-sine * -halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom;
- y4 = cy - (-sine * halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom;
+ x1 = cx - (cosine * halveBoxWidth + sine * halveBoxHeight) * snode->backdrop_zoom;
+ x2 = cx - (cosine * -halveBoxWidth + sine * halveBoxHeight) * snode->backdrop_zoom;
+ x3 = cx - (cosine * -halveBoxWidth + sine * -halveBoxHeight) * snode->backdrop_zoom;
+ x4 = cx - (cosine * halveBoxWidth + sine * -halveBoxHeight) * snode->backdrop_zoom;
+ y1 = cy - (-sine * halveBoxWidth + cosine * halveBoxHeight) * snode->backdrop_zoom;
+ y2 = cy - (-sine * -halveBoxWidth + cosine * halveBoxHeight) * snode->backdrop_zoom;
+ y3 = cy - (-sine * -halveBoxWidth + cosine * -halveBoxHeight) * snode->backdrop_zoom;
+ y4 = cy - (-sine * halveBoxWidth + cosine * -halveBoxHeight) * snode->backdrop_zoom;
glBegin(GL_LINE_LOOP);
glVertex2f(x1, y1);
@@ -2243,17 +2286,17 @@ static void node_composit_backdrop_ellipsemask(SpaceNode *snode, ImBuf *backdrop
glColor3f(1.0, 1.0, 1.0);
- cx = x + snode->zoom * backdropWidth * ellipsemask->x;
- cy = y + snode->zoom * backdropHeight * ellipsemask->y;
+ cx = x + snode->backdrop_zoom * backdropWidth * ellipsemask->x;
+ cy = y + snode->backdrop_zoom * backdropHeight * ellipsemask->y;
- x1 = cx - (cosine * halveBoxWidth + sine * halveBoxHeight) * snode->zoom;
- x2 = cx - (cosine * -halveBoxWidth + sine * halveBoxHeight) * snode->zoom;
- x3 = cx - (cosine * -halveBoxWidth + sine * -halveBoxHeight) * snode->zoom;
- x4 = cx - (cosine * halveBoxWidth + sine * -halveBoxHeight) * snode->zoom;
- y1 = cy - (-sine * halveBoxWidth + cosine * halveBoxHeight) * snode->zoom;
- y2 = cy - (-sine * -halveBoxWidth + cosine * halveBoxHeight) * snode->zoom;
- y3 = cy - (-sine * -halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom;
- y4 = cy - (-sine * halveBoxWidth + cosine * -halveBoxHeight) * snode->zoom;
+ x1 = cx - (cosine * halveBoxWidth + sine * halveBoxHeight) * snode->backdrop_zoom;
+ x2 = cx - (cosine * -halveBoxWidth + sine * halveBoxHeight) * snode->backdrop_zoom;
+ x3 = cx - (cosine * -halveBoxWidth + sine * -halveBoxHeight) * snode->backdrop_zoom;
+ x4 = cx - (cosine * halveBoxWidth + sine * -halveBoxHeight) * snode->backdrop_zoom;
+ y1 = cy - (-sine * halveBoxWidth + cosine * halveBoxHeight) * snode->backdrop_zoom;
+ y2 = cy - (-sine * -halveBoxWidth + cosine * halveBoxHeight) * snode->backdrop_zoom;
+ y3 = cy - (-sine * -halveBoxWidth + cosine * -halveBoxHeight) * snode->backdrop_zoom;
+ y4 = cy - (-sine * halveBoxWidth + cosine * -halveBoxHeight) * snode->backdrop_zoom;
glBegin(GL_LINE_LOOP);
@@ -3188,8 +3231,8 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode, b
glaDefine2DArea(&ar->winrct);
wmOrtho2_region_pixelspace(ar);
- x = (ar->winx - snode->zoom * ibuf->x) / 2 + snode->xof;
- y = (ar->winy - snode->zoom * ibuf->y) / 2 + snode->yof;
+ x = (ar->winx - snode->backdrop_zoom * ibuf->x) / 2 + snode->backdrop_offset[0];
+ y = (ar->winy - snode->backdrop_zoom * ibuf->y) / 2 + snode->backdrop_offset[1];
if (ibuf->rect || ibuf->rect_float) {
unsigned char *display_buffer = NULL;
@@ -3210,7 +3253,7 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode, b
else ofs = 3;
#endif
- glPixelZoom(snode->zoom, snode->zoom);
+ glPixelZoom(snode->backdrop_zoom, snode->backdrop_zoom);
/* swap bytes, so alpha is most significant one, then just draw it as luminance int */
glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_LUMINANCE, GL_UNSIGNED_INT,
@@ -3221,7 +3264,7 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode, b
else if (snode->flag & SNODE_SHOW_ALPHA) {
display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, &cache_handle);
- glPixelZoom(snode->zoom, snode->zoom);
+ glPixelZoom(snode->backdrop_zoom, snode->backdrop_zoom);
/* swap bytes, so alpha is most significant one, then just draw it as luminance int */
#ifdef __BIG_ENDIAN__
glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
@@ -3236,17 +3279,17 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode, b
else if (snode->flag & SNODE_USE_ALPHA) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glPixelZoom(snode->zoom, snode->zoom);
+ glPixelZoom(snode->backdrop_zoom, snode->backdrop_zoom);
- glaDrawImBuf_glsl_ctx(C, ibuf, x, y, GL_NEAREST);
+ glaDrawImBuf_glsl_ctx(C, ibuf, x, y, GL_NEAREST, 1.0f);
glPixelZoom(1.0f, 1.0f);
glDisable(GL_BLEND);
}
else {
- glPixelZoom(snode->zoom, snode->zoom);
+ glPixelZoom(snode->backdrop_zoom, snode->backdrop_zoom);
- glaDrawImBuf_glsl_ctx(C, ibuf, x, y, GL_NEAREST);
+ glaDrawImBuf_glsl_ctx(C, ibuf, x, y, GL_NEAREST, 1.0f);
glPixelZoom(1.0f, 1.0f);
}
@@ -3275,10 +3318,10 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode, b
rcti pixel_border;
UI_ThemeColor(TH_ACTIVE);
BLI_rcti_init(&pixel_border,
- x + snode->zoom * viewer_border->xmin * ibuf->x,
- x + snode->zoom * viewer_border->xmax * ibuf->x,
- y + snode->zoom * viewer_border->ymin * ibuf->y,
- y + snode->zoom * viewer_border->ymax * ibuf->y);
+ x + snode->backdrop_zoom * viewer_border->xmin * ibuf->x,
+ x + snode->backdrop_zoom * viewer_border->xmax * ibuf->x,
+ y + snode->backdrop_zoom * viewer_border->ymin * ibuf->y,
+ y + snode->backdrop_zoom * viewer_border->ymax * ibuf->y);
glaDrawBorderCorners(&pixel_border, 1.0f, 1.0f);
}
}
diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c
index 34efba00b86..d27ddc99851 100644
--- a/source/blender/editors/space_node/node_draw.c
+++ b/source/blender/editors/space_node/node_draw.c
@@ -1352,7 +1352,23 @@ void drawnodespace(const bContext *C, ARegion *ar)
/* backdrop */
draw_nodespace_back_pix(C, ar, snode, path->parent_key);
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glaDefine2DArea(&ar->winrct);
+ wmOrtho2_pixelspace(ar->winx, ar->winy);
+
+ WM_widgets_update(C, ar->widgetmaps.first);
+ WM_widgets_draw(C, ar->widgetmaps.first, false);
+
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
draw_nodetree(C, ar, ntree, path->parent_key);
}
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c
index ffd51bcc44e..2af1dc01a0e 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.c
@@ -2516,11 +2516,11 @@ static void viewer_border_corner_to_backdrop(SpaceNode *snode, ARegion *ar, int
{
float bufx, bufy;
- bufx = backdrop_width * snode->zoom;
- bufy = backdrop_height * snode->zoom;
+ bufx = backdrop_width * snode->backdrop_zoom;
+ bufy = backdrop_height * snode->backdrop_zoom;
- *fx = (bufx > 0.0f ? ((float) x - 0.5f * ar->winx - snode->xof) / bufx + 0.5f : 0.0f);
- *fy = (bufy > 0.0f ? ((float) y - 0.5f * ar->winy - snode->yof) / bufy + 0.5f : 0.0f);
+ *fx = (bufx > 0.0f ? ((float) x - 0.5f * ar->winx - snode->backdrop_offset[0]) / bufx + 0.5f : 0.0f);
+ *fy = (bufy > 0.0f ? ((float) y - 0.5f * ar->winy - snode->backdrop_offset[1]) / bufy + 0.5f : 0.0f);
}
static int viewer_border_exec(bContext *C, wmOperator *op)
diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.c
index 8c5d2d82468..e218776ac8f 100644
--- a/source/blender/editors/space_node/node_view.c
+++ b/source/blender/editors/space_node/node_view.c
@@ -137,8 +137,8 @@ static int node_view_all_exec(bContext *C, wmOperator *op)
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
/* is this really needed? */
- snode->xof = 0;
- snode->yof = 0;
+ snode->backdrop_offset[0] = 0;
+ snode->backdrop_offset[1] = 0;
if (space_node_view_flag(C, snode, ar, 0, smooth_viewtx)) {
return OPERATOR_FINISHED;
@@ -208,14 +208,14 @@ static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *e
switch (event->type) {
case MOUSEMOVE:
- snode->xof -= (nvm->mvalo[0] - event->mval[0]);
- snode->yof -= (nvm->mvalo[1] - event->mval[1]);
+ snode->backdrop_offset[0] -= (nvm->mvalo[0] - event->mval[0]);
+ snode->backdrop_offset[1] -= (nvm->mvalo[1] - event->mval[1]);
nvm->mvalo[0] = event->mval[0];
nvm->mvalo[1] = event->mval[1];
/* prevent dragging image outside of the window and losing it! */
- CLAMP(snode->xof, nvm->xmin, nvm->xmax);
- CLAMP(snode->yof, nvm->ymin, nvm->ymax);
+ CLAMP(snode->backdrop_offset[0], nvm->xmin, nvm->xmax);
+ CLAMP(snode->backdrop_offset[1], nvm->ymin, nvm->ymax);
ED_region_tag_redraw(ar);
WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL);
@@ -259,10 +259,10 @@ static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *
nvm->mvalo[0] = event->mval[0];
nvm->mvalo[1] = event->mval[1];
- nvm->xmin = -(ar->winx / 2) - (ibuf->x * (0.5f * snode->zoom)) + pad;
- nvm->xmax = (ar->winx / 2) + (ibuf->x * (0.5f * snode->zoom)) - pad;
- nvm->ymin = -(ar->winy / 2) - (ibuf->y * (0.5f * snode->zoom)) + pad;
- nvm->ymax = (ar->winy / 2) + (ibuf->y * (0.5f * snode->zoom)) - pad;
+ nvm->xmin = -(ar->winx / 2) - (ibuf->x * (0.5f * snode->backdrop_zoom)) + pad;
+ nvm->xmax = (ar->winx / 2) + (ibuf->x * (0.5f * snode->backdrop_zoom)) - pad;
+ nvm->ymin = -(ar->winy / 2) - (ibuf->y * (0.5f * snode->backdrop_zoom)) + pad;
+ nvm->ymax = (ar->winy / 2) + (ibuf->y * (0.5f * snode->backdrop_zoom)) - pad;
BKE_image_release_ibuf(ima, ibuf, lock);
@@ -301,7 +301,8 @@ static int backimage_zoom_exec(bContext *C, wmOperator *op)
ARegion *ar = CTX_wm_region(C);
float fac = RNA_float_get(op->ptr, "factor");
- snode->zoom *= fac;
+ snode->backdrop_zoom *= fac;
+ snode->backdrop_zoom *= fac;
ED_region_tag_redraw(ar);
WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL);
@@ -340,7 +341,7 @@ static int backimage_fit_exec(bContext *C, wmOperator *UNUSED(op))
void *lock;
- float facx, facy;
+ float facx, facy, fac;
ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
@@ -350,15 +351,18 @@ static int backimage_fit_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
}
- facx = 1.0f * (ar->sizex - pad) / (ibuf->x * snode->zoom);
- facy = 1.0f * (ar->sizey - pad) / (ibuf->y * snode->zoom);
+ facx = 1.0f * (ar->sizex - pad) / (ibuf->x);
+ facy = 1.0f * (ar->sizey - pad) / (ibuf->y);
BKE_image_release_ibuf(ima, ibuf, lock);
+
+ fac = min_ff(facx, facy);
- snode->zoom *= min_ff(facx, facy);
-
- snode->xof = 0;
- snode->yof = 0;
+ snode->backdrop_zoom = fac;
+ snode->backdrop_zoom = fac;
+
+ snode->backdrop_offset[0] = 0;
+ snode->backdrop_offset[1] = 0;
ED_region_tag_redraw(ar);
WM_main_add_notifier(NC_NODE | ND_DISPLAY, NULL);
@@ -444,10 +448,10 @@ bool ED_space_node_color_sample(Scene *scene, SpaceNode *snode, ARegion *ar, int
}
/* map the mouse coords to the backdrop image space */
- bufx = ibuf->x * snode->zoom;
- bufy = ibuf->y * snode->zoom;
- fx = (bufx > 0.0f ? ((float)mval[0] - 0.5f * ar->winx - snode->xof) / bufx + 0.5f : 0.0f);
- fy = (bufy > 0.0f ? ((float)mval[1] - 0.5f * ar->winy - snode->yof) / bufy + 0.5f : 0.0f);
+ bufx = ibuf->x * snode->backdrop_zoom;
+ bufy = ibuf->y * snode->backdrop_zoom;
+ fx = (bufx > 0.0f ? ((float)mval[0] - 0.5f * ar->winx - snode->backdrop_offset[0]) / bufx + 0.5f : 0.0f);
+ fy = (bufy > 0.0f ? ((float)mval[1] - 0.5f * ar->winy - snode->backdrop_offset[1]) / bufy + 0.5f : 0.0f);
if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) {
const float *fp;
@@ -502,10 +506,10 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
}
/* map the mouse coords to the backdrop image space */
- bufx = ibuf->x * snode->zoom;
- bufy = ibuf->y * snode->zoom;
- fx = (bufx > 0.0f ? ((float)event->mval[0] - 0.5f * ar->winx - snode->xof) / bufx + 0.5f : 0.0f);
- fy = (bufy > 0.0f ? ((float)event->mval[1] - 0.5f * ar->winy - snode->yof) / bufy + 0.5f : 0.0f);
+ bufx = ibuf->x * snode->backdrop_zoom;
+ bufy = ibuf->y * snode->backdrop_zoom;
+ fx = (bufx > 0.0f ? ((float)event->mval[0] - 0.5f * ar->winx - snode->backdrop_offset[0]) / bufx + 0.5f : 0.0f);
+ fy = (bufy > 0.0f ? ((float)event->mval[1] - 0.5f * ar->winy - snode->backdrop_offset[1]) / bufy + 0.5f : 0.0f);
if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) {
const float *fp;
diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c
index de90c417bdd..a1980c88d7e 100644
--- a/source/blender/editors/space_node/space_node.c
+++ b/source/blender/editors/space_node/space_node.c
@@ -38,7 +38,10 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
+#include "IMB_imbuf_types.h"
+
#include "BKE_context.h"
+#include "BKE_image.h"
#include "BKE_library.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
@@ -56,6 +59,8 @@
#include "RNA_access.h"
+#include "WM_api.h"
+
#include "node_intern.h" /* own include */
@@ -300,7 +305,7 @@ static SpaceLink *node_new(const bContext *UNUSED(C))
snode->flag = SNODE_SHOW_GPENCIL | SNODE_USE_ALPHA;
/* backdrop */
- snode->zoom = 1.0f;
+ snode->backdrop_zoom = 1.0f;
/* select the first tree type for valid type */
NODE_TREE_TYPES_BEGIN (treetype)
@@ -642,6 +647,13 @@ static void node_main_area_init(wmWindowManager *wm, ARegion *ar)
UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_CUSTOM, ar->winx, ar->winy);
+ /* widgets stay in the background for now - quick patchjob to make sure nodes themselves work */
+ if (BLI_listbase_is_empty(&ar->widgetmaps)) {
+ BLI_addhead(&ar->widgetmaps, WM_widgetmap_from_type("Node_Canvas", SPACE_NODE, RGN_TYPE_WINDOW, false));
+ }
+
+ WM_event_add_area_widgetmap_handlers(ar);
+
/* own keymaps */
keymap = WM_keymap_find(wm->defaultconf, "Node Generic", SPACE_NODE, 0);
WM_event_add_keymap_handler(&ar->handlers, keymap);
@@ -821,6 +833,61 @@ static int node_context(const bContext *C, const char *member, bContextDataResul
return 0;
}
+static int WIDGETGROUP_node_transform_poll(const struct bContext *C, struct wmWidgetGroupType *UNUSED(wgrouptype))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+
+ if (snode && snode->edittree && snode->edittree->type == NTREE_COMPOSIT) {
+ bNode *node = nodeGetActive(snode->edittree);
+
+ if (node && node->type == CMP_NODE_VIEWER)
+ return true;
+ }
+
+ return false;
+}
+
+static void WIDGETGROUP_node_transform_update(const struct bContext *C, struct wmWidgetGroup *wgroup)
+{
+ Image *ima;
+ ImBuf *ibuf;
+ void *lock;
+
+ ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
+ ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ if (ibuf) {
+ wmWidget *cage;
+ SpaceNode *snode = CTX_wm_space_node(C);
+ ARegion *ar = CTX_wm_region(C);
+ float origin[3];
+ float w, h;
+ PointerRNA nodeptr;
+
+ /* center is always at the origin */
+ origin[0] = ar->winx / 2;
+ origin[1] = ar->winy / 2;
+
+ w = (ibuf->x > 0) ? ibuf->x : 64.0f;
+ h = (ibuf->y > 0) ? ibuf->y : 64.0f;
+
+ cage = WIDGET_rect_transform_new(wgroup, WIDGET_RECT_TRANSFORM_STYLE_TRANSLATE | WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM, w, h);
+ RNA_pointer_create(snode->id, &RNA_SpaceNodeEditor, snode, &nodeptr);
+
+ WM_widget_set_origin(cage, origin);
+ WM_widget_property(cage, RECT_TRANSFORM_SLOT_OFFSET, &nodeptr, "backdrop_offset");
+ WM_widget_property(cage, RECT_TRANSFORM_SLOT_SCALE, &nodeptr, "backdrop_zoom");
+ }
+ BKE_image_release_ibuf(ima, ibuf, lock);
+}
+
+static void node_widgets(void)
+{
+ /* create the widgetmap for the area here */
+ WM_widgetmaptype_find("Node_Canvas", SPACE_NODE, RGN_TYPE_WINDOW, false, true);
+
+ WM_widgetgrouptype_new(WIDGETGROUP_node_transform_poll, WIDGETGROUP_node_transform_update, NULL, "Node_Canvas", SPACE_NODE, RGN_TYPE_WINDOW, false);
+}
+
/* only called once, from space/spacetypes.c */
void ED_spacetype_node(void)
{
@@ -840,6 +907,7 @@ void ED_spacetype_node(void)
st->refresh = node_area_refresh;
st->context = node_context;
st->dropboxes = node_dropboxes;
+ st->widgets = node_widgets;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype node region");
diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index 86d1fe71ade..5b95a418dc2 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -940,6 +940,37 @@ void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot)
}
+void SEQUENCER_OT_image_sequence_add(struct wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Add Image Strip";
+ ot->idname = "SEQUENCER_OT_image_sequence_add";
+ ot->description = "Add an sequence of images with identical names to the sequencer";
+
+ /* api callbacks */
+ ot->invoke = sequencer_add_image_strip_invoke;
+ ot->exec = sequencer_add_image_strip_exec;
+ ot->cancel = sequencer_add_cancel;
+ ot->ui = sequencer_add_draw;
+
+ ot->poll = ED_operator_sequencer_active_editable;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE, FILE_SPECIAL, FILE_OPENFILE,
+ WM_FILESEL_DIRECTORY | WM_FILESEL_RELPATH | WM_FILESEL_FILES | WM_FILESEL_IMAGE_COLLAPSE,
+ FILE_DEFAULTDISPLAY);
+ sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME);
+
+ /* hidden property always set to true */
+ prop = RNA_def_boolean(ot->srna, "use_placeholders", true, "Use Placeholders", "Use placeholders for missing frames of the strip");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+}
+
+
/* add_effect_strip operator */
static int sequencer_add_effect_strip_exec(bContext *C, wmOperator *op)
{
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 27eb28bec8f..eb00c37a483 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -293,6 +293,20 @@ static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1,
int chan_range = 0;
float draw_range = y2 - y1;
float draw_height;
+ ListBase *seqbase;
+ int offset;
+
+ seqbase = BKE_sequence_seqbase_get(seqm, &offset);
+ if (!seqbase || BLI_listbase_is_empty(seqbase)) {
+ return;
+ }
+
+ if (seqm->type == SEQ_TYPE_SCENE) {
+ offset = seqm->start - offset;
+ }
+ else {
+ offset = 0;
+ }
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -300,7 +314,7 @@ static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1,
if (seqm->flag & SEQ_MUTE)
drawmeta_stipple(1);
- for (seq = seqm->seqbase.first; seq; seq = seq->next) {
+ for (seq = seqbase->first; seq; seq = seq->next) {
chan_min = min_ii(chan_min, seq->machine);
chan_max = max_ii(chan_max, seq->machine);
}
@@ -310,11 +324,14 @@ static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1,
col[3] = 196; /* alpha, used for all meta children */
- for (seq = seqm->seqbase.first; seq; seq = seq->next) {
- if ((seq->startdisp > x2 || seq->enddisp < x1) == 0) {
+ for (seq = seqbase->first; seq; seq = seq->next) {
+ const int startdisp = seq->startdisp + offset;
+ const int enddisp = seq->enddisp + offset;
+
+ if ((startdisp > x2 || enddisp < x1) == 0) {
float y_chan = (seq->machine - chan_min) / (float)(chan_range) * draw_range;
- float x1_chan = seq->startdisp;
- float x2_chan = seq->enddisp;
+ float x1_chan = startdisp;
+ float x2_chan = enddisp;
float y1_chan, y2_chan;
if ((seqm->flag & SEQ_MUTE) == 0 && (seq->flag & SEQ_MUTE))
@@ -817,23 +834,27 @@ static void draw_seq_strip(const bContext *C, SpaceSeq *sseq, Scene *scene, AReg
glDisable(GL_LINE_STIPPLE);
}
- if (seq->type == SEQ_TYPE_META) {
+ if ((seq->type == SEQ_TYPE_META) ||
+ ((seq->type == SEQ_TYPE_SCENE) && (seq->flag & SEQ_SCENE_STRIPS)))
+ {
drawmeta_contents(scene, seq, x1, y1, x2, y2);
}
-
- /* calculate if seq is long enough to print a name */
- x1 = seq->startdisp + handsize_clamped;
- x2 = seq->enddisp - handsize_clamped;
- /* info text on the strip */
- if (x1 < v2d->cur.xmin) x1 = v2d->cur.xmin;
- else if (x1 > v2d->cur.xmax) x1 = v2d->cur.xmax;
- if (x2 < v2d->cur.xmin) x2 = v2d->cur.xmin;
- else if (x2 > v2d->cur.xmax) x2 = v2d->cur.xmax;
+ if (!(sseq->flag & SEQ_NO_INFO)) {
+ /* calculate if seq is long enough to print a name */
+ x1 = seq->startdisp + handsize_clamped;
+ x2 = seq->enddisp - handsize_clamped;
+
+ /* info text on the strip */
+ if (x1 < v2d->cur.xmin) x1 = v2d->cur.xmin;
+ else if (x1 > v2d->cur.xmax) x1 = v2d->cur.xmax;
+ if (x2 < v2d->cur.xmin) x2 = v2d->cur.xmin;
+ else if (x2 > v2d->cur.xmax) x2 = v2d->cur.xmax;
- /* 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);
+ /* 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);
+ }
}
}
@@ -955,7 +976,7 @@ static ImBuf *sequencer_make_scope(Scene *scene, ImBuf *ibuf, ImBuf *(*make_scop
return scope;
}
-static void sequencer_display_size(Scene *scene, SpaceSeq *sseq, float r_viewrect[2])
+void sequencer_display_size(Scene *scene, SpaceSeq *sseq, float r_viewrect[2])
{
float render_size, proxy_size;
@@ -1051,7 +1072,7 @@ static void sequencer_draw_background(const SpaceSeq *sseq, View2D *v2d, const f
}
}
-void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq, int cfra, int frame_ofs, bool draw_overlay, bool draw_backdrop)
+void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq, int cfra, int frame_ofs, bool draw_overlay, bool draw_overdrop)
{
struct Main *bmain = CTX_data_main(C);
struct ImBuf *ibuf = NULL;
@@ -1070,6 +1091,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq
const bool draw_gpencil = ((sseq->flag & SEQ_SHOW_GPENCIL) && sseq->gpd);
const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
bool draw_metadata = false;
+ rctf metadataframe;
if (G.is_rendering == false && (scene->r.seq_flag & R_SEQ_GL_PREV) == 0) {
/* stop all running jobs, except screen one. currently previews frustrate Render
@@ -1085,7 +1107,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq
}
}
- if ((!draw_overlay || sseq->overlay_type == SEQ_DRAW_OVERLAY_REFERENCE) && !draw_backdrop) {
+ if ((!draw_overlay || sseq->overlay_type == SEQ_DRAW_OVERLAY_REFERENCE) && !draw_overdrop) {
UI_GetThemeColor3fv(TH_SEQ_PREVIEW, col);
glClearColor(col[0], col[1], col[2], 0.0);
glClear(GL_COLOR_BUFFER_BIT);
@@ -1122,7 +1144,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq
sequencer_display_size(scene, sseq, viewrect);
- if (!draw_backdrop && (sseq->mainb != SEQ_DRAW_IMG_IMBUF || sseq->zebra != 0)) {
+ if (!draw_overdrop && (sseq->mainb != SEQ_DRAW_IMG_IMBUF || sseq->zebra != 0)) {
SequencerScopes *scopes = &sseq->scopes;
sequencer_check_scopes(scopes, ibuf);
@@ -1181,8 +1203,26 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq
}
}
- if (!draw_backdrop) {
- sequencer_draw_background(sseq, v2d, viewrect);
+ /* without this colors can flicker from previous opengl state */
+ glColor4ub(255, 255, 255, 255);
+
+ if (!draw_overdrop) {
+ UI_view2d_totRect_set(v2d, viewrect[0] + 0.5f, viewrect[1] + 0.5f);
+ UI_view2d_curRect_validate(v2d);
+
+ /* setting up the view - actual drawing starts here */
+ UI_view2d_view_ortho(v2d);
+
+ /* only draw alpha for main buffer */
+ if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) {
+ if (sseq->flag & SEQ_USE_ALPHA) {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ fdrawcheckerboard(v2d->tot.xmin, v2d->tot.ymin, v2d->tot.xmax, v2d->tot.ymax);
+ glColor4f(1.0, 1.0, 1.0, 1.0);
+ }
+ }
}
if (scope) {
@@ -1274,13 +1314,8 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq
else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, ibuf->x, ibuf->y, 0, format, type, display_buffer);
- if (draw_backdrop) {
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glLoadIdentity();
+ if (draw_overdrop) {
+ UI_view2d_view_restore(C);
}
glBegin(GL_QUADS);
@@ -1304,30 +1339,25 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq
glTexCoord2f(1.0f, 0.0f); glVertex2f(v2d->tot.xmax, v2d->tot.ymin);
}
}
- else if (draw_backdrop) {
- float aspect;
- float image_aspect = viewrect[0] / viewrect[1];
- float imagex, imagey;
+ else if (draw_overdrop) {
+ float imagex = (scene->r.size * scene->r.xsch) / 200.0f * sseq->overdrop_zoom;
+ float imagey = (scene->r.size * scene->r.ysch) / 200.0f * sseq->overdrop_zoom;
+ float xofs = BLI_rcti_size_x(&ar->winrct)/2.0f + sseq->overdrop_offset[0];
+ float yofs = BLI_rcti_size_y(&ar->winrct)/2.0f + sseq->overdrop_offset[1];
- aspect = BLI_rcti_size_x(&ar->winrct) / (float)BLI_rcti_size_y(&ar->winrct);
+ draw_metadata = ((sseq->flag & SEQ_SHOW_METADATA) != 0);
- if (aspect >= image_aspect) {
- imagex = image_aspect / aspect;
- imagey = 1.0f;
- }
- else {
- imagex = 1.0f;
- imagey = aspect / image_aspect;
- }
+ BLI_rctf_init(&metadataframe, -imagex + xofs, imagex + xofs, -imagey + yofs, imagey + yofs);
- glTexCoord2f(0.0f, 0.0f); glVertex2f(-imagex, -imagey);
- glTexCoord2f(0.0f, 1.0f); glVertex2f(-imagex, imagey);
- glTexCoord2f(1.0f, 1.0f); glVertex2f(imagex, imagey);
- glTexCoord2f(1.0f, 0.0f); glVertex2f(imagex, -imagey);
+ glTexCoord2f(0.0f, 0.0f); glVertex2f(-imagex + xofs, -imagey + yofs);
+ glTexCoord2f(0.0f, 1.0f); glVertex2f(-imagex + xofs, imagey + yofs);
+ glTexCoord2f(1.0f, 1.0f); glVertex2f(imagex + xofs, imagey + yofs);
+ glTexCoord2f(1.0f, 0.0f); glVertex2f(imagex + xofs, -imagey + yofs);
}
else {
draw_metadata = ((sseq->flag & SEQ_SHOW_METADATA) != 0);
+ metadataframe = v2d->tot;
glTexCoord2f(0.0f, 0.0f); glVertex2f(v2d->tot.xmin, v2d->tot.ymin);
glTexCoord2f(0.0f, 1.0f); glVertex2f(v2d->tot.xmin, v2d->tot.ymax);
glTexCoord2f(1.0f, 1.0f); glVertex2f(v2d->tot.xmax, v2d->tot.ymax);
@@ -1351,14 +1381,10 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq
IMB_freeImBuf(ibuf);
if (draw_metadata) {
- ED_region_image_metadata_draw(0.0, 0.0, ibuf, v2d->tot, 1.0, 1.0);
+ ED_region_image_metadata_draw(0.0, 0.0, ibuf, metadataframe, 1.0, 1.0);
}
- if (draw_backdrop) {
- glPopMatrix();
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
+ if (draw_overdrop) {
return;
}
@@ -1592,11 +1618,13 @@ void draw_timeline_seq(const bContext *C, ARegion *ar)
// NOTE: the gridlines are currently spaced every 25 frames, which is only fine for 25 fps, but maybe not for 30...
UI_view2d_constant_grid_draw(v2d);
+ /*
if (sseq->draw_flag & SEQ_DRAW_BACKDROP) {
draw_image_seq(C, scene, ar, sseq, scene->r.cfra, 0, false, true);
UI_view2d_view_ortho(v2d);
}
-
+ */
+
ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW);
seq_draw_sfra_efra(scene, v2d);
@@ -1636,12 +1664,22 @@ void draw_timeline_seq(const bContext *C, ARegion *ar)
glEnd();
}
-
+
/* callback */
ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW);
+ if (sseq->draw_flag & SEQ_DRAW_OVERDROP) {
+ draw_image_seq(C, scene, ar, sseq, scene->r.cfra, 0, false, true);
+ UI_SetTheme(SPACE_SEQ, RGN_TYPE_WINDOW);
+ UI_view2d_view_ortho(v2d);
+ }
+
/* reset view matrix */
UI_view2d_view_restore(C);
+
+ /* finally draw any widgets here */
+ WM_widgets_update(C, ar->widgetmaps.first);
+ WM_widgets_draw(C, ar->widgetmaps.first, false);
/* scrollers */
unit = (sseq->flag & SEQ_DRAWFRAMES) ? V2D_UNIT_FRAMES : V2D_UNIT_SECONDS;
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 8030efbf756..74ba107c895 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -1265,6 +1265,7 @@ void SEQUENCER_OT_snap(struct wmOperatorType *ot)
RNA_def_int(ot->srna, "frame", 0, INT_MIN, INT_MAX, "Frame", "Frame where selected strips will be snapped", INT_MIN, INT_MAX);
}
+
typedef struct SlipData {
int init_mouse[2];
float init_mouseloc[2];
@@ -2476,7 +2477,16 @@ static int sequencer_meta_toggle_exec(bContext *C, wmOperator *UNUSED(op))
ed->seqbasep = &last_seq->seqbase;
BKE_sequencer_active_set(scene, NULL);
+ }
+ /* scene strip */
+ else if (last_seq && last_seq->type == SEQ_TYPE_SCENE && last_seq->scene &&
+ (last_seq->flag & SEQ_SCENE_STRIPS) && (last_seq->flag & SELECT))
+ {
+ ED_screen_set_scene(C, CTX_wm_screen(C), last_seq->scene);
+
+ WM_event_add_notifier(C, NC_SCENE | ND_SCENEBROWSE, last_seq->scene);
+ return OPERATOR_FINISHED;
}
else {
/* Exit Metastrip (if possible) */
diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index eaac43095e8..c9e6a70c410 100644
--- a/source/blender/editors/space_sequencer/sequencer_intern.h
+++ b/source/blender/editors/space_sequencer/sequencer_intern.h
@@ -58,6 +58,7 @@ void draw_image_seq(const struct bContext *C, struct Scene *scene, struct ARegi
void color3ubv_from_seq(struct Scene *curscene, struct Sequence *seq, unsigned char col[3]);
void draw_shadedstrip(struct Sequence *seq, unsigned char col[3], float x1, float y1, float x2, float y2);
void draw_sequence_extensions(struct Scene *scene, struct ARegion *ar, struct Sequence *seq);
+void sequencer_display_size(struct Scene *scene, struct SpaceSeq *sseq, float r_viewrect[2]);
void sequencer_special_update_set(Sequence *seq);
@@ -134,6 +135,9 @@ void SEQUENCER_OT_paste(struct wmOperatorType *ot);
void SEQUENCER_OT_rebuild_proxy(struct wmOperatorType *ot);
void SEQUENCER_OT_enable_proxies(struct wmOperatorType *ot);
+void SEQUENCER_OT_overdrop_transform(struct wmOperatorType *ot);
+void SEQUENCER_OT_image_transform_widget(struct wmOperatorType *ot);
+
/* preview specific operators */
void SEQUENCER_OT_view_all_preview(struct wmOperatorType *ot);
@@ -157,6 +161,7 @@ void SEQUENCER_OT_movieclip_strip_add(struct wmOperatorType *ot);
void SEQUENCER_OT_mask_strip_add(struct wmOperatorType *ot);
void SEQUENCER_OT_sound_strip_add(struct wmOperatorType *ot);
void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot);
+void SEQUENCER_OT_image_sequence_add(struct wmOperatorType *ot);
void SEQUENCER_OT_effect_strip_add(struct wmOperatorType *ot);
enum {
diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c
index dadd187f36c..f89c8fc351e 100644
--- a/source/blender/editors/space_sequencer/sequencer_ops.c
+++ b/source/blender/editors/space_sequencer/sequencer_ops.c
@@ -112,6 +112,7 @@ void sequencer_operatortypes(void)
WM_operatortype_append(SEQUENCER_OT_movie_strip_add);
WM_operatortype_append(SEQUENCER_OT_sound_strip_add);
WM_operatortype_append(SEQUENCER_OT_image_strip_add);
+ WM_operatortype_append(SEQUENCER_OT_image_sequence_add);
WM_operatortype_append(SEQUENCER_OT_effect_strip_add);
/* sequencer_buttons.c */
@@ -125,6 +126,8 @@ void sequencer_operatortypes(void)
/* sequencer_view.h */
WM_operatortype_append(SEQUENCER_OT_sample);
+ WM_operatortype_append(SEQUENCER_OT_overdrop_transform);
+ WM_operatortype_append(SEQUENCER_OT_image_transform_widget);
}
@@ -200,6 +203,8 @@ void sequencer_keymap(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "SEQUENCER_OT_view_all", HOMEKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "SEQUENCER_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "SEQUENCER_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0);
+
+ WM_keymap_add_item(keymap, "SEQUENCER_OT_overdrop_transform", VKEY, KM_PRESS, 0, 0);
kmi = WM_keymap_add_item(keymap, "SEQUENCER_OT_strip_jump", PAGEUPKEY, KM_PRESS, 0, 0);
RNA_boolean_set(kmi->ptr, "next", true);
@@ -343,6 +348,8 @@ void sequencer_keymap(wmKeyConfig *keyconf)
/* would prefer to use numpad keys for job */
RNA_float_set(WM_keymap_add_item(keymap, "SEQUENCER_OT_view_zoom_ratio", PAD1, KM_PRESS, 0, 0)->ptr, "ratio", 1.0f);
+// WM_keymap_add_item(keymap, "SEQUENCER_OT_image_transform_widget", VKEY, KM_PRESS, 0, 0);
+
/* Setting zoom levels is not that useful, except for back to zoom level 1, removing keymap because of conflicts for now */
#if 0
RNA_float_set(WM_keymap_add_item(keymap, "SEQUENCER_OT_view_zoom_ratio", PAD8, KM_PRESS, KM_SHIFT, 0)->ptr, "ratio", 8.0f);
diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c
index 4d6ea865b40..a0f0c80883d 100644
--- a/source/blender/editors/space_sequencer/sequencer_view.c
+++ b/source/blender/editors/space_sequencer/sequencer_view.c
@@ -33,8 +33,10 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BLI_rect.h"
#include "DNA_scene_types.h"
+#include "DNA_widget_types.h"
#include "BKE_context.h"
#include "BKE_main.h"
@@ -47,6 +49,7 @@
#include "ED_image.h"
#include "ED_screen.h"
#include "ED_space_api.h"
+#include "ED_sequencer.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@@ -54,6 +57,8 @@
#include "UI_view2d.h"
+#include "RNA_define.h"
+
/* own include */
#include "sequencer_intern.h"
@@ -241,3 +246,333 @@ void SEQUENCER_OT_sample(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_BLOCKING;
}
+
+/******** Backdrop Transform *******/
+
+typedef struct OverDropTransformData {
+ ImBuf *ibuf; /* image to be transformed (preview image transformation widget) */
+ int init_size[2];
+ float init_zoom;
+ float init_offset[2];
+ int event_type;
+ wmWidgetGroupType *cagetype;
+} OverDropTransformData;
+
+static int sequencer_overdrop_transform_poll(bContext *C)
+{
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
+ ARegion *ar = CTX_wm_region(C);
+
+ return (sseq && ar && ar->type->regionid == RGN_TYPE_WINDOW && (sseq->draw_flag & SEQ_DRAW_OVERDROP));
+}
+
+static void widgetgroup_overdrop_draw(const struct bContext *C, struct wmWidgetGroup *wgroup)
+{
+ ARegion *ar = CTX_wm_region(C);
+ wmOperator *op = wgroup->type->op;
+ Scene *sce = CTX_data_scene(C);
+ int sizex = (sce->r.size * sce->r.xsch) / 100;
+ int sizey = (sce->r.size * sce->r.ysch) / 100;
+ float origin[3];
+
+ wmWidget *cage = WIDGET_rect_transform_new(wgroup, WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM |
+ WIDGET_RECT_TRANSFORM_STYLE_TRANSLATE, sizex, sizey);
+ WM_widget_property(cage, RECT_TRANSFORM_SLOT_OFFSET, op->ptr, "offset");
+ WM_widget_property(cage, RECT_TRANSFORM_SLOT_SCALE, op->ptr, "scale");
+
+ origin[0] = BLI_rcti_size_x(&ar->winrct)/2.0f;
+ origin[1] = BLI_rcti_size_y(&ar->winrct)/2.0f;
+
+ WM_widget_set_origin(cage, origin);
+}
+
+static int sequencer_overdrop_transform_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
+ /* no poll, lives always for the duration of the operator */
+ wmWidgetGroupType *cagetype = WM_widgetgrouptype_new(NULL, widgetgroup_overdrop_draw, CTX_data_main(C), "Seq_Canvas", SPACE_SEQ, RGN_TYPE_WINDOW, false);
+ struct wmEventHandler *handler = WM_event_add_modal_handler(C, op);
+ OverDropTransformData *data = MEM_mallocN(sizeof(OverDropTransformData), "overdrop transform data");
+ WM_modal_handler_attach_widgetgroup(C, handler, cagetype, op);
+
+ RNA_float_set_array(op->ptr, "offset", sseq->overdrop_offset);
+ RNA_float_set(op->ptr, "scale", sseq->overdrop_zoom);
+
+ copy_v2_v2(data->init_offset, sseq->overdrop_offset);
+ data->init_zoom = sseq->overdrop_zoom;
+ data->cagetype = cagetype;
+ data->event_type = event->type;
+
+ op->customdata = data;
+
+ ED_area_headerprint(sa, "Drag to place, and scale, Space/Enter/Caller key to confirm, R to recenter, RClick/Esc to cancel");
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void sequencer_overdrop_finish(bContext *C, OverDropTransformData *data)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ ED_area_headerprint(sa, NULL);
+ WM_widgetgrouptype_unregister(C, CTX_data_main(C), data->cagetype);
+ MEM_freeN(data);
+}
+
+static void sequencer_overdrop_cancel(struct bContext *C, struct wmOperator *op)
+{
+ OverDropTransformData *data = op->customdata;
+ sequencer_overdrop_finish(C, data);
+}
+
+static int sequencer_overdrop_transform_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ OverDropTransformData *data = op->customdata;
+
+ if (event->type == data->event_type && event->val == KM_PRESS) {
+ sequencer_overdrop_finish(C, data);
+ return OPERATOR_FINISHED;
+ }
+
+ switch (event->type) {
+ case EVT_WIDGET_UPDATE: {
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
+ RNA_float_get_array(op->ptr, "offset", sseq->overdrop_offset);
+ sseq->overdrop_zoom = RNA_float_get(op->ptr, "scale");
+ break;
+ }
+
+ case RKEY:
+ {
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
+ ARegion *ar = CTX_wm_region(C);
+ float zero[2] = {0.0f};
+ RNA_float_set_array(op->ptr, "offset", zero);
+ RNA_float_set(op->ptr, "scale", 1.0f);
+ copy_v2_v2(sseq->overdrop_offset, zero);
+ sseq->overdrop_zoom = 1.0;
+ ED_region_tag_redraw(ar);
+ /* add a mousemove to refresh the widget */
+ WM_event_add_mousemove(C);
+ break;
+ }
+ case RETKEY:
+ case PADENTER:
+ case SPACEKEY:
+ {
+ sequencer_overdrop_finish(C, data);
+ return OPERATOR_FINISHED;
+ }
+
+ case ESCKEY:
+ case RIGHTMOUSE:
+ {
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
+ copy_v2_v2(sseq->overdrop_offset, data->init_offset);
+ sseq->overdrop_zoom = data->init_zoom;
+
+ sequencer_overdrop_finish(C, data);
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void SEQUENCER_OT_overdrop_transform(struct wmOperatorType *ot)
+{
+ float default_offset[2] = {0.0f, 0.0f};
+
+ /* identifiers */
+ ot->name = "Change Data/Files";
+ ot->idname = "SEQUENCER_OT_overdrop_transform";
+ ot->description = "";
+
+ /* api callbacks */
+ ot->invoke = sequencer_overdrop_transform_invoke;
+ ot->modal = sequencer_overdrop_transform_modal;
+ ot->poll = sequencer_overdrop_transform_poll;
+ ot->cancel = sequencer_overdrop_cancel;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_float_array(ot->srna, "offset", 2, default_offset, FLT_MIN, FLT_MAX, "Offset", "Offset of the backdrop", FLT_MIN, FLT_MAX);
+ RNA_def_float(ot->srna, "scale", 1.0f, 0.0f, FLT_MAX, "Scale", "Scale of the backdrop", 0.0f, FLT_MAX);
+}
+
+/******** transform widget (preview area) *******/
+
+typedef struct ImageTransformData {
+ ImBuf *ibuf; /* image to be transformed (preview image transformation widget) */
+ int init_size[2];
+ int event_type;
+ wmWidgetGroupType *cagetype;
+} ImageTransformData;
+
+static int sequencer_image_transform_widget_poll(bContext *C)
+{
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
+ ARegion *ar = CTX_wm_region(C);
+
+ return (sseq && ar && ar->type->regionid == RGN_TYPE_PREVIEW);
+}
+
+static void widgetgroup_image_transform_draw(const struct bContext *C, struct wmWidgetGroup *wgroup)
+{
+ ARegion *ar = CTX_wm_region(C);
+ View2D *v2d = &ar->v2d;
+ wmOperator *op = wgroup->type->op;
+ wmWidget *cage;
+ float origin[3];
+ float viewrect[2];
+ float scale[2];
+
+ sequencer_display_size(CTX_data_scene(C), CTX_wm_space_seq(C), viewrect);
+ UI_view2d_scale_get(v2d, &scale[0], &scale[1]);
+
+ cage = WIDGET_rect_transform_new(wgroup, WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM |
+ WIDGET_RECT_TRANSFORM_STYLE_TRANSLATE,
+ viewrect[0] * scale[0], viewrect[1] * scale[1]);
+ WM_widget_property(cage, RECT_TRANSFORM_SLOT_SCALE, op->ptr, "scale");
+
+ origin[0] = -(v2d->cur.xmin * scale[0]);
+ origin[1] = -(v2d->cur.ymin * scale[1]);
+ WM_widget_set_origin(cage, origin);
+}
+
+static int sequencer_image_transform_widget_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
+ Scene *scene = CTX_data_scene(C);
+ /* no poll, lives always for the duration of the operator */
+ wmWidgetGroupType *cagetype = WM_widgetgrouptype_new(NULL, widgetgroup_image_transform_draw, CTX_data_main(C),
+ "Seq_Canvas", SPACE_SEQ, RGN_TYPE_PREVIEW, false);
+ struct wmEventHandler *handler = WM_event_add_modal_handler(C, op);
+ ImageTransformData *data = MEM_mallocN(sizeof(ImageTransformData), "overdrop transform data");
+ ImBuf *ibuf = sequencer_ibuf_get(CTX_data_main(C), scene, sseq, CFRA, 0, NULL);
+
+ if (!ibuf || !ED_space_sequencer_check_show_imbuf(sseq)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ WM_modal_handler_attach_widgetgroup(C, handler, cagetype, op);
+
+ copy_v2_v2_int(data->init_size, &ibuf->x);
+ data->cagetype = cagetype;
+ data->event_type = event->type;
+ data->ibuf = ibuf;
+
+ op->customdata = data;
+
+ ED_area_headerprint(sa, "Drag to place, and scale, Space/Enter/Caller key to confirm, R to recenter, RClick/Esc to cancel");
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void sequencer_image_transform_widget_finish(bContext *C, ImageTransformData *data)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ ED_area_headerprint(sa, NULL);
+ WM_widgetgrouptype_unregister(C, CTX_data_main(C), data->cagetype);
+ MEM_freeN(data);
+}
+
+static void sequencer_image_transform_widget_cancel(struct bContext *C, struct wmOperator *op)
+{
+ ImageTransformData *data = op->customdata;
+ sequencer_image_transform_widget_finish(C, data);
+}
+
+static int sequencer_image_transform_widget_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ImageTransformData *data = op->customdata;
+
+ if (event->type == data->event_type && event->val == KM_PRESS) {
+ sequencer_image_transform_widget_finish(C, data);
+ return OPERATOR_FINISHED;
+ }
+
+ switch (event->type) {
+ case EVT_WIDGET_UPDATE:
+ {
+ ARegion *ar = CTX_wm_region(C);
+ Scene *scene = CTX_data_scene(C);
+ wmWidgetMap *wmap = ar->widgetmaps.first;
+ float scale_fac = RNA_float_get(op->ptr, "scale");
+ float new_size[2];
+ float offset[2];
+
+ new_size[0] = (float)data->init_size[0] * scale_fac;
+ new_size[1] = (float)data->init_size[1] * scale_fac;
+
+ /* sale image */
+ IMB_scalefastImBuf(data->ibuf, (unsigned int)new_size[0], (unsigned int)new_size[1]);
+
+ /* update view */
+ scene->r.xsch = (int)(new_size[0] / ((float)scene->r.size / 100));
+ scene->r.ysch = (int)(new_size[1] / ((float)scene->r.size / 100));
+
+ /* no offset needed in this case */
+ offset[0] = offset[1] = 0;
+ WIDGET_rect_transform_set_offset(wmap->active_widget, offset);
+ break;
+ }
+
+ case RKEY:
+ {
+// SpaceSeq *sseq = CTX_wm_space_seq(C);
+ ARegion *ar = CTX_wm_region(C);
+// float zero[2] = {0.0f};
+// RNA_float_set_array(op->ptr, "offset", zero);
+// RNA_float_set(op->ptr, "scale", 1.0f);
+// copy_v2_v2(sseq->overdrop_offset, zero);
+// sseq->overdrop_zoom = 1.0;
+ ED_region_tag_redraw(ar);
+ /* add a mousemove to refresh the widget */
+ WM_event_add_mousemove(C);
+ break;
+ }
+ case RETKEY:
+ case PADENTER:
+ case SPACEKEY:
+ {
+ sequencer_image_transform_widget_finish(C, data);
+ return OPERATOR_FINISHED;
+ }
+
+ case ESCKEY:
+ case RIGHTMOUSE:
+ {
+// SpaceSeq *sseq = CTX_wm_space_seq(C);
+// copy_v2_v2(sseq->overdrop_offset, data->init_offset);
+// sseq->overdrop_zoom = data->init_zoom;
+
+ sequencer_image_transform_widget_finish(C, data);
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void SEQUENCER_OT_image_transform_widget(struct wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Image Transform";
+ ot->idname = "SEQUENCER_OT_image_transform_widget";
+ ot->description = "Transform the image using a widget";
+
+ /* api callbacks */
+ ot->invoke = sequencer_image_transform_widget_invoke;
+ ot->modal = sequencer_image_transform_widget_modal;
+ ot->poll = sequencer_image_transform_widget_poll;
+ ot->cancel = sequencer_image_transform_widget_cancel;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_float(ot->srna, "scale", 1.0f, 0.0f, FLT_MAX, "Scale", "Scale of the backdrop", 0.0f, FLT_MAX);
+}
+
diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c
index 769718e1567..92a16ce6b7b 100644
--- a/source/blender/editors/space_sequencer/space_sequencer.c
+++ b/source/blender/editors/space_sequencer/space_sequencer.c
@@ -121,6 +121,9 @@ static SpaceLink *sequencer_new(const bContext *C)
sseq->mainb = SEQ_DRAW_IMG_IMBUF;
sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA;
+ /* backdrop */
+ sseq->overdrop_zoom = 1.0f;
+
/* header */
ar = MEM_callocN(sizeof(ARegion), "header for sequencer");
@@ -166,7 +169,7 @@ static SpaceLink *sequencer_new(const bContext *C)
BLI_addtail(&sseq->regionbase, ar);
ar->regiontype = RGN_TYPE_WINDOW;
-
+
/* seq space goes from (0,8) to (0, efra) */
ar->v2d.tot.xmin = 0.0f;
@@ -180,7 +183,7 @@ static SpaceLink *sequencer_new(const bContext *C)
ar->v2d.min[1] = 0.5f;
ar->v2d.max[0] = MAXFRAMEF;
- ar->v2d.max[1] = MAXSEQ;
+ ar->v2d.max[1] = MAXSEQ * 4;
ar->v2d.minzoom = 0.01f;
ar->v2d.maxzoom = 100.0f;
@@ -478,8 +481,13 @@ static void sequencer_main_area_init(wmWindowManager *wm, ARegion *ar)
/* add drop boxes */
lb = WM_dropboxmap_find("Sequencer", SPACE_SEQ, RGN_TYPE_WINDOW);
-
+
WM_event_add_dropbox_handler(&ar->handlers, lb);
+
+ /* no modal keymap here, only operators use this currently */
+ if (BLI_listbase_is_empty(&ar->widgetmaps)) {
+ BLI_addhead(&ar->widgetmaps, WM_widgetmap_from_type("Seq_Canvas", SPACE_SEQ, RGN_TYPE_WINDOW, false));
+ }
}
static void sequencer_main_area_draw(const bContext *C, ARegion *ar)
@@ -556,6 +564,10 @@ static void sequencer_preview_area_init(wmWindowManager *wm, ARegion *ar)
/* own keymap */
keymap = WM_keymap_find(wm->defaultconf, "SequencerPreview", SPACE_SEQ, 0);
WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct);
+
+ if (BLI_listbase_is_empty(&ar->widgetmaps)) {
+ BLI_addhead(&ar->widgetmaps, WM_widgetmap_from_type("Seq_Canvas", SPACE_SEQ, RGN_TYPE_PREVIEW, false));
+ }
}
static void sequencer_preview_area_draw(const bContext *C, ARegion *ar)
@@ -592,6 +604,9 @@ static void sequencer_preview_area_draw(const bContext *C, ARegion *ar)
ED_region_visible_rect(ar, &rect);
ED_scene_draw_fps(scene, &rect);
}
+
+ WM_widgets_update(C, ar->widgetmaps.first);
+ WM_widgets_draw(C, ar->widgetmaps.first, false);
}
static void sequencer_preview_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn)
@@ -687,8 +702,18 @@ static void sequencer_buttons_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED
break;
}
}
+
/* ************************************* */
+static void sequencer_widgets(void)
+{
+ /* create the widgetmap for the area here */
+ WM_widgetmaptype_find("Seq_Canvas", SPACE_SEQ, RGN_TYPE_WINDOW, false, true);
+
+ WM_widgetmaptype_find("Seq_Canvas", SPACE_SEQ, RGN_TYPE_PREVIEW, false, true);
+}
+
+
/* only called once, from space/spacetypes.c */
void ED_spacetype_sequencer(void)
{
@@ -708,6 +733,7 @@ void ED_spacetype_sequencer(void)
st->dropboxes = sequencer_dropboxes;
st->refresh = sequencer_refresh;
st->listener = sequencer_listener;
+ st->widgets = sequencer_widgets;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype sequencer region");
diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt
index d8d29d63686..9879475e50d 100644
--- a/source/blender/editors/space_view3d/CMakeLists.txt
+++ b/source/blender/editors/space_view3d/CMakeLists.txt
@@ -46,6 +46,7 @@ set(SRC
drawmesh.c
drawobject.c
drawsimdebug.c
+ drawstrands.c
drawvolume.c
space_view3d.c
view3d_buttons.c
@@ -89,4 +90,11 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_OPENVDB)
+ add_definitions(-DWITH_OPENVDB)
+ list(APPEND INC
+ ../../../../intern/openvdb
+ )
+endif()
+
blender_add_lib(bf_editor_space_view3d "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/editors/space_view3d/SConscript b/source/blender/editors/space_view3d/SConscript
index 78f24948070..d5e36be8c38 100644
--- a/source/blender/editors/space_view3d/SConscript
+++ b/source/blender/editors/space_view3d/SConscript
@@ -66,4 +66,8 @@ if env['WITH_BF_INTERNATIONAL']:
if env['WITH_BF_FREESTYLE']:
defs.append('WITH_FREESTYLE')
+if env['WITH_BF_OPENVDB']:
+ incs.append('#intern/openvdb')
+ defs.append('WITH_OPENVDB')
+
env.BlenderLib ( 'bf_editors_space_view3d', sources, incs, defines = defs, libtype=['core'], priority=[40] )
diff --git a/source/blender/editors/space_view3d/drawarmature.c b/source/blender/editors/space_view3d/drawarmature.c
index f33f074e6c5..863a2d64646 100644
--- a/source/blender/editors/space_view3d/drawarmature.c
+++ b/source/blender/editors/space_view3d/drawarmature.c
@@ -1739,6 +1739,8 @@ static void draw_pose_bones(Scene *scene, View3D *v3d, ARegion *ar, Base *base,
{
if (bone->layer & arm->layer) {
const bool use_custom = (pchan->custom) && !(arm->flag & ARM_NO_CUSTOM);
+
+ /* skip drawing in that case */
glPushMatrix();
if (use_custom && pchan->custom_tx) {
@@ -2305,7 +2307,7 @@ static void draw_ebones(View3D *v3d, ARegion *ar, Object *ob, const short dt)
/* draw bone paths
* - in view space
*/
-static void draw_pose_paths(Scene *scene, View3D *v3d, ARegion *ar, Object *ob)
+void draw_pose_paths(Scene *scene, View3D *v3d, ARegion *ar, Object *ob)
{
bAnimVizSettings *avs = &ob->pose->avs;
bArmature *arm = ob->data;
@@ -2313,7 +2315,7 @@ static void draw_pose_paths(Scene *scene, View3D *v3d, ARegion *ar, Object *ob)
/* setup drawing environment for paths */
draw_motion_paths_init(v3d, ar);
-
+
/* draw paths where they exist and they releated bone is visible */
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
if ((pchan->bone->layer & arm->layer) && (pchan->mpath))
@@ -2606,7 +2608,7 @@ bool draw_armature(Scene *scene, View3D *v3d, ARegion *ar, Base *base,
if (v3d->flag2 & V3D_RENDER_OVERRIDE)
return true;
-
+
if (dt > OB_WIRE && !ELEM(arm->drawtype, ARM_LINE, ARM_WIRE)) {
/* we use color for solid lighting */
const float white[4] = {1.0f, 1.0f, 1.0f, 1.0f};
@@ -2658,7 +2660,6 @@ bool draw_armature(Scene *scene, View3D *v3d, ARegion *ar, Base *base,
if (ob == modifiers_isDeformedByArmature(OBACT))
arm->flag |= ARM_POSEMODE;
}
- draw_pose_paths(scene, v3d, ar, ob);
}
}
}
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index 8cca1c8b1ef..4636fd5d968 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -29,6 +29,7 @@
#include "MEM_guardedalloc.h"
+#include "DNA_armature_types.h"
#include "DNA_camera_types.h"
#include "DNA_curve_types.h"
#include "DNA_constraint_types.h" /* for drawing constraint */
@@ -48,6 +49,7 @@
#include "BLI_string.h"
#include "BLI_math.h"
#include "BLI_memarena.h"
+#include "BLI_rand.h"
#include "BKE_anim.h" /* for the where_on_path function */
#include "BKE_armature.h"
@@ -58,6 +60,7 @@
#include "BKE_DerivedMesh.h"
#include "BKE_deform.h"
#include "BKE_displist.h"
+#include "BKE_editstrands.h"
#include "BKE_font.h"
#include "BKE_global.h"
#include "BKE_image.h"
@@ -88,6 +91,7 @@
#include "GPU_draw.h"
#include "GPU_extensions.h"
#include "GPU_select.h"
+#include "GPU_buffers.h"
#include "ED_mesh.h"
#include "ED_particle.h"
@@ -101,6 +105,10 @@
#include "WM_api.h"
#include "BLF_api.h"
+#ifdef WITH_OPENVDB
+# include "openvdb_capi.h"
+#endif
+
#include "view3d_intern.h" /* bad level include */
/* Workaround for sequencer scene render mode.
@@ -311,9 +319,6 @@ bool draw_glsl_material(Scene *scene, Object *ob, View3D *v3d, const char dt)
static bool check_alpha_pass(Base *base)
{
- if (base->flag & OB_FROMDUPLI)
- return false;
-
if (G.f & G_PICKSEL)
return false;
@@ -3973,7 +3978,7 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d,
static void draw_mesh_object_outline(View3D *v3d, Object *ob, DerivedMesh *dm)
{
if ((v3d->transp == false) && /* not when we draw the transparent pass */
- (ob->mode & OB_MODE_ALL_PAINT) == false) /* not when painting (its distracting) - campbell */
+ (ob->mode & OB_MODE_ALL_BRUSH) == false) /* not when painting (its distracting) - campbell */
{
glLineWidth(UI_GetThemeValuef(TH_OUTLINE_WIDTH) * 2.0f);
glDepthMask(0);
@@ -4345,21 +4350,29 @@ static bool draw_mesh_object(Scene *scene, ARegion *ar, View3D *v3d, RegionView3
}
draw_mesh_fancy(scene, ar, v3d, rv3d, base, dt, ob_wire_col, dflag);
-
+
GPU_end_object_materials();
if (me->totvert == 0) retval = true;
}
}
- if ((dflag & DRAW_PICKING) == 0 && (base->flag & OB_FROMDUPLI) == 0 && (v3d->flag2 & V3D_RENDER_SHADOW) == 0) {
+ if ((dflag & DRAW_PICKING) == 0 && ((base->flag & OB_FROMDUPLI) == 0 || (ob->dtx & OB_DRAWTRANSP)) && (v3d->flag2 & V3D_RENDER_SHADOW) == 0) {
/* GPU_begin_object_materials checked if this is needed */
if (do_alpha_after) {
- if (ob->dtx & OB_DRAWXRAY) {
+ /* duplis only for transparency */
+ if ((ob->dtx & OB_DRAWXRAY) && !(base->flag & OB_FROMDUPLI)) {
ED_view3d_after_add(&v3d->afterdraw_xraytransp, base, dflag);
}
else {
- ED_view3d_after_add(&v3d->afterdraw_transp, base, dflag);
+ if (base->flag & OB_FROMDUPLI) {
+ Base *nbase = MEM_mallocN(sizeof(Base), "dupli_trans");
+ *nbase = *base;
+ ED_view3d_after_add(&v3d->afterdraw_transp, nbase, dflag);
+ }
+ else {
+ ED_view3d_after_add(&v3d->afterdraw_transp, base, dflag);
+ }
}
}
else if (ob->dtx & OB_DRAWXRAY && ob->dtx & OB_DRAWTRANSP) {
@@ -4822,6 +4835,377 @@ static bool drawDispList(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *ba
}
/* *********** drawing for particles ************* */
+
+BLI_INLINE unsigned int hash_int_2d(unsigned int kx, unsigned int ky)
+{
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+ unsigned int a, b, c;
+
+ a = b = c = 0xdeadbeef + (2 << 2) + 13;
+ a += kx;
+ b += ky;
+
+ c ^= b; c -= rot(b,14);
+ a ^= c; a -= rot(c,11);
+ b ^= a; b -= rot(a,25);
+ c ^= b; c -= rot(b,16);
+ a ^= c; a -= rot(c,4);
+ b ^= a; b -= rot(a,14);
+ c ^= b; c -= rot(b,24);
+
+ return c;
+
+#undef rot
+}
+
+BLI_INLINE unsigned int hash_int(unsigned int k)
+{
+ return hash_int_2d(k, 0);
+}
+
+static void particle_path_color(int index, float col[3])
+{
+ unsigned seed = hash_int(index);
+
+ BLI_srandom(seed);
+ hsv_to_rgb(BLI_frand(), 1.0f, 1.0f, col+0, col+1, col+2);
+}
+
+#ifdef USE_PARTICLE_HULL_DRAWING
+static void draw_particle_hull_section(ParticleCacheKey *path, ParticleCacheKey *npath)
+{
+ int segments = max_ii(path->segments, npath->segments);
+ int k;
+
+ for (k = 0; k < segments; ++k) {
+ int k0 = max_ii(k-1, 0);
+ int k1 = k;
+ int k2 = k+1;
+ int k3 = min_ii(k+2, path->segments);
+ int nk0 = max_ii(min_ii(k-1, npath->segments), 0);
+ int nk1 = min_ii(k, npath->segments);
+ int nk2 = min_ii(k+1, npath->segments);
+ int nk3 = min_ii(k+2, npath->segments);
+ float *co[2][4];
+
+ float nor01[3], nor11[3], nor02[3], nor12[3];
+
+ co[0][0] = path[k0].co;
+ co[0][1] = path[k1].co;
+ co[0][2] = path[k2].co;
+ co[0][3] = path[k3].co;
+ co[1][0] = npath[nk0].co;
+ co[1][1] = npath[nk1].co;
+ co[1][2] = npath[nk2].co;
+ co[1][3] = npath[nk3].co;
+
+ normal_quad_v3(nor01, co[0][1], co[0][0], co[1][1], co[0][2]);
+ normal_quad_v3(nor02, co[0][2], co[0][1], co[1][2], co[0][3]);
+ normal_quad_v3(nor11, co[1][1], co[1][2], co[0][1], co[1][0]);
+ normal_quad_v3(nor12, co[1][2], co[1][3], co[0][2], co[1][1]);
+
+ glNormal3fv(nor01);
+ glVertex3fv(path[k1].co);
+ glNormal3fv(nor11);
+ glVertex3fv(npath[nk1].co);
+ glNormal3fv(nor12);
+ glVertex3fv(npath[nk2].co);
+ glNormal3fv(nor02);
+ glVertex3fv(path[k2].co);
+ }
+}
+
+static void draw_particle_hull_cap(ParticleCacheKey *a0, ParticleCacheKey *a1, ParticleCacheKey *a2, ParticleCacheKey *a3,
+ ParticleCacheKey *b0, ParticleCacheKey *b1, ParticleCacheKey *b2, ParticleCacheKey *b3)
+{
+ float *ca[4], *cb[4];
+
+ float na[4][3], nb[4][3];
+
+ if (a1 == b1) {
+ ca[1] = cb[1] = a1[a1->segments].co;
+ ca[2] = a2[a2->segments].co;
+ cb[2] = b2[b2->segments].co;
+ ca[3] = a3[a3->segments].co;
+ cb[3] = b3[b3->segments].co;
+
+ normal_tri_v3(na[1], ca[1], cb[2], ca[2]);
+ normal_quad_v3(na[2], ca[2], ca[1], cb[2], ca[3]);
+ normal_quad_v3(nb[2], cb[2], cb[3], ca[2], cb[1]);
+
+ glNormal3fv(na[2]);
+ glVertex3fv(ca[2]);
+
+ glNormal3fv(na[1]);
+ glVertex3fv(ca[1]);
+
+ glNormal3fv(nb[2]);
+ glVertex3fv(cb[2]);
+ }
+ else if (a2 == b2) {
+ ca[0] = a0[a0->segments].co;
+ cb[0] = b0[b0->segments].co;
+ ca[1] = a1[a1->segments].co;
+ cb[1] = b1[b1->segments].co;
+ ca[2] = cb[2] = a2[a2->segments].co;
+
+ normal_quad_v3(na[1], ca[1], ca[0], cb[1], ca[2]);
+ normal_quad_v3(nb[1], cb[1], cb[2], ca[1], cb[0]);
+ normal_tri_v3(na[2], ca[2], ca[1], cb[1]);
+
+ glNormal3fv(na[1]);
+ glVertex3fv(ca[1]);
+
+ glNormal3fv(nb[1]);
+ glVertex3fv(cb[1]);
+
+ glNormal3fv(nb[2]);
+ glVertex3fv(cb[2]);
+ }
+ else {
+ ca[0] = a0[a0->segments].co;
+ cb[0] = b0[b0->segments].co;
+ ca[1] = a1[a1->segments].co;
+ cb[1] = b1[b1->segments].co;
+ ca[2] = a2[a2->segments].co;
+ cb[2] = b2[b2->segments].co;
+ ca[3] = a3[a3->segments].co;
+ cb[3] = b3[b3->segments].co;
+
+ normal_quad_v3(na[1], ca[1], ca[0], cb[1], ca[2]);
+ normal_quad_v3(na[2], ca[2], ca[1], cb[2], ca[3]);
+ normal_quad_v3(nb[1], cb[1], cb[2], ca[1], cb[0]);
+ normal_quad_v3(nb[2], cb[2], cb[3], ca[2], cb[1]);
+
+ glNormal3fv(na[1]);
+ glVertex3fv(ca[1]);
+
+ glNormal3fv(nb[1]);
+ glVertex3fv(cb[1]);
+
+ glNormal3fv(nb[2]);
+ glVertex3fv(cb[2]);
+
+ glNormal3fv(na[2]);
+ glVertex3fv(ca[2]);
+
+ glNormal3fv(na[1]);
+ glVertex3fv(ca[1]);
+
+ glNormal3fv(nb[2]);
+ glVertex3fv(cb[2]);
+ }
+}
+
+BLI_INLINE bool particle_path_valid(ParticleCacheKey **cache, int p)
+{
+ return (cache[p]->segments >= 0 && cache[p]->hull_parent >= 0);
+}
+
+BLI_INLINE int particle_path_next(ParticleCacheKey **cache, int pmax, int p)
+{
+ do {
+ ++p;
+ if (p >= pmax)
+ break;
+ if (particle_path_valid(cache, p))
+ break;
+ } while (true);
+
+ return p;
+}
+
+BLI_INLINE int particle_path_prev(ParticleCacheKey **cache, int pmin, int p)
+{
+ do {
+ --p;
+ if (p < pmin)
+ break;
+ if (particle_path_valid(cache, p))
+ break;
+ } while (true);
+
+ return p;
+}
+
+static void draw_particle_hair_hull(Scene *UNUSED(scene), View3D *v3d, RegionView3D *rv3d,
+ Base *base, ParticleSystem *psys,
+ const char UNUSED(ob_dt), const short dflag)
+{
+ Object *ob = base->object;
+ ParticleSettings *part = psys->part;
+ Material *ma = give_current_material(ob, part->omat);
+ unsigned char tcol[4] = {0, 0, 0, 255};
+ GLint polygonmode[2];
+ int totchild;
+
+ bool draw_constcolor = dflag & DRAW_CONSTCOLOR;
+
+ ParticleCacheKey **cache;
+
+ if (part->type == PART_HAIR && !psys->childcache)
+ totchild = 0;
+ else
+ totchild = psys->totchild * part->disp / 100;
+
+ if (v3d->zbuf)
+ glDepthMask(true);
+
+ glGetIntegerv(GL_POLYGON_MODE, polygonmode);
+
+ cache = psys->childcache;
+
+ switch (part->draw_col) {
+ case PART_DRAW_COL_NONE:
+ draw_constcolor = true;
+ break;
+ case PART_DRAW_COL_MAT:
+ if (ma)
+ rgb_float_to_uchar(tcol, &(ma->r));
+ else
+ tcol[0] = tcol[1] = tcol[2] = 1.0f;
+ break;
+ case PART_DRAW_COL_VEL:
+ tcol[0] = tcol[1] = tcol[2] = 1.0f;
+ break;
+ case PART_DRAW_COL_ACC:
+ tcol[0] = tcol[1] = tcol[2] = 1.0f;
+ break;
+ case PART_DRAW_COL_PARENT:
+ /* handled per child group */
+ break;
+ default:
+ BLI_assert(0); /* should never happen */
+ break;
+ }
+
+ if (!draw_constcolor) {
+ glColor3ubv(tcol);
+ }
+
+ /* draw child particles */
+ {
+ int pstart;
+ float col[3];
+
+ glEnable(GL_LIGHTING);
+ glEnable(GL_COLOR_MATERIAL);
+ glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
+ glShadeModel(GL_SMOOTH);
+
+ glBegin(GL_QUADS);
+ pstart = particle_path_next(cache, totchild, -1);
+ while (pstart < totchild) {
+ int p = pstart;
+ int np = particle_path_next(cache, totchild, p);
+
+ if (part->draw_col == PART_DRAW_COL_PARENT) {
+ particle_path_color(pstart, col);
+ rgb_float_to_uchar(tcol, col);
+ glColor3ubv(tcol);
+ }
+
+ while (np < totchild && cache[np]->hull_parent == cache[pstart]->hull_parent) {
+ draw_particle_hull_section(cache[p], cache[np]);
+
+ p = np;
+ np = particle_path_next(cache, totchild, np);
+ }
+ if (p > pstart + 1)
+ draw_particle_hull_section(cache[p], cache[pstart]);
+
+ pstart = np;
+ }
+ glEnd();
+
+ glBegin(GL_TRIANGLES);
+ pstart = particle_path_next(cache, totchild, -1);
+ while (pstart < totchild) {
+ int groupend;
+
+ if (part->draw_col == PART_DRAW_COL_PARENT) {
+ particle_path_color(pstart, col);
+ rgb_float_to_uchar(tcol, col);
+ glColor3ubv(tcol);
+ }
+
+ {
+ int p = particle_path_next(cache, totchild, pstart);
+ while (p < totchild && cache[p]->hull_parent == cache[pstart]->hull_parent) {
+ p = particle_path_next(cache, totchild, p);
+ }
+ groupend = p;
+ }
+
+ {
+ int a[4], b[4];
+
+ #define NEXT \
+ a[3] = a[2]; \
+ b[3] = b[2]; \
+ a[2] = a[1]; \
+ b[2] = b[1]; \
+ a[1] = a[0]; \
+ b[1] = b[0]; \
+ a[0] = particle_path_next(cache, groupend, a[0]); \
+ b[0] = particle_path_prev(cache, pstart, b[0]);
+
+ a[3] = pstart - 1;
+ b[3] = particle_path_prev(cache, pstart, groupend) + 1;
+ a[2] = particle_path_next(cache, groupend, a[3]);
+ b[2] = particle_path_prev(cache, pstart, b[3]);
+ a[1] = particle_path_next(cache, groupend, a[2]);
+ b[1] = particle_path_prev(cache, pstart, b[2]);
+ a[0] = particle_path_next(cache, groupend, a[1]);
+ b[0] = particle_path_prev(cache, pstart, b[1]);
+
+ /* first element */
+ if (a[1] <= b[1]) {
+ if (a[0] <= b[0])
+ draw_particle_hull_cap(cache[a[0]], cache[a[1]], cache[a[2]], cache[a[2]],
+ cache[b[0]], cache[b[1]], cache[b[2]], cache[b[2]]);
+ else
+ draw_particle_hull_cap(cache[a[1]], cache[a[1]], cache[a[2]], cache[a[2]],
+ cache[b[1]], cache[b[1]], cache[b[2]], cache[b[2]]);
+ }
+ NEXT
+
+ while (true) {
+ if (a[1] <= b[1]) {
+ if (a[0] <= b[0])
+ draw_particle_hull_cap(cache[a[0]], cache[a[1]], cache[a[2]], cache[a[3]],
+ cache[b[0]], cache[b[1]], cache[b[2]], cache[b[3]]);
+ else
+ draw_particle_hull_cap(cache[a[1]], cache[a[1]], cache[a[2]], cache[a[3]],
+ cache[b[1]], cache[b[1]], cache[b[2]], cache[b[3]]);
+ }
+ else
+ break;
+
+ NEXT
+ }
+
+ #undef NEXT
+ }
+
+ pstart = groupend;
+ }
+ glEnd();
+
+ glDisable(GL_COLOR_MATERIAL);
+ glDisable(GL_LIGHTING);
+ }
+
+ glPolygonMode(GL_FRONT, polygonmode[0]);
+ glPolygonMode(GL_BACK, polygonmode[1]);
+
+ if ((base->flag & OB_FROMDUPLI) && (ob->flag & OB_FROMGROUP)) {
+ glLoadMatrixf(rv3d->viewmat);
+ }
+}
+#endif
+
static void draw_particle_arrays(int draw_as, int totpoint, int ob_dt, int select)
{
/* draw created data arrays */
@@ -4846,6 +5230,7 @@ static void draw_particle_arrays(int draw_as, int totpoint, int ob_dt, int selec
break;
}
}
+
static void draw_particle(ParticleKey *state, int draw_as, short draw, float pixsize,
float imat[4][4], const float draw_line[2], ParticleBillboardData *bb, ParticleDrawData *pdd)
{
@@ -4996,6 +5381,7 @@ static void draw_particle(ParticleKey *state, int draw_as, short draw, float pix
}
}
}
+
static void draw_particle_data(ParticleSystem *psys, RegionView3D *rv3d,
ParticleKey *state, int draw_as,
float imat[4][4], ParticleBillboardData *bb, ParticleDrawData *pdd,
@@ -5059,7 +5445,7 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv
Material *ma;
float vel[3], imat[4][4];
float timestep, pixsize_scale = 1.0f, pa_size, r_tilt, r_length;
- float pa_time, pa_birthtime, pa_dietime, pa_health, intensity;
+ float pa_time, pa_birthtime, pa_dietime, pa_health;
float cfra;
float ma_col[3] = {0.0f, 0.0f, 0.0f};
int a, totpart, totpoint = 0, totve = 0, drawn, draw_as, totchild = 0;
@@ -5078,14 +5464,17 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv
/* don't draw normal paths in edit mode */
if (psys_in_edit_mode(scene, psys) && (pset->flag & PE_DRAW_PART) == 0)
return;
-
- if (part->draw_as == PART_DRAW_REND)
- draw_as = part->ren_as;
- else
- draw_as = part->draw_as;
-
- if (draw_as == PART_DRAW_NOT)
+
+ draw_as = part->draw_as == PART_DRAW_REND ? part->ren_as : part->draw_as;
+ if (draw_as == PART_DRAW_NOT) {
+ return;
+ }
+ else if (draw_as == PART_DRAW_HULL) {
+#ifdef USE_PARTICLE_HULL_DRAWING
+ draw_particle_hair_hull(scene, v3d, rv3d, base, psys, ob_dt, dflag);
+#endif
return;
+ }
/* prepare curvemapping tables */
if ((psys->part->child_flag & PART_CHILD_USE_CLUMP_CURVE) && psys->part->clumpcurve)
@@ -5329,20 +5718,32 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv
r_length = psys_frand(psys, a + 22);
if (part->draw_col > PART_DRAW_COL_MAT) {
+ float intensity;
switch (part->draw_col) {
case PART_DRAW_COL_VEL:
intensity = len_v3(pa->state.vel) / part->color_vec_max;
+ CLAMP(intensity, 0.0f, 1.0f);
+ weight_to_rgb(ma_col, intensity);
break;
case PART_DRAW_COL_ACC:
intensity = len_v3v3(pa->state.vel, pa->prev_state.vel) / ((pa->state.time - pa->prev_state.time) * part->color_vec_max);
+ CLAMP(intensity, 0.0f, 1.0f);
+ weight_to_rgb(ma_col, intensity);
+ break;
+ case PART_DRAW_COL_PARENT:
+ particle_path_color(a, ma_col);
+ break;
+ case PART_DRAW_COL_TEX: {
+ ParticleTexture ptex;
+ psys_get_texture(&sim, pa, &ptex, PAMAP_COLOR, cfra);
+ copy_v3_v3(ma_col, ptex.color);
break;
+ }
default:
- intensity = 1.0f; /* should never happen */
+ weight_to_rgb(ma_col, 1.0f);
BLI_assert(0);
break;
}
- CLAMP(intensity, 0.0f, 1.0f);
- weight_to_rgb(ma_col, intensity);
}
}
else {
@@ -5650,8 +6051,24 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv
if (1) { //ob_dt > OB_WIRE) {
glNormalPointer(GL_FLOAT, sizeof(ParticleCacheKey), path->vel);
if ((dflag & DRAW_CONSTCOLOR) == 0) {
- if (part->draw_col == PART_DRAW_COL_MAT) {
- glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col);
+ float col[3];
+
+ switch (part->draw_col) {
+ case PART_DRAW_COL_MAT:
+ glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col);
+ break;
+ case PART_DRAW_COL_PARENT:
+ /* this switches colors to each new parent because new hull children come first */
+ if (cache[a]->hull_parent >= 0) {
+ particle_path_color(cache[a]->hull_parent, col);
+ glColor3fv(col);
+ }
+ break;
+ case PART_DRAW_COL_TEX:
+ // TODO
+ break;
+ default:
+ break;
}
}
}
@@ -7526,6 +7943,72 @@ static void draw_object_wire_color(Scene *scene, Base *base, unsigned char r_ob_
r_ob_wire_col[3] = 255;
}
+
+static float draw_object_wire_grey = -1.0f;
+void draw_object_bg_wire_color_set(const float color[3])
+{
+ draw_object_wire_grey = rgb_to_grayscale(color);
+}
+
+
+static void tint_neg(float rgb[3], float fac)
+{
+ mul_v3_fl(rgb, fac);
+}
+
+static void tint_pos(float rgb[3], float fac)
+{
+ rgb[0] = 1.0 - rgb[0];
+ rgb[1] = 1.0 - rgb[1];
+ rgb[2] = 1.0 - rgb[2];
+
+ mul_v3_fl(rgb, fac);
+
+ rgb[0] = 1.0 - rgb[0];
+ rgb[1] = 1.0 - rgb[1];
+ rgb[2] = 1.0 - rgb[2];
+}
+
+static void draw_object_wire_color_adjust_contrast(
+ unsigned char ob_wire_col[3],
+ /* 0 == normal, 1 == select, 2 == obact */
+ const int select_state,
+ const short draw_type)
+{
+ float rgb[3];
+
+ BLI_assert(draw_object_wire_grey != -1.0);
+
+ rgb_uchar_to_float(rgb, ob_wire_col);
+
+ if (select_state == 0) {
+ tint_neg(rgb, 0.5f);
+ }
+ else {
+ tint_pos(rgb, select_state == 2 ? 0.15f : 0.35f);
+ }
+
+
+ /* when no solid --- ensure contrast */
+ if (draw_type <= OB_WIRE) {
+ const float contrast = 0.1f;
+
+ const float fill_bw = draw_object_wire_grey;
+ const float wire_bw = rgb_to_grayscale(rgb);
+ const float delta = wire_bw - fill_bw;
+
+ if (fabsf(delta) < contrast) {
+ if (delta > 0.0f) {
+ add_v3_fl(rgb, (contrast - delta) / 2.0f);
+ }
+ else {
+ add_v3_fl(rgb, (contrast + delta) / -2.0f);
+ }
+ }
+ }
+ rgb_float_to_uchar(ob_wire_col, rgb);
+}
+
static void draw_object_matcap_check(View3D *v3d, Object *ob)
{
/* fixed rule, active object draws as matcap */
@@ -7612,8 +8095,10 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
bool zbufoff = false, is_paint = false, empty_object = false;
const bool is_obact = (ob == OBACT);
const bool render_override = (v3d->flag2 & V3D_RENDER_OVERRIDE) != 0;
+ const bool show_motionpaths = !(v3d->flag3 & V3D_HIDE_MOTIONPATHS) != 0;
const bool is_picking = (G.f & G_PICKSEL) != 0;
const bool has_particles = (ob->particlesystem.first != NULL);
+ const bool is_wire_color = V3D_IS_WIRECOLOR_OBJECT(scene, v3d, ob);
bool skip_object = false; /* Draw particles but not their emitter object. */
SmokeModifierData *smd = NULL;
@@ -7700,34 +8185,36 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
view3d_cached_text_draw_begin();
/* draw motion paths (in view space) */
- if (ob->mpath && !render_override) {
- bAnimVizSettings *avs = &ob->avs;
-
- /* setup drawing environment for paths */
- draw_motion_paths_init(v3d, ar);
-
- /* draw motion path for object */
- draw_motion_path_instance(scene, ob, NULL, avs, ob->mpath);
-
- /* cleanup after drawing */
- draw_motion_paths_cleanup(v3d);
+ if (ob->mpath && show_motionpaths && !(base->flag & OB_FROMDUPLI)) {
+ ED_view3d_after_add(&v3d->afterdraw_nodepth, base, 0);
}
- /* multiply view with object matrix.
- * local viewmat and persmat, to calculate projections */
- ED_view3d_init_mats_rv3d_gl(ob, rv3d);
-
/* which wire color */
if ((dflag & DRAW_CONSTCOLOR) == 0) {
ED_view3d_project_base(ar, base);
- draw_object_wire_color(scene, base, _ob_wire_col);
+ if (is_wire_color) {
+ rgb_float_to_uchar(_ob_wire_col, ob->col);
+ _ob_wire_col[3] = 255;
+
+ draw_object_wire_color_adjust_contrast(
+ _ob_wire_col,
+ (ob->flag & SELECT) ? (is_obact ? 2 : 1) : 0,
+ v3d->drawtype);
+ }
+ else {
+ draw_object_wire_color(scene, base, _ob_wire_col);
+ }
ob_wire_col = _ob_wire_col;
glColor3ubv(ob_wire_col);
}
+ /* multiply view with object matrix.
+ * local viewmat and persmat, to calculate projections */
+ ED_view3d_init_mats_rv3d_gl(ob, rv3d);
+
/* maximum drawtype */
dt = v3d->drawtype;
if (dt == OB_RENDER) dt = OB_SOLID;
@@ -7906,6 +8393,12 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
GPU_disable_material();
}
}
+ /* draw motion paths if forced */
+ if (show_motionpaths && !(dflag & DRAW_SCENESET)) {
+ bArmature *arm = ob->data;
+ if (!arm->edbo && !(base->flag & OB_FROMDUPLI))
+ ED_view3d_after_add(&v3d->afterdraw_nodepth, base, 0);
+ }
break;
default:
if (!render_override) {
@@ -7940,7 +8433,8 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
/* code for new particle system */
if ((ob->particlesystem.first) &&
- (ob != scene->obedit))
+ (ob != scene->obedit) &&
+ !(ob->transflag & OB_IS_DUPLI_CACHE))
{
ParticleSystem *psys;
@@ -7957,14 +8451,16 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
view3d_cached_text_draw_begin();
for (psys = ob->particlesystem.first; psys; psys = psys->next) {
- /* run this so that possible child particles get cached */
- if (ob->mode & OB_MODE_PARTICLE_EDIT && is_obact) {
- PTCacheEdit *edit = PE_create_current(scene, ob);
- if (edit && edit->psys == psys)
- draw_update_ptcache_edit(scene, ob, edit);
+ if (!(ob->mode & OB_MODE_HAIR_EDIT)) {
+ /* run this so that possible child particles get cached */
+ if (ob->mode & OB_MODE_PARTICLE_EDIT && is_obact) {
+ PTCacheEdit *edit = PE_create_current(scene, ob);
+ if (edit && edit->psys == psys)
+ draw_update_ptcache_edit(scene, ob, edit);
+ }
+
+ draw_new_particle_system(scene, v3d, rv3d, base, psys, dt, dflag);
}
-
- draw_new_particle_system(scene, v3d, rv3d, base, psys, dt, dflag);
}
invert_m4_m4(ob->imat, ob->obmat);
view3d_cached_text_draw_end(v3d, ar, 0, NULL);
@@ -7989,6 +8485,13 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
glMultMatrixf(ob->obmat);
}
}
+
+ if (ob->mode & OB_MODE_HAIR_EDIT && is_obact) {
+ BMEditStrands *edit = BKE_editstrands_from_object(ob);
+ if (edit) {
+ draw_strands_edit_hair(scene, v3d, ar, edit);
+ }
+ }
}
/* draw code for smoke */
@@ -8049,12 +8552,37 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
BKE_boundbox_init_from_minmax(&bb, sds->p0, sds->p1);
draw_box(bb.vec);
#endif
+
+ /* draw a single voxel to hint the user about the resolution of the fluid */
+ copy_v3_v3(p0, sds->p0);
+
+ if (sds->flags & MOD_SMOKE_HIGHRES) {
+ madd_v3_v3v3fl(p1, p0, sds->cell_size, 1.0f / (sds->amplify + 1));
+ }
+ else {
+ add_v3_v3v3(p1, p0, sds->cell_size);
+ }
+
+ BKE_boundbox_init_from_minmax(&bb, p0, p1);
+ draw_box(bb.vec, false);
+
+#ifdef WITH_OPENVDB
+ glLoadMatrixf(rv3d->viewmat);
+ if (sds->density)
+ OpenVDB_draw_primitive(sds->density, true, true, true, true);
+ if (sds->density_high)
+ OpenVDB_draw_primitive(sds->density_high, true, true, true, true);
+ glMultMatrixf(sds->fluidmat);
+#endif
}
/* don't show smoke before simulation starts, this could be made an option in the future */
if (smd->domain->fluid && CFRA >= smd->domain->point_cache[0]->startframe) {
float p0[3], p1[3];
+ glLoadMatrixf(rv3d->viewmat);
+ glMultMatrixf(ob->obmat);
+
/* get view vector */
invert_m4_m4(ob->imat, ob->obmat);
mul_v3_mat3_m4v3(viewnormal, ob->imat, rv3d->viewinv[2]);
@@ -8194,7 +8722,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
}
/* object centers, need to be drawn in viewmat space for speed, but OK for picking select */
- if (!is_obact || !(ob->mode & OB_MODE_ALL_PAINT)) {
+ if (!is_obact || !(ob->mode & OB_MODE_ALL_BRUSH)) {
int do_draw_center = -1; /* defines below are zero or positive... */
if (render_override) {
@@ -8657,6 +9185,48 @@ static void draw_object_mesh_instance(Scene *scene, View3D *v3d, RegionView3D *r
if (dm) dm->release(dm);
}
+void ED_draw_object_facemap(Scene *scene, struct Object *ob, int facemap)
+{
+ DerivedMesh *dm = NULL;
+
+ dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
+ if (!dm || !CustomData_has_layer(&dm->polyData, CD_FACEMAP))
+ return;
+
+ DM_update_materials(dm, ob);
+
+ glFrontFace((ob->transflag & OB_NEG_SCALE) ? GL_CW : GL_CCW);
+
+ /* add polygon offset so we draw above the original surface */
+ glPolygonOffset(1.0, 1.0);
+
+ dm->totfmaps = BLI_listbase_count(&ob->fmaps);
+
+ GPU_facemap_setup(dm);
+
+ glColor4f(0.7, 1.0, 1.0, 0.5);
+
+ glPushAttrib(GL_ENABLE_BIT);
+ glEnable(GL_BLEND);
+ glDisable(GL_LIGHTING);
+
+ if (dm->drawObject->facemapindices) {
+ if (dm->drawObject->facemapindices->use_vbo)
+ glDrawElements(GL_TRIANGLES, dm->drawObject->facemap_count[facemap], GL_UNSIGNED_INT,
+ (int *)NULL + dm->drawObject->facemap_start[facemap]);
+ else
+ glDrawElements(GL_TRIANGLES, dm->drawObject->facemap_count[facemap], GL_UNSIGNED_INT,
+ (int *)dm->drawObject->facemapindices->pointer + dm->drawObject->facemap_start[facemap]);
+ }
+ glPopAttrib();
+
+ GPU_buffer_unbind();
+
+ glPolygonOffset(0.0, 0.0);
+ dm->release(dm);
+}
+
+
void draw_object_instance(Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *ob, const char dt, int outline)
{
if (ob == NULL)
diff --git a/source/blender/editors/space_view3d/drawstrands.c b/source/blender/editors/space_view3d/drawstrands.c
new file mode 100644
index 00000000000..af2e04ee3a7
--- /dev/null
+++ b/source/blender/editors/space_view3d/drawstrands.c
@@ -0,0 +1,520 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2014 by the Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/space_view3d/drawstrands.c
+ * \ingroup spview3d
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_utildefines.h"
+#include "BLI_math.h"
+
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BKE_editstrands.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_strands.h"
+
+#include "bmesh.h"
+
+#include "ED_screen.h"
+#include "ED_types.h"
+
+#include "UI_resources.h"
+#include "UI_interface_icons.h"
+
+#include "BIF_gl.h"
+#include "BIF_glutil.h"
+
+#include "GPU_draw.h"
+#include "GPU_extensions.h"
+#include "GPU_select.h"
+
+#include "view3d_intern.h"
+
+typedef struct StrandsDrawGLState {
+ GLint polygonmode[2];
+} StrandsDrawGLState;
+
+typedef enum StrandsShadeMode {
+ STRANDS_SHADE_FLAT,
+ STRANDS_SHADE_HAIR,
+} StrandsShadeMode;
+
+static void draw_strands_begin(StrandsDrawGLState *state, short dflag)
+{
+ glGetIntegerv(GL_POLYGON_MODE, state->polygonmode);
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ /* setup gl flags */
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ if ((dflag & DRAW_CONSTCOLOR) == 0) {
+// if (part->draw_col == PART_DRAW_COL_MAT)
+// glEnableClientState(GL_COLOR_ARRAY);
+ }
+
+ glColor3f(1,1,1);
+ glEnable(GL_LIGHTING);
+// glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
+// glEnable(GL_COLOR_MATERIAL);
+}
+
+static void draw_strands_end(StrandsDrawGLState *state)
+{
+ /* restore & clean up */
+// if (part->draw_col == PART_DRAW_COL_MAT)
+// glDisableClientState(GL_COLOR_ARRAY);
+ glDisable(GL_LIGHTING);
+ glDisable(GL_COLOR_MATERIAL);
+
+ glLineWidth(1.0f);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ glPolygonMode(GL_FRONT, state->polygonmode[0]);
+ glPolygonMode(GL_BACK, state->polygonmode[1]);
+}
+
+static void draw_strand_lines(Strands *strands, short dflag)
+{
+ const bool has_motion_state = strands->state;
+ StrandsDrawGLState gl_state;
+ StrandIterator it_strand;
+
+ draw_strands_begin(&gl_state, dflag);
+
+ for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ if (it_strand.tot <= 0)
+ continue;
+
+ if (has_motion_state) {
+ glVertexPointer(3, GL_FLOAT, sizeof(StrandsMotionState), it_strand.state->co);
+ glNormalPointer(GL_FLOAT, sizeof(StrandsMotionState), it_strand.state->nor);
+ }
+ else {
+ glVertexPointer(3, GL_FLOAT, sizeof(StrandsVertex), it_strand.verts->co);
+ glNormalPointer(GL_FLOAT, sizeof(StrandsVertex), it_strand.verts->nor);
+ }
+ if ((dflag & DRAW_CONSTCOLOR) == 0) {
+// if (part->draw_col == PART_DRAW_COL_MAT) {
+// glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col);
+// }
+ }
+
+ glDrawArrays(GL_LINE_STRIP, 0, it_strand.curve->numverts);
+ }
+
+ draw_strands_end(&gl_state);
+}
+
+static void draw_strand_child_lines(StrandsChildren *children, short dflag)
+{
+ StrandsDrawGLState gl_state;
+ StrandChildIterator it_strand;
+
+ draw_strands_begin(&gl_state, dflag);
+
+ for (BKE_strand_child_iter_init(&it_strand, children); BKE_strand_child_iter_valid(&it_strand); BKE_strand_child_iter_next(&it_strand)) {
+ StrandsChildCurve *curve = it_strand.curve;
+ const int numverts = curve->cutoff < 0.0f ? curve->numverts : min_ii(curve->numverts, (int)ceilf(curve->cutoff) + 1);
+
+ if (it_strand.tot <= 0)
+ continue;
+
+ glVertexPointer(3, GL_FLOAT, sizeof(StrandsChildVertex), it_strand.verts->co);
+ glNormalPointer(GL_FLOAT, sizeof(StrandsChildVertex), it_strand.verts->nor);
+
+ if ((dflag & DRAW_CONSTCOLOR) == 0) {
+// if (part->draw_col == PART_DRAW_COL_MAT) {
+// glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col);
+// }
+ }
+
+
+ glDrawArrays(GL_LINE_STRIP, 0, numverts);
+ }
+
+ draw_strands_end(&gl_state);
+}
+
+void draw_strands(Scene *UNUSED(scene), View3D *UNUSED(v3d), ARegion *ar, Object *ob, Strands *strands, StrandsChildren *children, short dflag)
+{
+ RegionView3D *rv3d = ar->regiondata;
+ float imat[4][4];
+
+ invert_m4_m4(imat, rv3d->viewmatob);
+
+// glDepthMask(GL_FALSE);
+// glEnable(GL_BLEND);
+
+ glPushMatrix();
+
+ glLoadMatrixf(rv3d->viewmat);
+ glMultMatrixf(ob->obmat);
+
+ if (children)
+ draw_strand_child_lines(children, dflag);
+ else
+ draw_strand_lines(strands, dflag);
+
+ glPopMatrix();
+
+// glDepthMask(GL_TRUE);
+// glDisable(GL_BLEND);
+}
+
+/* ------------------------------------------------------------------------- */
+
+typedef struct StrandsDrawInfo {
+ bool has_zbuf;
+ bool use_zbuf_select;
+
+ StrandsShadeMode shade_mode;
+ int select_mode;
+
+ float col_base[4];
+ float col_select[4];
+} StrandsDrawInfo;
+
+BLI_INLINE bool strands_use_normals(const StrandsDrawInfo *info)
+{
+ return ELEM(info->shade_mode, STRANDS_SHADE_HAIR);
+}
+
+static void init_draw_info(StrandsDrawInfo *info, View3D *v3d,
+ StrandsShadeMode shade_mode, int select_mode)
+{
+ info->has_zbuf = v3d->zbuf;
+ info->use_zbuf_select = (v3d->flag & V3D_ZBUF_SELECT);
+
+ info->shade_mode = shade_mode;
+ info->select_mode = select_mode;
+
+ /* get selection theme colors */
+ UI_GetThemeColor4fv(TH_VERTEX, info->col_base);
+ UI_GetThemeColor4fv(TH_VERTEX_SELECT, info->col_select);
+}
+
+static void set_opengl_state_strands(const StrandsDrawInfo *info)
+{
+ if (!info->use_zbuf_select)
+ glDisable(GL_DEPTH_TEST);
+ glEnable(GL_BLEND);
+
+ if (ELEM(info->shade_mode, STRANDS_SHADE_HAIR)) {
+ glEnable(GL_LIGHTING);
+ glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
+ glEnable(GL_COLOR_MATERIAL);
+ glShadeModel(GL_SMOOTH);
+ }
+ else {
+ glDisable(GL_LIGHTING);
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ if (strands_use_normals(info))
+ glEnableClientState(GL_NORMAL_ARRAY);
+}
+
+static void set_opengl_state_dots(const StrandsDrawInfo *info)
+{
+ if (!info->use_zbuf_select)
+ glDisable(GL_DEPTH_TEST);
+ glEnable(GL_BLEND);
+
+ glDisable(GL_LIGHTING);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glPointSize(3.0);
+}
+
+static void restore_opengl_state(const StrandsDrawInfo *info)
+{
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glDisable(GL_BLEND);
+ glDisable(GL_LIGHTING);
+ glDisable(GL_COLOR_MATERIAL);
+ glShadeModel(GL_FLAT);
+ if (info->has_zbuf)
+ glEnable(GL_DEPTH_TEST);
+ glLineWidth(1.0f);
+ glPointSize(1.0);
+}
+
+/* ------------------------------------------------------------------------- */
+/* strands */
+
+static void setup_gpu_buffers_strands(BMEditStrands *edit, const StrandsDrawInfo *info)
+{
+ const size_t size_v3 = sizeof(float) * 3;
+ const size_t size_vertex = (strands_use_normals(info) ? 2*size_v3 : size_v3);
+
+// int totstrands = BM_strands_count(edit->bm);
+ int totvert = edit->bm->totvert;
+ int totedge = edit->bm->totedge;
+
+ if (!edit->vertex_glbuf)
+ glGenBuffers(1, &edit->vertex_glbuf);
+ if (!edit->elem_glbuf)
+ glGenBuffers(1, &edit->elem_glbuf);
+
+ glBindBuffer(GL_ARRAY_BUFFER, edit->vertex_glbuf);
+ glBufferData(GL_ARRAY_BUFFER, size_vertex * totvert, NULL, GL_DYNAMIC_DRAW);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, edit->elem_glbuf);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * totedge * 2, NULL, GL_DYNAMIC_DRAW);
+
+ glVertexPointer(3, GL_FLOAT, size_vertex, NULL);
+ if (strands_use_normals(info))
+ glNormalPointer(GL_FLOAT, size_vertex, (GLubyte *)NULL + size_v3);
+}
+
+static void unbind_gpu_buffers_strands(void)
+{
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+static int write_gpu_buffers_strands(BMEditStrands *edit, const StrandsDrawInfo *info)
+{
+ const size_t size_v3 = sizeof(float) * 3;
+ const size_t size_vertex = (strands_use_normals(info) ? 2*size_v3 : size_v3);
+
+ GLubyte *vertex_data;
+ unsigned int *elem_data;
+ BMVert *root, *v, *vprev;
+ BMIter iter, iter_strand;
+ int index, indexprev, index_edge;
+ int k;
+
+ vertex_data = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
+ elem_data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);
+ if (!vertex_data || !elem_data)
+ return 0;
+
+ BM_mesh_elem_index_ensure(edit->bm, BM_VERT);
+
+ index_edge = 0;
+ BM_ITER_STRANDS(root, &iter, edit->bm, BM_STRANDS_OF_MESH) {
+ BM_ITER_STRANDS_ELEM_INDEX(v, &iter_strand, root, BM_VERTS_OF_STRAND, k) {
+ size_t offset_co;
+
+ index = BM_elem_index_get(v);
+
+ offset_co = index * size_vertex;
+ copy_v3_v3((float *)(vertex_data + offset_co), v->co);
+
+ if (k > 0) {
+ if (strands_use_normals(info)) {
+ size_t offset_nor = offset_co + size_v3;
+ float nor[3];
+ sub_v3_v3v3(nor, v->co, vprev->co);
+ normalize_v3(nor);
+ copy_v3_v3((float *)(vertex_data + offset_nor), nor);
+
+ if (k == 1) {
+ /* define root normal: same as first segment */
+ size_t offset_root_nor = indexprev * size_vertex + size_v3;
+ copy_v3_v3((float *)(vertex_data + offset_root_nor), nor);
+ }
+ }
+
+ {
+ elem_data[index_edge + 0] = indexprev;
+ elem_data[index_edge + 1] = index;
+ index_edge += 2;
+ }
+ }
+
+ vprev = v;
+ indexprev = index;
+ }
+ }
+
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+ glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
+
+ return index_edge;
+}
+
+/* ------------------------------------------------------------------------- */
+/* dots */
+
+static void setup_gpu_buffers_dots(BMEditStrands *edit, const StrandsDrawInfo *info, bool selected)
+{
+ const size_t size_v3 = sizeof(float) * 3;
+ const size_t size_vertex = size_v3;
+ BMesh *bm = edit->bm;
+
+ BMVert *v;
+ BMIter iter;
+ int totvert;
+
+ switch (info->select_mode) {
+ case HAIR_SELECT_STRAND:
+ totvert = 0;
+ break;
+ case HAIR_SELECT_VERTEX:
+ totvert = selected ? bm->totvertsel : bm->totvert - bm->totvertsel;
+ break;
+ case HAIR_SELECT_TIP:
+ totvert = 0;
+ BM_ITER_MESH(v, &iter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test_bool(v, BM_ELEM_SELECT) != selected)
+ continue;
+ if (!BM_strands_vert_is_tip(v))
+ continue;
+
+ ++totvert;
+ }
+ break;
+ }
+
+ if (totvert == 0)
+ return;
+
+ if (!edit->dot_glbuf)
+ glGenBuffers(1, &edit->dot_glbuf);
+
+ glBindBuffer(GL_ARRAY_BUFFER, edit->dot_glbuf);
+ glBufferData(GL_ARRAY_BUFFER, size_vertex * totvert, NULL, GL_DYNAMIC_DRAW);
+
+ glVertexPointer(3, GL_FLOAT, size_vertex, NULL);
+}
+
+static void unbind_gpu_buffers_dots(void)
+{
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+static int write_gpu_buffers_dots(BMEditStrands *edit, const StrandsDrawInfo *info, bool selected)
+{
+ const size_t size_v3 = sizeof(float) * 3;
+ const size_t size_vertex = size_v3;
+
+ GLubyte *vertex_data;
+ BMVert *v;
+ BMIter iter;
+ int index_dot;
+
+ if (info->select_mode == HAIR_SELECT_STRAND)
+ return 0;
+
+ vertex_data = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
+ if (!vertex_data)
+ return 0;
+
+ BM_mesh_elem_index_ensure(edit->bm, BM_VERT);
+
+ index_dot = 0;
+ switch (info->select_mode) {
+ case HAIR_SELECT_STRAND:
+ /* already exited, but keep the case for the compiler */
+ break;
+ case HAIR_SELECT_VERTEX:
+ BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) {
+ size_t offset_co;
+
+ if (BM_elem_flag_test_bool(v, BM_ELEM_SELECT) != selected)
+ continue;
+
+ offset_co = index_dot * size_vertex;
+ copy_v3_v3((float *)(vertex_data + offset_co), v->co);
+ ++index_dot;
+ }
+ break;
+ case HAIR_SELECT_TIP:
+ BM_ITER_MESH(v, &iter, edit->bm, BM_VERTS_OF_MESH) {
+ size_t offset_co;
+
+ if (BM_elem_flag_test_bool(v, BM_ELEM_SELECT) != selected)
+ continue;
+ if (!BM_strands_vert_is_tip(v))
+ continue;
+
+ offset_co = index_dot * size_vertex;
+ copy_v3_v3((float *)(vertex_data + offset_co), v->co);
+ ++index_dot;
+ }
+ break;
+ }
+
+ glUnmapBuffer(GL_ARRAY_BUFFER);
+
+ return index_dot;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void draw_dots(BMEditStrands *edit, const StrandsDrawInfo *info, bool selected)
+{
+ int totelem;
+
+ if (selected)
+ glColor3fv(info->col_select);
+ else
+ glColor3fv(info->col_base);
+
+ setup_gpu_buffers_dots(edit, info, selected);
+ totelem = write_gpu_buffers_dots(edit, info, selected);
+ if (totelem > 0)
+ glDrawArrays(GL_POINTS, 0, totelem);
+}
+
+void draw_strands_edit_hair(Scene *scene, View3D *v3d, ARegion *UNUSED(ar), BMEditStrands *edit)
+{
+ HairEditSettings *settings = &scene->toolsettings->hair_edit;
+
+ StrandsDrawInfo info;
+ int totelem;
+
+ init_draw_info(&info, v3d, STRANDS_SHADE_HAIR, settings->select_mode);
+
+ set_opengl_state_strands(&info);
+ setup_gpu_buffers_strands(edit, &info);
+ totelem = write_gpu_buffers_strands(edit, &info);
+ if (totelem > 0)
+ glDrawElements(GL_LINES, totelem, GL_UNSIGNED_INT, NULL);
+ unbind_gpu_buffers_strands();
+
+ set_opengl_state_dots(&info);
+ draw_dots(edit, &info, false);
+ draw_dots(edit, &info, true);
+ unbind_gpu_buffers_dots();
+
+ restore_opengl_state(&info);
+}
diff --git a/source/blender/editors/space_view3d/drawvolume.c b/source/blender/editors/space_view3d/drawvolume.c
index d6691f431dd..2ee1241a2a4 100644
--- a/source/blender/editors/space_view3d/drawvolume.c
+++ b/source/blender/editors/space_view3d/drawvolume.c
@@ -106,6 +106,7 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob,
int gl_depth = 0, gl_blend = 0;
const bool use_fire = (sds->active_fields & SM_ACTIVE_FIRE) != 0;
+ const float thickness = sds->display_thickness;
/* draw slices of smoke is adapted from c++ code authored
* by: Johannes Schmid and Ingemar Rask, 2006, johnny@grob.org */
@@ -288,9 +289,9 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob,
GPU_program_parameter_4f(smoke_program, 0, dx, dx, dx, 1.0);
/* custom parameter for smoke style (higher = thicker) */
if (sds->active_fields & SM_ACTIVE_COLORS)
- GPU_program_parameter_4f(smoke_program, 1, 1.0, 1.0, 1.0, 10.0);
+ GPU_program_parameter_4f(smoke_program, 1, 1.0, 1.0, 1.0, 10.0 * thickness);
else
- GPU_program_parameter_4f(smoke_program, 1, sds->active_color[0], sds->active_color[1], sds->active_color[2], 10.0);
+ GPU_program_parameter_4f(smoke_program, 1, sds->active_color[0], sds->active_color[1], sds->active_color[2], 10.0 * thickness);
}
else
printf("Your gfx card does not support 3D View smoke drawing.\n");
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 096d9e8a40a..e4f3e1d75ea 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -32,9 +32,13 @@
#include <string.h>
#include <stdio.h>
+#include "DNA_armature_types.h"
#include "DNA_material_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_camera_types.h"
+#include "DNA_key_types.h"
#include "MEM_guardedalloc.h"
@@ -42,17 +46,22 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BKE_action.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
#include "BKE_icons.h"
+#include "BKE_key.h"
#include "BKE_library.h"
#include "BKE_main.h"
+#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "ED_space_api.h"
#include "ED_screen.h"
+#include "ED_transform.h"
+#include "ED_view3d.h"
#include "GPU_extensions.h"
#include "GPU_material.h"
@@ -69,6 +78,7 @@
#include "RNA_access.h"
#include "UI_resources.h"
+#include "UI_interface.h"
#ifdef WITH_PYTHON
# include "BPY_extern.h"
@@ -391,12 +401,13 @@ static SpaceLink *view3d_new(const bContext *C)
ar->regiontype = RGN_TYPE_WINDOW;
ar->regiondata = MEM_callocN(sizeof(RegionView3D), "region view3d");
+
rv3d = ar->regiondata;
rv3d->viewquat[0] = 1.0f;
rv3d->persp = RV3D_PERSP;
rv3d->view = RV3D_VIEW_PERSPORTHO;
rv3d->dist = 10.0;
-
+
return (SpaceLink *)v3d;
}
@@ -437,8 +448,7 @@ static void view3d_free(SpaceLink *sl)
/* spacetype; init callback */
static void view3d_init(wmWindowManager *UNUSED(wm), ScrArea *UNUSED(sa))
-{
-
+{
}
static SpaceLink *view3d_duplicate(SpaceLink *sl)
@@ -473,6 +483,7 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl)
}
v3dn->properties_storage = NULL;
+
if (v3dn->fx_settings.dof)
v3dn->fx_settings.dof = MEM_dupallocN(v3do->fx_settings.dof);
if (v3dn->fx_settings.ssao)
@@ -487,6 +498,12 @@ static void view3d_main_area_init(wmWindowManager *wm, ARegion *ar)
ListBase *lb;
wmKeyMap *keymap;
+ if (BLI_listbase_is_empty(&ar->widgetmaps)) {
+ BLI_addhead(&ar->widgetmaps, WM_widgetmap_from_type("View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW, true));
+ }
+
+ WM_event_add_area_widgetmap_handlers(ar);
+
/* object ops. */
/* important to be before Pose keymap since they can both be enabled at once */
@@ -543,6 +560,9 @@ static void view3d_main_area_init(wmWindowManager *wm, ARegion *ar)
keymap = WM_keymap_find(wm->defaultconf, "Particle", 0, 0);
WM_event_add_keymap_handler(&ar->handlers, keymap);
+ keymap = WM_keymap_find(wm->defaultconf, "Hair", 0, 0);
+ WM_event_add_keymap_handler(&ar->handlers, keymap);
+
/* editfont keymap swallows all... */
keymap = WM_keymap_find(wm->defaultconf, "Font", 0, 0);
WM_event_add_keymap_handler(&ar->handlers, keymap);
@@ -564,7 +584,6 @@ static void view3d_main_area_init(wmWindowManager *wm, ARegion *ar)
lb = WM_dropboxmap_find("View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW);
WM_event_add_dropbox_handler(&ar->handlers, lb);
-
}
static void view3d_main_area_exit(wmWindowManager *wm, ARegion *ar)
@@ -713,6 +732,162 @@ static void view3d_dropboxes(void)
}
+static int WIDGETGROUP_camera_poll(const struct bContext *C, struct wmWidgetGroupType *UNUSED(wgrouptype))
+{
+ Object *ob = CTX_data_active_object(C);
+
+ if (ob && ob->type == OB_CAMERA) {
+ Camera *ca = ob->data;
+ return (ca->flag & CAM_SHOWLIMITS) != 0;
+ }
+ return false;
+}
+
+static void WIDGETGROUP_camera_draw(const struct bContext *C, struct wmWidgetGroup *wgroup)
+{
+ float color_camera[4] = {1.0f, 0.3f, 0.0f, 1.0f};
+ Object *ob = CTX_data_active_object(C);
+ Camera *ca = ob->data;
+ wmWidget *widget;
+ PointerRNA cameraptr;
+ float dir[3];
+
+ widget = WIDGET_arrow_new(wgroup, WIDGET_ARROW_STYLE_CROSS);
+ WM_widget_set_draw_on_hover_only(widget, true);
+ WM_widget_set_3d_scale(widget, false);
+ WIDGET_arrow_set_color(widget, color_camera);
+
+ RNA_pointer_create(&ca->id, &RNA_Camera, ca, &cameraptr);
+ WM_widget_set_origin(widget, ob->obmat[3]);
+ WM_widget_property(widget, ARROW_SLOT_OFFSET_WORLD_SPACE, &cameraptr, "dof_distance");
+ negate_v3_v3(dir, ob->obmat[2]);
+ WIDGET_arrow_set_direction(widget, dir);
+ WIDGET_arrow_set_up_vector(widget, ob->obmat[1]);
+ WM_widget_set_scale(widget, ca->drawsize);
+}
+
+#if 0
+static int WIDGETGROUP_shapekey_poll(const struct bContext *C, struct wmWidgetGroupType *UNUSED(wgrouptype))
+{
+ Object *ob = CTX_data_active_object(C);
+
+ if (ob && ob->type == OB_MESH) {
+ Key *key = BKE_key_from_object(ob);
+ KeyBlock *kb;
+
+ if (key == NULL)
+ return false;
+
+ kb = BLI_findlink(&key->block, ob->shapenr - 1);
+
+ if (kb)
+ return true;
+ }
+ return false;
+}
+
+static void WIDGETGROUP_shapekey_draw(const struct bContext *C, struct wmWidgetGroup *wgroup)
+{
+ float color_shape[4] = {1.0f, 0.3f, 0.0f, 1.0f};
+ Object *ob = CTX_data_active_object(C);
+ Key *key = BKE_key_from_object(ob);
+ KeyBlock *kb = BLI_findlink(&key->block, ob->shapenr - 1);
+ wmWidget *widget;
+ PointerRNA shapeptr;
+ float dir[3];
+
+ widget = WIDGET_arrow_new(wgroup, WIDGET_ARROW_STYLE_NORMAL);
+ WM_widget_set_3d_scale(widget, false);
+ WIDGET_arrow_set_color(widget, color_shape);
+ RNA_pointer_create(&key->id, &RNA_ShapeKey, kb, &shapeptr);
+ WM_widget_set_origin(widget, ob->obmat[3]);
+ WM_widget_property(widget, ARROW_SLOT_OFFSET_WORLD_SPACE, &shapeptr, "value");
+ negate_v3_v3(dir, ob->obmat[2]);
+ WIDGET_arrow_set_direction(widget, dir);
+}
+#endif
+
+static int WIDGETGROUP_armature_facemap_poll(const struct bContext *C, struct wmWidgetGroupType *UNUSED(wgrouptype))
+{
+ Object *ob = CTX_data_active_object(C);
+
+ if (ob && ob->type == OB_MESH && ob->fmaps.first) {
+ ModifierData *md;
+ VirtualModifierData virtualModifierData;
+
+ md = modifiers_getVirtualModifierList(ob, &virtualModifierData);
+
+ /* exception for shape keys because we can edit those */
+ for (; md; md = md->next) {
+ if (modifier_isEnabled(CTX_data_scene(C), md, eModifierMode_Realtime) && md->type == eModifierType_Armature) {
+ ArmatureModifierData *amd = (ArmatureModifierData *) md;
+ if (amd->object && (amd->deformflag & ARM_DEF_FACEMAPS))
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void WIDGETGROUP_armature_facemap_draw(const struct bContext *C, struct wmWidgetGroup *wgroup)
+{
+ float color_shape[4] = {1.0f, 0.3f, 0.0f, 1.0f};
+ Object *ob = CTX_data_active_object(C);
+ wmWidget *widget;
+ Object *armature;
+ PointerRNA famapptr;
+ PropertyRNA *prop;
+ ModifierData *md;
+ VirtualModifierData virtualModifierData;
+ int index = 0;
+ bFaceMap *fmap = ob->fmaps.first;
+
+ md = modifiers_getVirtualModifierList(ob, &virtualModifierData);
+
+ /* exception for shape keys because we can edit those */
+ for (; md; md = md->next) {
+ if (modifier_isEnabled(CTX_data_scene(C), md, eModifierMode_Realtime) && md->type == eModifierType_Armature) {
+ ArmatureModifierData *amd = (ArmatureModifierData *) md;
+ if (amd->object && (amd->deformflag & ARM_DEF_FACEMAPS)) {
+ armature = amd->object;
+ break;
+ }
+ }
+ }
+
+
+ for (; fmap; fmap = fmap->next, index++) {
+ if (BKE_pose_channel_find_name(armature->pose, fmap->name)) {
+ PointerRNA *opptr;
+ widget = WIDGET_facemap_new(wgroup, 0, ob, index);
+ RNA_pointer_create(&ob->id, &RNA_FaceMap, fmap, &famapptr);
+ WM_widget_property(widget, FACEMAP_SLOT_FACEMAP, &famapptr, "name");
+ opptr = WM_widget_operator(widget, "TRANSFORM_OT_translate");
+ if ((prop = RNA_struct_find_property(opptr, "release_confirm"))) {
+ RNA_property_boolean_set(opptr, prop, true);
+ }
+ WIDGET_facemap_set_color(widget, color_shape);
+ WM_widget_set_draw_on_hover_only(widget, true);
+ }
+ }
+}
+
+
+static void view3d_widgets(void)
+{
+ WM_widgetmaptype_find("View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW, true, true);
+
+ WM_widgetgrouptype_new(WIDGETGROUP_lamp_poll, WIDGETGROUP_lamp_draw, NULL, "View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW, true);
+ WM_widgetgrouptype_new(WIDGETGROUP_camera_poll, WIDGETGROUP_camera_draw, NULL, "View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW, true);
+ WM_widgetgrouptype_new(WIDGETGROUP_armature_facemap_poll, WIDGETGROUP_armature_facemap_draw, NULL, "View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW, true);
+
+#if 0
+ wgroup_manipulator = WM_widgetgrouptype_new(
+ WIDGETGROUP_manipulator_poll,
+ WIDGETGROUP_manipulator_update);
+#endif
+}
+
/* type callback, not region itself */
static void view3d_main_area_free(ARegion *ar)
@@ -1416,6 +1591,7 @@ void ED_spacetype_view3d(void)
st->operatortypes = view3d_operatortypes;
st->keymap = view3d_keymap;
st->dropboxes = view3d_dropboxes;
+ st->widgets = view3d_widgets;
st->context = view3d_context;
/* regions: main window */
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index fb0f437884a..5ccb16e514c 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -1947,16 +1947,19 @@ typedef struct View3DAfter {
struct View3DAfter *next, *prev;
struct Base *base;
short dflag;
+ float obmat[4][4];
} View3DAfter;
/* temp storage of Objects that need to be drawn as last */
void ED_view3d_after_add(ListBase *lb, Base *base, const short dflag)
{
View3DAfter *v3da = MEM_callocN(sizeof(View3DAfter), "View 3d after");
- BLI_assert((base->flag & OB_FROMDUPLI) == 0);
BLI_addtail(lb, v3da);
v3da->base = base;
v3da->dflag = dflag;
+ if (base->flag & OB_FROMDUPLI) {
+ copy_m4_m4(v3da->obmat, base->object->obmat);
+ }
}
/* disables write in zbuffer and draws it over */
@@ -1968,8 +1971,17 @@ static void view3d_draw_transp(Scene *scene, ARegion *ar, View3D *v3d)
v3d->transp = true;
for (v3da = v3d->afterdraw_transp.first; v3da; v3da = next) {
+ float obmat[4][4];
next = v3da->next;
+ if (v3da->base->flag & OB_FROMDUPLI) {
+ copy_m4_m4(obmat, v3da->base->object->obmat);
+ copy_m4_m4(v3da->base->object->obmat, v3da->obmat);
+ }
draw_object(scene, ar, v3d, v3da->base, v3da->dflag);
+ if (v3da->base->flag & OB_FROMDUPLI) {
+ copy_m4_m4(v3da->base->object->obmat, obmat);
+ MEM_freeN(v3da->base);
+ }
BLI_remlink(&v3d->afterdraw_transp, v3da);
MEM_freeN(v3da);
}
@@ -2004,6 +2016,7 @@ static void view3d_draw_xray(Scene *scene, ARegion *ar, View3D *v3d, bool *clear
static void view3d_draw_xraytransp(Scene *scene, ARegion *ar, View3D *v3d, const bool clear)
{
View3DAfter *v3da, *next;
+ float obmat[4][4];
if (clear && v3d->zbuf)
glClear(GL_DEPTH_BUFFER_BIT);
@@ -2015,7 +2028,15 @@ static void view3d_draw_xraytransp(Scene *scene, ARegion *ar, View3D *v3d, const
for (v3da = v3d->afterdraw_xraytransp.first; v3da; v3da = next) {
next = v3da->next;
+ if (v3da->base->flag & OB_FROMDUPLI) {
+ copy_m4_m4(obmat, v3da->base->object->obmat);
+ copy_m4_m4(v3da->base->object->obmat, v3da->obmat);
+ }
draw_object(scene, ar, v3d, v3da->base, v3da->dflag);
+ if (v3da->base->flag & OB_FROMDUPLI) {
+ copy_m4_m4(v3da->base->object->obmat, obmat);
+ MEM_freeN(v3da->base);
+ }
BLI_remlink(&v3d->afterdraw_xraytransp, v3da);
MEM_freeN(v3da);
}
@@ -2026,6 +2047,46 @@ static void view3d_draw_xraytransp(Scene *scene, ARegion *ar, View3D *v3d, const
glDepthMask(GL_TRUE);
}
+static void view3d_draw_nodepth(Scene *scene, ARegion *ar, View3D *v3d)
+{
+ View3DAfter *v3da, *next;
+
+ RegionView3D *rv3d = ar->regiondata;
+
+ glDepthMask(GL_FALSE);
+
+ /* setup drawing environment for paths */
+
+ for (v3da = v3d->afterdraw_nodepth.first; v3da; v3da = next) {
+ Object *ob = v3da->base->object;
+ next = v3da->next;
+
+ glPushMatrix();
+ ED_view3d_init_mats_rv3d_gl(ob, rv3d);
+ view3d_cached_text_draw_begin();
+ if (ob->type == OB_MESH) {
+ bAnimVizSettings *avs = &ob->avs;
+ /* draw motion path for object */
+ draw_motion_paths_init(v3d, ar);
+ draw_motion_path_instance(scene, ob, NULL, avs, ob->mpath);
+ draw_motion_paths_cleanup(v3d);
+ }
+ else if (ob->type == OB_ARMATURE) {
+ draw_pose_paths(scene, v3d, ar, ob);
+ }
+ view3d_cached_text_draw_end(v3d, ar, 1, NULL);
+ ED_view3d_clear_mats_rv3d(rv3d);
+
+ glPopMatrix();
+
+ BLI_remlink(&v3d->afterdraw_nodepth, v3da);
+ MEM_freeN(v3da);
+ }
+
+ /* cleanup after drawing */
+ glDepthMask(GL_TRUE);
+}
+
/* *********************** */
/*
@@ -2046,6 +2107,27 @@ int dupli_ob_sort(void *arg1, void *arg2)
}
#endif
+static void draw_dupli_object(Scene *scene, ARegion *ar, View3D *v3d,
+ Base *base, DupliObject *UNUSED(dob), DupliObjectData *dob_data,
+ short dflag, bool draw_dupli_strands)
+{
+ draw_object(scene, ar, v3d, base, dflag);
+
+ if (dob_data) {
+
+ /* draw strands only when not editing */
+ if (draw_dupli_strands) {
+ DupliObjectDataStrands *link;
+
+ for (link = dob_data->strands.first; link; link = link->next) {
+ struct Strands *strands = link->strands;
+ struct StrandsChildren *children = link->strands_children;
+
+ draw_strands(scene, v3d, ar, base->object, strands, children, dflag);
+ }
+ }
+ }
+}
static DupliObject *dupli_step(DupliObject *dob)
{
@@ -2067,6 +2149,7 @@ static void draw_dupli_objects_color(
GLuint displist = 0;
unsigned char color_rgb[3];
const short dflag_dupli = dflag | DRAW_CONSTCOLOR;
+ const bool draw_dupli_strands = !(base->object->mode & OB_MODE_HAIR_EDIT);
short transflag;
bool use_displist = false; /* -1 is initialize */
char dt;
@@ -2084,7 +2167,7 @@ static void draw_dupli_objects_color(
UI_GetThemeColorBlend3ubv(color, TH_BACK, 0.5f, color_rgb);
}
- tbase.flag = OB_FROMDUPLI | base->flag;
+ tbase.flag |= OB_FROMDUPLI;
lb = object_duplilist(G.main->eval_ctx, scene, base->object);
// BLI_listbase_sort(lb, dupli_ob_sort); /* might be nice to have if we have a dupli list with mixed objects. */
@@ -2094,7 +2177,12 @@ static void draw_dupli_objects_color(
if (dob) dob_next = dupli_step(dob->next);
for (; dob; dob_prev = dob, dob = dob_next, dob_next = dob_next ? dupli_step(dob_next->next) : NULL) {
+ /* for restoring after override */
+ DupliObjectData *dob_data = NULL;
+ DerivedMesh *store_final_dm;
+
tbase.object = dob->ob;
+ store_final_dm = dob->ob->derivedFinal;
/* Make sure lod is updated from dupli's position */
@@ -2116,7 +2204,7 @@ static void draw_dupli_objects_color(
* slow it down too much */
dtx = tbase.object->dtx;
if (tbase.object->dt != OB_BOUNDBOX)
- tbase.object->dtx = base->object->dtx;
+ tbase.object->dtx = base->object->dtx | (dtx & OB_DRAWTRANSP);
/* negative scale flag has to propagate */
transflag = tbase.object->transflag;
@@ -2131,6 +2219,21 @@ static void draw_dupli_objects_color(
glColor3ubv(color_rgb);
}
+ /* override final DM */
+ bb_tmp = NULL;
+ tbase.object->transflag &= ~OB_IS_DUPLI_CACHE;
+ if (base->object->dup_cache) {
+ dob_data = BKE_dupli_cache_find_data(base->object->dup_cache, tbase.object);
+ if (dob_data && dob_data->dm) {
+ tbase.object->transflag |= OB_IS_DUPLI_CACHE;
+
+ tbase.object->derivedFinal = dob_data->dm;
+ bb_tmp = &dob_data->bb;
+ }
+ }
+ if (!bb_tmp)
+ bb_tmp = BKE_object_boundbox_get(dob->ob);
+
/* generate displist, test for new object */
if (dob_prev && dob_prev->ob != dob->ob) {
if (use_displist == true)
@@ -2139,7 +2242,7 @@ static void draw_dupli_objects_color(
use_displist = false;
}
- if ((bb_tmp = BKE_object_boundbox_get(dob->ob))) {
+ if (bb_tmp) {
bb = *bb_tmp; /* must make a copy */
testbb = true;
}
@@ -2161,7 +2264,8 @@ static void draw_dupli_objects_color(
!bb_tmp ||
draw_glsl_material(scene, dob->ob, v3d, dt) ||
check_object_draw_texture(scene, v3d, dt) ||
- (v3d->flag2 & V3D_SOLID_MATCAP) != 0)
+ (v3d->flag2 & V3D_SOLID_MATCAP) != 0 ||
+ ((dtx & OB_DRAWTRANSP) != 0))
{
// printf("draw_dupli_objects_color: skipping displist for %s\n", dob->ob->id.name + 2);
use_displist = false;
@@ -2176,7 +2280,7 @@ static void draw_dupli_objects_color(
displist = glGenLists(1);
glNewList(displist, GL_COMPILE);
- draw_object(scene, ar, v3d, &tbase, dflag_dupli);
+ draw_dupli_object(scene, ar, v3d, &tbase, dob, dob_data, dflag_dupli, draw_dupli_strands);
glEndList();
use_displist = true;
@@ -2192,10 +2296,26 @@ static void draw_dupli_objects_color(
}
else {
copy_m4_m4(dob->ob->obmat, dob->mat);
- draw_object(scene, ar, v3d, &tbase, dflag_dupli);
+ draw_dupli_object(scene, ar, v3d, &tbase, dob, dob_data, dflag_dupli, draw_dupli_strands);
}
}
+ /* restore final DM */
+ if (tbase.object->transflag & OB_IS_DUPLI_CACHE) {
+ DerivedMesh *cur = tbase.object->derivedFinal;
+
+ /* in some cases drawing code can recreate the derivedFinal,
+ * make sure we free those first before restoring
+ */
+ if (cur && cur != dob_data->dm) {
+ cur->needsFree = 1;
+ cur->release(cur);
+ }
+
+ tbase.object->transflag &= ~OB_IS_DUPLI_CACHE;
+ tbase.object->derivedFinal = store_final_dm;
+ }
+
tbase.object->dt = dt;
tbase.object->dtx = dtx;
tbase.object->transflag = transflag;
@@ -2213,17 +2333,27 @@ static void draw_dupli_objects_color(
glDeleteLists(displist, 1);
}
-static void draw_dupli_objects(Scene *scene, ARegion *ar, View3D *v3d, Base *base)
+static void draw_dupli_objects(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const bool is_wire_color)
{
/* define the color here so draw_dupli_objects_color can be called
* from the set loop */
-
- int color = (base->flag & SELECT) ? TH_SELECT : TH_WIRE;
- /* debug */
- if (base->object->dup_group && base->object->dup_group->id.us < 1)
- color = TH_REDALERT;
-
- draw_dupli_objects_color(scene, ar, v3d, base, 0, color);
+ short dflag;
+ int color;
+
+ if (is_wire_color) {
+ glColor3fv(base->object->col);
+ color = TH_UNDEFINED;
+ dflag = DRAW_CONSTCOLOR;
+ }
+ else {
+ color = (base->flag & SELECT) ? TH_SELECT : TH_WIRE;
+ /* debug */
+ if (base->object->dup_group && base->object->dup_group->id.us < 1)
+ color = TH_REDALERT;
+ dflag = 0;
+ }
+
+ draw_dupli_objects_color(scene, ar, v3d, base, dflag, color);
}
/* XXX warning, not using gpu offscreen here */
@@ -2724,6 +2854,7 @@ static void view3d_draw_objects(
RegionView3D *rv3d = ar->regiondata;
Base *base;
const bool do_camera_frame = !draw_offscreen;
+ const bool is_wire_color = V3D_IS_WIRECOLOR(scene, v3d);
const bool draw_grids = !draw_offscreen && (v3d->flag2 & V3D_RENDER_OVERRIDE) == 0;
const bool draw_floor = (rv3d->view == RV3D_VIEW_USER) || (rv3d->persp != RV3D_ORTHO);
/* only draw grids after in solid modes, else it hovers over mesh wires */
@@ -2814,7 +2945,7 @@ static void view3d_draw_objects(
if (v3d->lay & base->lay) {
/* dupli drawing */
if (base->object->transflag & OB_DUPLI)
- draw_dupli_objects(scene, ar, v3d, base);
+ draw_dupli_objects(scene, ar, v3d, base, is_wire_color && (base->object->dtx & OB_DRAW_WIRECOLOR));
draw_object(scene, ar, v3d, base, 0);
}
@@ -2831,7 +2962,7 @@ static void view3d_draw_objects(
/* dupli drawing */
if (base->object->transflag & OB_DUPLI) {
- draw_dupli_objects(scene, ar, v3d, base);
+ draw_dupli_objects(scene, ar, v3d, base, is_wire_color && (base->object->dtx & OB_DRAW_WIRECOLOR));
}
if ((base->flag & SELECT) == 0) {
if (base->object != scene->obedit)
@@ -2843,6 +2974,10 @@ static void view3d_draw_objects(
/* mask out localview */
v3d->lay_used = lay_used & ((1 << 20) - 1);
+ if (is_wire_color && (v3d->drawtype <= OB_WIRE)) {
+ glClear(GL_DEPTH_BUFFER_BIT);
+ }
+
/* draw selected and editmode */
for (base = scene->base.first; base; base = base->next) {
if (v3d->lay & base->lay) {
@@ -2878,6 +3013,8 @@ static void view3d_draw_objects(
if (v3d->afterdraw_xray.first) view3d_draw_xray(scene, ar, v3d, &xrayclear);
if (v3d->afterdraw_xraytransp.first) view3d_draw_xraytransp(scene, ar, v3d, xrayclear);
+ if (v3d->afterdraw_nodepth.first) view3d_draw_nodepth(scene, ar, v3d);
+
if (fx && do_composite_xray) {
GPU_fx_compositor_XRay_resolve(fx);
}
@@ -2894,10 +3031,6 @@ static void view3d_draw_objects(
view3d_draw_bgpic_test(scene, ar, v3d, true, do_camera_frame);
}
- if (!draw_offscreen) {
- BIF_draw_manipulator(C);
- }
-
/* cleanup */
if (v3d->zbuf) {
v3d->zbuf = false;
@@ -2934,6 +3067,8 @@ void ED_view3d_draw_offscreen_init(Scene *scene, View3D *v3d)
*/
static void view3d_main_area_clear(Scene *scene, View3D *v3d, ARegion *ar)
{
+ const bool is_wire_color = V3D_IS_WIRECOLOR(scene, v3d);
+
if (scene->world && (v3d->flag3 & V3D_SHOW_WORLD)) {
bool glsl = GPU_glsl_support() && BKE_scene_use_new_shading_nodes(scene) && scene->world->nodetree && scene->world->use_nodes;
@@ -3096,6 +3231,12 @@ static void view3d_main_area_clear(Scene *scene, View3D *v3d, ARegion *ar)
#undef VIEWGRAD_RES_X
#undef VIEWGRAD_RES_Y
+
+ if (is_wire_color) {
+ float col_mid[3];
+ mid_v3_v3v3(col_mid, col_hor, col_zen);
+ draw_object_bg_wire_color_set(col_mid);
+ }
}
else { /* solid sky */
float col_hor[3];
@@ -3104,10 +3245,18 @@ static void view3d_main_area_clear(Scene *scene, View3D *v3d, ARegion *ar)
glClearColor(col_hor[0], col_hor[1], col_hor[2], 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ if (is_wire_color) {
+ draw_object_bg_wire_color_set(col_hor);
+ }
}
}
else {
if (UI_GetThemeValue(TH_SHOW_BACK_GRAD)) {
+ float col_low[3], col_high[3];
+
+ UI_GetThemeColor3fv(TH_HIGH_GRAD, col_high);
+ UI_GetThemeColor3fv(TH_LOW_GRAD, col_low);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
@@ -3119,10 +3268,10 @@ static void view3d_main_area_clear(Scene *scene, View3D *v3d, ARegion *ar)
glDepthFunc(GL_ALWAYS);
glShadeModel(GL_SMOOTH);
glBegin(GL_QUADS);
- UI_ThemeColor(TH_LOW_GRAD);
+ glColor3fv(col_low);
glVertex3f(-1.0, -1.0, 1.0);
glVertex3f(1.0, -1.0, 1.0);
- UI_ThemeColor(TH_HIGH_GRAD);
+ glColor3fv(col_high);
glVertex3f(1.0, 1.0, 1.0);
glVertex3f(-1.0, 1.0, 1.0);
glEnd();
@@ -3136,10 +3285,23 @@ static void view3d_main_area_clear(Scene *scene, View3D *v3d, ARegion *ar)
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
+
+ if (is_wire_color) {
+ float col_mid[3];
+ mid_v3_v3v3(col_mid, col_low, col_high);
+ draw_object_bg_wire_color_set(col_mid);
+ }
}
else {
+ float col[3];
+
+ UI_GetThemeColor3fv(TH_HIGH_GRAD, col);
UI_ThemeClearColorAlpha(TH_HIGH_GRAD, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ if (is_wire_color) {
+ draw_object_bg_wire_color_set(col);
+ }
}
}
}
@@ -3759,6 +3921,8 @@ static void view3d_main_area_draw_objects(const bContext *C, Scene *scene, View3
/* main drawing call */
view3d_draw_objects(C, scene, v3d, ar, grid_unit, true, false, do_compositing ? rv3d->compositor : NULL);
+ WM_widgets_draw(C, ar->widgetmaps.first, true);
+
/* post process */
if (do_compositing) {
GPU_fx_do_composite_pass(rv3d->compositor, rv3d->winmat, rv3d->is_persp, scene, NULL);
@@ -3872,7 +4036,7 @@ static void view3d_main_area_draw_info(const bContext *C, Scene *scene,
if ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) {
wmWindowManager *wm = CTX_wm_manager(C);
- if ((U.uiflag & USER_SHOW_FPS) && ED_screen_animation_playing(wm)) {
+ if ((U.uiflag & USER_SHOW_FPS) && ED_screen_animation_no_scrub(wm)) {
ED_scene_draw_fps(scene, &rect);
}
else if (U.uiflag & USER_SHOW_VIEWPORTNAME) {
@@ -3907,6 +4071,8 @@ void view3d_main_area_draw(const bContext *C, ARegion *ar)
render_border = ED_view3d_calc_render_border(scene, v3d, ar, &border_rect);
clip_border = (render_border && !BLI_rcti_compare(&ar->drawrct, &border_rect));
+ WM_widgets_update(C, ar->widgetmaps.first);
+
/* draw viewport using opengl */
if (v3d->drawtype != OB_RENDER || !view3d_main_area_do_render_draw(scene) || clip_border) {
view3d_main_area_draw_objects(C, scene, v3d, ar, &grid_unit);
@@ -3916,14 +4082,18 @@ void view3d_main_area_draw(const bContext *C, ARegion *ar)
#endif
if (G.debug & G_DEBUG_SIMDATA)
draw_sim_debug_data(scene, v3d, ar);
-
- ED_region_pixelspace(ar);
}
/* draw viewport using external renderer */
if (v3d->drawtype == OB_RENDER)
view3d_main_area_draw_engine(C, scene, ar, v3d, clip_border, &border_rect);
+ view3d_main_area_setup_view(scene, v3d, ar, NULL, NULL);
+ glClear(GL_DEPTH_BUFFER_BIT);
+ WM_widgets_draw(C, ar->widgetmaps.first, false);
+ BIF_draw_manipulator(C);
+ ED_region_pixelspace(ar);
+
view3d_main_area_draw_info(C, scene, ar, v3d, grid_unit, render_border);
v3d->flag |= V3D_INVALID_BACKBUF;
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index d38de4a426e..c88a8eaac20 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -862,7 +862,7 @@ void viewrotate_modal_keymap(wmKeyConfig *keyconf)
{VIEWROT_MODAL_AXIS_SNAP_ENABLE, "AXIS_SNAP_ENABLE", 0, "Enable Axis Snap", ""},
{VIEWROT_MODAL_AXIS_SNAP_DISABLE, "AXIS_SNAP_DISABLE", 0, "Disable Axis Snap", ""},
-
+
{VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
{VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
@@ -889,7 +889,7 @@ void viewrotate_modal_keymap(wmKeyConfig *keyconf)
WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
#endif
-
+
/* assign map to operators */
WM_modalkeymap_assign(keymap, "VIEW3D_OT_rotate");
@@ -1627,7 +1627,7 @@ void view3d_ndof_fly(
*/
static int ndof_orbit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
-
+
if (event->type != NDOF_MOTION) {
return OPERATOR_CANCELLED;
}
@@ -1693,7 +1693,7 @@ void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot)
static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
-
+
if (event->type != NDOF_MOTION) {
return OPERATOR_CANCELLED;
}
@@ -1889,7 +1889,7 @@ void viewmove_modal_keymap(wmKeyConfig *keyconf)
{
static EnumPropertyItem modal_items[] = {
{VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
-
+
{VIEWROT_MODAL_SWITCH_ZOOM, "SWITCH_TO_ZOOM", 0, "Switch to Zoom"},
{VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
@@ -1913,7 +1913,7 @@ void viewmove_modal_keymap(wmKeyConfig *keyconf)
WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ZOOM);
WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
#endif
-
+
/* assign map to operators */
WM_modalkeymap_assign(keymap, "VIEW3D_OT_move");
}
@@ -2022,9 +2022,9 @@ static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
/* invert it, trackpad scroll follows same principle as 2d windows this way */
viewmove_apply(vod, 2 * event->x - event->prevx, 2 * event->y - event->prevy);
ED_view3d_depth_tag_update(vod->rv3d);
-
+
viewops_data_free(C, op);
-
+
return OPERATOR_FINISHED;
}
else {
@@ -2066,7 +2066,7 @@ void viewzoom_modal_keymap(wmKeyConfig *keyconf)
{
static EnumPropertyItem modal_items[] = {
{VIEW_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
-
+
{VIEWROT_MODAL_SWITCH_ROTATE, "SWITCH_TO_ROTATE", 0, "Switch to Rotate"},
{VIEWROT_MODAL_SWITCH_MOVE, "SWITCH_TO_MOVE", 0, "Switch to Move"},
@@ -2090,7 +2090,7 @@ void viewzoom_modal_keymap(wmKeyConfig *keyconf)
WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
#endif
-
+
/* assign map to operators */
WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom");
}
@@ -2482,7 +2482,7 @@ void viewdolly_modal_keymap(wmKeyConfig *keyconf)
WM_modalkeymap_add_item(keymap, LEFTCTRLKEY, KM_RELEASE, KM_ANY, 0, VIEWROT_MODAL_SWITCH_ROTATE);
WM_modalkeymap_add_item(keymap, LEFTSHIFTKEY, KM_PRESS, KM_ANY, 0, VIEWROT_MODAL_SWITCH_MOVE);
#endif
-
+
/* assign map to operators */
WM_modalkeymap_assign(keymap, "VIEW3D_OT_dolly");
}
@@ -3202,7 +3202,7 @@ static int viewcenter_cursor_exec(bContext *C, wmOperator *op)
View3D *v3d = CTX_wm_view3d(C);
RegionView3D *rv3d = CTX_wm_region_view3d(C);
Scene *scene = CTX_data_scene(C);
-
+
if (rv3d) {
ARegion *ar = CTX_wm_region(C);
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
@@ -3213,10 +3213,10 @@ static int viewcenter_cursor_exec(bContext *C, wmOperator *op)
ED_view3d_smooth_view(C, v3d, ar, NULL, NULL,
new_ofs, NULL, NULL, NULL,
smooth_viewtx);
-
+
/* smooth view does viewlock RV3D_BOXVIEW copy */
}
-
+
return OPERATOR_FINISHED;
}
@@ -3226,11 +3226,11 @@ void VIEW3D_OT_view_center_cursor(wmOperatorType *ot)
ot->name = "Center View to Cursor";
ot->description = "Center the view so that the cursor is in the middle of the view";
ot->idname = "VIEW3D_OT_view_center_cursor";
-
+
/* api callbacks */
ot->exec = viewcenter_cursor_exec;
ot->poll = ED_operator_view3d_active;
-
+
/* flags */
ot->flag = 0;
}
@@ -3542,17 +3542,17 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
/* Get Z Depths, needed for perspective, nice for ortho */
bgl_get_mats(&mats);
ED_view3d_draw_depth(scene, ar, v3d, true);
-
+
{
/* avoid allocating the whole depth buffer */
ViewDepths depth_temp = {0};
/* avoid view3d_update_depths() for speed. */
view3d_update_depths_rect(ar, &depth_temp, &rect);
-
+
/* find the closest Z pixel */
depth_close = view3d_depth_near(&depth_temp);
-
+
MEM_SAFE_FREE(depth_temp.depths);
}
@@ -3692,7 +3692,7 @@ static void view3d_set_1_to_1_viewborder(Scene *scene, ARegion *ar, View3D *v3d)
RegionView3D *rv3d = ar->regiondata;
float size[2];
int im_width = (scene->r.size * scene->r.xsch) / 100;
-
+
ED_view3d_calc_camera_border_size(scene, ar, v3d, rv3d, size);
rv3d->camzoom = BKE_screen_view3d_zoom_from_fac((float)im_width / size[0]);
@@ -4056,7 +4056,7 @@ void VIEW3D_OT_view_orbit(wmOperatorType *ot)
/* flags */
ot->flag = 0;
-
+
/* properties */
prop = RNA_def_float(ot->srna, "angle", 0, -FLT_MAX, FLT_MAX, "Roll", "", -FLT_MAX, FLT_MAX);
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
@@ -4338,7 +4338,7 @@ void VIEW3D_OT_view_pan(wmOperatorType *ot)
/* flags */
ot->flag = 0;
-
+
/* Properties */
ot->prop = RNA_def_enum(ot->srna, "type", prop_view_pan_items, 0, "Pan", "Direction of View Pan");
}
@@ -4440,7 +4440,7 @@ static int background_image_add_invoke(bContext *C, wmOperator *op, const wmEven
v3d->flag |= V3D_DISPBGPICS;
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
-
+
return OPERATOR_FINISHED;
}
@@ -4460,7 +4460,7 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot)
/* flags */
ot->flag = 0;
-
+
/* properties */
RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Image name to assign");
WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE, FILE_SPECIAL, FILE_OPENFILE,
@@ -4507,7 +4507,7 @@ void VIEW3D_OT_background_image_remove(wmOperatorType *ot)
/* flags */
ot->flag = 0;
-
+
/* properties */
RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Background image index to remove", 0, INT_MAX);
}
@@ -4610,14 +4610,14 @@ void ED_view3d_cursor3d_position(bContext *C, float fp[3], const int mval[2])
RegionView3D *rv3d = ar->regiondata;
bool flip;
bool depth_used = false;
-
+
/* normally the caller should ensure this,
* but this is called from areas that aren't already dealing with the viewport */
if (rv3d == NULL)
return;
ED_view3d_calc_zfac(rv3d, fp, &flip);
-
+
/* reset the depth based on the view offset (we _know_ the offset is infront of us) */
if (flip) {
negate_v3_v3(fp, rv3d->ofs);
@@ -4742,14 +4742,14 @@ static int enable_manipulator_invoke(bContext *C, wmOperator *op, const wmEvent
View3D *v3d = CTX_wm_view3d(C);
v3d->twtype = 0;
-
+
if (RNA_boolean_get(op->ptr, "translate"))
v3d->twtype |= V3D_MANIP_TRANSLATE;
if (RNA_boolean_get(op->ptr, "rotate"))
v3d->twtype |= V3D_MANIP_ROTATE;
if (RNA_boolean_get(op->ptr, "scale"))
v3d->twtype |= V3D_MANIP_SCALE;
-
+
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
return OPERATOR_FINISHED;
@@ -4763,11 +4763,11 @@ void VIEW3D_OT_enable_manipulator(wmOperatorType *ot)
ot->name = "Enable 3D Manipulator";
ot->description = "Enable the transform manipulator for use";
ot->idname = "VIEW3D_OT_enable_manipulator";
-
+
/* api callbacks */
ot->invoke = enable_manipulator_invoke;
ot->poll = ED_operator_view3d_active;
-
+
/* rna later */
prop = RNA_def_boolean(ot->srna, "translate", 0, "Translate", "Enable the translate manipulator");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
@@ -4957,7 +4957,7 @@ float ED_view3d_offset_distance(float mat[4][4], const float ofs[3], const float
float pos[4] = {0.0f, 0.0f, 0.0f, 1.0f};
float dir[4] = {0.0f, 0.0f, 1.0f, 0.0f};
float dist;
-
+
mul_m4_v4(mat, pos);
add_v3_v3(pos, ofs);
mul_m4_v4(mat, dir);
diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c
index f8912345e1c..95532bc09a5 100644
--- a/source/blender/editors/space_view3d/view3d_header.c
+++ b/source/blender/editors/space_view3d/view3d_header.c
@@ -340,7 +340,7 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C)
}
/* Manipulators aren't used in paint modes */
- if (!ELEM(ob->mode, OB_MODE_SCULPT, OB_MODE_PARTICLE_EDIT)) {
+ if (!ELEM(ob->mode, OB_MODE_SCULPT, OB_MODE_PARTICLE_EDIT, OB_MODE_HAIR_EDIT)) {
/* masks aren't used for sculpt and particle painting */
PointerRNA meshptr;
diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h
index 95c0ef92680..f4ecde79365 100644
--- a/source/blender/editors/space_view3d/view3d_intern.h
+++ b/source/blender/editors/space_view3d/view3d_intern.h
@@ -46,6 +46,9 @@ struct bContext;
struct bMotionPath;
struct bPoseChannel;
struct Mesh;
+struct BMEditStrands;
+struct Strands;
+struct StrandsChildren;
struct wmNDOFMotionData;
struct wmOperatorType;
struct wmWindowManager;
@@ -132,6 +135,7 @@ void draw_motion_paths_cleanup(View3D *v3d);
/* drawobject.c */
+void draw_object_bg_wire_color_set(const float color[3]);
void draw_object(Scene *scene, struct ARegion *ar, View3D *v3d, Base *base, const short dflag);
bool draw_glsl_material(Scene *scene, struct Object *ob, View3D *v3d, const char dt);
void draw_object_instance(Scene *scene, View3D *v3d, RegionView3D *rv3d, struct Object *ob, const char dt, int outline);
@@ -158,6 +162,7 @@ enum {
bool draw_armature(Scene *scene, View3D *v3d, ARegion *ar, Base *base,
const short dt, const short dflag, const unsigned char ob_wire_col[4],
const bool is_outline);
+void draw_pose_paths(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, struct Object *ob);
/* drawmesh.c */
void draw_mesh_textured(Scene *scene, View3D *v3d, RegionView3D *rv3d,
@@ -179,6 +184,10 @@ void draw_mesh_paint(View3D *v3d, RegionView3D *rv3d,
/* drawsimdebug.c */
void draw_sim_debug_data(Scene *scene, View3D *v3d, ARegion *ar);
+/* drawstrands.c */
+void draw_strands(Scene *scene, View3D *v3d, ARegion *ar, struct Object *ob, struct Strands *strands, struct StrandsChildren *children, short dflag);
+void draw_strands_edit_hair(Scene *scene, View3D *v3d, ARegion *ar, struct BMEditStrands *edit);
+
/* view3d_draw.c */
void view3d_main_area_draw(const struct bContext *C, struct ARegion *ar);
void ED_view3d_draw_depth(Scene *scene, struct ARegion *ar, View3D *v3d, bool alphaoverride);
diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c
index 6a2c948aa8d..56faf945fb9 100644
--- a/source/blender/editors/space_view3d/view3d_ops.c
+++ b/source/blender/editors/space_view3d/view3d_ops.c
@@ -4,7 +4,7 @@
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
+ * of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -18,7 +18,7 @@
* The Original Code is Copyright (C) 2008 Blender Foundation.
* All rights reserved.
*
- *
+ *
* Contributor(s): Blender Foundation
*
* ***** END GPL LICENSE BLOCK *****
@@ -68,9 +68,9 @@ static int view3d_copybuffer_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
char str[FILE_MAX];
-
+
BKE_copybuffer_begin(bmain);
-
+
/* context, selection, could be generalized */
CTX_DATA_BEGIN (C, Object *, ob, selected_objects)
{
@@ -80,7 +80,7 @@ static int view3d_copybuffer_exec(bContext *C, wmOperator *op)
BLI_make_file_string("/", str, BKE_tempdir_base(), "copybuffer.blend");
BKE_copybuffer_save(str, op->reports);
-
+
BKE_report(op->reports, RPT_INFO, "Copied selected objects to buffer");
return OPERATOR_FINISHED;
@@ -88,12 +88,12 @@ static int view3d_copybuffer_exec(bContext *C, wmOperator *op)
static void VIEW3D_OT_copybuffer(wmOperatorType *ot)
{
-
+
/* identifiers */
ot->name = "Copy Selection to Buffer";
ot->idname = "VIEW3D_OT_copybuffer";
ot->description = "Selected objects are saved in a temp file";
-
+
/* api callbacks */
ot->exec = view3d_copybuffer_exec;
ot->poll = ED_operator_view3d_active;
@@ -119,16 +119,16 @@ static int view3d_pastebuffer_exec(bContext *C, wmOperator *op)
static void VIEW3D_OT_pastebuffer(wmOperatorType *ot)
{
-
+
/* identifiers */
ot->name = "Paste Selection from Buffer";
ot->idname = "VIEW3D_OT_pastebuffer";
ot->description = "Contents of copy buffer gets pasted";
-
+
/* api callbacks */
ot->exec = view3d_pastebuffer_exec;
ot->poll = ED_operator_view3d_active;
-
+
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
@@ -186,17 +186,17 @@ void view3d_operatortypes(void)
WM_operatortype_append(VIEW3D_OT_layers);
WM_operatortype_append(VIEW3D_OT_copybuffer);
WM_operatortype_append(VIEW3D_OT_pastebuffer);
-
+
WM_operatortype_append(VIEW3D_OT_properties);
WM_operatortype_append(VIEW3D_OT_toolshelf);
-
+
WM_operatortype_append(VIEW3D_OT_snap_selected_to_grid);
WM_operatortype_append(VIEW3D_OT_snap_selected_to_cursor);
WM_operatortype_append(VIEW3D_OT_snap_cursor_to_grid);
WM_operatortype_append(VIEW3D_OT_snap_cursor_to_center);
WM_operatortype_append(VIEW3D_OT_snap_cursor_to_selected);
WM_operatortype_append(VIEW3D_OT_snap_cursor_to_active);
-
+
transform_operatortypes();
}
@@ -204,12 +204,12 @@ void view3d_keymap(wmKeyConfig *keyconf)
{
wmKeyMap *keymap;
wmKeyMapItem *kmi;
-
+
keymap = WM_keymap_find(keyconf, "3D View Generic", SPACE_VIEW3D, 0);
WM_keymap_add_item(keymap, "VIEW3D_OT_properties", NKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "VIEW3D_OT_toolshelf", TKEY, KM_PRESS, 0, 0);
-
+
/* only for region 3D window */
keymap = WM_keymap_find(keyconf, "3D View", SPACE_VIEW3D, 0);
@@ -219,9 +219,9 @@ void view3d_keymap(wmKeyConfig *keyconf)
* Doesn't work with KM_SHIFT, have to use KM_ANY and filter in invoke
* */
// WM_keymap_verify_item(keymap, "VIEW3D_OT_manipulator", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0);
-
+
WM_keymap_verify_item(keymap, "VIEW3D_OT_cursor3d", ACTIONMOUSE, KM_PRESS, 0, 0);
-
+
WM_keymap_verify_item(keymap, "VIEW3D_OT_rotate", MIDDLEMOUSE, KM_PRESS, 0, 0);
WM_keymap_verify_item(keymap, "VIEW3D_OT_move", MIDDLEMOUSE, KM_PRESS, KM_SHIFT, 0);
WM_keymap_verify_item(keymap, "VIEW3D_OT_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0);
@@ -244,14 +244,14 @@ void view3d_keymap(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "VIEW3D_OT_move", MOUSEPAN, 0, KM_SHIFT, 0);
WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", MOUSEZOOM, 0, 0, 0);
WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", MOUSEPAN, 0, KM_CTRL, 0);
-
+
/*numpad +/-*/
RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", PADPLUSKEY, KM_PRESS, 0, 0)->ptr, "delta", 1);
RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", PADMINUS, KM_PRESS, 0, 0)->ptr, "delta", -1);
/*ctrl +/-*/
RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", EQUALKEY, KM_PRESS, KM_CTRL, 0)->ptr, "delta", 1);
RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", MINUSKEY, KM_PRESS, KM_CTRL, 0)->ptr, "delta", -1);
-
+
/*wheel mouse forward/back*/
RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", WHEELINMOUSE, KM_PRESS, 0, 0)->ptr, "delta", 1);
RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_zoom", WHEELOUTMOUSE, KM_PRESS, 0, 0)->ptr, "delta", -1);
@@ -287,7 +287,7 @@ void view3d_keymap(wmKeyConfig *keyconf)
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", PAD3, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_RIGHT);
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_orbit", PAD4, KM_PRESS, 0, 0)->ptr, "type", V3D_VIEW_STEPLEFT);
WM_keymap_add_item(keymap, "VIEW3D_OT_view_persportho", PAD5, KM_PRESS, 0, 0);
-
+
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_orbit", PAD6, KM_PRESS, 0, 0)->ptr, "type", V3D_VIEW_STEPRIGHT);
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", PAD7, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_TOP);
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_orbit", PAD8, KM_PRESS, 0, 0)->ptr, "type", V3D_VIEW_STEPUP);
@@ -313,7 +313,7 @@ void view3d_keymap(wmKeyConfig *keyconf)
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_orbit", WHEELDOWNMOUSE, KM_PRESS, KM_CTRL | KM_ALT, 0)->ptr, "type", V3D_VIEW_STEPRIGHT);
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_orbit", WHEELUPMOUSE, KM_PRESS, KM_SHIFT | KM_ALT, 0)->ptr, "type", V3D_VIEW_STEPUP);
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_orbit", WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT | KM_ALT, 0)->ptr, "type", V3D_VIEW_STEPDOWN);
-
+
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", WHEELUPMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0)->ptr, "type", V3D_VIEW_STEPLEFT);
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", WHEELDOWNMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0)->ptr, "type", V3D_VIEW_STEPRIGHT);
@@ -338,7 +338,7 @@ void view3d_keymap(wmKeyConfig *keyconf)
RNA_boolean_set(kmi->ptr, "align_active", true);
WM_keymap_add_item(keymap, "VIEW3D_OT_localview", PADSLASHKEY, KM_PRESS, 0, 0);
-
+
/* NDOF: begin */
/* note: positioned here so keymaps show keyboard keys if assigned */
/* 3D mouse */
@@ -358,7 +358,7 @@ void view3d_keymap(wmKeyConfig *keyconf)
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_RIGHT, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_RIGHT);
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_TOP, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_TOP);
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_BOTTOM, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_BOTTOM);
-
+
/* 3D mouse align */
kmi = WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_FRONT, KM_PRESS, KM_SHIFT, 0);
RNA_enum_set(kmi->ptr, "type", RV3D_VIEW_FRONT);
@@ -370,7 +370,7 @@ void view3d_keymap(wmKeyConfig *keyconf)
RNA_enum_set(kmi->ptr, "type", RV3D_VIEW_TOP);
RNA_boolean_set(kmi->ptr, "align_active", true);
/* NDOF: end */
-
+
/* layers, shift + alt are properties set in invoke() */
RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_layers", ACCENTGRAVEKEY, KM_PRESS, 0, 0)->ptr, "nr", 0);
@@ -384,7 +384,7 @@ void view3d_keymap(wmKeyConfig *keyconf)
RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_layers", EIGHTKEY, KM_PRESS, KM_ANY, 0)->ptr, "nr", 8);
RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_layers", NINEKEY, KM_PRESS, KM_ANY, 0)->ptr, "nr", 9);
RNA_int_set(WM_keymap_add_item(keymap, "VIEW3D_OT_layers", ZEROKEY, KM_PRESS, KM_ANY, 0)->ptr, "nr", 10);
-
+
/* drawtype */
kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle_enum", ZKEY, KM_PRESS, 0, 0);
@@ -468,7 +468,7 @@ void view3d_keymap(wmKeyConfig *keyconf)
kmi = WM_keymap_add_item(keymap, "VIEW3D_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_SHIFT | KM_CTRL, 0);
RNA_boolean_set(kmi->ptr, "deselect", true);
WM_keymap_add_item(keymap, "VIEW3D_OT_select_circle", CKEY, KM_PRESS, 0, 0);
-
+
WM_keymap_add_item(keymap, "VIEW3D_OT_clip_border", BKEY, KM_PRESS, KM_ALT, 0);
WM_keymap_add_item(keymap, "VIEW3D_OT_zoom_border", BKEY, KM_PRESS, KM_SHIFT, 0);
@@ -478,19 +478,19 @@ void view3d_keymap(wmKeyConfig *keyconf)
RNA_boolean_set(kmi->ptr, "camera_only", false);
WM_keymap_add_item(keymap, "VIEW3D_OT_clear_render_border", BKEY, KM_PRESS, KM_CTRL | KM_ALT, 0);
-
+
WM_keymap_add_item(keymap, "VIEW3D_OT_camera_to_view", PAD0, KM_PRESS, KM_ALT | KM_CTRL, 0);
WM_keymap_add_item(keymap, "VIEW3D_OT_object_as_camera", PAD0, KM_PRESS, KM_CTRL, 0);
-
+
WM_keymap_add_menu(keymap, "VIEW3D_MT_snap", SKEY, KM_PRESS, KM_SHIFT, 0);
-
+
#ifdef __APPLE__
WM_keymap_add_item(keymap, "VIEW3D_OT_copybuffer", CKEY, KM_PRESS, KM_OSKEY, 0);
WM_keymap_add_item(keymap, "VIEW3D_OT_pastebuffer", VKEY, KM_PRESS, KM_OSKEY, 0);
#endif
WM_keymap_add_item(keymap, "VIEW3D_OT_copybuffer", CKEY, KM_PRESS, KM_CTRL, 0);
WM_keymap_add_item(keymap, "VIEW3D_OT_pastebuffer", VKEY, KM_PRESS, KM_CTRL, 0);
-
+
/* context ops */
kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", COMMAKEY, KM_PRESS, 0, 0);
RNA_string_set(kmi->ptr, "data_path", "space_data.pivot_point");
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 49e42cf164a..1847de3c6a2 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -85,9 +85,11 @@
#include "ED_armature.h"
#include "ED_curve.h"
+#include "ED_physics.h"
#include "ED_particle.h"
#include "ED_mesh.h"
#include "ED_object.h"
+#include "ED_physics.h"
#include "ED_screen.h"
#include "ED_sculpt.h"
#include "ED_mball.h"
@@ -834,6 +836,8 @@ static void view3d_lasso_select(bContext *C, ViewContext *vc,
}
else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT))
PE_lasso_select(C, mcords, moves, extend, select);
+ else if (ob && (ob->mode & OB_MODE_HAIR_EDIT))
+ ED_hair_lasso_select(C, mcords, moves, extend, select);
else {
do_lasso_select_objects(vc, mcords, moves, extend, select);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene);
@@ -2146,6 +2150,9 @@ static int view3d_borderselect_exec(bContext *C, wmOperator *op)
else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) {
ret = PE_border_select(C, &rect, select, extend);
}
+ else if (vc.obact && vc.obact->mode & OB_MODE_HAIR_EDIT) {
+ ret = ED_hair_border_select(C, &rect, select, extend);
+ }
else { /* object mode with none active */
ret = do_object_pose_box_select(C, &vc, &rect, select, extend);
}
@@ -2276,6 +2283,8 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
}
else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT)
return PE_mouse_particles(C, location, extend, deselect, toggle);
+ else if (obact && obact->mode & OB_MODE_HAIR_EDIT)
+ return ED_hair_mouse_select(C, location, extend, deselect, toggle);
else if (obact && BKE_paint_select_face_test(obact))
retval = paintface_mouse_select(C, obact, location, extend, deselect, toggle);
else if (BKE_paint_select_vert_test(obact))
@@ -2791,7 +2800,7 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
RNA_int_get(op->ptr, "y")};
if (CTX_data_edit_object(C) || BKE_paint_select_elem_test(obact) ||
- (obact && (obact->mode & (OB_MODE_PARTICLE_EDIT | OB_MODE_POSE))) )
+ (obact && (obact->mode & (OB_MODE_PARTICLE_EDIT | OB_MODE_POSE | OB_MODE_HAIR_EDIT))) )
{
ViewContext vc;
@@ -2811,10 +2820,16 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
paint_vertsel_circle_select(&vc, select, mval, (float)radius);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data);
}
- else if (obact->mode & OB_MODE_POSE)
+ else if (obact->mode & OB_MODE_POSE) {
pose_circle_select(&vc, select, mval, (float)radius);
- else
+ }
+ else if (obact->mode & OB_MODE_HAIR_EDIT) {
+ ED_hair_circle_select(C, select, mval, (float)radius);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data);
+ }
+ else {
return PE_circle_select(C, select, mval, (float)radius);
+ }
}
else if (obact && obact->mode & OB_MODE_SCULPT) {
return OPERATOR_CANCELLED;
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index 529e5fcf756..e9cb6dcdc44 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -44,6 +44,7 @@
#include "BKE_camera.h"
#include "BKE_context.h"
#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
#include "BKE_object.h"
#include "BKE_global.h"
#include "BKE_main.h"
@@ -1030,23 +1031,55 @@ static void view3d_select_loop(ViewContext *vc, Scene *scene, View3D *v3d, ARegi
lb = object_duplilist(G.main->eval_ctx, scene, base->object);
for (dob = lb->first; dob; dob = dob->next) {
- float omat[4][4];
+ /* for restoring after override */
+ DupliObjectData *dob_data = NULL;
+ DerivedMesh *store_final_dm;
+ float store_obmat[4][4];
tbase.object = dob->ob;
- copy_m4_m4(omat, dob->ob->obmat);
+ copy_m4_m4(store_obmat, dob->ob->obmat);
copy_m4_m4(dob->ob->obmat, dob->mat);
+ store_final_dm = dob->ob->derivedFinal;
/* extra service: draw the duplicator in drawtype of parent */
/* MIN2 for the drawtype to allow bounding box objects in groups for lods */
dt = tbase.object->dt; tbase.object->dt = MIN2(tbase.object->dt, base->object->dt);
dtx = tbase.object->dtx; tbase.object->dtx = base->object->dtx;
+ /* override final DM */
+ tbase.object->transflag &= ~OB_IS_DUPLI_CACHE;
+ if (base->object->dup_cache) {
+ dob_data = BKE_dupli_cache_find_data(base->object->dup_cache, tbase.object);
+ if (dob_data && dob_data->dm) {
+ tbase.object->transflag |= OB_IS_DUPLI_CACHE;
+
+ tbase.object->derivedFinal = dob_data->dm;
+ }
+ }
+
draw_object(scene, ar, v3d, &tbase, DRAW_PICKING | DRAW_CONSTCOLOR);
+ /* restore final DM */
+ if (tbase.object->transflag & OB_IS_DUPLI_CACHE) {
+ DerivedMesh *cur = tbase.object->derivedFinal;
+
+ /* in some cases drawing code can recreate the derivedFinal,
+ * make sure we free those first before restoring
+ */
+ if (cur && cur != dob_data->dm) {
+ cur->needsFree = 1;
+ cur->release(cur);
+ }
+
+ tbase.object->transflag &= ~OB_IS_DUPLI_CACHE;
+ tbase.object->derivedFinal = store_final_dm;
+ }
+
tbase.object->dt = dt;
tbase.object->dtx = dtx;
- copy_m4_m4(dob->ob->obmat, omat);
+ /* restore obmat and final DM */
+ copy_m4_m4(dob->ob->obmat, store_obmat);
}
free_object_duplilist(lb);
}
diff --git a/source/blender/editors/transform/SConscript b/source/blender/editors/transform/SConscript
index 1a2e9ab074a..d1931399055 100644
--- a/source/blender/editors/transform/SConscript
+++ b/source/blender/editors/transform/SConscript
@@ -28,6 +28,7 @@
Import ('env')
sources = env.Glob('*.c')
+sources.remove('manipulator_widget.c')
incs = [
'#/intern/guardedalloc',
diff --git a/source/blender/editors/transform/manipulator_widget.c b/source/blender/editors/transform/manipulator_widget.c
new file mode 100644
index 00000000000..eff9fda4378
--- /dev/null
+++ b/source/blender/editors/transform/manipulator_widget.c
@@ -0,0 +1,2082 @@
+/*
+ * ***** 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) 2005 Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/transform/transform_manipulator.c
+ * \ingroup edtransform
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+
+#include "DNA_armature_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_lattice_types.h"
+#include "DNA_meta_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_view3d_types.h"
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+#include "BLI_listbase.h"
+
+#include "RNA_access.h"
+
+#include "BKE_action.h"
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_global.h"
+#include "BKE_particle.h"
+#include "BKE_pointcache.h"
+#include "BKE_editmesh.h"
+#include "BKE_lattice.h"
+
+#include "BIF_gl.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_armature.h"
+#include "ED_curve.h"
+#include "ED_particle.h"
+#include "ED_view3d.h"
+#include "ED_screen.h"
+
+#include "UI_resources.h"
+#include "UI_interface.h"
+
+/* local module include */
+#include "transform.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "GPU_select.h"
+
+/* drawing flags */
+
+#define MAN_TRANS_X (1 << 0)
+#define MAN_TRANS_Y (1 << 1)
+#define MAN_TRANS_Z (1 << 2)
+#define MAN_TRANS_C (MAN_TRANS_X | MAN_TRANS_Y | MAN_TRANS_Z)
+
+#define MAN_ROT_X (1 << 3)
+#define MAN_ROT_Y (1 << 4)
+#define MAN_ROT_Z (1 << 5)
+#define MAN_ROT_V (1 << 6)
+#define MAN_ROT_T (1 << 7)
+#define MAN_ROT_C (MAN_ROT_X | MAN_ROT_Y | MAN_ROT_Z | MAN_ROT_V | MAN_ROT_T)
+
+#define MAN_SCALE_X (1 << 8)
+#define MAN_SCALE_Y (1 << 9)
+#define MAN_SCALE_Z (1 << 10)
+#define MAN_SCALE_C (MAN_SCALE_X | MAN_SCALE_Y | MAN_SCALE_Z)
+
+/* return codes for select */
+enum {
+ MAN_SEL_TRANS_X = 0,
+ MAN_SEL_TRANS_Y,
+ MAN_SEL_TRANS_Z,
+
+ MAN_SEL_ROT_X,
+ MAN_SEL_ROT_Y,
+ MAN_SEL_ROT_Z,
+ MAN_SEL_ROT_V,
+ MAN_SEL_ROT_T,
+
+ MAN_SEL_SCALE_X,
+ MAN_SEL_SCALE_Y,
+ MAN_SEL_SCALE_Z,
+
+ /* those two stay at the end so the rest can be inferred with bitshifting */
+ MAN_SEL_SCALE_C,
+ MAN_SEL_TRANS_C,
+
+ MAN_SEL_MAX
+};
+
+/* color codes */
+
+#define MAN_RGB 0
+#define MAN_GHOST 1
+#define MAN_MOVECOL 2
+
+/* threshold for testing view aligned manipulator axis */
+#define TW_AXIS_DOT_MIN 0.02f
+#define TW_AXIS_DOT_MAX 0.1f
+
+/* transform widget center calc helper for below */
+static void calc_tw_center(Scene *scene, const float co[3])
+{
+ float *twcent = scene->twcent;
+ float *min = scene->twmin;
+ float *max = scene->twmax;
+
+ minmax_v3v3_v3(min, max, co);
+ add_v3_v3(twcent, co);
+}
+
+static void protectflag_to_drawflags(short protectflag, short *drawflags)
+{
+ if (protectflag & OB_LOCK_LOCX)
+ *drawflags &= ~MAN_TRANS_X;
+ if (protectflag & OB_LOCK_LOCY)
+ *drawflags &= ~MAN_TRANS_Y;
+ if (protectflag & OB_LOCK_LOCZ)
+ *drawflags &= ~MAN_TRANS_Z;
+
+ if (protectflag & OB_LOCK_ROTX)
+ *drawflags &= ~MAN_ROT_X;
+ if (protectflag & OB_LOCK_ROTY)
+ *drawflags &= ~MAN_ROT_Y;
+ if (protectflag & OB_LOCK_ROTZ)
+ *drawflags &= ~MAN_ROT_Z;
+
+ if (protectflag & OB_LOCK_SCALEX)
+ *drawflags &= ~MAN_SCALE_X;
+ if (protectflag & OB_LOCK_SCALEY)
+ *drawflags &= ~MAN_SCALE_Y;
+ if (protectflag & OB_LOCK_SCALEZ)
+ *drawflags &= ~MAN_SCALE_Z;
+}
+
+/* for pose mode */
+static void stats_pose(Scene *scene, RegionView3D *rv3d, bPoseChannel *pchan)
+{
+ Bone *bone = pchan->bone;
+
+ if (bone) {
+ calc_tw_center(scene, pchan->pose_head);
+ protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag);
+ }
+}
+
+/* for editmode*/
+static void stats_editbone(RegionView3D *rv3d, EditBone *ebo)
+{
+ if (ebo->flag & BONE_EDITMODE_LOCKED)
+ protectflag_to_drawflags(OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE, &rv3d->twdrawflag);
+}
+
+/* could move into BLI_math however this is only useful for display/editing purposes */
+static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle)
+{
+ /* X/Y are arbitrary axies, most importantly Z is the axis of rotation */
+
+ float cross_vec[3];
+ float quat[4];
+
+ /* this is an un-scientific method to get a vector to cross with
+ * XYZ intentionally YZX */
+ cross_vec[0] = axis[1];
+ cross_vec[1] = axis[2];
+ cross_vec[2] = axis[0];
+
+ /* X-axis */
+ cross_v3_v3v3(gmat[0], cross_vec, axis);
+ normalize_v3(gmat[0]);
+ axis_angle_to_quat(quat, axis, angle);
+ mul_qt_v3(quat, gmat[0]);
+
+ /* Y-axis */
+ axis_angle_to_quat(quat, axis, M_PI / 2.0);
+ copy_v3_v3(gmat[1], gmat[0]);
+ mul_qt_v3(quat, gmat[1]);
+
+ /* Z-axis */
+ copy_v3_v3(gmat[2], axis);
+
+ normalize_m3(gmat);
+}
+
+
+static int test_rotmode_euler(short rotmode)
+{
+ return (ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) ? 0 : 1;
+}
+
+bool gimbal_axis(Object *ob, float gmat[3][3])
+{
+ if (ob) {
+ if (ob->mode & OB_MODE_POSE) {
+ bPoseChannel *pchan = BKE_pose_channel_active(ob);
+
+ if (pchan) {
+ float mat[3][3], tmat[3][3], obmat[3][3];
+ if (test_rotmode_euler(pchan->rotmode)) {
+ eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode);
+ }
+ else if (pchan->rotmode == ROT_MODE_AXISANGLE) {
+ axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle);
+ }
+ else { /* quat */
+ return 0;
+ }
+
+
+ /* apply bone transformation */
+ mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat);
+
+ if (pchan->parent) {
+ float parent_mat[3][3];
+
+ copy_m3_m4(parent_mat, pchan->parent->pose_mat);
+ mul_m3_m3m3(mat, parent_mat, tmat);
+
+ /* needed if object transformation isn't identity */
+ copy_m3_m4(obmat, ob->obmat);
+ mul_m3_m3m3(gmat, obmat, mat);
+ }
+ else {
+ /* needed if object transformation isn't identity */
+ copy_m3_m4(obmat, ob->obmat);
+ mul_m3_m3m3(gmat, obmat, tmat);
+ }
+
+ normalize_m3(gmat);
+ return 1;
+ }
+ }
+ else {
+ if (test_rotmode_euler(ob->rotmode)) {
+ eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode);
+ }
+ else if (ob->rotmode == ROT_MODE_AXISANGLE) {
+ axis_angle_to_gimbal_axis(gmat, ob->rotAxis, ob->rotAngle);
+ }
+ else { /* quat */
+ return 0;
+ }
+
+ if (ob->parent) {
+ float parent_mat[3][3];
+ copy_m3_m4(parent_mat, ob->parent->obmat);
+ normalize_m3(parent_mat);
+ mul_m3_m3m3(gmat, parent_mat, gmat);
+ }
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+/* centroid, boundbox, of selection */
+/* returns total items selected */
+static int calc_manipulator_stats(const bContext *C)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ ARegion *ar = CTX_wm_region(C);
+ Scene *scene = CTX_data_scene(C);
+ Object *obedit = CTX_data_edit_object(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ View3D *v3d = sa->spacedata.first;
+ RegionView3D *rv3d = ar->regiondata;
+ Base *base;
+ Object *ob = OBACT;
+ int a, totsel = 0;
+
+ /* transform widget matrix */
+ unit_m4(rv3d->twmat);
+
+ rv3d->twdrawflag = 0xFFFF;
+
+ /* transform widget centroid/center */
+ INIT_MINMAX(scene->twmin, scene->twmax);
+ zero_v3(scene->twcent);
+
+ if (obedit) {
+ ob = obedit;
+ if ((ob->lay & v3d->lay) == 0) return 0;
+
+ if (obedit->type == OB_MESH) {
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMEditSelection ese;
+ float vec[3] = {0, 0, 0};
+
+ /* USE LAST SELECTE WITH ACTIVE */
+ if ((v3d->around == V3D_ACTIVE) && BM_select_history_active_get(em->bm, &ese)) {
+ BM_editselection_center(&ese, vec);
+ calc_tw_center(scene, vec);
+ totsel = 1;
+ }
+ else {
+ BMesh *bm = em->bm;
+ BMVert *eve;
+
+ BMIter iter;
+
+ /* do vertices/edges/faces for center depending on selection
+ * mode. note we can't use just vertex selection flag because
+ * it is not flush down on changes */
+ if (ts->selectmode & SCE_SELECT_VERTEX) {
+ BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
+ if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+ if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ totsel++;
+ calc_tw_center(scene, eve->co);
+ }
+ }
+ }
+ }
+ else if (ts->selectmode & SCE_SELECT_EDGE) {
+ BMIter itersub;
+ BMEdge *eed;
+ BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
+ if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+ /* check the vertex has a selected edge, only add it once */
+ BM_ITER_ELEM (eed, &itersub, eve, BM_EDGES_OF_VERT) {
+ if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
+ totsel++;
+ calc_tw_center(scene, eve->co);
+ break;
+ }
+ }
+ }
+ }
+ }
+ else {
+ BMIter itersub;
+ BMFace *efa;
+ BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
+ if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+ /* check the vertex has a selected face, only add it once */
+ BM_ITER_ELEM (efa, &itersub, eve, BM_FACES_OF_VERT) {
+ if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ totsel++;
+ calc_tw_center(scene, eve->co);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ } /* end editmesh */
+ else if (obedit->type == OB_ARMATURE) {
+ bArmature *arm = obedit->data;
+ EditBone *ebo;
+
+ if ((v3d->around == V3D_ACTIVE) && (ebo = arm->act_edbone)) {
+ /* doesn't check selection or visibility intentionally */
+ if (ebo->flag & BONE_TIPSEL) {
+ calc_tw_center(scene, ebo->tail);
+ totsel++;
+ }
+ if ((ebo->flag & BONE_ROOTSEL) ||
+ ((ebo->flag & BONE_TIPSEL) == false)) /* ensure we get at least one point */
+ {
+ calc_tw_center(scene, ebo->head);
+ totsel++;
+ }
+ stats_editbone(rv3d, ebo);
+ }
+ else {
+ for (ebo = arm->edbo->first; ebo; ebo = ebo->next) {
+ if (EBONE_VISIBLE(arm, ebo)) {
+ if (ebo->flag & BONE_TIPSEL) {
+ calc_tw_center(scene, ebo->tail);
+ totsel++;
+ }
+ if (ebo->flag & BONE_ROOTSEL) {
+ calc_tw_center(scene, ebo->head);
+ totsel++;
+ }
+ if (ebo->flag & BONE_SELECTED) {
+ stats_editbone(rv3d, ebo);
+ }
+ }
+ }
+ }
+ }
+ else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
+ Curve *cu = obedit->data;
+ float center[3];
+
+ if (v3d->around == V3D_ACTIVE && ED_curve_active_center(cu, center)) {
+ calc_tw_center(scene, center);
+ totsel++;
+ }
+ else {
+ Nurb *nu;
+ BezTriple *bezt;
+ BPoint *bp;
+ ListBase *nurbs = BKE_curve_editNurbs_get(cu);
+
+ nu = nurbs->first;
+ while (nu) {
+ if (nu->type == CU_BEZIER) {
+ bezt = nu->bezt;
+ a = nu->pntsu;
+ while (a--) {
+ /* exceptions
+ * if handles are hidden then only check the center points.
+ * If the center knot is selected then only use this as the center point.
+ */
+ if (cu->drawflag & CU_HIDE_HANDLES) {
+ if (bezt->f2 & SELECT) {
+ calc_tw_center(scene, bezt->vec[1]);
+ totsel++;
+ }
+ }
+ else if (bezt->f2 & SELECT) {
+ calc_tw_center(scene, bezt->vec[1]);
+ totsel++;
+ }
+ else {
+ if (bezt->f1 & SELECT) {
+ calc_tw_center(scene, bezt->vec[(v3d->around == V3D_LOCAL) ? 1 : 0]);
+ totsel++;
+ }
+ if (bezt->f3 & SELECT) {
+ calc_tw_center(scene, bezt->vec[(v3d->around == V3D_LOCAL) ? 1 : 2]);
+ totsel++;
+ }
+ }
+ bezt++;
+ }
+ }
+ else {
+ bp = nu->bp;
+ a = nu->pntsu * nu->pntsv;
+ while (a--) {
+ if (bp->f1 & SELECT) {
+ calc_tw_center(scene, bp->vec);
+ totsel++;
+ }
+ bp++;
+ }
+ }
+ nu = nu->next;
+ }
+ }
+ }
+ else if (obedit->type == OB_MBALL) {
+ MetaBall *mb = (MetaBall *)obedit->data;
+ MetaElem *ml;
+
+ if ((v3d->around == V3D_ACTIVE) && (ml = mb->lastelem)) {
+ calc_tw_center(scene, &ml->x);
+ totsel++;
+ }
+ else {
+ for (ml = mb->editelems->first; ml; ml = ml->next) {
+ if (ml->flag & SELECT) {
+ calc_tw_center(scene, &ml->x);
+ totsel++;
+ }
+ }
+ }
+ }
+ else if (obedit->type == OB_LATTICE) {
+ Lattice *lt = ((Lattice *)obedit->data)->editlatt->latt;
+ BPoint *bp;
+
+ if ((v3d->around == V3D_ACTIVE) && (bp = BKE_lattice_active_point_get(lt))) {
+ calc_tw_center(scene, bp->vec);
+ totsel++;
+ }
+ else {
+ bp = lt->def;
+ a = lt->pntsu * lt->pntsv * lt->pntsw;
+ while (a--) {
+ if (bp->f1 & SELECT) {
+ calc_tw_center(scene, bp->vec);
+ totsel++;
+ }
+ bp++;
+ }
+ }
+ }
+
+ /* selection center */
+ if (totsel) {
+ mul_v3_fl(scene->twcent, 1.0f / (float)totsel); // centroid!
+ mul_m4_v3(obedit->obmat, scene->twcent);
+ mul_m4_v3(obedit->obmat, scene->twmin);
+ mul_m4_v3(obedit->obmat, scene->twmax);
+ }
+ }
+ else if (ob && (ob->mode & OB_MODE_POSE)) {
+ bPoseChannel *pchan;
+ int mode = TFM_ROTATION; // mislead counting bones... bah. We don't know the manipulator mode, could be mixed
+ bool ok = false;
+
+ if ((ob->lay & v3d->lay) == 0) return 0;
+
+ if ((v3d->around == V3D_ACTIVE) && (pchan = BKE_pose_channel_active(ob))) {
+ /* doesn't check selection or visibility intentionally */
+ Bone *bone = pchan->bone;
+ if (bone) {
+ stats_pose(scene, rv3d, pchan);
+ totsel = 1;
+ ok = true;
+ }
+ }
+ else {
+ totsel = count_set_pose_transflags(&mode, 0, ob);
+
+ if (totsel) {
+ /* use channels to get stats */
+ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+ Bone *bone = pchan->bone;
+ if (bone && (bone->flag & BONE_TRANSFORM)) {
+ stats_pose(scene, rv3d, pchan);
+ }
+ }
+ ok = true;
+ }
+ }
+
+ if (ok) {
+ mul_v3_fl(scene->twcent, 1.0f / (float)totsel); // centroid!
+ mul_m4_v3(ob->obmat, scene->twcent);
+ mul_m4_v3(ob->obmat, scene->twmin);
+ mul_m4_v3(ob->obmat, scene->twmax);
+ }
+ }
+ else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
+ /* pass */
+ }
+ else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) {
+ PTCacheEdit *edit = PE_get_current(scene, ob);
+ PTCacheEditPoint *point;
+ PTCacheEditKey *ek;
+ int k;
+
+ if (edit) {
+ point = edit->points;
+ for (a = 0; a < edit->totpoint; a++, point++) {
+ if (point->flag & PEP_HIDE) continue;
+
+ for (k = 0, ek = point->keys; k < point->totkey; k++, ek++) {
+ if (ek->flag & PEK_SELECT) {
+ calc_tw_center(scene, ek->flag & PEK_USE_WCO ? ek->world_co : ek->co);
+ totsel++;
+ }
+ }
+ }
+
+ /* selection center */
+ if (totsel)
+ mul_v3_fl(scene->twcent, 1.0f / (float)totsel); // centroid!
+ }
+ }
+ else {
+
+ /* we need the one selected object, if its not active */
+ ob = OBACT;
+ if (ob && !(ob->flag & SELECT)) ob = NULL;
+
+ for (base = scene->base.first; base; base = base->next) {
+ if (TESTBASELIB(v3d, base)) {
+ if (ob == NULL)
+ ob = base->object;
+ calc_tw_center(scene, base->object->obmat[3]);
+ protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag);
+ totsel++;
+ }
+ }
+
+ /* selection center */
+ if (totsel) {
+ mul_v3_fl(scene->twcent, 1.0f / (float)totsel); // centroid!
+ }
+ }
+
+ /* global, local or normal orientation? */
+ if (ob && totsel) {
+
+ switch (v3d->twmode) {
+
+ case V3D_MANIP_GLOBAL:
+ {
+ break; /* nothing to do */
+ }
+ case V3D_MANIP_GIMBAL:
+ {
+ float mat[3][3];
+ if (gimbal_axis(ob, mat)) {
+ copy_m4_m3(rv3d->twmat, mat);
+ break;
+ }
+ /* if not gimbal, fall through to normal */
+ /* fall-through */
+ }
+ case V3D_MANIP_NORMAL:
+ {
+ if (obedit || ob->mode & OB_MODE_POSE) {
+ float mat[3][3];
+ ED_getTransformOrientationMatrix(C, mat, (v3d->around == V3D_ACTIVE));
+ copy_m4_m3(rv3d->twmat, mat);
+ break;
+ }
+ /* no break we define 'normal' as 'local' in Object mode */
+ /* fall-through */
+ }
+ case V3D_MANIP_LOCAL:
+ {
+ if (ob->mode & OB_MODE_POSE) {
+ /* each bone moves on its own local axis, but to avoid confusion,
+ * use the active pones axis for display [#33575], this works as expected on a single bone
+ * and users who select many bones will understand whats going on and what local means
+ * when they start transforming */
+ float mat[3][3];
+ ED_getTransformOrientationMatrix(C, mat, (v3d->around == V3D_ACTIVE));
+ copy_m4_m3(rv3d->twmat, mat);
+ break;
+ }
+ copy_m4_m4(rv3d->twmat, ob->obmat);
+ normalize_m4(rv3d->twmat);
+ break;
+ }
+ case V3D_MANIP_VIEW:
+ {
+ float mat[3][3];
+ copy_m3_m4(mat, rv3d->viewinv);
+ normalize_m3(mat);
+ copy_m4_m3(rv3d->twmat, mat);
+ break;
+ }
+ default: /* V3D_MANIP_CUSTOM */
+ {
+ float mat[3][3];
+ if (applyTransformOrientation(C, mat, NULL)) {
+ copy_m4_m3(rv3d->twmat, mat);
+ }
+ break;
+ }
+ }
+
+ }
+
+ return totsel;
+}
+
+/* don't draw axis perpendicular to the view */
+static void test_manipulator_axis(const bContext *C)
+{
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ float view_vec[3], axis_vec[3];
+ float idot;
+ int i;
+
+ const int twdrawflag_axis[3] = {
+ (MAN_TRANS_X | MAN_SCALE_X),
+ (MAN_TRANS_Y | MAN_SCALE_Y),
+ (MAN_TRANS_Z | MAN_SCALE_Z)};
+
+ ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], view_vec);
+
+ for (i = 0; i < 3; i++) {
+ normalize_v3_v3(axis_vec, rv3d->twmat[i]);
+ rv3d->tw_idot[i] = idot = 1.0f - fabsf(dot_v3v3(view_vec, axis_vec));
+ if (idot < TW_AXIS_DOT_MIN) {
+ rv3d->twdrawflag &= ~twdrawflag_axis[i];
+ }
+ }
+}
+
+
+/* ******************** DRAWING STUFFIES *********** */
+
+static float screen_aligned(RegionView3D *rv3d, float mat[4][4])
+{
+ glTranslatef(mat[3][0], mat[3][1], mat[3][2]);
+
+ /* sets view screen aligned */
+ glRotatef(-360.0f * saacos(rv3d->viewquat[0]) / (float)M_PI, rv3d->viewquat[1], rv3d->viewquat[2], rv3d->viewquat[3]);
+
+ return len_v3(mat[0]); /* draw scale */
+}
+
+
+/* radring = radius of doughnut rings
+ * radhole = radius hole
+ * start = starting segment (based on nrings)
+ * end = end segment
+ * nsides = amount of points in ring
+ * nrigns = amount of rings
+ */
+static void partial_doughnut(float radring, float radhole, int start, int end, int nsides, int nrings)
+{
+ float theta, phi, theta1;
+ float cos_theta, sin_theta;
+ float cos_theta1, sin_theta1;
+ float ring_delta, side_delta;
+ int i, j, do_caps = true;
+
+ if (start == 0 && end == nrings) do_caps = false;
+
+ ring_delta = 2.0f * (float)M_PI / (float)nrings;
+ side_delta = 2.0f * (float)M_PI / (float)nsides;
+
+ theta = (float)M_PI + 0.5f * ring_delta;
+ cos_theta = cosf(theta);
+ sin_theta = sinf(theta);
+
+ for (i = nrings - 1; i >= 0; i--) {
+ theta1 = theta + ring_delta;
+ cos_theta1 = cosf(theta1);
+ sin_theta1 = sinf(theta1);
+
+ if (do_caps && i == start) { // cap
+ glBegin(GL_POLYGON);
+ phi = 0.0;
+ for (j = nsides; j >= 0; j--) {
+ float cos_phi, sin_phi, dist;
+
+ phi += side_delta;
+ cos_phi = cosf(phi);
+ sin_phi = sinf(phi);
+ dist = radhole + radring * cos_phi;
+
+ glVertex3f(cos_theta1 * dist, -sin_theta1 * dist, radring * sin_phi);
+ }
+ glEnd();
+ }
+ if (i >= start && i <= end) {
+ glBegin(GL_QUAD_STRIP);
+ phi = 0.0;
+ for (j = nsides; j >= 0; j--) {
+ float cos_phi, sin_phi, dist;
+
+ phi += side_delta;
+ cos_phi = cosf(phi);
+ sin_phi = sinf(phi);
+ dist = radhole + radring * cos_phi;
+
+ glVertex3f(cos_theta1 * dist, -sin_theta1 * dist, radring * sin_phi);
+ glVertex3f(cos_theta * dist, -sin_theta * dist, radring * sin_phi);
+ }
+ glEnd();
+ }
+
+ if (do_caps && i == end) { // cap
+ glBegin(GL_POLYGON);
+ phi = 0.0;
+ for (j = nsides; j >= 0; j--) {
+ float cos_phi, sin_phi, dist;
+
+ phi -= side_delta;
+ cos_phi = cosf(phi);
+ sin_phi = sinf(phi);
+ dist = radhole + radring * cos_phi;
+
+ glVertex3f(cos_theta * dist, -sin_theta * dist, radring * sin_phi);
+ }
+ glEnd();
+ }
+
+
+ theta = theta1;
+ cos_theta = cos_theta1;
+ sin_theta = sin_theta1;
+ }
+}
+
+static char axisBlendAngle(float idot)
+{
+ if (idot > TW_AXIS_DOT_MAX) {
+ return 255;
+ }
+ else if (idot < TW_AXIS_DOT_MIN) {
+ return 0;
+ }
+ else {
+ return (char)(255.0f * (idot - TW_AXIS_DOT_MIN) / (TW_AXIS_DOT_MAX - TW_AXIS_DOT_MIN));
+ }
+}
+
+/* three colors can be set:
+ * gray for ghosting
+ * moving: in transform theme color
+ * else the red/green/blue
+ */
+static void manipulator_setcolor(View3D *v3d, char axis, int colcode, unsigned char alpha, bool highlight)
+{
+ unsigned char col[4] = {0};
+ int offset = (highlight) ? 80 : 0;
+ col[3] = alpha;
+
+ if (colcode == MAN_GHOST) {
+ col[3] = 70;
+ }
+ else if (colcode == MAN_MOVECOL) {
+ UI_GetThemeColor3ubv(TH_TRANSFORM, col);
+ }
+ else {
+ switch (axis) {
+ case 'C':
+ UI_GetThemeColor3ubv(TH_TRANSFORM, col);
+ if (v3d->twmode == V3D_MANIP_LOCAL) {
+ col[0] = col[0] > 200 ? 255 : col[0] + 55;
+ col[1] = col[1] > 200 ? 255 : col[1] + 55;
+ col[2] = col[2] > 200 ? 255 : col[2] + 55;
+ }
+ else if (v3d->twmode == V3D_MANIP_NORMAL) {
+ col[0] = col[0] < 55 ? 0 : col[0] - 55;
+ col[1] = col[1] < 55 ? 0 : col[1] - 55;
+ col[2] = col[2] < 55 ? 0 : col[2] - 55;
+ }
+ break;
+ case 'X':
+ UI_GetThemeColorShade3ubv(TH_AXIS_X, offset, col);
+ break;
+ case 'Y':
+ UI_GetThemeColorShade3ubv(TH_AXIS_Y, offset, col);
+ break;
+ case 'Z':
+ UI_GetThemeColorShade3ubv(TH_AXIS_Z, offset, col);
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+ }
+
+ glColor4ubv(col);
+}
+
+static void manipulator_axis_order(RegionView3D *rv3d, int r_axis_order[3])
+{
+ float axis_values[3];
+ float vec[3];
+
+ ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], vec);
+
+ axis_values[0] = -dot_v3v3(rv3d->twmat[0], vec);
+ axis_values[1] = -dot_v3v3(rv3d->twmat[1], vec);
+ axis_values[2] = -dot_v3v3(rv3d->twmat[2], vec);
+
+ axis_sort_v3(axis_values, r_axis_order);
+}
+
+/* viewmatrix should have been set OK, also no shademode! */
+static void draw_manipulator_axes_single(View3D *v3d, RegionView3D *rv3d, int colcode,
+ int flagx, int flagy, int flagz, int axis,
+ const int selectionbase, int highlight)
+{
+ switch (axis) {
+ case 0:
+ /* axes */
+ if (flagx) {
+ if (selectionbase != -1) {
+ if (flagx & MAN_SCALE_X) GPU_select_load_id(selectionbase);
+ else if (flagx & MAN_TRANS_X) GPU_select_load_id(selectionbase);
+ }
+ else {
+ manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->tw_idot[0]), (highlight & (MAN_TRANS_X | MAN_SCALE_X)) != 0);
+ }
+ glBegin(GL_LINES);
+ glVertex3f(0.2f, 0.0f, 0.0f);
+ glVertex3f(1.0f, 0.0f, 0.0f);
+ glEnd();
+ }
+ break;
+ case 1:
+ if (flagy) {
+ if (selectionbase != -1) {
+ if (flagy & MAN_SCALE_Y) GPU_select_load_id(selectionbase);
+ else if (flagy & MAN_TRANS_Y) GPU_select_load_id(selectionbase);
+ }
+ else {
+ manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->tw_idot[1]), (highlight & (MAN_TRANS_Y | MAN_SCALE_Y)) != 0);
+ }
+ glBegin(GL_LINES);
+ glVertex3f(0.0f, 0.2f, 0.0f);
+ glVertex3f(0.0f, 1.0f, 0.0f);
+ glEnd();
+ }
+ break;
+ case 2:
+ if (flagz) {
+ if (selectionbase != -1) {
+ if (flagz & MAN_SCALE_Z) GPU_select_load_id(selectionbase);
+ else if (flagz & MAN_TRANS_Z) GPU_select_load_id(selectionbase);
+ }
+ else {
+ manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->tw_idot[2]), (highlight & (MAN_TRANS_Z | MAN_SCALE_Z)) != 0);
+ }
+ glBegin(GL_LINES);
+ glVertex3f(0.0f, 0.0f, 0.2f);
+ glVertex3f(0.0f, 0.0f, 1.0f);
+ glEnd();
+ }
+ break;
+ }
+}
+static void draw_manipulator_axes(View3D *v3d, RegionView3D *rv3d, int colcode,
+ int flagx, int flagy, int flagz,
+ const int axis_order[3], const int selectionbase, int highlight)
+{
+ int i;
+ for (i = 0; i < 3; i++) {
+ draw_manipulator_axes_single(v3d, rv3d, colcode, flagx, flagy, flagz, axis_order[i], selectionbase, highlight);
+ }
+}
+
+static void preOrthoFront(const bool ortho, float twmat[4][4], int axis)
+{
+ if (ortho == false) {
+ float omat[4][4];
+ copy_m4_m4(omat, twmat);
+ orthogonalize_m4(omat, axis);
+ glPushMatrix();
+ glMultMatrixf(omat);
+ glFrontFace(is_negative_m4(omat) ? GL_CW : GL_CCW);
+ }
+}
+
+static void postOrtho(const bool ortho)
+{
+ if (ortho == false) {
+ glPopMatrix();
+ }
+}
+
+static void draw_manipulator_rotate(
+ View3D *v3d, RegionView3D *rv3d, const int drawflags, int highlight, const int combo,
+ const bool is_moving, const int selectionbase)
+{
+ double plane[4];
+ float matt[4][4];
+ float size, unitmat[4][4];
+ float cywid = 0.33f * 0.01f * (float)U.tw_handlesize;
+ float cusize = cywid * 0.65f;
+ int arcs = (G.debug_value != 2);
+ const int colcode = (is_moving) ? MAN_MOVECOL : MAN_RGB;
+ bool ortho;
+
+ /* when called while moving in mixed mode, do not draw when... */
+ if ((drawflags & MAN_ROT_C) == 0) return;
+
+ /* Init stuff */
+ glDisable(GL_DEPTH_TEST);
+ unit_m4(unitmat);
+
+ /* prepare for screen aligned draw */
+ size = len_v3(rv3d->twmat[0]);
+ glPushMatrix();
+ glTranslatef(rv3d->twmat[3][0], rv3d->twmat[3][1], rv3d->twmat[3][2]);
+
+ if (arcs) {
+ /* clipplane makes nice handles, calc here because of multmatrix but with translate! */
+ copy_v3db_v3fl(plane, rv3d->viewinv[2]);
+ plane[3] = -0.02f * size; // clip just a bit more
+ glClipPlane(GL_CLIP_PLANE0, plane);
+ }
+ /* sets view screen aligned */
+ glRotatef(-360.0f * saacos(rv3d->viewquat[0]) / (float)M_PI, rv3d->viewquat[1], rv3d->viewquat[2], rv3d->viewquat[3]);
+
+ /* Screen aligned help circle */
+ if (arcs) {
+ if (selectionbase == -1) {
+ UI_ThemeColorShade(TH_BACK, -30);
+ drawcircball(GL_LINE_LOOP, unitmat[3], size, unitmat);
+ }
+ }
+
+ /* Screen aligned trackball rot circle */
+ if (drawflags & MAN_ROT_T) {
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else UI_ThemeColor(TH_TRANSFORM);
+
+ drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f * size, unitmat);
+ }
+
+ /* Screen aligned view rot circle */
+ if (drawflags & MAN_ROT_V) {
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else UI_ThemeColor(TH_TRANSFORM);
+ drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f * size, unitmat);
+
+ if (is_moving) {
+ float vec[3];
+ vec[0] = 0; // XXX (float)(t->imval[0] - t->center2d[0]);
+ vec[1] = 0; // XXX (float)(t->imval[1] - t->center2d[1]);
+ vec[2] = 0.0f;
+ normalize_v3(vec);
+ mul_v3_fl(vec, 1.2f * size);
+ glBegin(GL_LINES);
+ glVertex3f(0.0f, 0.0f, 0.0f);
+ glVertex3fv(vec);
+ glEnd();
+ }
+ }
+ glPopMatrix();
+
+
+ ortho = is_orthogonal_m4(rv3d->twmat);
+
+ /* apply the transform delta */
+ if (is_moving) {
+ copy_m4_m4(matt, rv3d->twmat); // to copy the parts outside of [3][3]
+ // XXX mul_m4_m3m4(matt, t->mat, rv3d->twmat);
+ if (ortho) {
+ glMultMatrixf(matt);
+ glFrontFace(is_negative_m4(matt) ? GL_CW : GL_CCW);
+ }
+ }
+ else {
+ if (ortho) {
+ glFrontFace(is_negative_m4(rv3d->twmat) ? GL_CW : GL_CCW);
+ glMultMatrixf(rv3d->twmat);
+ }
+ }
+
+ /* axes */
+ if (arcs == 0) {
+ if (selectionbase == -1) {
+ if ((combo & V3D_MANIP_SCALE) == 0) {
+ /* axis */
+ if ((drawflags & MAN_ROT_X) || (is_moving && (drawflags & MAN_ROT_Z))) {
+ preOrthoFront(ortho, rv3d->twmat, 2);
+ manipulator_setcolor(v3d, 'X', colcode, 255, (highlight & MAN_ROT_X) != 0);
+ glBegin(GL_LINES);
+ glVertex3f(0.2f, 0.0f, 0.0f);
+ glVertex3f(1.0f, 0.0f, 0.0f);
+ glEnd();
+ postOrtho(ortho);
+ }
+ if ((drawflags & MAN_ROT_Y) || (is_moving && (drawflags & MAN_ROT_X))) {
+ preOrthoFront(ortho, rv3d->twmat, 0);
+ manipulator_setcolor(v3d, 'Y', colcode, 255, (highlight & MAN_ROT_Y) != 0);
+ glBegin(GL_LINES);
+ glVertex3f(0.0f, 0.2f, 0.0f);
+ glVertex3f(0.0f, 1.0f, 0.0f);
+ glEnd();
+ postOrtho(ortho);
+ }
+ if ((drawflags & MAN_ROT_Z) || (is_moving && (drawflags & MAN_ROT_Y))) {
+ preOrthoFront(ortho, rv3d->twmat, 1);
+ manipulator_setcolor(v3d, 'Z', colcode, 255, (highlight & MAN_ROT_Y) != 0);
+ glBegin(GL_LINES);
+ glVertex3f(0.0f, 0.0f, 0.2f);
+ glVertex3f(0.0f, 0.0f, 1.0f);
+ glEnd();
+ postOrtho(ortho);
+ }
+ }
+ }
+ }
+
+ if (arcs == 0 && is_moving) {
+
+ /* Z circle */
+ if (drawflags & MAN_ROT_Z) {
+ preOrthoFront(ortho, matt, 2);
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'Z', colcode, 255, (highlight & MAN_ROT_Z) != 0);
+ drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat);
+ postOrtho(ortho);
+ }
+ /* X circle */
+ if (drawflags & MAN_ROT_X) {
+ preOrthoFront(ortho, matt, 0);
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'X', colcode, 255, (highlight & MAN_ROT_X) != 0);
+ glRotatef(90.0, 0.0, 1.0, 0.0);
+ drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat);
+ glRotatef(-90.0, 0.0, 1.0, 0.0);
+ postOrtho(ortho);
+ }
+ /* Y circle */
+ if (drawflags & MAN_ROT_Y) {
+ preOrthoFront(ortho, matt, 1);
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'Y', colcode, 255, (highlight & MAN_ROT_Y) != 0);
+ glRotatef(-90.0, 1.0, 0.0, 0.0);
+ drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat);
+ glRotatef(90.0, 1.0, 0.0, 0.0);
+ postOrtho(ortho);
+ }
+
+ if (arcs) glDisable(GL_CLIP_PLANE0);
+ }
+ // donut arcs
+ if (arcs) {
+ glEnable(GL_CLIP_PLANE0);
+
+ /* Z circle */
+ if (drawflags & MAN_ROT_Z) {
+ preOrthoFront(ortho, rv3d->twmat, 2);
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'Z', colcode, 255, (highlight & MAN_ROT_Z) != 0);
+ partial_doughnut(cusize / 4.0f, 1.0f, 0, 48, 8, 48);
+ postOrtho(ortho);
+ }
+ /* X circle */
+ if (drawflags & MAN_ROT_X) {
+ preOrthoFront(ortho, rv3d->twmat, 0);
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'X', colcode, 255, (highlight & MAN_ROT_X) != 0);
+ glRotatef(90.0, 0.0, 1.0, 0.0);
+ partial_doughnut(cusize / 4.0f, 1.0f, 0, 48, 8, 48);
+ glRotatef(-90.0, 0.0, 1.0, 0.0);
+ postOrtho(ortho);
+ }
+ /* Y circle */
+ if (drawflags & MAN_ROT_Y) {
+ preOrthoFront(ortho, rv3d->twmat, 1);
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'Y', colcode, 255, (highlight & MAN_ROT_Y) != 0);
+ glRotatef(-90.0, 1.0, 0.0, 0.0);
+ partial_doughnut(cusize / 4.0f, 1.0f, 0, 48, 8, 48);
+ glRotatef(90.0, 1.0, 0.0, 0.0);
+ postOrtho(ortho);
+ }
+
+ glDisable(GL_CLIP_PLANE0);
+ }
+
+ if (arcs == 0) {
+
+ /* Z handle on X axis */
+ if (drawflags & MAN_ROT_Z) {
+ preOrthoFront(ortho, rv3d->twmat, 2);
+ glPushMatrix();
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'Z', colcode, 255, (highlight & MAN_ROT_Z) != 0);
+
+ partial_doughnut(0.7f * cusize, 1.0f, 31, 33, 8, 64);
+
+ glPopMatrix();
+ postOrtho(ortho);
+ }
+
+ /* Y handle on X axis */
+ if (drawflags & MAN_ROT_Y) {
+ preOrthoFront(ortho, rv3d->twmat, 1);
+ glPushMatrix();
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'Y', colcode, 255, (highlight & MAN_ROT_Y) != 0);
+
+ glRotatef(90.0, 1.0, 0.0, 0.0);
+ glRotatef(90.0, 0.0, 0.0, 1.0);
+ partial_doughnut(0.7f * cusize, 1.0f, 31, 33, 8, 64);
+
+ glPopMatrix();
+ postOrtho(ortho);
+ }
+
+ /* X handle on Z axis */
+ if (drawflags & MAN_ROT_X) {
+ preOrthoFront(ortho, rv3d->twmat, 0);
+ glPushMatrix();
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'X', colcode, 255, (highlight & MAN_ROT_X) != 0);
+
+ glRotatef(-90.0, 0.0, 1.0, 0.0);
+ glRotatef(90.0, 0.0, 0.0, 1.0);
+ partial_doughnut(0.7f * cusize, 1.0f, 31, 33, 8, 64);
+
+ glPopMatrix();
+ postOrtho(ortho);
+ }
+
+ }
+
+ /* restore */
+ glLoadMatrixf(rv3d->viewmat);
+ if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
+
+}
+
+static void drawsolidcube(float size)
+{
+ const float cube[8][3] = {
+ {-1.0, -1.0, -1.0},
+ {-1.0, -1.0, 1.0},
+ {-1.0, 1.0, 1.0},
+ {-1.0, 1.0, -1.0},
+ { 1.0, -1.0, -1.0},
+ { 1.0, -1.0, 1.0},
+ { 1.0, 1.0, 1.0},
+ { 1.0, 1.0, -1.0},
+ };
+ float n[3] = {0.0f};
+
+ glPushMatrix();
+ glScalef(size, size, size);
+
+ glBegin(GL_QUADS);
+ n[0] = -1.0;
+ glNormal3fv(n);
+ glVertex3fv(cube[0]); glVertex3fv(cube[1]); glVertex3fv(cube[2]); glVertex3fv(cube[3]);
+ n[0] = 0;
+ glEnd();
+
+ glBegin(GL_QUADS);
+ n[1] = -1.0;
+ glNormal3fv(n);
+ glVertex3fv(cube[0]); glVertex3fv(cube[4]); glVertex3fv(cube[5]); glVertex3fv(cube[1]);
+ n[1] = 0;
+ glEnd();
+
+ glBegin(GL_QUADS);
+ n[0] = 1.0;
+ glNormal3fv(n);
+ glVertex3fv(cube[4]); glVertex3fv(cube[7]); glVertex3fv(cube[6]); glVertex3fv(cube[5]);
+ n[0] = 0;
+ glEnd();
+
+ glBegin(GL_QUADS);
+ n[1] = 1.0;
+ glNormal3fv(n);
+ glVertex3fv(cube[7]); glVertex3fv(cube[3]); glVertex3fv(cube[2]); glVertex3fv(cube[6]);
+ n[1] = 0;
+ glEnd();
+
+ glBegin(GL_QUADS);
+ n[2] = 1.0;
+ glNormal3fv(n);
+ glVertex3fv(cube[1]); glVertex3fv(cube[5]); glVertex3fv(cube[6]); glVertex3fv(cube[2]);
+ n[2] = 0;
+ glEnd();
+
+ glBegin(GL_QUADS);
+ n[2] = -1.0;
+ glNormal3fv(n);
+ glVertex3fv(cube[7]); glVertex3fv(cube[4]); glVertex3fv(cube[0]); glVertex3fv(cube[3]);
+ glEnd();
+
+ glPopMatrix();
+}
+
+
+static void draw_manipulator_scale(
+ View3D *v3d, RegionView3D *rv3d, const int drawflags, int highlight, const int combo, const int colcode,
+ const bool is_moving, const int selectionbase)
+{
+ float cywid = 0.25f * 0.01f * (float)U.tw_handlesize;
+ float cusize = cywid * 0.75f, dz;
+ int axis_order[3] = {2, 0, 1};
+ int i;
+
+ /* when called while moving in mixed mode, do not draw when... */
+ if ((drawflags & MAN_SCALE_C) == 0) return;
+
+ manipulator_axis_order(rv3d, axis_order);
+
+ glDisable(GL_DEPTH_TEST);
+
+ /* not in combo mode */
+ if ((combo & (V3D_MANIP_TRANSLATE | V3D_MANIP_ROTATE)) == 0) {
+ float size, unitmat[4][4];
+ int shift = 0; // XXX
+
+ /* center circle, do not add to selection when shift is pressed (planar constraint) */
+ if (selectionbase != -1 && shift == 0) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'C', colcode, 255, (highlight & MAN_SCALE_C) != 0);
+
+ glPushMatrix();
+ size = screen_aligned(rv3d, rv3d->twmat);
+ unit_m4(unitmat);
+ drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f * size, unitmat);
+ glPopMatrix();
+
+ dz = 1.0;
+ }
+ else {
+ dz = 1.0f - 4.0f * cusize;
+ }
+
+ if (is_moving) {
+ float matt[4][4];
+
+ copy_m4_m4(matt, rv3d->twmat); // to copy the parts outside of [3][3]
+ // XXX mul_m4_m3m4(matt, t->mat, rv3d->twmat);
+ glMultMatrixf(matt);
+ glFrontFace(is_negative_m4(matt) ? GL_CW : GL_CCW);
+ }
+ else {
+ glMultMatrixf(rv3d->twmat);
+ glFrontFace(is_negative_m4(rv3d->twmat) ? GL_CW : GL_CCW);
+ }
+
+ /* axis */
+
+ /* in combo mode, this is always drawn as first type */
+ draw_manipulator_axes(v3d, rv3d, colcode,
+ drawflags & MAN_SCALE_X, drawflags & MAN_SCALE_Y, drawflags & MAN_SCALE_Z,
+ axis_order, selectionbase, highlight);
+
+
+ for (i = 0; i < 3; i++) {
+ switch (axis_order[i]) {
+ case 0: /* X cube */
+ if (drawflags & MAN_SCALE_X) {
+ glTranslatef(dz, 0.0, 0.0);
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->tw_idot[0]), (highlight & MAN_SCALE_X) != 0);
+ drawsolidcube(cusize);
+ glTranslatef(-dz, 0.0, 0.0);
+ }
+ break;
+ case 1: /* Y cube */
+ if (drawflags & MAN_SCALE_Y) {
+ glTranslatef(0.0, dz, 0.0);
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->tw_idot[1]), (highlight & MAN_SCALE_Y) != 0);
+ drawsolidcube(cusize);
+ glTranslatef(0.0, -dz, 0.0);
+ }
+ break;
+ case 2: /* Z cube */
+ if (drawflags & MAN_SCALE_Z) {
+ glTranslatef(0.0, 0.0, dz);
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->tw_idot[2]), (highlight & MAN_SCALE_Z) != 0);
+ drawsolidcube(cusize);
+ glTranslatef(0.0, 0.0, -dz);
+ }
+ break;
+ }
+ }
+
+ /* if shiftkey, center point as last, for selectbuffer order */
+ if (selectionbase != -1) {
+ int shift = 0; // XXX
+
+ if (shift) {
+ glTranslatef(0.0, -dz, 0.0);
+ GPU_select_load_id(selectionbase);
+ glBegin(GL_POINTS);
+ glVertex3f(0.0, 0.0, 0.0);
+ glEnd();
+ }
+ }
+
+ /* restore */
+ glLoadMatrixf(rv3d->viewmat);
+
+ if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
+ glFrontFace(GL_CCW);
+}
+
+
+static void draw_cone(GLUquadricObj *qobj, float len, float width)
+{
+ glTranslatef(0.0, 0.0, -0.5f * len);
+ gluCylinder(qobj, width, 0.0, len, 8, 1);
+ gluQuadricOrientation(qobj, GLU_INSIDE);
+ gluDisk(qobj, 0.0, width, 8, 1);
+ gluQuadricOrientation(qobj, GLU_OUTSIDE);
+ glTranslatef(0.0, 0.0, 0.5f * len);
+}
+
+static void draw_cylinder(GLUquadricObj *qobj, float len, float width)
+{
+
+ width *= 0.8f; // just for beauty
+
+ glTranslatef(0.0, 0.0, -0.5f * len);
+ gluCylinder(qobj, width, width, len, 8, 1);
+ gluQuadricOrientation(qobj, GLU_INSIDE);
+ gluDisk(qobj, 0.0, width, 8, 1);
+ gluQuadricOrientation(qobj, GLU_OUTSIDE);
+ glTranslatef(0.0, 0.0, len);
+ gluDisk(qobj, 0.0, width, 8, 1);
+ glTranslatef(0.0, 0.0, -0.5f * len);
+}
+
+
+static void draw_manipulator_translate(
+ View3D *v3d, RegionView3D *rv3d, int drawflags, int highlightflags, int combo, int colcode,
+ const bool UNUSED(is_moving), const int selectionbase)
+{
+ GLUquadricObj *qobj;
+ float cylen = 0.01f * (float)U.tw_handlesize;
+ float cywid = 0.25f * cylen, dz, size;
+ float unitmat[4][4];
+ int shift = 0; // XXX
+ int axis_order[3] = {0, 1, 2};
+ int i;
+
+ /* when called while moving in mixed mode, do not draw when... */
+ if ((drawflags & MAN_TRANS_C) == 0) return;
+
+ manipulator_axis_order(rv3d, axis_order);
+
+ // XXX if (moving) glTranslatef(t->vec[0], t->vec[1], t->vec[2]);
+ glDisable(GL_DEPTH_TEST);
+
+ /* center circle, do not add to selection when shift is pressed (planar constraint) */
+ if (selectionbase != -1 && shift == 0) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'C', colcode, 255, (highlightflags & MAN_TRANS_C) != 0);
+
+ glPushMatrix();
+ size = screen_aligned(rv3d, rv3d->twmat);
+ unit_m4(unitmat);
+ drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f * size, unitmat);
+ glPopMatrix();
+
+ glPushMatrix();
+ /* and now apply matrix, we move to local matrix drawing */
+ glMultMatrixf(rv3d->twmat);
+
+#if 0
+ // translate drawn as last, only axis when no combo with scale, or for ghosting
+ if ((combo & V3D_MANIP_SCALE) == 0 || colcode == MAN_GHOST) {
+ draw_manipulator_axes(v3d, rv3d, colcode,
+ drawflags & MAN_TRANS_X, 0, drawflags & MAN_TRANS_Z,
+ axis_order, selectionbase, highlightflags);
+ }
+
+ /* offset in combo mode, for rotate a bit more */
+ if (combo & (V3D_MANIP_ROTATE)) dz = 1.0f + 2.0f * cylen;
+ else if (combo & (V3D_MANIP_SCALE)) dz = 1.0f + 0.5f * cylen;
+ else dz = 1.0f;
+
+ qobj = gluNewQuadric();
+ gluQuadricDrawStyle(qobj, GLU_FILL);
+
+ for (i = 0; i < 3; i++) {
+ switch (axis_order[i]) {
+ case 0: /* Z Cone */
+ if (drawflags & MAN_TRANS_Z) {
+ glPushMatrix();
+ glTranslatef(0.0, 0.0, dz);
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->tw_idot[2]), (highlightflags & MAN_TRANS_Z) != 0);
+ draw_cone(qobj, cylen, cywid);
+ glPopMatrix();
+ }
+ break;
+ case 1: /* X Cone */
+ if (drawflags & MAN_TRANS_X) {
+ glPushMatrix();
+ glTranslatef(dz, 0.0, 0.0);
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->tw_idot[0]), (highlightflags & MAN_TRANS_X) != 0);
+ glRotatef(90.0, 0.0, 1.0, 0.0);
+ draw_cone(qobj, cylen, cywid);
+ glPopMatrix();
+ }
+ break;
+ case 2: /* Y Cone */
+ if (drawflags & MAN_TRANS_Y) {
+ glPushMatrix();
+ glTranslatef(0.0, dz, 0.0);
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ else manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->tw_idot[1]), (highlightflags & MAN_TRANS_Y) != 0);
+ glRotatef(-90.0, 1.0, 0.0, 0.0);
+ draw_cone(qobj, cylen, cywid);
+ glPopMatrix();
+ }
+ break;
+ }
+ }
+
+ gluDeleteQuadric(qobj);
+#endif
+
+ glPopMatrix();
+
+ if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
+
+}
+
+static void draw_manipulator_rotate_cyl(
+ View3D *v3d, RegionView3D *rv3d, int drawflags, int highlight, const int combo, const int colcode,
+ const bool is_moving, const int selectionbase)
+{
+ GLUquadricObj *qobj;
+ float size;
+ float cylen = 0.01f * (float)U.tw_handlesize;
+ float cywid = 0.25f * cylen;
+ int axis_order[3] = {2, 0, 1};
+ int i;
+
+ /* when called while moving in mixed mode, do not draw when... */
+ if ((drawflags & MAN_ROT_C) == 0) return;
+
+ manipulator_axis_order(rv3d, axis_order);
+
+ /* prepare for screen aligned draw */
+ glPushMatrix();
+ size = screen_aligned(rv3d, rv3d->twmat);
+
+ glDisable(GL_DEPTH_TEST);
+
+ qobj = gluNewQuadric();
+
+ /* Screen aligned view rot circle */
+ if (drawflags & MAN_ROT_V) {
+ float unitmat[4][4];
+
+ unit_m4(unitmat);
+
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ UI_ThemeColor(TH_TRANSFORM);
+ drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f * size, unitmat);
+
+ if (is_moving) {
+ float vec[3];
+ vec[0] = 0; // XXX (float)(t->imval[0] - t->center2d[0]);
+ vec[1] = 0; // XXX (float)(t->imval[1] - t->center2d[1]);
+ vec[2] = 0.0f;
+ normalize_v3(vec);
+ mul_v3_fl(vec, 1.2f * size);
+ glBegin(GL_LINES);
+ glVertex3f(0.0, 0.0, 0.0);
+ glVertex3fv(vec);
+ glEnd();
+ }
+ }
+ glPopMatrix();
+
+ /* apply the transform delta */
+ if (is_moving) {
+ float matt[4][4];
+ copy_m4_m4(matt, rv3d->twmat); // to copy the parts outside of [3][3]
+ // XXX if (t->flag & T_USES_MANIPULATOR) {
+ // XXX mul_m4_m3m4(matt, t->mat, rv3d->twmat);
+ // XXX }
+ glMultMatrixf(matt);
+ }
+ else {
+ glMultMatrixf(rv3d->twmat);
+ }
+
+ glFrontFace(is_negative_m4(rv3d->twmat) ? GL_CW : GL_CCW);
+
+ /* axis */
+ if (selectionbase != -1) {
+
+ // only draw axis when combo didn't draw scale axes
+ if ((combo & V3D_MANIP_SCALE) == 0) {
+ draw_manipulator_axes(v3d, rv3d, colcode,
+ drawflags & MAN_ROT_X, drawflags & MAN_ROT_Y, drawflags & MAN_ROT_Z,
+ axis_order, selectionbase, highlight);
+ }
+
+ /* only has to be set when not in picking */
+ gluQuadricDrawStyle(qobj, GLU_FILL);
+ }
+
+ for (i = 0; i < 3; i++) {
+ switch (axis_order[i]) {
+ case 0: /* X cylinder */
+ if (drawflags & MAN_ROT_X) {
+ glTranslatef(1.0, 0.0, 0.0);
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ glRotatef(90.0, 0.0, 1.0, 0.0);
+ manipulator_setcolor(v3d, 'X', colcode, 255, (highlight & MAN_ROT_X) != 0);
+ draw_cylinder(qobj, cylen, cywid);
+ glRotatef(-90.0, 0.0, 1.0, 0.0);
+ glTranslatef(-1.0, 0.0, 0.0);
+ }
+ break;
+ case 1: /* Y cylinder */
+ if (drawflags & MAN_ROT_Y) {
+ glTranslatef(0.0, 1.0, 0.0);
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ glRotatef(-90.0, 1.0, 0.0, 0.0);
+ manipulator_setcolor(v3d, 'Y', colcode, 255, (highlight & MAN_ROT_Y) != 0);
+ draw_cylinder(qobj, cylen, cywid);
+ glRotatef(90.0, 1.0, 0.0, 0.0);
+ glTranslatef(0.0, -1.0, 0.0);
+ }
+ break;
+ case 2: /* Z cylinder */
+ if (drawflags & MAN_ROT_Z) {
+ glTranslatef(0.0, 0.0, 1.0);
+ if (selectionbase != -1) GPU_select_load_id(selectionbase);
+ manipulator_setcolor(v3d, 'Z', colcode, 255, (highlight & MAN_ROT_Z) != 0);
+ draw_cylinder(qobj, cylen, cywid);
+ glTranslatef(0.0, 0.0, -1.0);
+ }
+ break;
+ }
+ }
+
+ /* restore */
+
+ gluDeleteQuadric(qobj);
+ glLoadMatrixf(rv3d->viewmat);
+
+ if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
+
+}
+
+
+/* ********************************************* */
+
+/* main call, does calc centers & orientation too */
+static int drawflags = 0xFFFF; // only for the calls below, belongs in scene...?
+
+static int manipulator_flags_from_active(int active)
+{
+ int val;
+
+ if (active != -1) {
+ if (active == MAN_SEL_TRANS_C) {
+ val = MAN_TRANS_C;
+ }
+ else if (active == MAN_SEL_SCALE_C) {
+ val = MAN_SCALE_C;
+ }
+ else {
+ val = 1 << active;
+ }
+ }
+ else
+ val = 0;
+
+ return val;
+}
+
+void WIDGET_manipulator_draw(wmWidget *UNUSED(widget), const bContext *C)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ ARegion *ar = CTX_wm_region(C);
+ View3D *v3d = sa->spacedata.first;
+ RegionView3D *rv3d = ar->regiondata;
+
+ if (v3d->twflag & V3D_DRAW_MANIPULATOR) {
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ if (v3d->twtype & V3D_MANIP_ROTATE) {
+
+ if (G.debug_value == 3) {
+ if (G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT))
+ draw_manipulator_rotate_cyl(v3d, rv3d, drawflags, 0, v3d->twtype, MAN_MOVECOL, true, -1);
+ else
+ draw_manipulator_rotate_cyl(v3d, rv3d, drawflags, 0, v3d->twtype, MAN_RGB, false, -1);
+ }
+ else {
+ draw_manipulator_rotate(v3d, rv3d, drawflags, 0, v3d->twtype, false, -1);
+ }
+ }
+ if (v3d->twtype & V3D_MANIP_SCALE) {
+ draw_manipulator_scale(v3d, rv3d, drawflags, 0, v3d->twtype, MAN_RGB, false, -1);
+ }
+ if (v3d->twtype & V3D_MANIP_TRANSLATE) {
+ draw_manipulator_translate(v3d, rv3d, drawflags, 0, v3d->twtype, MAN_RGB, false, -1);
+ }
+
+ glDisable(GL_BLEND);
+ }
+}
+
+void WIDGETGROUP_manipulator_update(struct wmWidgetGroup *wgroup, const struct bContext *C)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ ARegion *ar = CTX_wm_region(C);
+ Scene *scene = CTX_data_scene(C);
+ View3D *v3d = sa->spacedata.first;
+ RegionView3D *rv3d = ar->regiondata;
+ ManipulatorGroup *manipulator = WM_widgetgroup_customdata(wgroup);
+
+ int totsel;
+
+ v3d->twflag &= ~V3D_DRAW_MANIPULATOR;
+
+ totsel = calc_manipulator_stats(C);
+ if (totsel == 0) {
+ WM_widget_set_draw(manipulator->translate_x, false);
+ WM_widget_set_draw(manipulator->translate_y, false);
+ WM_widget_set_draw(manipulator->translate_z, false);
+
+ WM_widget_set_draw(manipulator->rotate_x, false);
+ WM_widget_set_draw(manipulator->rotate_y, false);
+ WM_widget_set_draw(manipulator->rotate_z, false);
+ return;
+ }
+ v3d->twflag |= V3D_DRAW_MANIPULATOR;
+
+ /* now we can define center */
+ switch (v3d->around) {
+ case V3D_CENTER:
+ case V3D_ACTIVE:
+ {
+ Object *ob;
+ if (((v3d->around == V3D_ACTIVE) && (scene->obedit == NULL)) &&
+ ((ob = OBACT) && !(ob->mode & OB_MODE_POSE)))
+ {
+ copy_v3_v3(rv3d->twmat[3], ob->obmat[3]);
+ }
+ else {
+ mid_v3_v3v3(rv3d->twmat[3], scene->twmin, scene->twmax);
+ }
+ break;
+ }
+ case V3D_LOCAL:
+ case V3D_CENTROID:
+ copy_v3_v3(rv3d->twmat[3], scene->twcent);
+ break;
+ case V3D_CURSOR:
+ copy_v3_v3(rv3d->twmat[3], ED_view3d_cursor3d_get(scene, v3d));
+ break;
+ }
+
+ mul_mat3_m4_fl(rv3d->twmat, ED_view3d_pixel_size(rv3d, rv3d->twmat[3]) * U.tw_size);
+
+ /* when looking through a selected camera, the manipulator can be at the
+ * exact same position as the view, skip so we don't break selection */
+ if (fabsf(mat4_to_scale(rv3d->twmat)) < 1e-7f) {
+ WM_widget_set_draw(manipulator->translate_x, false);
+ WM_widget_set_draw(manipulator->translate_y, false);
+ WM_widget_set_draw(manipulator->translate_z, false);
+
+ WM_widget_set_draw(manipulator->rotate_x, false);
+ WM_widget_set_draw(manipulator->rotate_y, false);
+ WM_widget_set_draw(manipulator->rotate_z, false);
+
+ return;
+ }
+
+ test_manipulator_axis(C);
+ drawflags = rv3d->twdrawflag; /* set in calc_manipulator_stats */
+
+ WM_widget_operator(manipulator->translate_x, WIDGET_manipulator_handler_trans, "TRANSFORM_OT_translate", NULL);
+ WM_widget_operator(manipulator->translate_y, WIDGET_manipulator_handler_trans, "TRANSFORM_OT_translate", NULL);
+ WM_widget_operator(manipulator->translate_z, WIDGET_manipulator_handler_trans, "TRANSFORM_OT_translate", NULL);
+ WM_widget_operator(manipulator->rotate_x, WIDGET_manipulator_handler_rot, "TRANSFORM_OT_rotate", NULL);
+ WM_widget_operator(manipulator->rotate_y, WIDGET_manipulator_handler_rot, "TRANSFORM_OT_rotate", NULL);
+ WM_widget_operator(manipulator->rotate_z, WIDGET_manipulator_handler_rot, "TRANSFORM_OT_rotate", NULL);
+
+ if (v3d->twtype & V3D_MANIP_TRANSLATE) {
+ /* should be added according to the order of axis */
+ WM_widget_set_origin(manipulator->translate_x, rv3d->twmat[3]);
+ WIDGET_arrow_set_direction(manipulator->translate_x, rv3d->twmat[0]);
+
+ WM_widget_set_origin(manipulator->translate_y, rv3d->twmat[3]);
+ WIDGET_arrow_set_direction(manipulator->translate_y, rv3d->twmat[1]);
+
+ WM_widget_set_origin(manipulator->translate_z, rv3d->twmat[3]);
+ WIDGET_arrow_set_direction(manipulator->translate_z, rv3d->twmat[2]);
+
+ WM_widget_set_draw(manipulator->translate_x, true);
+ WM_widget_set_draw(manipulator->translate_y, true);
+ WM_widget_set_draw(manipulator->translate_z, true);
+ }
+ else {
+ WM_widget_set_draw(manipulator->translate_x, false);
+ WM_widget_set_draw(manipulator->translate_y, false);
+ WM_widget_set_draw(manipulator->translate_z, false);
+ }
+
+ if (v3d->twtype & V3D_MANIP_ROTATE) {
+ /* should be added according to the order of axis */
+
+ WM_widget_set_origin(manipulator->rotate_x, rv3d->twmat[3]);
+ WIDGET_dial_set_direction(manipulator->rotate_x, rv3d->twmat[0]);
+
+ WM_widget_set_origin(manipulator->rotate_y, rv3d->twmat[3]);
+ WIDGET_dial_set_direction(manipulator->rotate_y, rv3d->twmat[1]);
+
+ WM_widget_set_origin(manipulator->rotate_z, rv3d->twmat[3]);
+ WIDGET_dial_set_direction(manipulator->rotate_z, rv3d->twmat[2]);
+
+ WM_widget_set_draw(manipulator->rotate_x, true);
+ WM_widget_set_draw(manipulator->rotate_y, true);
+ WM_widget_set_draw(manipulator->rotate_z, true);
+ }
+ else {
+ WM_widget_set_draw(manipulator->rotate_x, false);
+ WM_widget_set_draw(manipulator->rotate_y, false);
+ WM_widget_set_draw(manipulator->rotate_z, false);
+ }
+}
+
+
+bool WIDGETGROUP_manipulator_poll(wmWidgetGroup *UNUSED(wgroup), const struct bContext *C)
+{
+ /* it's a given we only use this in 3D view */
+ ScrArea *sa = CTX_wm_area(C);
+ View3D *v3d = sa->spacedata.first;
+
+ return ((v3d->twflag & V3D_USE_MANIPULATOR) != 0);
+}
+
+void WIDGET_manipulator_render_3d_intersect(const bContext *C, wmWidget *UNUSED(widget), int selectionbase)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ View3D *v3d = sa->spacedata.first;
+ ARegion *ar = CTX_wm_region(C);
+ RegionView3D *rv3d = ar->regiondata;
+
+ /* do the drawing */
+ if (v3d->twtype & V3D_MANIP_ROTATE) {
+ //if (G.debug_value == 3) draw_manipulator_rotate_cyl(v3d, rv3d, MAN_ROT_C & rv3d->twdrawflag, 0, v3d->twtype, MAN_RGB, false, selectionbase);
+ //else draw_manipulator_rotate(v3d, rv3d, MAN_ROT_C & rv3d->twdrawflag, 0, v3d->twtype, false, selectionbase);
+ }
+ if (v3d->twtype & V3D_MANIP_SCALE)
+ draw_manipulator_scale(v3d, rv3d, MAN_SCALE_C & rv3d->twdrawflag, 0, v3d->twtype, MAN_RGB, false, selectionbase);
+ //if (v3d->twtype & V3D_MANIP_TRANSLATE)
+ // draw_manipulator_translate(v3d, rv3d, MAN_TRANS_C & rv3d->twdrawflag, 0, v3d->twtype, MAN_RGB, false, selectionbase);
+}
+
+/* return 0; nothing happened */
+int WIDGET_manipulator_handler(bContext *C, const struct wmEvent *event, wmWidget *UNUSED(widget), wmOperator *op)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ View3D *v3d = sa->spacedata.first;
+ int constraint_axis[3] = {0, 0, 0};
+ int val;
+ int shift = event->shift;
+
+ struct IDProperty *properties = NULL; /* operator properties, assigned to ptr->data and can be written to a file */
+ struct PointerRNA *ptr = NULL; /* rna pointer to access properties */
+
+ val = manipulator_flags_from_active(0);
+
+ if (!((v3d->twflag & V3D_USE_MANIPULATOR) && (v3d->twflag & V3D_DRAW_MANIPULATOR)) ||
+ !(event->keymodifier == 0 || event->keymodifier == KM_SHIFT) ||
+ !((event->val == KM_PRESS) && (event->type == LEFTMOUSE)))
+ {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ if (val) {
+ if (val & MAN_TRANS_C) {
+ switch (val) {
+ case MAN_TRANS_C:
+ break;
+ case MAN_TRANS_X:
+ if (shift) {
+ constraint_axis[1] = 1;
+ constraint_axis[2] = 1;
+ }
+ else
+ constraint_axis[0] = 1;
+ break;
+ case MAN_TRANS_Y:
+ if (shift) {
+ constraint_axis[0] = 1;
+ constraint_axis[2] = 1;
+ }
+ else
+ constraint_axis[1] = 1;
+ break;
+ case MAN_TRANS_Z:
+ if (shift) {
+ constraint_axis[0] = 1;
+ constraint_axis[1] = 1;
+ }
+ else
+ constraint_axis[2] = 1;
+ break;
+ }
+ WM_operator_properties_alloc(&ptr, &properties, "TRANSFORM_OT_translate");
+ /* Force orientation */
+ RNA_boolean_set(ptr, "release_confirm", true);
+ RNA_enum_set(ptr, "constraint_orientation", v3d->twmode);
+ RNA_boolean_set_array(ptr, "constraint_axis", constraint_axis);
+ WM_operator_name_call(C, "TRANSFORM_OT_translate", WM_OP_INVOKE_DEFAULT, ptr);
+ }
+ else if (val & MAN_SCALE_C) {
+ switch (val) {
+ case MAN_SCALE_X:
+ if (shift) {
+ constraint_axis[1] = 1;
+ constraint_axis[2] = 1;
+ }
+ else
+ constraint_axis[0] = 1;
+ break;
+ case MAN_SCALE_Y:
+ if (shift) {
+ constraint_axis[0] = 1;
+ constraint_axis[2] = 1;
+ }
+ else
+ constraint_axis[1] = 1;
+ break;
+ case MAN_SCALE_Z:
+ if (shift) {
+ constraint_axis[0] = 1;
+ constraint_axis[1] = 1;
+ }
+ else
+ constraint_axis[2] = 1;
+ break;
+ }
+ WM_operator_properties_alloc(&ptr, &properties, "TRANSFORM_OT_resize");
+ /* Force orientation */
+ RNA_boolean_set(ptr, "release_confirm", true);
+ RNA_enum_set(ptr, "constraint_orientation", v3d->twmode);
+ RNA_boolean_set_array(ptr, "constraint_axis", constraint_axis);
+ WM_operator_name_call(C, "TRANSFORM_OT_resize", WM_OP_INVOKE_DEFAULT, ptr);
+ }
+ else if (val == MAN_ROT_T) { /* trackball need special case, init is different */
+ /* Do not pass op->ptr!!! trackball has no "constraint" properties!
+ * See [#34621], it's a miracle it did not cause more problems!!! */
+ /* However, we need to copy the "release_confirm" property, but only if defined, see T41112. */
+ PointerRNA props_ptr;
+ wmOperatorType *ot = WM_operatortype_find("TRANSFORM_OT_trackball", true);
+ WM_operator_properties_create_ptr(&props_ptr, ot);
+ RNA_boolean_set(&props_ptr, "release_confirm", true);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr);
+ WM_operator_properties_free(&props_ptr);
+ }
+ else if (val & MAN_ROT_C) {
+ switch (val) {
+ case MAN_ROT_X:
+ constraint_axis[0] = 1;
+ break;
+ case MAN_ROT_Y:
+ constraint_axis[1] = 1;
+ break;
+ case MAN_ROT_Z:
+ constraint_axis[2] = 1;
+ break;
+ }
+ WM_operator_properties_alloc(&ptr, &properties, "TRANSFORM_OT_rotate");
+ /* Force orientation */
+ RNA_boolean_set(ptr, "release_confirm", true);
+ RNA_enum_set(ptr, "constraint_orientation", v3d->twmode);
+ RNA_boolean_set_array(ptr, "constraint_axis", constraint_axis);
+ WM_operator_name_call(C, "TRANSFORM_OT_rotate", WM_OP_INVOKE_DEFAULT, ptr);
+ }
+ }
+
+ if (ptr) {
+ WM_operator_properties_free(ptr);
+ MEM_freeN(ptr);
+ }
+
+ return (val) ? OPERATOR_FINISHED : OPERATOR_PASS_THROUGH;
+}
+
+/* return 0; nothing happened */
+int WIDGET_manipulator_handler_trans(bContext *C, const struct wmEvent *event, wmWidget *widget, struct PointerRNA *ptr)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ View3D *v3d = sa->spacedata.first;
+ int constraint_axis[3] = {0, 0, 0};
+ int shift = event->shift;
+ int direction = GET_INT_FROM_POINTER(WM_widget_customdata(widget));
+
+ if (!((v3d->twflag & V3D_USE_MANIPULATOR) && (v3d->twflag & V3D_DRAW_MANIPULATOR)) ||
+ !(event->keymodifier == 0 || event->keymodifier == KM_SHIFT))
+ {
+ ;//return OPERATOR_PASS_THROUGH;
+ }
+
+ if (shift) {
+ int d = 0;
+ for (; d < 3; d++) {
+ if (d != direction)
+ constraint_axis[d] = 1;
+ }
+ }
+ else
+ constraint_axis[direction] = 1;
+
+ /* Force orientation */
+ RNA_boolean_set(ptr, "release_confirm", true);
+ RNA_enum_set(ptr, "constraint_orientation", v3d->twmode);
+ RNA_boolean_set_array(ptr, "constraint_axis", constraint_axis);
+ RNA_boolean_set(ptr, "use_widget_input", true);
+
+ return OPERATOR_FINISHED;
+}
+
+/* return 0; nothing happened */
+int WIDGET_manipulator_handler_rot(bContext *C, const struct wmEvent *UNUSED(event), wmWidget *widget, struct PointerRNA *ptr)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ View3D *v3d = sa->spacedata.first;
+ int constraint_axis[3] = {0, 0, 0};
+ int direction = GET_INT_FROM_POINTER(WM_widget_customdata(widget));
+
+ if (!(v3d->twflag & V3D_USE_MANIPULATOR) && (v3d->twflag & V3D_DRAW_MANIPULATOR))
+ {
+ ;//return OPERATOR_PASS_THROUGH;
+ }
+
+ constraint_axis[direction] = 1;
+
+ /* Force orientation */
+ RNA_boolean_set(ptr, "release_confirm", true);
+ RNA_enum_set(ptr, "constraint_orientation", v3d->twmode);
+ RNA_boolean_set_array(ptr, "constraint_axis", constraint_axis);
+ RNA_boolean_set(ptr, "use_widget_input", true);
+
+ return OPERATOR_FINISHED;
+}
+
+void WIDGETGROUP_manipulator_free(struct wmWidgetGroup *wgroup)
+{
+ ManipulatorGroup *manipulator = WM_widgetgroup_customdata(wgroup);
+
+ MEM_freeN(manipulator);
+}
+
+void WIDGETGROUP_manipulator_create(struct wmWidgetGroup *wgroup)
+{
+ float color_green[4] = {0.0f, 1.0f, 0.0f, 1.0f};
+ float color_red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
+ float color_blue[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+
+ wmWidget *widget = NULL;
+
+ ManipulatorGroup *manipulator = MEM_callocN(sizeof(ManipulatorGroup), "manipulator_data");
+
+ widget = WM_widget_new(WIDGET_manipulator_draw,
+ WIDGET_manipulator_render_3d_intersect,
+ NULL,
+ WIDGET_manipulator_handler,
+ NULL, false);
+
+ WM_widget_register(wgroup, widget);
+
+ manipulator->translate_x = WIDGET_arrow_new(0, NULL);
+ WIDGET_arrow_set_color(manipulator->translate_x, color_red);
+ WM_widget_register(wgroup, manipulator->translate_x);
+
+ manipulator->translate_y = WIDGET_arrow_new(0, SET_INT_IN_POINTER(1));
+ WIDGET_arrow_set_color(manipulator->translate_y, color_green);
+ WM_widget_register(wgroup, manipulator->translate_y);
+
+ manipulator->translate_z = WIDGET_arrow_new(0, SET_INT_IN_POINTER(2));
+ WIDGET_arrow_set_color(manipulator->translate_z, color_blue);
+ WM_widget_register(wgroup, manipulator->translate_z);
+
+ manipulator->rotate_x = WIDGET_dial_new(UI_DIAL_STYLE_RING_CLIPPED, NULL);
+ WIDGET_dial_set_color(manipulator->rotate_x, color_red);
+ WM_widget_register(wgroup, manipulator->rotate_x);
+
+ manipulator->rotate_y = WIDGET_dial_new(UI_DIAL_STYLE_RING_CLIPPED, SET_INT_IN_POINTER(1));
+ WIDGET_dial_set_color(manipulator->rotate_y, color_green);
+ WM_widget_register(wgroup, manipulator->rotate_y);
+
+ manipulator->rotate_z = WIDGET_dial_new(UI_DIAL_STYLE_RING_CLIPPED, SET_INT_IN_POINTER(2));
+ WIDGET_dial_set_color(manipulator->rotate_z, color_blue);
+ WM_widget_register(wgroup, manipulator->rotate_z);
+
+ WM_widgetgroup_customdata_set(wgroup, manipulator);
+}
+
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index ccd57db5959..c43b9bf65cd 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -2009,6 +2009,7 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
}
}
+
/* note: caller needs to free 't' on a 0 return */
bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode)
{
@@ -2094,6 +2095,11 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
else
unit_m3(t->spacemtx);
+ /* initialize snapping factors */
+ t->snap_aspect[0] = t->snap_aspect[1] = t->snap_aspect[2] = 1.0f;
+
+ initSnappingAspect(t);
+
createTransData(C, t); // make TransData structs from selection
if (t->total == 0) {
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index b826b7f9c73..431b52c2bd5 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -365,6 +365,7 @@ typedef struct TransInfo {
short event_type; /* event->type used to invoke transform */
short idx_max; /* maximum index on the input vector */
float snap[3]; /* Snapping Gears */
+
float snap_spatial[3]; /* Spatial snapping gears(even when rotating, scaling... etc) */
char frame_side; /* Mouse side of the cfra, 'L', 'R' or 'B' */
@@ -480,6 +481,8 @@ typedef struct TransInfo {
/* alternative transformation. used to add offset to tracking markers */
#define T_ALT_TRANSFORM (1 << 24)
+#define T_USE_WIDGET (1 << 24)
+
/* TransInfo->modifiers */
#define MOD_CONSTRAINT_SELECT 0x01
#define MOD_PRECISION 0x02
@@ -568,6 +571,7 @@ void flushTransGraphData(TransInfo *t);
void remake_graph_transdata(TransInfo *t, struct ListBase *anim_data);
void flushTransUVs(TransInfo *t);
void flushTransParticles(TransInfo *t);
+void flushTransStrands(TransInfo *t);
bool clipUVTransform(TransInfo *t, float vec[2], const bool resize);
void clipUVData(TransInfo *t);
void flushTransNodes(TransInfo *t);
diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c
index cae1fca9a79..65c0e413c4a 100644
--- a/source/blender/editors/transform/transform_conversions.c
+++ b/source/blender/editors/transform/transform_conversions.c
@@ -69,6 +69,7 @@
#include "BKE_crazyspace.h"
#include "BKE_curve.h"
#include "BKE_depsgraph.h"
+#include "BKE_editstrands.h"
#include "BKE_fcurve.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
@@ -1930,6 +1931,211 @@ void flushTransParticles(TransInfo *t)
PE_update_object(scene, OBACT, 1);
}
+
+/* ******************* hair edit **************** */
+
+static void editmesh_set_connectivity_distance(BMesh *bm, float mtx[3][3], float *dists);
+static struct TransIslandData *bmesh_islands_info_calc(BMesh *bm, short selectmode, int *r_island_tot, int **r_island_vert_map);
+
+static void StrandVertsToTransData(TransInfo *t, TransData *td,
+ BMEditStrands *UNUSED(edit), BMVert *eve, const float nor[3], const float tang[3],
+ struct TransIslandData *v_island)
+{
+ BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0);
+
+ td->flag = 0;
+ td->loc = eve->co;
+ copy_v3_v3(td->iloc, td->loc);
+
+ if (v_island) {
+ copy_v3_v3(td->center, v_island->co);
+ copy_m3_m3(td->axismtx, v_island->axismtx);
+ }
+ else if (t->around == V3D_LOCAL) {
+ copy_v3_v3(td->center, td->loc);
+ createSpaceNormalTangent(td->axismtx, nor, tang);
+ }
+ else {
+ copy_v3_v3(td->center, td->loc);
+
+ /* Setting normals */
+ copy_v3_v3(td->axismtx[2], nor);
+ td->axismtx[0][0] =
+ td->axismtx[0][1] =
+ td->axismtx[0][2] =
+ td->axismtx[1][0] =
+ td->axismtx[1][1] =
+ td->axismtx[1][2] = 0.0f;
+ }
+
+ td->ext = NULL;
+ td->val = NULL;
+ td->extra = NULL;
+}
+
+static void createTransStrandVerts(TransInfo *t)
+{
+ Scene *scene = t->scene;
+ Object *ob = OBACT;
+ BMEditStrands *edit = BKE_editstrands_from_object(ob);
+ BMesh *bm = edit->bm;
+ TransData *tob = NULL;
+ BMVert *eve;
+ BMIter iter;
+ float mtx[3][3], smtx[3][3];
+ float *dists = NULL;
+ int a;
+ int propmode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0;
+ int mirror = 0;
+
+ struct TransIslandData *island_info = NULL;
+ int island_info_tot;
+ int *island_vert_map = NULL;
+
+ if (t->flag & T_MIRROR) {
+#if 0 // TODO mirror relies on EDBM functions which don't have an equivalent for editstrands yet
+ EDBM_verts_mirror_cache_begin(em, 0, false, (t->flag & T_PROP_EDIT) == 0, false);
+ mirror = 1;
+#endif
+ }
+
+ /* quick check if we can transform */
+ /* note: in prop mode we need at least 1 selected */
+ if (bm->totvertsel == 0) {
+ goto cleanup;
+ }
+
+ if (propmode) {
+ unsigned int count = 0;
+ BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
+ if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+ count++;
+ }
+ }
+
+ t->total = count;
+
+ /* allocating scratch arrays */
+ if (propmode & T_PROP_CONNECTED)
+ dists = MEM_mallocN(bm->totvert * sizeof(float), "scratch nears");
+ }
+ else {
+ t->total = bm->totvertsel;
+ }
+
+ tob = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Strands EditMode)");
+
+ copy_m3_m4(mtx, ob->obmat);
+ /* we use a pseudoinverse so that when one of the axes is scaled to 0,
+ * matrix inversion still works and we can still moving along the other */
+ pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
+
+ if (propmode & T_PROP_CONNECTED) {
+ editmesh_set_connectivity_distance(bm, mtx, dists);
+ }
+
+ if (t->around == V3D_LOCAL) {
+ island_info = bmesh_islands_info_calc(edit->bm, SCE_SELECT_VERTEX, &island_info_tot, &island_vert_map);
+ }
+
+ /* find out which half we do */
+ if (mirror) {
+#if 0 // TODO
+ BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(eve, BM_ELEM_SELECT) && eve->co[0] != 0.0f) {
+ if (eve->co[0] < 0.0f) {
+ t->mirror = -1;
+ mirror = -1;
+ }
+ break;
+ }
+ }
+#endif
+ }
+
+ t->customData = BKE_editstrands_get_locations(edit);
+ t->flag |= T_FREE_CUSTOMDATA;
+
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
+ if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+ if (propmode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ struct TransIslandData *v_island = (island_info && island_vert_map[a] != -1) ?
+ &island_info[island_vert_map[a]] : NULL;
+ /* XXX TODO calculate normal and tangent along the strand */
+ float nor[3], tang[3];
+ nor[0] = 0.0f; nor[1] = 0.0f; nor[2] = 1.0f;
+ tang[0] = 0.0f; tang[1] = 1.0f; tang[2] = 0.0f;
+
+ StrandVertsToTransData(t, tob, edit, eve, nor, tang, v_island);
+
+ /* selected */
+ if (BM_elem_flag_test(eve, BM_ELEM_SELECT))
+ tob->flag |= TD_SELECTED;
+
+ if (propmode) {
+ if (propmode & T_PROP_CONNECTED) {
+ tob->dist = dists[a];
+ }
+ else {
+ tob->flag |= TD_NOTCONNECTED;
+ tob->dist = FLT_MAX;
+ }
+ }
+
+ copy_m3_m3(tob->smtx, smtx);
+ copy_m3_m3(tob->mtx, mtx);
+
+ /* Mirror? */
+ if ((mirror > 0 && tob->iloc[0] > 0.0f) || (mirror < 0 && tob->iloc[0] < 0.0f)) {
+#if 0 // TODO
+ BMVert *vmir = EDBM_verts_mirror_get(em, eve);
+ if (vmir && vmir != eve) {
+ tob->extra = vmir;
+ }
+#endif
+ }
+ tob++;
+ }
+ }
+ }
+
+ if (island_info) {
+ MEM_freeN(island_info);
+ MEM_freeN(island_vert_map);
+ }
+
+ if (mirror != 0) {
+#if 0 // TODO
+ tob = t->data;
+ for (a = 0; a < t->total; a++, tob++) {
+ if (ABS(tob->loc[0]) <= 0.00001f) {
+ tob->flag |= TD_MIRROR_EDGE;
+ }
+ }
+#endif
+ }
+
+cleanup:
+ if (dists)
+ MEM_freeN(dists);
+
+ if (t->flag & T_MIRROR) {
+#if 0 // TODO
+ EDBM_verts_mirror_cache_end(em);
+#endif
+ }
+}
+
+void flushTransStrands(TransInfo *t)
+{
+ Scene *scene = t->scene;
+ Object *ob = OBACT;
+ BMEditStrands *edit = BKE_editstrands_from_object(ob);
+ BMEditStrandsLocations origlocs = t->customData;
+
+ BKE_editstrands_solve_constraints(ob, edit, origlocs);
+}
+
/* ********************* mesh ****************** */
static bool bmesh_test_dist_add(BMVert *v, BMVert *v_other,
@@ -2082,9 +2288,8 @@ static void editmesh_set_connectivity_distance(BMesh *bm, float mtx[3][3], float
MEM_freeN(dists_prev);
}
-static struct TransIslandData *editmesh_islands_info_calc(BMEditMesh *em, int *r_island_tot, int **r_island_vert_map)
+static struct TransIslandData *bmesh_islands_info_calc(BMesh *bm, short selectmode, int *r_island_tot, int **r_island_vert_map)
{
- BMesh *bm = em->bm;
struct TransIslandData *trans_islands;
char htype;
char itype;
@@ -2098,7 +2303,7 @@ static struct TransIslandData *editmesh_islands_info_calc(BMEditMesh *em, int *r
int *vert_map;
- if (em->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) {
+ if (selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) {
groups_array = MEM_mallocN(sizeof(*groups_array) * bm->totedgesel, __func__);
group_tot = BM_mesh_calc_edge_groups(bm, groups_array, &group_index,
NULL, NULL,
@@ -2362,7 +2567,7 @@ static void createTransEditVerts(TransInfo *t)
}
if (t->around == V3D_LOCAL) {
- island_info = editmesh_islands_info_calc(em, &island_info_tot, &island_vert_map);
+ island_info = bmesh_islands_info_calc(em->bm, em->selectmode, &island_info_tot, &island_vert_map);
}
/* detect CrazySpace [tm] */
@@ -4827,6 +5032,7 @@ static int SeqToTransData_Recursive(TransInfo *t, ListBase *seqbase, TransData *
Sequence *seq;
int recursive, count, flag;
int tot = 0;
+ int max = INT32_MIN, min = INT32_MAX;
for (seq = seqbase->first; seq; seq = seq->next) {
@@ -4849,15 +5055,21 @@ static int SeqToTransData_Recursive(TransInfo *t, ListBase *seqbase, TransData *
if (flag & SEQ_LEFTSEL) {
SeqToTransData(td++, td2d++, tdsq++, seq, flag, SEQ_LEFTSEL);
tot++;
+ min = min_ii(seq->startdisp, min);
+ max = max_ii(seq->startdisp, max);
}
if (flag & SEQ_RIGHTSEL) {
SeqToTransData(td++, td2d++, tdsq++, seq, flag, SEQ_RIGHTSEL);
tot++;
+ min = min_ii(seq->enddisp, min);
+ max = max_ii(seq->enddisp, max);
}
}
else {
SeqToTransData(td++, td2d++, tdsq++, seq, flag, SELECT);
tot++;
+ min = min_ii(seq->startdisp, min);
+ max = max_ii(seq->enddisp, max);
}
}
}
@@ -6296,6 +6508,13 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
{
/* do nothing */
}
+ else if ((t->scene->basact) &&
+ (ob = t->scene->basact->object) &&
+ (ob->mode & OB_MODE_HAIR_EDIT) &&
+ BKE_editstrands_from_object(ob))
+ {
+ /* do nothing */
+ }
else { /* Objects */
int i;
@@ -8052,6 +8271,16 @@ void createTransData(bContext *C, TransInfo *t)
sort_trans_data_dist(t);
}
}
+ else if (ob && (ob->mode & OB_MODE_HAIR_EDIT) && BKE_editstrands_from_object(ob)) {
+ createTransStrandVerts(t);
+ t->flag |= T_POINTS;
+
+ if (t->data && t->flag & T_PROP_EDIT) {
+ sort_trans_data(t); // makes selected become first in array
+ set_prop_dist(t, 1);
+ sort_trans_data_dist(t);
+ }
+ }
else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
if ((t->options & CTX_PAINT_CURVE) && !ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) {
t->flag |= T_POINTS | T_2D_EDIT;
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index 33aef92db1e..25f5c8884e5 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -71,6 +71,7 @@
#include "BKE_armature.h"
#include "BKE_curve.h"
#include "BKE_depsgraph.h"
+#include "BKE_editstrands.h"
#include "BKE_fcurve.h"
#include "BKE_lattice.h"
#include "BKE_nla.h"
@@ -899,6 +900,12 @@ static void recalcData_objects(TransInfo *t)
}
flushTransParticles(t);
}
+ else if (base && (base->object->mode & OB_MODE_HAIR_EDIT) && BKE_editstrands_from_object(base->object)) {
+ if (t->state != TRANS_CANCEL) {
+ applyProject(t);
+ }
+ flushTransStrands(t);
+ }
else {
int i;
@@ -1190,10 +1197,16 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
t->view = v3d;
t->animtimer = (animscreen) ? animscreen->animtimer : NULL;
-
+
+ if (op && ((prop = RNA_struct_find_property(op->ptr, "use_widget_input")) &&
+ RNA_property_is_set(op->ptr, prop)))
+ {
+ if (RNA_property_boolean_get(op->ptr, prop))
+ t->flag |= T_USE_WIDGET;
+ }
+
/* turn manipulator off during transform */
- // FIXME: but don't do this when USING the manipulator...
- if (t->flag & T_MODAL) {
+ if ((t->flag & T_MODAL) && !(t->flag & T_USE_WIDGET)) {
t->twtype = v3d->twtype;
v3d->twtype = 0;
}
@@ -1474,7 +1487,7 @@ void postTrans(bContext *C, TransInfo *t)
else if (t->spacetype == SPACE_VIEW3D) {
View3D *v3d = t->sa->spacedata.first;
/* restore manipulator */
- if (t->flag & T_MODAL) {
+ if ((t->flag & T_MODAL) && !(t->flag & T_USE_WIDGET)) {
v3d->twtype = t->twtype;
}
}
diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c
index acc6108f264..667c0f01c18 100644
--- a/source/blender/editors/transform/transform_manipulator.c
+++ b/source/blender/editors/transform/transform_manipulator.c
@@ -66,6 +66,7 @@
#include "ED_curve.h"
#include "ED_particle.h"
#include "ED_view3d.h"
+#include "ED_transform.h"
#include "UI_resources.h"
@@ -549,7 +550,7 @@ static int calc_manipulator_stats(const bContext *C)
if (ob && totsel) {
switch (v3d->twmode) {
-
+
case V3D_MANIP_GLOBAL:
{
break; /* nothing to do */
@@ -973,7 +974,7 @@ static void draw_manipulator_rotate(
ortho = is_orthogonal_m4(rv3d->twmat);
-
+
/* apply the transform delta */
if (is_moving) {
copy_m4_m4(matt, rv3d->twmat); // to copy the parts outside of [3][3]
@@ -1881,4 +1882,3 @@ int BIF_do_manipulator(bContext *C, const struct wmEvent *event, wmOperator *op)
return val;
}
-
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index 013e47886eb..58db9ea5082 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -82,6 +82,7 @@ static const char OP_EDGE_CREASE[] = "TRANSFORM_OT_edge_crease";
static const char OP_EDGE_BWEIGHT[] = "TRANSFORM_OT_edge_bevelweight";
static const char OP_SEQ_SLIDE[] = "TRANSFORM_OT_seq_slide";
+
static void TRANSFORM_OT_translate(struct wmOperatorType *ot);
static void TRANSFORM_OT_rotate(struct wmOperatorType *ot);
static void TRANSFORM_OT_tosphere(struct wmOperatorType *ot);
diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c
index 7fea8e163fd..030e29d0ebb 100644
--- a/source/blender/editors/transform/transform_orientations.c
+++ b/source/blender/editors/transform/transform_orientations.c
@@ -1010,7 +1010,7 @@ int getTransformOrientation(const bContext *C, float normal[3], float plane[3],
result = ORIENTATION_EDGE;
}
}
- else if (ob && (ob->mode & (OB_MODE_ALL_PAINT | OB_MODE_PARTICLE_EDIT))) {
+ else if (ob && (ob->mode & (OB_MODE_ALL_BRUSH | OB_MODE_PARTICLE_EDIT))) {
/* pass */
}
else {
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index 4ccc05b6614..d5480745b60 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -2447,7 +2447,6 @@ static void applyGridIncrement(TransInfo *t, float *val, int max_index, const fl
/* Early bailing out if no need to snap */
if (fac[action] == 0.0f) {
return;
- }
if (use_aspect) {
/* custom aspect for fcurve */
diff --git a/source/blender/editors/util/editmode_undo.c b/source/blender/editors/util/editmode_undo.c
index bc7a8374c73..7c4db697b01 100644
--- a/source/blender/editors/util/editmode_undo.c
+++ b/source/blender/editors/util/editmode_undo.c
@@ -84,6 +84,7 @@ typedef struct UndoElem {
void *undodata;
uintptr_t undosize;
char name[BKE_UNDO_STR_MAX];
+ Object *(*get_object)(const bContext *C);
void * (*getdata)(bContext * C);
void (*freedata)(void *);
void (*to_editmode)(void *, void *, void *);
@@ -106,14 +107,15 @@ static void undo_restore(UndoElem *undo, void *editdata, void *obdata)
/* name can be a dynamic string */
void undo_editmode_push(bContext *C, const char *name,
- void * (*getdata)(bContext * C),
+ Object *(*get_object)(const bContext *C),
+ void * (*getdata)(bContext *C),
void (*freedata)(void *),
void (*to_editmode)(void *, void *, void *),
void *(*from_editmode)(void *, void *),
int (*validate_undo)(void *, void *))
{
UndoElem *uel;
- Object *obedit = CTX_data_edit_object(C);
+ Object *obedit = get_object(C);
void *editdata;
int nr;
uintptr_t memused, totmem, maxmem;
@@ -133,6 +135,7 @@ void undo_editmode_push(bContext *C, const char *name,
BLI_strncpy(uel->name, name, sizeof(uel->name));
BLI_addtail(&undobase, uel);
+ uel->get_object = get_object;
uel->getdata = getdata;
uel->freedata = freedata;
uel->to_editmode = to_editmode;
@@ -193,19 +196,19 @@ void undo_editmode_push(bContext *C, const char *name,
static void undo_clean_stack(bContext *C)
{
UndoElem *uel, *next;
- Object *obedit = CTX_data_edit_object(C);
/* global undo changes pointers, so we also allow identical names */
/* side effect: when deleting/renaming object and start editing new one with same name */
uel = undobase.first;
while (uel) {
+ Object *obedit = uel->get_object(C);
void *editdata = uel->getdata(C);
bool is_valid = false;
next = uel->next;
/* for when objects are converted, renamed, or global undo changes pointers... */
- if (uel->type == obedit->type) {
+ if (obedit && uel->type == obedit->type) {
if (STREQ(uel->id.name, obedit->id.name)) {
if (uel->validate_undo == NULL)
is_valid = true;
@@ -232,12 +235,13 @@ static void undo_clean_stack(bContext *C)
/* 1 = an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
void undo_editmode_step(bContext *C, int step)
{
- Object *obedit = CTX_data_edit_object(C);
+ Object *obedit;
/* prevent undo to happen on wrong object, stack can be a mix */
undo_clean_stack(C);
if (step == 0) {
+ obedit = curundo->get_object(C);
undo_restore(curundo, curundo->getdata(C), obedit->data);
}
else if (step == 1) {
@@ -248,6 +252,7 @@ void undo_editmode_step(bContext *C, int step)
else {
if (G.debug & G_DEBUG) printf("undo %s\n", curundo->name);
curundo = curundo->prev;
+ obedit = curundo->get_object(C);
undo_restore(curundo, curundo->getdata(C), obedit->data);
}
}
@@ -258,15 +263,19 @@ void undo_editmode_step(bContext *C, int step)
error("No more steps to redo");
}
else {
+ obedit = curundo->get_object(C);
undo_restore(curundo->next, curundo->getdata(C), obedit->data);
curundo = curundo->next;
if (G.debug & G_DEBUG) printf("redo %s\n", curundo->name);
}
}
+ obedit = curundo->get_object(C);
+
/* special case for editmesh, mode must be copied back to the scene */
if (obedit->type == OB_MESH) {
- EDBM_selectmode_to_scene(C);
+ if (obedit == CTX_data_edit_object(C))
+ EDBM_selectmode_to_scene(C);
}
DAG_id_tag_update(&obedit->id, OB_RECALC_DATA);
diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c
index ee6101d2952..3eef8401db9 100644
--- a/source/blender/editors/util/undo.c
+++ b/source/blender/editors/util/undo.c
@@ -55,6 +55,7 @@
#include "ED_mball.h"
#include "ED_mesh.h"
#include "ED_object.h"
+#include "ED_physics.h"
#include "ED_render.h"
#include "ED_screen.h"
#include "ED_paint.h"
@@ -104,6 +105,9 @@ void ED_undo_push(bContext *C, const char *str)
PE_undo_push(CTX_data_scene(C), str);
}
+ else if (obact && obact->mode & OB_MODE_HAIR_EDIT) {
+ undo_push_strands(C, str);
+ }
else if (obact && obact->mode & OB_MODE_SCULPT) {
/* do nothing for now */
}
@@ -189,6 +193,14 @@ static int ed_undo_step(bContext *C, int step, const char *undoname)
else
PE_redo(scene);
}
+ else if (obact && obact->mode & OB_MODE_HAIR_EDIT) {
+ if (undoname)
+ undo_editmode_name(C, undoname);
+ else
+ undo_editmode_step(C, step);
+
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
+ }
else if (U.uiflag & USER_GLOBALUNDO) {
// note python defines not valid here anymore.
//#ifdef WITH_PYTHON
diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h
index a8630d0c5be..4c9b8c0519b 100644
--- a/source/blender/gpu/GPU_buffers.h
+++ b/source/blender/gpu/GPU_buffers.h
@@ -48,6 +48,9 @@ struct GSet;
struct GPUVertPointLink;
struct PBVH;
+typedef void (*GPUBufferCopyFunc)(DerivedMesh *dm, float *varray, int *index,
+ int *mat_orig_to_new, void *user_data);
+
typedef struct GPUBuffer {
int size; /* in bytes */
void *pointer; /* used with vertex arrays */
@@ -88,6 +91,7 @@ typedef struct GPUDrawObject {
GPUBuffer *colors;
GPUBuffer *edges;
GPUBuffer *uvedges;
+ GPUBuffer *facemapindices;
/* for each triangle, the original MFace index */
int *triangle_to_mface;
@@ -114,6 +118,10 @@ typedef struct GPUDrawObject {
int totvert;
int totedge;
+ int totfacemaps; /* total facemaps */
+ int *facemap_start; /* beginning of facemap */
+ int *facemap_count; /* elements per facemap */
+
int loose_edge_offset;
int tot_loose_edge_drawn;
int tot_edge_drawn;
@@ -132,9 +140,21 @@ void GPU_global_buffer_pool_free_unused(void);
GPUBuffer *GPU_buffer_alloc(int size, bool force_vertex_arrays);
void GPU_buffer_free(GPUBuffer *buffer);
-GPUDrawObject *GPU_drawobject_new(struct DerivedMesh *dm);
void GPU_drawobject_free(struct DerivedMesh *dm);
+/* flag that controls data type to fill buffer with, a modifier will prepare. */
+typedef enum {
+ GPU_BUFFER_VERTEX = 0,
+ GPU_BUFFER_NORMAL,
+ GPU_BUFFER_COLOR,
+ GPU_BUFFER_UV,
+ GPU_BUFFER_UV_TEXPAINT,
+ GPU_BUFFER_EDGE,
+ GPU_BUFFER_UVEDGE,
+ GPU_BUFFER_FACEMAP
+} GPUBufferType;
+
+
/* called before drawing */
void GPU_vertex_setup(struct DerivedMesh *dm);
void GPU_normal_setup(struct DerivedMesh *dm);
@@ -144,6 +164,7 @@ void GPU_texpaint_uv_setup(struct DerivedMesh *dm);
void GPU_color_setup(struct DerivedMesh *dm, int colType);
void GPU_edge_setup(struct DerivedMesh *dm); /* does not mix with other data */
void GPU_uvedge_setup(struct DerivedMesh *dm);
+void GPU_facemap_setup(struct DerivedMesh *dm);
int GPU_attrib_element_size(GPUAttrib data[], int numdata);
void GPU_interleaved_attrib_setup(GPUBuffer *buffer, GPUAttrib data[], int numdata);
diff --git a/source/blender/gpu/GPU_renderer.h b/source/blender/gpu/GPU_renderer.h
new file mode 100644
index 00000000000..7ebc864c454
--- /dev/null
+++ b/source/blender/gpu/GPU_renderer.h
@@ -0,0 +1,58 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2014 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Antony Riakiotakis.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __GPU_RENDERER_H__
+#define __GPU_RENDERER_H__
+
+#include "DNA_listBase.h"
+
+struct Object;
+
+/* batches per material sorted by some criterion, ie texture image changes */
+typedef struct SubBatch {
+ int start;
+ int len;
+ void *data; /* extra data relevant to the subbatch, such as image */
+} SubBatch;
+
+
+/* stores data related to the object that can be used for rendering */
+typedef struct MaterialBatch {
+ int start;
+ int len;
+ ListBase subbatches;
+ struct Object *ob;
+ void *data;
+} MaterialBatch;
+
+
+/* new type of material */
+typedef struct GPUMaterialX {
+ int datarequest; /* type of data requested from objects */
+} GPUMaterialX;
+
+void GPU_renderer_material_draw(struct ListBase *materials);
+
+#endif // __GPU_RENDERER_H__
diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c
index 62843f0905f..8b4e16c76ff 100644
--- a/source/blender/gpu/intern/gpu_buffers.c
+++ b/source/blender/gpu/intern/gpu_buffers.c
@@ -69,6 +69,34 @@ typedef enum {
GPU_BUFFER_ELEMENT_STATE = (1 << 5),
} GPUBufferState;
+typedef struct {
+ GPUBufferCopyFunc copy;
+ GLenum gl_buffer_type;
+ int vector_size;
+} GPUBufferTypeSettings;
+
+static void GPU_buffer_copy_vertex(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user);
+static void GPU_buffer_copy_normal(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user);
+static void GPU_buffer_copy_mcol(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user);
+static void GPU_buffer_copy_uv(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user);
+static void GPU_buffer_copy_uv_texpaint(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user);
+static void GPU_buffer_copy_edge(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user);
+static void GPU_buffer_copy_uvedge(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user);
+static void GPU_buffer_copy_facemap(DerivedMesh *dm, float *varray, int *index, int *mat_orig_to_new, void *user);
+
+static int gpu_buffer_size_from_type(DerivedMesh *dm, GPUBufferType type);
+
+const GPUBufferTypeSettings gpu_buffer_type_settings[] = {
+ {GPU_buffer_copy_vertex, GL_ARRAY_BUFFER_ARB, 3},
+ {GPU_buffer_copy_normal, GL_ARRAY_BUFFER_ARB, 3},
+ {GPU_buffer_copy_mcol, GL_ARRAY_BUFFER_ARB, 3},
+ {GPU_buffer_copy_uv, GL_ARRAY_BUFFER_ARB, 2},
+ {GPU_buffer_copy_uv_texpaint, GL_ARRAY_BUFFER_ARB, 4},
+ {GPU_buffer_copy_edge, GL_ELEMENT_ARRAY_BUFFER_ARB, 2},
+ {GPU_buffer_copy_uvedge, GL_ELEMENT_ARRAY_BUFFER_ARB, 4},
+ {GPU_buffer_copy_facemap, GL_ELEMENT_ARRAY_BUFFER_ARB, 3}
+};
+
#define MAX_GPU_ATTRIB_DATA 32
#define BUFFER_OFFSET(n) ((GLubyte *)NULL + (n))
@@ -505,7 +533,7 @@ static void gpu_drawobject_init_vert_points(GPUDrawObject *gdo, MFace *f, int to
/* see GPUDrawObject's structure definition for a description of the
* data being initialized here */
-GPUDrawObject *GPU_drawobject_new(DerivedMesh *dm)
+static GPUDrawObject *GPU_drawobject_new(DerivedMesh *dm)
{
GPUDrawObject *gdo;
MFace *mface;
@@ -572,8 +600,14 @@ void GPU_drawobject_free(DerivedMesh *dm)
return;
MEM_freeN(gdo->materials);
- MEM_freeN(gdo->triangle_to_mface);
- MEM_freeN(gdo->vert_points);
+ if (gdo->triangle_to_mface)
+ MEM_freeN(gdo->triangle_to_mface);
+ if (gdo->vert_points)
+ MEM_freeN(gdo->vert_points);
+ if (gdo->facemap_count)
+ MEM_freeN(gdo->facemap_count);
+ if (gdo->facemap_start)
+ MEM_freeN(gdo->facemap_start);
#ifdef USE_GPU_POINT_LINK
MEM_freeN(gdo->vert_points_mem);
#endif
@@ -584,6 +618,7 @@ void GPU_drawobject_free(DerivedMesh *dm)
GPU_buffer_free(gdo->colors);
GPU_buffer_free(gdo->edges);
GPU_buffer_free(gdo->uvedges);
+ GPU_buffer_free(gdo->facemapindices);
MEM_freeN(gdo);
dm->drawObject = NULL;
@@ -609,8 +644,7 @@ typedef void (*GPUBufferCopyFunc)(DerivedMesh *dm, float *varray, int *index,
int *mat_orig_to_new, void *user_data);
static GPUBuffer *gpu_buffer_setup(DerivedMesh *dm, GPUDrawObject *object,
- int vector_size, int size, GLenum target,
- void *user, GPUBufferCopyFunc copy_f)
+ int type, void *user)
{
GPUBufferPool *pool;
GPUBuffer *buffer;
@@ -618,6 +652,10 @@ static GPUBuffer *gpu_buffer_setup(DerivedMesh *dm, GPUDrawObject *object,
int *mat_orig_to_new;
int *cur_index_per_mat;
int i;
+ const GPUBufferTypeSettings *ts = &gpu_buffer_type_settings[type];
+ GLenum target = ts->gl_buffer_type;
+ int vector_size = ts->vector_size;
+ int size = gpu_buffer_size_from_type(dm, type);
bool use_VBOs = (GLEW_ARB_vertex_buffer_object) && !(U.gameflags & USER_DISABLE_VBO);
GLboolean uploaded;
@@ -674,7 +712,10 @@ static GPUBuffer *gpu_buffer_setup(DerivedMesh *dm, GPUDrawObject *object,
uploaded = GL_FALSE;
/* attempt to upload the data to the VBO */
while (uploaded == GL_FALSE) {
- (*copy_f)(dm, varray, cur_index_per_mat, mat_orig_to_new, user);
+ if (dm->copy_gpu_data)
+ dm->copy_gpu_data(dm, type, varray, cur_index_per_mat, mat_orig_to_new, user);
+ else
+ ts->copy(dm, varray, cur_index_per_mat, mat_orig_to_new, user);
/* glUnmapBuffer returns GL_FALSE if
* the data store is corrupted; retry
* in that case */
@@ -691,7 +732,10 @@ static GPUBuffer *gpu_buffer_setup(DerivedMesh *dm, GPUDrawObject *object,
if (buffer) {
varray = buffer->pointer;
- (*copy_f)(dm, varray, cur_index_per_mat, mat_orig_to_new, user);
+ if (dm->copy_gpu_data)
+ dm->copy_gpu_data(dm, type, varray, cur_index_per_mat, mat_orig_to_new, user);
+ else
+ ts->copy(dm, varray, cur_index_per_mat, mat_orig_to_new, user);
}
}
@@ -948,6 +992,8 @@ static void GPU_buffer_copy_edge(DerivedMesh *dm, float *varray_, int *UNUSED(in
medge_base = medge = dm->getEdgeArray(dm);
totedge = dm->getNumEdges(dm);
+
+ /* two passes, one to count number of faces per facemap, one to actually copy the data */
for (i = 0; i < totedge; i++, medge++) {
if (medge->flag & ME_EDGEDRAW) {
@@ -1032,31 +1078,63 @@ static void GPU_buffer_copy_uvedge(DerivedMesh *dm, float *varray, int *UNUSED(i
}
}
-typedef enum {
- GPU_BUFFER_VERTEX = 0,
- GPU_BUFFER_NORMAL,
- GPU_BUFFER_COLOR,
- GPU_BUFFER_UV,
- GPU_BUFFER_UV_TEXPAINT,
- GPU_BUFFER_EDGE,
- GPU_BUFFER_UVEDGE,
-} GPUBufferType;
+static void GPU_buffer_copy_facemap(DerivedMesh *dm, float *varray_, int *UNUSED(index), int *UNUSED(mat_orig_to_new), void *UNUSED(user))
+{
+ GPUDrawObject *gdo = dm->drawObject;
+ int facemap;
+ int *facemap_po = CustomData_get_layer(&dm->polyData, CD_FACEMAP);
+ unsigned int *varray = (unsigned int *)varray_;
+ int i, totface, offset = 0;
+ MFace *f, *f_base = dm->getTessFaceArray(dm);
+ int *facemap_offset;
+ int *orig_index = dm->getTessFaceDataArray(dm, CD_ORIGINDEX);
+
+ totface = dm->getNumTessFaces(dm);
-typedef struct {
- GPUBufferCopyFunc copy;
- GLenum gl_buffer_type;
- int vector_size;
-} GPUBufferTypeSettings;
+ gdo->totfacemaps = dm->totfmaps;
-const GPUBufferTypeSettings gpu_buffer_type_settings[] = {
- {GPU_buffer_copy_vertex, GL_ARRAY_BUFFER_ARB, 3},
- {GPU_buffer_copy_normal, GL_ARRAY_BUFFER_ARB, 3},
- {GPU_buffer_copy_mcol, GL_ARRAY_BUFFER_ARB, 3},
- {GPU_buffer_copy_uv, GL_ARRAY_BUFFER_ARB, 2},
- {GPU_buffer_copy_uv_texpaint, GL_ARRAY_BUFFER_ARB, 4},
- {GPU_buffer_copy_edge, GL_ELEMENT_ARRAY_BUFFER_ARB, 2},
- {GPU_buffer_copy_uvedge, GL_ELEMENT_ARRAY_BUFFER_ARB, 4}
-};
+ gdo->facemap_start = MEM_callocN(gdo->totfacemaps * sizeof(*gdo->facemap_start), "GDO_facemap_start");
+ gdo->facemap_count = MEM_callocN(gdo->totfacemaps * sizeof(*gdo->facemap_count), "GDO_facemap_count");
+ facemap_offset = MEM_callocN(gdo->totfacemaps * sizeof(*facemap_offset), "facemap_offset");
+
+ f = f_base;
+ for (i = 0; i < totface; i++, f++) {
+ facemap = facemap_po[orig_index[i]];
+ if (facemap != -1)
+ gdo->facemap_count[facemap] += (f->v4) ? 6 : 3;
+ }
+
+ for (i = 0; i < gdo->totfacemaps; i++) {
+ gdo->facemap_start[i] = offset;
+ offset += gdo->facemap_count[i];
+ }
+
+ f = f_base;
+ for (i = 0; i < totface; i++, f++) {
+ int fmap_offset;
+ facemap = facemap_po[orig_index[i]];
+
+ if (facemap == -1)
+ continue;
+ fmap_offset = gdo->facemap_start[facemap] + facemap_offset[facemap];
+
+ varray[fmap_offset] = gdo->vert_points[f->v1].point_index;
+ varray[fmap_offset + 1] = gdo->vert_points[f->v2].point_index;
+ varray[fmap_offset + 2] = gdo->vert_points[f->v3].point_index;
+
+ facemap_offset[facemap] += 3;
+
+ if (f->v4) {
+ varray[fmap_offset + 3] = dm->drawObject->vert_points[f->v3].point_index;
+ varray[fmap_offset + 4] = dm->drawObject->vert_points[f->v4].point_index;
+ varray[fmap_offset + 5] = dm->drawObject->vert_points[f->v1].point_index;
+
+ facemap_offset[facemap] += 3;
+ }
+ }
+
+ MEM_freeN(facemap_offset);
+}
/* get the GPUDrawObject buffer associated with a type */
static GPUBuffer **gpu_drawobject_buffer_from_type(GPUDrawObject *gdo, GPUBufferType type)
@@ -1076,6 +1154,8 @@ static GPUBuffer **gpu_drawobject_buffer_from_type(GPUDrawObject *gdo, GPUBuffer
return &gdo->edges;
case GPU_BUFFER_UVEDGE:
return &gdo->uvedges;
+ case GPU_BUFFER_FACEMAP:
+ return &gdo->facemapindices;
default:
return NULL;
}
@@ -1104,6 +1184,8 @@ static int gpu_buffer_size_from_type(DerivedMesh *dm, GPUBufferType type)
* less so here we can over allocate and assume all
* tris. */
return sizeof(float) * 4 * dm->drawObject->tot_triangle_point;
+ case GPU_BUFFER_FACEMAP:
+ return sizeof(int) * 3 * dm->drawObject->tot_triangle_point;
default:
return -1;
}
@@ -1112,12 +1194,9 @@ static int gpu_buffer_size_from_type(DerivedMesh *dm, GPUBufferType type)
/* call gpu_buffer_setup with settings for a particular type of buffer */
static GPUBuffer *gpu_buffer_setup_type(DerivedMesh *dm, GPUBufferType type)
{
- const GPUBufferTypeSettings *ts;
void *user_data = NULL;
GPUBuffer *buf;
- ts = &gpu_buffer_type_settings[type];
-
/* special handling for MCol and UV buffers */
if (type == GPU_BUFFER_COLOR) {
if (!(user_data = DM_get_tessface_data_layer(dm, dm->drawObject->colType)))
@@ -1128,9 +1207,7 @@ static GPUBuffer *gpu_buffer_setup_type(DerivedMesh *dm, GPUBufferType type)
return NULL;
}
- buf = gpu_buffer_setup(dm, dm->drawObject, ts->vector_size,
- gpu_buffer_size_from_type(dm, type),
- ts->gl_buffer_type, user_data, ts->copy);
+ buf = gpu_buffer_setup(dm, dm->drawObject, type, user_data);
return buf;
}
@@ -1141,9 +1218,13 @@ static GPUBuffer *gpu_buffer_setup_common(DerivedMesh *dm, GPUBufferType type)
{
GPUBuffer **buf;
- if (!dm->drawObject)
- dm->drawObject = GPU_drawobject_new(dm);
-
+ if (!dm->drawObject) {
+ if (dm->gpuObjectNew)
+ dm->drawObject = dm->gpuObjectNew(dm);
+ else
+ dm->drawObject = GPU_drawobject_new(dm);
+ }
+
buf = gpu_drawobject_buffer_from_type(dm->drawObject, type);
if (!(*buf))
*buf = gpu_buffer_setup_type(dm, type);
@@ -1307,6 +1388,32 @@ void GPU_uvedge_setup(DerivedMesh *dm)
GLStates |= GPU_BUFFER_VERTEX_STATE;
}
+void GPU_facemap_setup(DerivedMesh *dm)
+{
+ if (!gpu_buffer_setup_common(dm, GPU_BUFFER_FACEMAP))
+ return;
+
+ if (!gpu_buffer_setup_common(dm, GPU_BUFFER_VERTEX))
+ return;
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ if (dm->drawObject->points->use_vbo) {
+ glBindBufferARB(GL_ARRAY_BUFFER_ARB, dm->drawObject->points->id);
+ glVertexPointer(3, GL_FLOAT, 0, 0);
+ }
+ else {
+ glVertexPointer(3, GL_FLOAT, 0, dm->drawObject->points->pointer);
+ }
+
+ GLStates |= GPU_BUFFER_VERTEX_STATE;
+ if (dm->drawObject->facemapindices->use_vbo) {
+ glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, dm->drawObject->facemapindices->id);
+ }
+
+ GLStates |= GPU_BUFFER_ELEMENT_STATE;
+}
+
+
static int GPU_typesize(int type)
{
switch (type) {
diff --git a/source/blender/gpu/intern/gpu_compositing.c b/source/blender/gpu/intern/gpu_compositing.c
index 2bafee0fb52..d19be0a8b7e 100644
--- a/source/blender/gpu/intern/gpu_compositing.c
+++ b/source/blender/gpu/intern/gpu_compositing.c
@@ -294,7 +294,6 @@ static GPUTexture * create_jitter_texture(void)
return GPU_texture_create_2D_procedural(64, 64, &jitter[0][0], true, NULL);
}
-
bool GPU_fx_compositor_initialize_passes(
GPUFX *fx, const rcti *rect, const rcti *scissor_rect,
const GPUFXSettings *fx_settings)
diff --git a/source/blender/gpu/intern/gpu_renderer.c b/source/blender/gpu/intern/gpu_renderer.c
new file mode 100644
index 00000000000..6ca0c8b7254
--- /dev/null
+++ b/source/blender/gpu/intern/gpu_renderer.c
@@ -0,0 +1,36 @@
+/*
+ * ***** 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): Antony Riakiotakis.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+
+#include "GPU_renderer.h"
+
+
+/* iterates through all added materials, prepares data if needed and draws their meshes */
+void GPU_renderer_material_draw(struct ListBase *materials)
+{
+ (void)materials;
+}
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index f68de03614b..f9128363dee 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -238,6 +238,7 @@ typedef struct PreviewImage {
#define ID_LS MAKE_ID2('L', 'S') /* FreestyleLineStyle */
#define ID_PAL MAKE_ID2('P', 'L') /* Palette */
#define ID_PC MAKE_ID2('P', 'C') /* PaintCurve */
+#define ID_CL MAKE_ID2('C', 'L') /* CacheLibrary */
/* NOTE! Fake IDs, needed for g.sipo->blocktype or outliner */
#define ID_SEQ MAKE_ID2('S', 'Q')
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index 761e76eb249..b8688e5e12a 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -471,6 +471,9 @@ typedef enum eActionGroup_Flag {
AGRP_NOTVISIBLE = (1 << 5),
/* for UI (Graph Editor), sub-channels are shown */
AGRP_EXPANDED_G = (1 << 6),
+
+ /* sub channel modifiers off */
+ AGRP_MODIFIERS_OFF = (1 << 7),
AGRP_TEMP = (1 << 30),
AGRP_MOVED = (1 << 31)
diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h
index 68f80cb27d8..590179e0a0f 100644
--- a/source/blender/makesdna/DNA_anim_types.h
+++ b/source/blender/makesdna/DNA_anim_types.h
@@ -479,7 +479,7 @@ typedef enum eFCurve_Flags {
/* fcurve uses 'auto-handles', which stay horizontal... */
// DEPRECATED
FCURVE_AUTO_HANDLES = (1<<5),
-
+ FCURVE_MOD_OFF = (1<<6),
/* skip evaluation, as RNA-path cannot be resolved (similar to muting, but cannot be set by user) */
FCURVE_DISABLED = (1<<10),
/* curve can only have whole-number values (integer types) */
diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h
index 6b7e70f454b..af4b91f5457 100644
--- a/source/blender/makesdna/DNA_armature_types.h
+++ b/source/blender/makesdna/DNA_armature_types.h
@@ -154,7 +154,8 @@ typedef enum eArmature_DeformFlag {
ARM_DEF_ENVELOPE = (1<<1),
ARM_DEF_QUATERNION = (1<<2),
ARM_DEF_B_BONE_REST = (1<<3), /* deprecated */
- ARM_DEF_INVERT_VGROUP = (1<<4)
+ ARM_DEF_INVERT_VGROUP = (1<<4),
+ ARM_DEF_FACEMAPS = (1<<5)
} eArmature_DeformFlag;
#if (DNA_DEPRECATED_GCC_POISON == 1)
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index f83caea66a2..a799e851887 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -102,7 +102,10 @@ typedef struct Brush {
char vertexpaint_tool; /* active vertex/weight paint blend mode (poorly named) */
char imagepaint_tool; /* active image paint tool */
char mask_tool; /* enum BrushMaskTool, only used if sculpt_tool is SCULPT_TOOL_MASK */
-
+ char hair_tool; /* active hair tool */
+ char pad2[3];
+ int pad3;
+
float autosmooth_factor;
float crease_pinch_factor;
@@ -295,6 +298,16 @@ typedef enum BrushImagePaintTool {
PAINT_TOOL_MASK = 5
} BrushImagePaintTool;
+typedef enum BrushHairTool {
+ HAIR_TOOL_COMB = 1,
+ HAIR_TOOL_CUT = 2,
+ HAIR_TOOL_LENGTH = 3,
+ HAIR_TOOL_PUFF = 4,
+ HAIR_TOOL_ADD = 5,
+ HAIR_TOOL_SMOOTH = 6,
+ HAIR_TOOL_WEIGHT = 7,
+} BrushHairTool;
+
/* direction that the brush displaces along */
enum {
SCULPT_DISP_DIR_AREA = 0,
diff --git a/source/blender/makesdna/DNA_cache_library_types.h b/source/blender/makesdna/DNA_cache_library_types.h
new file mode 100644
index 00000000000..0def355e7d5
--- /dev/null
+++ b/source/blender/makesdna/DNA_cache_library_types.h
@@ -0,0 +1,294 @@
+/*
+ * ***** 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) 2015 by Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file DNA_cache_library_types.h
+ * \ingroup DNA
+ */
+
+#ifndef __DNA_CACHE_LIBRARY_TYPES_H__
+#define __DNA_CACHE_LIBRARY_TYPES_H__
+
+#include "DNA_defs.h"
+#include "DNA_ID.h"
+#include "DNA_listBase.h"
+
+#define MAX_CACHE_GROUP_LEVEL 8
+
+typedef enum eCacheLibrary_SourceMode {
+ CACHE_LIBRARY_SOURCE_SCENE = 0, /* use generated scene data as input */
+ CACHE_LIBRARY_SOURCE_CACHE = 1, /* use cached data as input*/
+} eCacheLibrary_SourceMode;
+
+typedef enum eCacheLibrary_DisplayMode {
+ CACHE_LIBRARY_DISPLAY_SOURCE = 0, /* display source data */
+ CACHE_LIBRARY_DISPLAY_RESULT = 1, /* display result data */
+ CACHE_LIBRARY_DISPLAY_MODIFIERS = 2, /* display input with modifiers */
+} eCacheLibrary_DisplayMode;
+
+typedef enum eCacheDataType {
+ CACHE_TYPE_OBJECT = (1 << 0),
+ CACHE_TYPE_DERIVED_MESH = (1 << 1),
+ CACHE_TYPE_HAIR = (1 << 2),
+ CACHE_TYPE_HAIR_PATHS = (1 << 3),
+ CACHE_TYPE_PARTICLES = (1 << 4),
+
+ CACHE_TYPE_ALL = CACHE_TYPE_OBJECT | CACHE_TYPE_DERIVED_MESH | CACHE_TYPE_HAIR | CACHE_TYPE_HAIR_PATHS | CACHE_TYPE_PARTICLES,
+} eCacheDataType;
+
+typedef enum eCacheReadSampleResult {
+ CACHE_READ_SAMPLE_INVALID = 0, /* no valid result can be retrieved */
+ CACHE_READ_SAMPLE_EARLY = 1, /* request time before first sample */
+ CACHE_READ_SAMPLE_LATE = 2, /* request time after last sample */
+ CACHE_READ_SAMPLE_EXACT = 3, /* found sample for requested frame */
+ CACHE_READ_SAMPLE_INTERPOLATED = 4, /* no exact sample, but found enclosing samples for interpolation */
+} eCacheReadSampleResult;
+
+typedef enum eCacheLibrary_Flag {
+ CACHE_LIBRARY_BAKING = (1 << 0), /* perform modifier evaluation when evaluating */
+} eCacheLibrary_Flag;
+
+typedef enum eCacheLibrary_DisplayFlag {
+ CACHE_LIBRARY_DISPLAY_MOTION = (1 << 0), /* display motion state result from simulation, if available */
+ CACHE_LIBRARY_DISPLAY_CHILDREN = (1 << 1), /* display child strands, if available */
+} eCacheLibrary_DisplayFlag;
+
+typedef struct CacheLibrary {
+ ID id;
+
+ int flag;
+ short eval_mode DNA_DEPRECATED;
+ short source_mode;
+ short display_mode;
+ short pad;
+ int display_flag;
+ int render_flag DNA_DEPRECATED;
+ int data_types;
+ struct Group *filter_group;
+ char description[256];
+
+ char input_filepath[1024]; /* 1024 = FILE_MAX */
+ char output_filepath[1024]; /* 1024 = FILE_MAX */
+
+ ListBase modifiers;
+
+ struct CacheArchiveInfo *archive_info;
+} CacheLibrary;
+
+/* ========================================================================= */
+
+/* These are runtime structs, included in DNA only for easier RNA parsing */
+
+typedef struct CacheArchiveInfoNode {
+ struct CacheArchiveInfoNode *next, *prev;
+
+ short type;
+ short flag;
+ int pad;
+ char name[256];
+
+ ListBase child_nodes;
+
+ int64_t bytes_size; /* overall size of data stored in this node and children */
+
+ char datatype_name[64];
+ short datatype_extent;
+ short pad2;
+
+ int num_samples;
+
+ /* array properties */
+ int array_size;
+ int pad3;
+} CacheArchiveInfoNode;
+
+typedef enum eCacheArchiveInfoNode_Flag {
+ eCacheArchiveInfoNode_Flag_Expand,
+} eCacheArchiveInfoNode_Flag;
+
+typedef enum eCacheArchiveInfoNode_Type {
+ eCacheArchiveInfoNode_Type_Object,
+ eCacheArchiveInfoNode_Type_ScalarProperty,
+ eCacheArchiveInfoNode_Type_ArrayProperty,
+ eCacheArchiveInfoNode_Type_CompoundProperty,
+} eCacheArchiveInfoNode_Type;
+
+typedef struct CacheArchiveInfo {
+ char filepath[1024]; /* FILE_MAX */
+
+ char app_name[64]; /* MAX_NAME */
+ char date_written[64]; /* MAX_NAME */
+ char description[256];
+
+ struct CacheArchiveInfoNode *root_node;
+} CacheArchiveInfo;
+
+/* ========================================================================= */
+
+/* XXX here be dragons ...
+ * stuff below is a production hack,
+ * should not be considered a permanent solution ...
+ */
+typedef struct CacheModifier {
+ struct CacheModifier *next, *prev;
+
+ short type, pad;
+ int flag;
+ char name[64]; /* MAX_NAME */
+} CacheModifier;
+
+typedef enum eCacheModifier_Type {
+ eCacheModifierType_None = 0,
+
+ eCacheModifierType_HairSimulation = 1,
+ eCacheModifierType_ForceField = 2,
+ eCacheModifierType_ShrinkWrap = 3,
+ eCacheModifierType_StrandsKey = 4,
+ eCacheModifierType_Haircut = 5,
+
+ NUM_CACHE_MODIFIER_TYPES
+} eCacheModifier_Type;
+
+typedef struct HairSimParams {
+ int flag;
+ float timescale;
+ int substeps;
+ int pad;
+
+ struct EffectorWeights *effector_weights;
+
+ float mass;
+ float drag;
+ float goal_stiffness, goal_damping;
+ struct CurveMapping *goal_stiffness_mapping;
+ float stretch_stiffness, stretch_damping;
+ float bend_stiffness, bend_damping;
+ struct CurveMapping *bend_stiffness_mapping;
+} HairSimParams;
+
+typedef enum eHairSimParams_Flag {
+ eHairSimParams_Flag_UseGoalStiffnessCurve = (1 << 0),
+ eHairSimParams_Flag_UseBendStiffnessCurve = (1 << 1),
+ eHairSimParams_Flag_UseGoalDeflect = (1 << 2),
+} eHairSimParams_Flag;
+
+typedef struct HairSimCacheModifier {
+ CacheModifier modifier;
+
+ struct Object *object;
+ int hair_system;
+ int pad;
+
+ HairSimParams sim_params;
+} HairSimCacheModifier;
+
+/* cached mesh data for calculating velocities */
+typedef struct ForceFieldVertexCache {
+ float frame_prev;
+ int totvert;
+ float (*co_prev)[3];
+ float (*vel)[3];
+} ForceFieldVertexCache;
+
+typedef struct ForceFieldCacheModifier {
+ CacheModifier modifier;
+
+ struct Object *object;
+
+ struct ForceFieldVertexCache *vertex_cache;
+
+ int type;
+ int flag;
+ float strength;
+ float min_distance, max_distance;
+ float falloff;
+} ForceFieldCacheModifier;
+
+typedef enum eForceFieldCacheModifier_Type {
+ eForceFieldCacheModifier_Type_Deflect = 0,
+ eForceFieldCacheModifier_Type_Drag = 1,
+} eForceFieldCacheModifier_Type;
+
+typedef enum eForceFieldCacheModifier_Flag {
+ eForceFieldCacheModifier_Flag_DoubleSided = (1 << 0),
+} eForceFieldCacheModifier_Flag;
+
+typedef struct ShrinkWrapCacheModifier {
+ CacheModifier modifier;
+
+ struct Object *object;
+ int hair_system;
+ int flag;
+
+ struct Object *target;
+} ShrinkWrapCacheModifier;
+
+typedef enum eShrinkWrapCacheModifier_Flag {
+ eShrinkWrapCacheModifier_Flag_InternalTarget = (1 << 0),
+} eShrinkWrapCacheModifier_Flag;
+
+typedef struct StrandsKeyCacheModifier {
+ CacheModifier modifier;
+
+ struct Object *object;
+ int hair_system;
+ int flag;
+
+ struct Key *key;
+ int shapenr;
+ int pad;
+
+ struct BMEditStrands *edit; /* edit data (runtime) */
+} StrandsKeyCacheModifier;
+
+typedef enum eStrandsKeyCacheModifier_Flag {
+ eStrandsKeyCacheModifier_Flag_ShapeLock = (1 << 0),
+ eStrandsKeyCacheModifier_Flag_UseMotionState = (1 << 1),
+} eStrandsKeyCacheModifier_Flag;
+
+typedef struct HaircutCacheModifier {
+ CacheModifier modifier;
+
+ struct Object *object;
+ int hair_system;
+ int flag;
+
+ short cut_mode;
+ short pad[3];
+
+ struct Object *target;
+} HaircutCacheModifier;
+
+typedef enum eHaircutCacheModifier_Flag {
+ eHaircutCacheModifier_Flag_InternalTarget = (1 << 0),
+} eHaircutCacheModifier_Flag;
+
+typedef enum eHaircutCacheModifier_CutMode {
+ eHaircutCacheModifier_CutMode_Enter = (1 << 0),
+ eHaircutCacheModifier_CutMode_Exit = (1 << 1),
+} eHaircutCacheModifier_CutMode;
+
+#endif
diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h
index 74f5967db13..47cae0f4134 100644
--- a/source/blender/makesdna/DNA_customdata_types.h
+++ b/source/blender/makesdna/DNA_customdata_types.h
@@ -63,10 +63,10 @@ typedef struct CustomDataExternal {
* layers, each with a data type (e.g. MTFace, MDeformVert, etc.). */
typedef struct CustomData {
CustomDataLayer *layers; /* CustomDataLayers, ordered by type */
- int typemap[42]; /* runtime only! - maps types to indices of first layer of that type,
+ int typemap[44]; /* runtime only! - maps types to indices of first layer of that type,
* MUST be >= CD_NUMTYPES, but we cant use a define here.
* Correct size is ensured in CustomData_update_typemap assert() */
- int pad_i1;
+ int pad;
int totlayer, maxlayer; /* number of layers, size of layers array */
int totsize; /* in editmode, total size of all data layers */
struct BLI_mempool *pool; /* (BMesh Only): Memory pool for allocation of blocks */
@@ -121,8 +121,10 @@ typedef enum CustomDataType {
CD_MLOOPTANGENT = 39,
CD_TESSLOOPNORMAL = 40,
CD_CUSTOMLOOPNORMAL = 41,
+ CD_FACEMAP = 42, /* exclusive face group, each face can only be part of one */
+ CD_MSURFACE_SAMPLE = 43,
- CD_NUMTYPES = 42
+ CD_NUMTYPES = 44
} CustomDataType;
/* Bits for CustomDataMask */
@@ -170,6 +172,8 @@ typedef enum CustomDataType {
#define CD_MASK_MLOOPTANGENT (1LL << CD_MLOOPTANGENT)
#define CD_MASK_TESSLOOPNORMAL (1LL << CD_TESSLOOPNORMAL)
#define CD_MASK_CUSTOMLOOPNORMAL (1LL << CD_CUSTOMLOOPNORMAL)
+#define CD_MASK_FACEMAP (1LL << CD_FACEMAP)
+#define CD_MASK_MSURFACE_SAMPLE (1LL << CD_MSURFACE_SAMPLE)
/* CustomData.flag */
enum {
diff --git a/source/blender/makesdna/DNA_key_types.h b/source/blender/makesdna/DNA_key_types.h
index 60ab01c901b..5ab67d286b6 100644
--- a/source/blender/makesdna/DNA_key_types.h
+++ b/source/blender/makesdna/DNA_key_types.h
@@ -62,13 +62,27 @@ typedef struct KeyBlock {
void *data; /* array of shape key values, size is (Key->elemsize * KeyBlock->totelem) */
char name[64]; /* MAX_NAME (unique name, user assigned) */
char vgroup[64]; /* MAX_VGROUP_NAME (optional vertex group), array gets allocated into 'weights' when set */
+ char facemap[64]; /* facemap name, if applicable */
/* ranges, for RNA and UI only to clamp 'curval' */
float slidermin;
float slidermax;
-
} KeyBlock;
+typedef enum eKeyOwnerType {
+ /* 0 used as 'undefined', for versioning */
+ KEY_OWNER_MESH = 1,
+ KEY_OWNER_CURVE = 2,
+ KEY_OWNER_LATTICE = 3,
+ KEY_OWNER_PARTICLES = 4,
+ KEY_OWNER_CACHELIB = 5,
+} eKeyOwnerType;
+
+/* DEPRECATED */
+typedef struct KeyFrom {
+ int type DNA_DEPRECATED;
+ int index DNA_DEPRECATED; /* index of owner in the id */
+} KeyFrom;
typedef struct Key {
ID id;
@@ -90,6 +104,9 @@ typedef struct Key {
struct Ipo *ipo DNA_DEPRECATED; /* old animation system, deprecated for 2.5 */
ID *from;
+ KeyFrom from_extra DNA_DEPRECATED;
+ int fromtype; /* supplementary type of the Key owner */
+ int fromindex; /* index of owner in the id */
int totkey; /* (totkey == BLI_listbase_count(&key->block)) */
short flag;
diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h
index b76f40c884d..e3eb04a5578 100644
--- a/source/blender/makesdna/DNA_meshdata_types.h
+++ b/source/blender/makesdna/DNA_meshdata_types.h
@@ -298,6 +298,13 @@ enum {
FREESTYLE_FACE_MARK = 1,
};
+typedef struct MSurfaceSample {
+ unsigned int orig_verts[3];
+ float orig_weights[3];
+ int orig_poly;
+ unsigned int orig_loops[3];
+} MSurfaceSample;
+
/* mvert->flag */
enum {
/* SELECT = (1 << 0), */
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index 1fc66b9b016..bf2d5e5fa6c 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -714,22 +714,31 @@ typedef enum {
} ParticleSystemModifierFlag;
typedef enum {
- eParticleInstanceFlag_Parents = (1 << 0),
- eParticleInstanceFlag_Children = (1 << 1),
- eParticleInstanceFlag_Path = (1 << 2),
- eParticleInstanceFlag_Unborn = (1 << 3),
- eParticleInstanceFlag_Alive = (1 << 4),
- eParticleInstanceFlag_Dead = (1 << 5),
- eParticleInstanceFlag_KeepShape = (1 << 6),
- eParticleInstanceFlag_UseSize = (1 << 7),
+ eParticleInstanceFlag_Parents = (1 << 0),
+ eParticleInstanceFlag_Children = (1 << 1),
+ eParticleInstanceFlag_Path = (1 << 2),
+ eParticleInstanceFlag_Unborn = (1 << 3),
+ eParticleInstanceFlag_Alive = (1 << 4),
+ eParticleInstanceFlag_Dead = (1 << 5),
+ eParticleInstanceFlag_KeepShape = (1 << 6),
+ eParticleInstanceFlag_UseSize = (1 << 7),
} ParticleInstanceModifierFlag;
+typedef enum {
+ eParticleInstanceSpace_World = 0,
+ eParticleInstanceSpace_Local = 1,
+} ParticleInstanceModifierSpace;
+
typedef struct ParticleInstanceModifierData {
ModifierData modifier;
struct Object *ob;
- short psys, flag, axis, pad;
+ short psys, flag, axis, space;
float position, random_position;
+ float rotation, random_rotation;
+ float particle_amount, particle_offset;
+ char index_layer_name[64]; /* MAX_CUSTOMDATA_LAYER_NAME */
+ char value_layer_name[64]; /* MAX_CUSTOMDATA_LAYER_NAME */
} ParticleInstanceModifierData;
typedef enum {
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index bce13921903..44b6a26bc37 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -790,6 +790,17 @@ typedef struct NodeShaderVectTransform {
int pad;
} NodeShaderVectTransform;
+typedef struct NodeShaderTexPointDensity {
+ short point_source, pad;
+ int particle_system;
+ float radius;
+ int resolution;
+ short space;
+ short interpolation;
+ short color_source;
+ short pad2;
+} NodeShaderTexPointDensity;
+
/* TEX_output */
typedef struct TexNodeOutput {
char name[64];
@@ -857,6 +868,44 @@ typedef struct NodeShaderUVMap {
char uv_map[64];
} NodeShaderUVMap;
+typedef struct OpenVDBGridInfo {
+ struct OpenVDBGridInfo *next, *prev;
+
+ char name[64]; /* MAX_NAME */
+ short flag;
+ short type;
+ int pad;
+} OpenVDBGridInfo;
+
+typedef enum eOpenVDBGridInfo_Flag {
+ OPENVDB_FLAG_UNIFORM_VOXELS = (1 << 0),
+} eOpenVDBGridInfo_Flag;
+
+typedef enum eOpenVDBGridInfo_Type {
+ OPENVDB_TYPE_UNKNOWN = 0,
+ OPENVDB_TYPE_FLOAT = 1,
+ OPENVDB_TYPE_VEC3 = 2,
+ OPENVDB_TYPE_COLOR = 3,
+} eOpenVDBGridInfo_Type;
+
+typedef struct NodeShaderOpenVDB {
+ char filename[1024];
+ short sampling, source;
+ int pad;
+
+ ListBase grid_info;
+} NodeShaderOpenVDB;
+
+enum {
+ NODE_VDB_SAMPLE_POINT = 0,
+ NODE_VDB_SAMPLE_BOX = 1,
+};
+
+enum {
+ NODE_VDB_SRC_FILE = 0,
+ NODE_VDB_SRC_SEQ = 1,
+};
+
typedef struct NodeSunBeams {
float source[2];
@@ -1092,4 +1141,24 @@ enum {
#define CMP_NODE_PLANETRACKDEFORM_MBLUR_SAMPLES_MAX 64
+/* Point Density shader node */
+
+enum {
+ SHD_POINTDENSITY_SOURCE_PSYS = 0,
+ SHD_POINTDENSITY_SOURCE_OBJECT = 1,
+};
+
+enum {
+ SHD_POINTDENSITY_SPACE_OBJECT = 0,
+ SHD_POINTDENSITY_SPACE_WORLD = 1,
+};
+
+enum {
+ SHD_POINTDENSITY_COLOR_CONSTANT = 0,
+ SHD_POINTDENSITY_COLOR_PARTAGE = 1,
+ SHD_POINTDENSITY_COLOR_PARTSPEED = 2,
+ SHD_POINTDENSITY_COLOR_PARTVEL = 3,
+ SHD_POINTDENSITY_COLOR_PARTTEX = 4,
+};
+
#endif
diff --git a/source/blender/makesdna/DNA_object_force.h b/source/blender/makesdna/DNA_object_force.h
index c9c1f618e86..52ea42eb535 100644
--- a/source/blender/makesdna/DNA_object_force.h
+++ b/source/blender/makesdna/DNA_object_force.h
@@ -370,6 +370,7 @@ typedef struct SoftBody {
#define PFIELD_DO_ROTATION (1<<15)
#define PFIELD_GUIDE_PATH_WEIGHT (1<<16) /* apply curve weights */
#define PFIELD_SMOKE_DENSITY (1<<17) /* multiply smoke force by density */
+#define PFIELD_USE_SIGNED_DISTANCE (1<<18) /* surface shape: use negative distance on the interior */
/* pd->falloff */
#define PFIELD_FALL_SPHERE 0
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index 2daba6a3f0d..1d655a1cdce 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -65,6 +65,14 @@ typedef struct bDeformGroup {
/* need this flag for locking weights */
char flag, pad[7];
} bDeformGroup;
+
+/* Face Maps*/
+typedef struct bFaceMap {
+ struct bFaceMap *next, *prev;
+ char name[64]; /* MAX_VGROUP_NAME */
+} bFaceMap;
+
+
#define MAX_VGROUP_NAME 64
/* bDeformGroup->flag */
@@ -142,7 +150,8 @@ typedef struct Object {
ListBase effect DNA_DEPRECATED; // XXX deprecated... keep for readfile
ListBase defbase; /* list of bDeformGroup (vertex groups) names and flag only */
ListBase modifiers; /* list of ModifierData structures */
-
+ ListBase fmaps; /* list of facemaps */
+
int mode; /* Local object mode */
int restore_mode; /* Keep track of what mode to return to after toggling a mode */
@@ -180,7 +189,8 @@ typedef struct Object {
short flag; /* copy of Base */
short colbits DNA_DEPRECATED; /* deprecated, use 'matbits' */
- short transflag, protectflag; /* transformation settings and transform locks */
+ int transflag; /* transformation settings */
+ short protectflag, pad; /* transform locks */
short trackflag, upflag;
short nlaflag; /* used for DopeSheet filtering settings (expanded/collapsed) */
short ipoflag; // xxx deprecated... old animation system
@@ -191,9 +201,6 @@ typedef struct Object {
/* dupli-frame settings */
int dupon, dupoff, dupsta, dupend;
- /* did last modifier stack generation need mapping support? */
- int lastNeedMapping;
-
/* during realtime */
/* note that inertia is only called inertia for historical reasons
@@ -246,6 +253,12 @@ typedef struct Object {
short index; /* custom index, for renderpasses */
unsigned short actdef; /* current deformation group, note: index starts at 1 */
+ unsigned short actfmap; /* current face map, note: index starts at 1 */
+ unsigned short pad2[2];
+
+ /* did last modifier stack generation need mapping support? */
+ short lastNeedMapping;
+
float col[4]; /* object color */
int gameflag;
@@ -266,6 +279,8 @@ typedef struct Object {
struct PartDeflect *pd; /* particle deflector/attractor/collision data */
struct SoftBody *soft; /* if exists, saved in file */
struct Group *dup_group; /* object duplicator for group */
+ struct DupliCache *dup_cache; /* cached dupli overrides */
+ struct CacheLibrary *cache_library; /* cache library to use */
char body_type; /* for now used to temporarily holds the type of collision object */
char shapeflag; /* flag for pinning */
@@ -331,8 +346,43 @@ typedef struct DupliObject {
/* particle this dupli was generated from */
struct ParticleSystem *particle_system;
+
+ struct DupliObjectData *data;
} DupliObject;
+typedef struct DupliObjectDataStrands {
+ struct DupliObjectDataStrands *next, *prev;
+
+ char name[64]; /* MAX_NAME */
+ struct Strands *strands;
+ struct StrandsChildren *strands_children;
+} DupliObjectDataStrands;
+
+/* data that can be shared by multiple DupliObject instances */
+typedef struct DupliObjectData {
+ /* XXX eventually it should be possible to construct dupli instances
+ * entirely without Objects in the DNA, but current drawing code and
+ * others make this too difficult
+ */
+ struct Object *ob;
+ struct BoundBox bb;
+ struct DerivedMesh *dm;
+ ListBase strands;
+} DupliObjectData;
+
+typedef struct DupliCache {
+ short flag;
+ short result;
+ float cfra; /* frame for which the cache was constructed */
+
+ struct GHash *ghash;
+ ListBase duplilist;
+} DupliCache;
+
+typedef enum eDupliCache_Flag {
+ DUPCACHE_FLAG_DIRTY = (1 << 0), /* cache requires update */
+} eDupliCache_Flag;
+
/* **************** OBJECT ********************* */
/* used many places... should be specialized */
@@ -409,6 +459,8 @@ enum {
OB_NO_CONSTRAINTS = 1 << 13, /* runtime constraints disable */
OB_NO_PSYS_UPDATE = 1 << 14, /* hack to work around particle issue */
+ OB_IS_DUPLI_CACHE = 1 << 31, /* temporary flag: object data overridden from cache */
+
OB_DUPLI = OB_DUPLIFRAMES | OB_DUPLIVERTS | OB_DUPLIGROUP | OB_DUPLIFACES | OB_DUPLIPARTS,
};
@@ -457,6 +509,7 @@ enum {
/* enable transparent draw */
OB_DRAWTRANSP = 1 << 7,
OB_DRAW_ALL_EDGES = 1 << 8, /* only for meshes currently */
+ OB_DRAW_WIRECOLOR = 1 << 9,
};
/* empty_drawtype: no flags */
@@ -676,10 +729,12 @@ typedef enum ObjectMode {
OB_MODE_TEXTURE_PAINT = 1 << 4,
OB_MODE_PARTICLE_EDIT = 1 << 5,
OB_MODE_POSE = 1 << 6,
+ OB_MODE_HAIR_EDIT = 1 << 7,
} ObjectMode;
/* any mode where the brush system is used */
#define OB_MODE_ALL_PAINT (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)
+#define OB_MODE_ALL_BRUSH (OB_MODE_ALL_PAINT | OB_MODE_HAIR_EDIT)
#define MAX_DUPLI_RECUR 8
diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h
index 95cb5c84bed..07e944c360a 100644
--- a/source/blender/makesdna/DNA_particle_types.h
+++ b/source/blender/makesdna/DNA_particle_types.h
@@ -75,6 +75,7 @@ typedef struct ChildParticle {
float w[4]; /* interpolation weights for the above particles */
float fuv[4], foffset; /* face vertex weights and offset */
float rt;
+ int hull_parent; /* parent index of hull children, -1 if child is not part of the convex hull */
} ChildParticle;
typedef struct ParticleTarget {
@@ -123,6 +124,9 @@ typedef struct ParticleData {
int hair_index;
short flag;
short alive; /* the life state of a particle */
+
+ int blend_index[4]; /* particle indices for non-simulated hair */
+ float blend_weight[4]; /* blending weights for non-simulated hair */
} ParticleData;
typedef struct SPHFluidSettings {
@@ -240,6 +244,7 @@ typedef struct ParticleSettings {
struct CurveMapping *clumpcurve;
struct CurveMapping *roughcurve;
float clump_noise_size;
+ float clump_noise_random, clump_noise_random_size;
/* hair dynamics */
float bending_random;
@@ -268,12 +273,16 @@ typedef struct ParticleSystem {
struct ParticleSystem *next, *prev;
ParticleSettings *part; /* particle settings */
+ struct Key *key; /* hair goal shape keys */
+ short shapenr; /* current shape key for menu or pinned */
+ short pad[3];
ParticleData *particles; /* (parent) particles */
ChildParticle *child; /* child particles */
struct PTCacheEdit *edit; /* particle editmode (runtime) */
void (*free_edit)(struct PTCacheEdit *edit); /* free callback */
+ struct BMEditStrands *hairedit; /* hair edit data (runtime) */
struct ParticleCacheKey **pathcache; /* path cache (runtime) */
struct ParticleCacheKey **childcache; /* child cache (runtime) */
@@ -297,6 +306,8 @@ typedef struct ParticleSystem {
int seed, child_seed;
int flag, totpart, totunexist, totchild, totcached, totchildcache;
short recalc, target_psys, totkeyed, bakespace;
+ float hair_preview_factor; /* ratio of simulated to overall hairs */
+ int hair_num_simulated; /* current number of hairs tagged for simulation (for update check) */
char bb_uvname[3][64]; /* billboard uv name, MAX_CUSTOMDATA_LAYER_NAME */
@@ -321,7 +332,7 @@ typedef struct ParticleSystem {
struct ParticleDrawData *pdd;
float dt_frac; /* current time step, as a fraction of a frame */
- float _pad; /* spare capacity */
+ float pad2;
} ParticleSystem;
typedef enum eParticleDrawFlag {
@@ -436,11 +447,14 @@ typedef enum eParticleChildFlag {
} eParticleChildFlag;
/* part->draw_col */
-#define PART_DRAW_COL_NONE 0
-#define PART_DRAW_COL_MAT 1
-#define PART_DRAW_COL_VEL 2
-#define PART_DRAW_COL_ACC 3
-
+typedef enum eParticleDrawColorMode {
+ PART_DRAW_COL_NONE = 0,
+ PART_DRAW_COL_MAT = 1,
+ PART_DRAW_COL_VEL = 2,
+ PART_DRAW_COL_ACC = 3,
+ PART_DRAW_COL_PARENT = 4,
+ PART_DRAW_COL_TEX = 5,
+} eParticleDrawColorMode;
/* part->simplify_flag */
#define PART_SIMPLIFY_ENABLE 1
@@ -469,18 +483,21 @@ typedef enum eParticleChildFlag {
/* part->draw_as */
/* part->ren_as*/
-#define PART_DRAW_NOT 0
-#define PART_DRAW_DOT 1
-#define PART_DRAW_HALO 1
-#define PART_DRAW_CIRC 2
-#define PART_DRAW_CROSS 3
-#define PART_DRAW_AXIS 4
-#define PART_DRAW_LINE 5
-#define PART_DRAW_PATH 6
-#define PART_DRAW_OB 7
-#define PART_DRAW_GR 8
-#define PART_DRAW_BB 9
-#define PART_DRAW_REND 10
+typedef enum eParticleDrawMethod {
+ PART_DRAW_NOT = 0,
+ PART_DRAW_DOT = 1,
+ PART_DRAW_HALO = 1,
+ PART_DRAW_CIRC = 2,
+ PART_DRAW_CROSS = 3,
+ PART_DRAW_AXIS = 4,
+ PART_DRAW_LINE = 5,
+ PART_DRAW_PATH = 6,
+ PART_DRAW_OB = 7,
+ PART_DRAW_GR = 8,
+ PART_DRAW_BB = 9,
+ PART_DRAW_REND = 10,
+ PART_DRAW_HULL = 11,
+} eParticleDrawMethod;
/* part->integrator */
#define PART_INT_EULER 0
@@ -543,11 +560,13 @@ typedef enum eParticleChildFlag {
#define PSYS_DISABLED 8192
#define PSYS_OB_ANIM_RESTORE 16384 /* runtime flag */
-/* pars->flag */
-#define PARS_UNEXIST 1
-#define PARS_NO_DISP 2
-//#define PARS_STICKY 4 /* deprecated */
-#define PARS_REKEY 8
+typedef enum eParticleDataFlag {
+ PARS_UNEXIST = 1,
+ PARS_NO_DISP = 2,
+// PARS_STICKY = 4, /* deprecated */
+ PARS_REKEY = 8,
+ PARS_HAIR_BLEND = 16, /* exclude from simulation and instead blend result from surrounding hairs */
+} eParticleDataFlag;
/* pars->alive */
//#define PARS_KILLED 0 /* deprecated */
@@ -606,6 +625,9 @@ typedef enum eParticleTextureInfluence {
PAMAP_ROUGH = (1<<9),
PAMAP_LENGTH = (1<<4),
PAMAP_CHILD = (PAMAP_CLUMP | PAMAP_KINK_FREQ | PAMAP_KINK_AMP | PAMAP_ROUGH | PAMAP_LENGTH),
+ PAMAP_SHAPEKEY = (1<<13), /* shapekey blend multiplier */
+ /* color */
+ PAMAP_COLOR = (1<<14),
} eParticleTextureInfluence;
#endif
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 47fd1a4e07c..24cb3d92490 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1002,6 +1002,32 @@ typedef struct ParticleEditSettings {
} ParticleEditSettings;
/* ------------------------------------------- */
+/* Hair Edit */
+
+/* HairEditSettings->select_mode */
+typedef enum HairEditSelectMode {
+ HAIR_SELECT_STRAND = 0,
+ HAIR_SELECT_VERTEX = 1,
+ HAIR_SELECT_TIP = 2,
+} HairEditSelectMode;
+
+/* HairEditSettings->flag */
+typedef enum HairEditFlag {
+ HAIR_EDIT_SHOW_DEBUG = (1 << 16),
+} HairEditFlag;
+
+typedef struct HairEditSettings {
+ int flag;
+ int select_mode;
+
+ struct Brush *brush;
+ struct Object *shape_object;
+
+ /* WM Paint cursor */
+ void *paint_cursor;
+} HairEditSettings;
+
+/* ------------------------------------------- */
/* Sculpt */
/* Sculpt */
@@ -1227,7 +1253,10 @@ typedef struct ToolSettings {
/* Particle Editing */
struct ParticleEditSettings particle;
-
+
+ /* Hair Editing */
+ struct HairEditSettings hair_edit;
+
/* Transform Proportional Area of Effect */
float proportional_size;
@@ -1955,6 +1984,7 @@ typedef enum eGPencil_Source_3D {
/* ParticleBrushData->flag */
#define PE_BRUSH_DATA_PUFF_VOLUME 1
+#define PE_BRUSH_DATA_ADD_SINGLE 2
/* tooksettings->particle edittype */
#define PE_TYPE_PARTICLES 0
diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h
index eb76c90c0fc..5a707ef7c3b 100644
--- a/source/blender/makesdna/DNA_screen_types.h
+++ b/source/blender/makesdna/DNA_screen_types.h
@@ -44,6 +44,7 @@ struct PanelType;
struct Scene;
struct uiLayout;
struct wmTimer;
+struct wmWidgetMap;
typedef struct bScreen {
ID id;
@@ -68,7 +69,8 @@ typedef struct bScreen {
char do_draw_drag; /* notifier for dragging draw. */
char swap; /* indicator to survive swap-exchange systems */
char skip_handling; /* set to delay screen handling after switching back from maximized area */
- char pad[7];
+ char scrubbing; /* set while scrubbing to skip some updates done while animating */
+ char pad[6];
short mainwin; /* screensize subwindow, for screenedges and global menus */
short subwinactive; /* active subwindow */
@@ -256,6 +258,7 @@ typedef struct ARegion {
ListBase ui_lists; /* uiList */
ListBase ui_previews; /* uiPreview */
ListBase handlers; /* wmEventHandler */
+ ListBase widgetmaps; /* widgetmaps */
ListBase panels_category; /* Panel categories runtime */
struct wmTimer *regiontimer; /* blend in/out */
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index 76c27fd3547..d45467373d6 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -212,12 +212,18 @@ typedef struct MetaStack {
} MetaStack;
typedef struct Editing {
+
+ /* old data format */
ListBase *seqbasep; /* pointer to the current list of seq's being edited (can be within a meta strip) */
ListBase seqbase; /* pointer to the top-most seq's */
ListBase metastack;
/* Context vars, used to be static */
Sequence *act_seq;
+
+ /* new data */
+ ListBase nstripbase;
+
char act_imagedir[1024]; /* 1024 = FILE_MAX */
char act_sounddir[1024]; /* 1024 = FILE_MAX */
char proxy_dir[1024]; /* 1024 = FILE_MAX */
@@ -385,6 +391,8 @@ enum {
/* don't include Grease Pencil in OpenGL previews of Scene strips */
SEQ_SCENE_NO_GPENCIL = (1 << 28),
SEQ_USE_VIEWS = (1 << 29),
+ /* access scene strips directly (like a metastrip) */
+ SEQ_SCENE_STRIPS = (1 << 30),
SEQ_INVALID_EFFECT = (1 << 31),
};
@@ -502,4 +510,212 @@ enum {
SEQUENCE_MASK_INPUT_ID = 1
};
+/************************* NEW DATA TYPES **********************************/
+
+#define STRIP_NAME_MAXSTR 64
+
+/* nstrip->type */
+enum {
+ NSTRIP_TYPE_DATA = 1,
+ NSTRIP_TYPE_FX,
+ NSTRIP_TYPE_CONTAINER,
+};
+
+/* nstrip->classtype */
+enum {
+ NSTRIP_CLASS_MOVIE = 1,
+ NSTRIP_CLASS_MOVIECLIP,
+ NSTRIP_CLASS_MASK,
+ NSTRIP_CLASS_IMAGE,
+ NSTRIP_CLASS_SOUND,
+ NSTRIP_CLASS_SCENE,
+ NSTRIP_CLASS_EFFECT,
+ NSTRIP_CLASS_CROSS,
+ NSTRIP_CLASS_ADD,
+ NSTRIP_CLASS_SUB,
+ NSTRIP_CLASS_ALPHAOVER,
+ NSTRIP_CLASS_ALPHAUNDER,
+ NSTRIP_CLASS_GAMCROSS,
+ NSTRIP_CLASS_MUL,
+ NSTRIP_CLASS_OVERDROP,
+ NSTRIP_CLASS_WIPE,
+ NSTRIP_CLASS_GLOW,
+ NSTRIP_CLASS_TRANSFORM,
+ NSTRIP_CLASS_COLOR,
+ NSTRIP_CLASS_SPEED,
+ NSTRIP_CLASS_MULTICAM,
+ NSTRIP_CLASS_ADJUSTMENT,
+ NSTRIP_CLASS_GAUSSIAN_BLUR,
+};
+
+/* nclip->flag */
+enum {
+ NSTRIP_LEFTSEL = (1 << 1),
+ NSTRIP_RIGHTSEL = (1 << 2),
+ NSTRIP_OVERLAP = (1 << 3),
+ NSTRIP_FILTERY = (1 << 4),
+ NSTRIP_MUTE = (1 << 5),
+ NSTRIP_REVERSE_FRAMES = (1 << 6),
+ NSTRIP_IPO_FRAME_LOCKED = (1 << 7),
+ NSTRIP_EFFECT_NOT_LOADED = (1 << 8),
+ NSTRIP_FLAG_DELETE = (1 << 9),
+ NSTRIP_FLIPX = (1 << 10),
+ NSTRIP_FLIPY = (1 << 11),
+ NSTRIP_MAKE_FLOAT = (1 << 12),
+ NSTRIP_LOCK = (1 << 13),
+ NSTRIP_USE_PROXY = (1 << 14),
+ NSTRIP_USE_TRANSFORM = (1 << 15),
+ NSTRIP_USE_CROP = (1 << 16),
+ NSTRIP_USE_PROXY_CUSTOM_DIR = (1 << 17),
+
+ NSTRIP_USE_PROXY_CUSTOM_FILE = (1 << 18),
+ NSTRIP_USE_EFFECT_DEFAULT_FADE = (1 << 19),
+ NSTRIP_USE_LINEAR_MODIFIERS = (1 << 20),
+
+ /* flags for whether those properties are animated or not */
+ NSTRIP_AUDIO_VOLUME_ANIMATED = (1 << 21),
+ NSTRIP_AUDIO_PITCH_ANIMATED = (1 << 22),
+ NSTRIP_AUDIO_PAN_ANIMATED = (1 << 23),
+ NSTRIP_AUDIO_DRAW_WAVEFORM = (1 << 24),
+
+ NCLIP_INVALID_EFFECT = (1 << 25),
+};
+/* This is the base class and it's strictly related to the visual clip in the sequencer */
+typedef struct NStrip {
+ struct NStrip *next, *prev;
+ char name[64]; /* STRIP_NAME_MAXSTR - name, set by default and needs to be unique, for RNA paths */
+
+ /* general use flags */
+ int flag;
+
+ /* the track this clip exists in (used to be "machine" in the old code) */
+ int track;
+
+ /* depth in the sequence (how many levels of clip inclusion deep the strip is) */
+ int depth;
+
+ /* main type, data or fx */
+ short type;
+
+ /* classtype identifier, to quickly determine the type of strip */
+ short classtype;
+
+ /* frame position in the timeline */
+ int start;
+ int end;
+
+ /* attachments point of clip. if the clip moves, the attachments do too */
+ ListBase attachments;
+} NStrip;
+
+/* a data clip, it includes movies, sounds or image sequences */
+typedef struct NDataStrip {
+ NStrip clip;
+
+ /* length of source data (depends on source data type) */
+ int len;
+
+ /* offset of data from start of the clip */
+ int offset;
+} NDataStrip;
+
+/* a data clip, it includes movies, sounds or image sequences */
+typedef struct NFXStrip {
+ NStrip clip;
+
+ /* fader of the effect */
+ float effect_fader;
+ float pad;
+
+ /* specialize those per fx */
+ struct NStrip *clip1, *clip2, *clip3;
+} NFXStrip;
+
+/* old metastrip, a clip that contains other clips */
+typedef struct NContainerStrip {
+ NStrip clip;
+
+ /* list of contained clips */
+ ListBase clips;
+} NContainerStrip;
+
+typedef struct NMovieStrip {
+ NDataStrip data;
+
+ /* render flags */
+ int render_flag;
+
+ int blend_mode;
+
+ /* saturation */
+ float saturation;
+
+ float blend_opacity;
+
+ /* streamindex for sound files with several streams */
+ short streamindex;
+
+ /* animation movie preseek */
+ short anim_preseek;
+
+ int pad;
+
+ /* modifiers */
+ ListBase modifiers;
+} NMovieStrip;
+
+typedef struct NAnimStrip {
+ NMovieStrip movie;
+
+ /* for amination file */
+ struct anim *anim;
+} NAnimStrip;
+
+typedef struct NTrackerStrip {
+ NMovieStrip movie;
+
+ /* source MovieClip strip */
+ struct MovieClip *clip;
+} NTrackerStrip;
+
+typedef struct NMaskStrip {
+ NDataStrip data;
+
+ /* source mask */
+ struct Mask *mask;
+} NMaskStrip;
+
+/* sound clip */
+typedef struct NSoundStrip {
+ NDataStrip data;
+
+ /* streamindex for sound files with several streams */
+ int streamindex;
+
+ /* sound volume */
+ float volume;
+
+ /* pitch (-0.1..10) */
+ float pitch;
+
+ /* pan -2..2 */
+ float pan;
+
+ /* the linked "bSound" object */
+ struct bSound *sound;
+} NSoundStrip;
+
+/* scene strip */
+typedef struct NSceneStrip {
+ NDataStrip data;
+
+ /* the linked scene */
+ struct Scene *scene;
+
+ /* override of scene camera */
+ struct Object *scene_camera;
+} NSceneStrip;
+
+
+
#endif /* __DNA_SEQUENCE_TYPES_H__ */
diff --git a/source/blender/makesdna/DNA_smoke_types.h b/source/blender/makesdna/DNA_smoke_types.h
index 25c98b4f07e..6ca52f3288f 100644
--- a/source/blender/makesdna/DNA_smoke_types.h
+++ b/source/blender/makesdna/DNA_smoke_types.h
@@ -105,6 +105,8 @@ typedef struct SmokeDomainSettings {
float obj_shift_f[3]; /* how much object has shifted since previous smoke frame (used to "lock" domain while drawing) */
float imat[4][4]; /* domain object imat */
float obmat[4][4]; /* domain obmat */
+ float fluidmat[4][4]; /* low res fluid matrix */
+ float fluidmat_wt[4][4]; /* high res fluid matrix */
int base_res[3]; /* initial "non-adapted" resolution */
int res_min[3]; /* cell min */
@@ -137,6 +139,8 @@ typedef struct SmokeDomainSettings {
/* Smoke uses only one cache from now on (index [0]), but keeping the array for now for reading old files. */
struct PointCache *point_cache[2]; /* definition is in DNA_object_force.h */
struct ListBase ptcaches[2];
+ int point_cache_offset;
+ int pad;
struct EffectorWeights *effector_weights;
int border_collisions; /* How domain border collisions are handled */
float time_scale;
@@ -149,8 +153,36 @@ typedef struct SmokeDomainSettings {
float burning_rate, flame_smoke, flame_vorticity;
float flame_ignition, flame_max_temp;
float flame_smoke_color[3];
+
+ /* display */
+ float display_thickness;
+ int pad2;
+
+ struct ListBase vdb_caches;
+ short use_openvdb, pad3[3];
+ struct OpenVDBPrimitive *density, *density_high;
} SmokeDomainSettings;
+typedef struct OpenVDBCache {
+ struct OpenVDBCache *next, *prev;
+ struct OpenVDBReader *reader;
+ struct OpenVDBWriter *writer;
+ char path[1024];
+ char name[64];
+ int startframe, endframe;
+ short flags, compression, pad[2];
+} OpenVDBCache;
+
+enum {
+ VDB_CACHE_CURRENT = (1 << 0),
+ VDB_CACHE_SMOKE_EXPORTED = (1 << 1),
+};
+
+enum {
+ VDB_COMPRESSION_ZIP = 0,
+ VDB_COMPRESSION_BLOSC = 1,
+ VDB_COMPRESSION_NONE = 2,
+};
/* inflow / outflow */
@@ -173,6 +205,7 @@ typedef struct SmokeDomainSettings {
#define MOD_SMOKE_FLOW_INITVELOCITY (1<<2) /* passes particles speed to the smoke */
#define MOD_SMOKE_FLOW_TEXTUREEMIT (1<<3) /* use texture to control emission speed */
#define MOD_SMOKE_FLOW_USE_PART_SIZE (1<<4) /* use specific size for particles instead of closest cell */
+#define MOD_SMOKE_FLOW_USE_PART_TEXCOLOR (1<<5) /* passes particles texture color to the smoke */
typedef struct SmokeFlowSettings {
struct SmokeModifierData *smd; /* for fast RNA access */
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 9d5b6bb42b4..266fe5afe17 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -46,6 +46,7 @@
#include "DNA_node_types.h" /* for bNodeInstanceKey */
/* Hum ... Not really nice... but needed for spacebuts. */
#include "DNA_view2d_types.h"
+#include "DNA_widget_types.h"
struct ID;
struct Text;
@@ -323,6 +324,11 @@ typedef struct SpaceIpo {
ListBase ghostCurves; /* sampled snapshots of F-Curves used as in-session guides */
+ struct Object *backdrop_camera; /* the view from this camera is used to draw the backdrop */
+ float backdrop_offset[2]; /* offset of the backdrop */
+ float backdrop_zoom; /* zoom factor of the backdrop */
+ float backdrop_opacity; /* opacity of the backdrop */
+
short mode; /* mode for the Graph editor (eGraphEdit_Mode) */
short autosnap; /* time-transform autosnapping settings for Graph editor (eAnimEdit_AutoSnap in DNA_action_types.h) */
int flag; /* settings for Graph editor (eGraphEdit_Flag) */
@@ -367,6 +373,7 @@ typedef enum eGraphEdit_Flag {
/* normalize curves on display */
SIPO_NORMALIZE = (1 << 14),
SIPO_NORMALIZE_FREEZE = (1 << 15),
+ SIPO_DRAW_BACKDROP = (1 << 16),
} eGraphEdit_Flag;
/* SpaceIpo->mode (Graph Editor Mode) */
@@ -498,7 +505,8 @@ typedef struct SpaceSeq {
int view; /* see SEQ_VIEW_* below */
int overlay_type;
int draw_flag; /* overlay an image of the editing on below the strips */
- int pad;
+ float overdrop_zoom;
+ float overdrop_offset[2];
struct bGPdata *gpd; /* grease-pencil data */
@@ -520,7 +528,7 @@ typedef enum eSpaceSeq_RegionType {
/* sseq->draw_flag */
typedef enum eSpaceSeq_DrawFlag {
- SEQ_DRAW_BACKDROP = (1 << 0),
+ SEQ_DRAW_OVERDROP = (1 << 0),
SEQ_DRAW_OFFSET_EXT = (1 << 1),
} eSpaceSeq_DrawFlag;
@@ -538,6 +546,7 @@ typedef enum eSpaceSeq_Flag {
SEQ_NO_WAVEFORMS = (1 << 8), /* draw no waveforms */
SEQ_SHOW_SAFE_CENTER = (1 << 9),
SEQ_SHOW_METADATA = (1 << 10),
+ SEQ_NO_INFO = (1 << 11), /* do not draw names on strips */
} eSpaceSeq_Flag;
/* sseq->view */
@@ -695,17 +704,19 @@ typedef enum eFileSel_Action {
/* sfile->params->flag and simasel->flag */
typedef enum eFileSel_Params_Flag {
- FILE_SHOWSHORT = (1 << 0),
- FILE_RELPATH = (1 << 1), /* was FILE_STRINGCODE */
- FILE_LINK = (1 << 2),
- FILE_HIDE_DOT = (1 << 3),
- FILE_AUTOSELECT = (1 << 4),
- FILE_ACTIVELAY = (1 << 5),
-/* FILE_ATCURSOR = (1 << 6), */ /* deprecated */
- FILE_DIRSEL_ONLY = (1 << 7),
- FILE_FILTER = (1 << 8),
- FILE_BOOKMARKS = (1 << 9),
- FILE_GROUP_INSTANCE = (1 << 10),
+ FILE_SHOWSHORT = (1 << 0),
+ FILE_RELPATH = (1 << 1), /* was FILE_STRINGCODE */
+ FILE_LINK = (1 << 2),
+ FILE_HIDE_DOT = (1 << 3),
+ FILE_AUTOSELECT = (1 << 4),
+ FILE_ACTIVELAY = (1 << 5),
+/* FILE_ATCURSOR = (1 << 6), */ /* deprecated */
+ FILE_DIRSEL_ONLY = (1 << 7),
+ FILE_FILTER = (1 << 8),
+ FILE_BOOKMARKS = (1 << 9),
+ FILE_GROUP_INSTANCE = (1 << 10),
+ FILE_COLLAPSE_IMAGES = (1 << 11),
+ FILE_COLLAPSE_IMAGES_TMP = (1 << 12),
} eFileSel_Params_Flag;
@@ -733,6 +744,8 @@ typedef enum eDirEntry_SelectFlag {
FILE_SEL_HIGHLIGHTED = (1 << 2),
FILE_SEL_SELECTED = (1 << 3),
FILE_SEL_EDITING = (1 << 4),
+ FILE_SEL_COLLAPSED = (1 << 5),
+ FILE_SEL_PLAYING = (1 << 6),
} eDirEntry_SelectFlag;
/* Image/UV Editor ======================================== */
@@ -965,11 +978,13 @@ typedef struct SpaceNode {
struct ID *id, *from; /* context, no need to save in file? well... pinning... */
short flag, pad1; /* menunr: browse id block in header */
- float aspect, pad2; /* internal state variables */
+ float aspect; /* internal state variables */
- float xof, yof; /* offset for drawing the backdrop */
- float zoom; /* zoom for backdrop */
float cursor[2]; /* mouse pos for drawing socketless link and adding nodes */
+
+ float backdrop_offset[2];
+ float backdrop_zoom;
+ float pad;
/* XXX nodetree pointer info is all in the path stack now,
* remove later on and use bNodeTreePath instead. For now these variables are set when pushing/popping
diff --git a/source/blender/makesdna/DNA_strands_types.h b/source/blender/makesdna/DNA_strands_types.h
new file mode 100644
index 00000000000..c5d58e3439a
--- /dev/null
+++ b/source/blender/makesdna/DNA_strands_types.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file DNA_strands_types.h
+ * \ingroup DNA
+ */
+
+#ifndef __DNA_STRANDS_TYPES_H__
+#define __DNA_STRANDS_TYPES_H__
+
+#include "DNA_defs.h"
+
+#include "DNA_meshdata_types.h"
+
+typedef struct StrandsVertex {
+ float co[3];
+ float base[3]; /* base shape, defining deformation for children */
+ float time;
+ float weight;
+
+ /* utility data */
+ float nor[3]; /* normals (edge directions) */
+} StrandsVertex;
+
+typedef struct StrandsMotionState {
+ float co[3];
+ float vel[3];
+
+ /* utility data */
+ float nor[3]; /* normals (edge directions) */
+} StrandsMotionState;
+
+typedef struct StrandsCurve {
+ int numverts;
+ float root_matrix[3][3];
+
+ MSurfaceSample msurf;
+} StrandsCurve;
+
+typedef struct Strands {
+ StrandsCurve *curves;
+ StrandsVertex *verts;
+ int totcurves, totverts;
+
+ /* optional */
+ StrandsMotionState *state;
+} Strands;
+
+
+typedef struct StrandsChildCurve {
+ int numverts;
+ float root_matrix[4][4];
+ int parents[4];
+ float parent_weights[4];
+ float cutoff; /* shortens the curve if 0 <= cutoff < numverts */
+} StrandsChildCurve;
+
+typedef struct StrandsChildCurveUV {
+ float uv[2];
+} StrandsChildCurveUV;
+
+typedef struct StrandsChildCurveVCol {
+ float vcol[3];
+} StrandsChildCurveVCol;
+
+typedef struct StrandsChildVertex {
+ float co[3];
+ float base[3]; /* undeformed shape */
+ float time;
+
+ /* utility data */
+ float nor[3]; /* normals (edge directions) */
+} StrandsChildVertex;
+
+typedef struct StrandsChildren {
+ StrandsChildCurve *curves;
+ StrandsChildCurveUV *curve_uvs;
+ StrandsChildCurveVCol *curve_vcols;
+ StrandsChildVertex *verts;
+ int totcurves, totverts;
+ int numuv, numvcol; /* number of uv/vcol per curve */
+} StrandsChildren;
+
+#endif /* __DNA_STRANDS_TYPES_H__ */
diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h
index af6adbecd83..a1b736124a2 100644
--- a/source/blender/makesdna/DNA_texture_types.h
+++ b/source/blender/makesdna/DNA_texture_types.h
@@ -85,8 +85,9 @@ typedef struct MTex {
/* particles */
float timefac, lengthfac, clumpfac, dampfac;
float kinkfac, kinkampfac, roughfac, padensfac, gravityfac;
- float lifefac, sizefac, ivelfac, fieldfac;
- int pad2;
+ float lifefac, sizefac, ivelfac, fieldfac, pacolfac, pad2;
+ float shapefac;
+ char shapekey[64]; /* MAX_NAME */
/* lamp */
float shadowfac;
@@ -596,9 +597,11 @@ enum {
#define TEX_PD_COLOR_PARTAGE 1
#define TEX_PD_COLOR_PARTSPEED 2
#define TEX_PD_COLOR_PARTVEL 3
+#define TEX_PD_COLOR_PARTTEX 4
#define POINT_DATA_VEL 1
#define POINT_DATA_LIFE 2
+#define POINT_DATA_COLOR 3
/******************** Voxel Data *****************************/
/* flag */
diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h
index a0516590d43..6841393ab98 100644
--- a/source/blender/makesdna/DNA_view3d_types.h
+++ b/source/blender/makesdna/DNA_view3d_types.h
@@ -212,6 +212,9 @@ typedef struct View3D {
struct ListBase afterdraw_xray;
struct ListBase afterdraw_xraytransp;
+ /* draw after scene, does not need depth info, so different than xray */
+ struct ListBase afterdraw_nodepth;
+
/* drawflags, denoting state */
char zbuf, transp, xray;
@@ -291,6 +294,7 @@ typedef struct View3D {
((view >= RV3D_VIEW_FRONT) && (view <= RV3D_VIEW_BOTTOM))
/* View3d->flag2 (short) */
+#define V3D_WIRE_COLOR_NOCUSTOM (1 << 1)
#define V3D_RENDER_OVERRIDE (1 << 2)
#define V3D_SOLID_TEX (1 << 3)
#define V3D_SHOW_GPENCIL (1 << 4)
@@ -309,6 +313,7 @@ typedef struct View3D {
/* View3d->flag3 (short) */
#define V3D_SHOW_WORLD (1 << 0)
+#define V3D_HIDE_MOTIONPATHS (1 << 1)
/* View3D->around */
#define V3D_CENTER 0
@@ -348,9 +353,10 @@ typedef struct View3D {
/* View3d->twflag */
/* USE = user setting, DRAW = based on selection */
-#define V3D_USE_MANIPULATOR 1
-#define V3D_DRAW_MANIPULATOR 2
-/* #define V3D_CALC_MANIPULATOR 4 */ /*UNUSED*/
+#define V3D_USE_MANIPULATOR (1 << 0)
+#define V3D_DRAW_MANIPULATOR (1 << 1)
+#define V3D_3D_WIDGETS (1 << 2)
+#define V3D_SHADED_WIDGETS (1 << 3)
/* BGPic->flag */
/* may want to use 1 for select ? */
@@ -371,6 +377,13 @@ enum {
#define V3D_BGPIC_EXPANDED (V3D_BGPIC_EXPANDED | V3D_BGPIC_CAMERACLIP)
+#define V3D_IS_WIRECOLOR(scene, v3d) \
+ (((v3d)->drawtype <= OB_SOLID) && \
+ (((v3d)->flag2 & V3D_WIRE_COLOR_NOCUSTOM) == 0))
+
+#define V3D_IS_WIRECOLOR_OBJECT(scene, v3d, ob) \
+ V3D_IS_WIRECOLOR(scene, v3d) && ((ob)->dtx & OB_DRAW_WIRECOLOR)
+
/* BGPic->source */
/* may want to use 1 for select ?*/
#define V3D_BGPIC_IMAGE 0
diff --git a/source/blender/makesdna/DNA_widget_types.h b/source/blender/makesdna/DNA_widget_types.h
new file mode 100644
index 00000000000..5f261f52835
--- /dev/null
+++ b/source/blender/makesdna/DNA_widget_types.h
@@ -0,0 +1,50 @@
+/*
+ * ***** 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file DNA_widget_types.h
+ * \ingroup DNA
+ */
+
+#ifndef __DNA_WM_WIDGET_TYPES_H__
+#define __DNA_WM_WIDGET_TYPES_H__
+
+#include "DNA_vec_types.h"
+
+struct wmWidgetGroup {
+ struct wmWidgetGroup *next, *prev;
+
+ struct wmWidgetGroupType *type;
+ ListBase widgets;
+
+ void *py_instance; /* python stores the class instance here */
+ struct ReportList *reports; /* errors and warnings storage */
+
+ int flag;
+ int pad;
+};
+
+#endif
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index 51ed8bb6c0d..9c089ce0021 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -155,7 +155,8 @@ typedef struct wmWindowManager {
struct wmTimer *autosavetimer; /* timer for auto save */
char is_interface_locked; /* indicates whether interface is locked for user interaction */
- char par[7];
+ char par[3];
+ int num_ogl_widgets; /* widgets requiring an OpenGL pass to detect */
} wmWindowManager;
/* wmWindowManager.initialized */
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index a7ff4244d5f..6333db331fb 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -125,6 +125,7 @@ static const char *includefiles[] = {
/* if you add files here, please add them at the end
* of makesdna.c (this file) as well */
"DNA_windowmanager_types.h",
+ "DNA_widget_types.h",
"DNA_anim_types.h",
"DNA_boid_types.h",
"DNA_smoke_types.h",
@@ -136,6 +137,8 @@ static const char *includefiles[] = {
"DNA_rigidbody_types.h",
"DNA_freestyle_types.h",
"DNA_linestyle_types.h",
+ "DNA_cache_library_types.h",
+ "DNA_strands_types.h",
/* empty string to indicate end of includefiles */
""
@@ -1281,6 +1284,7 @@ int main(int argc, char **argv)
#include "DNA_cloth_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_windowmanager_types.h"
+#include "DNA_widget_types.h"
#include "DNA_anim_types.h"
#include "DNA_boid_types.h"
#include "DNA_smoke_types.h"
@@ -1292,4 +1296,6 @@ int main(int argc, char **argv)
#include "DNA_rigidbody_types.h"
#include "DNA_freestyle_types.h"
#include "DNA_linestyle_types.h"
+#include "DNA_cache_library_types.h"
+#include "DNA_strands_types.h"
/* end of list */
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 965d1d91614..acbaae167db 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -95,6 +95,11 @@ extern StructRNA RNA_Brush;
extern StructRNA RNA_BrushTextureSlot;
extern StructRNA RNA_BuildModifier;
extern StructRNA RNA_MeshCacheModifier;
+extern StructRNA RNA_CacheArchiveInfo;
+extern StructRNA RNA_CacheArchiveInfoNode;
+extern StructRNA RNA_CacheItem;
+extern StructRNA RNA_CacheLibrary;
+extern StructRNA RNA_CacheLibraryModifier;
extern StructRNA RNA_Camera;
extern StructRNA RNA_CastModifier;
extern StructRNA RNA_ChildOfConstraint;
@@ -228,6 +233,7 @@ extern StructRNA RNA_EnvironmentMapTexture;
extern StructRNA RNA_Event;
extern StructRNA RNA_ExplodeModifier;
extern StructRNA RNA_ExpressionController;
+extern StructRNA RNA_FaceMap;
extern StructRNA RNA_FCurve;
extern StructRNA RNA_FCurveSample;
extern StructRNA RNA_FFmpegSettings;
@@ -425,6 +431,7 @@ extern StructRNA RNA_ObstacleFluidSettings;
extern StructRNA RNA_OceanModifier;
extern StructRNA RNA_OceanTexData;
extern StructRNA RNA_OceanTexture;
+extern StructRNA RNA_OpenVDBCache;
extern StructRNA RNA_Operator;
extern StructRNA RNA_OperatorFileListElement;
extern StructRNA RNA_OperatorMousePath;
diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index 061133a9fcf..261d9ec03c8 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -56,6 +56,7 @@ extern EnumPropertyItem mesh_delimit_mode_items[];
extern EnumPropertyItem space_type_items[];
extern EnumPropertyItem region_type_items[];
extern EnumPropertyItem modifier_type_items[];
+extern EnumPropertyItem cache_modifier_type_items[];
extern EnumPropertyItem constraint_type_items[];
extern EnumPropertyItem boidrule_type_items[];
extern EnumPropertyItem sequence_modifier_type_items[];
@@ -109,6 +110,7 @@ extern EnumPropertyItem operator_return_items[];
extern EnumPropertyItem brush_sculpt_tool_items[];
extern EnumPropertyItem brush_vertex_tool_items[];
extern EnumPropertyItem brush_image_tool_items[];
+extern EnumPropertyItem brush_hair_tool_items[];
extern EnumPropertyItem symmetrize_direction_items[];
@@ -182,6 +184,9 @@ extern EnumPropertyItem prop_dynamicpaint_type_items[];
extern EnumPropertyItem clip_editor_mode_items[];
+extern EnumPropertyItem cache_library_data_type_items[];
+extern EnumPropertyItem cache_library_read_result_items[];
+
extern EnumPropertyItem icon_items[];
extern EnumPropertyItem uilist_layout_type_items[];
@@ -221,5 +226,7 @@ EnumPropertyItem *RNA_movieclip_itemf(struct bContext *C, struct PointerRNA *ptr
EnumPropertyItem *RNA_movieclip_local_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free);
EnumPropertyItem *RNA_mask_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free);
EnumPropertyItem *RNA_mask_local_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free);
+EnumPropertyItem *RNA_cachelibrary_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free);
+EnumPropertyItem *RNA_cachelibrary_local_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free);
#endif /* __RNA_ENUM_TYPES_H__ */
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index e878e0877d2..a05c1834ed1 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_cache_library.c
rna_camera.c
rna_cloth.c
rna_color.c
@@ -60,6 +61,7 @@ set(DEFSRC
rna_mask.c
rna_material.c
rna_mesh.c
+ rna_mesh_sample.c
rna_meta.c
rna_modifier.c
rna_movieclip.c
@@ -84,6 +86,7 @@ set(DEFSRC
rna_sound.c
rna_space.c
rna_speaker.c
+ rna_strands.c
rna_test.c
rna_text.c
rna_texture.c
@@ -300,6 +303,10 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_OPENVDB_BLOSC)
+ add_definitions(-DWITH_OPENVDB_BLOSC)
+endif()
+
# Build makesrna executable
blender_include_dirs(
.
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index dfd0f13ec1a..0c2082c9597 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -3287,6 +3287,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_boid.c", NULL, RNA_def_boid},
{"rna_brush.c", NULL, RNA_def_brush},
{"rna_camera.c", "rna_camera_api.c", RNA_def_camera},
+ {"rna_cache_library.c", NULL, RNA_def_cache_library},
{"rna_cloth.c", NULL, RNA_def_cloth},
{"rna_color.c", NULL, RNA_def_color},
{"rna_constraint.c", NULL, RNA_def_constraint},
@@ -3307,6 +3308,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_main.c", "rna_main_api.c", RNA_def_main},
{"rna_material.c", "rna_material_api.c", RNA_def_material},
{"rna_mesh.c", "rna_mesh_api.c", RNA_def_mesh},
+ {"rna_mesh_sample.c", NULL, RNA_def_mesh_sample},
{"rna_meta.c", "rna_meta_api.c", RNA_def_meta},
{"rna_modifier.c", NULL, RNA_def_modifier},
{"rna_nla.c", NULL, RNA_def_nla},
@@ -3328,6 +3330,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_smoke.c", NULL, RNA_def_smoke},
{"rna_space.c", "rna_space_api.c", RNA_def_space},
{"rna_speaker.c", NULL, RNA_def_speaker},
+ {"rna_strands.c", NULL, RNA_def_strands},
{"rna_test.c", NULL, RNA_def_test},
{"rna_text.c", "rna_text_api.c", RNA_def_text},
{"rna_timeline.c", NULL, RNA_def_timeline_marker},
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index b87b455b36f..d29450aa320 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -52,6 +52,7 @@ EnumPropertyItem 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_CL, "CACHELIBRARY", ICON_PHYSICS, "Cache Library", ""},
{ID_CU, "CURVE", ICON_CURVE_DATA, "Curve", ""},
{ID_VF, "FONT", ICON_FONT_DATA, "Font", ""},
{ID_GD, "GREASEPENCIL", ICON_GREASEPENCIL, "Grease Pencil", ""},
@@ -132,6 +133,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_CacheLibrary)) return ID_CL;
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;
@@ -172,6 +174,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_CL: return &RNA_CacheLibrary;
case ID_CU: return &RNA_Curve;
case ID_GD: return &RNA_GreasePencil;
case ID_GR: return &RNA_Group;
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index b03b099048f..4078d783ad1 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -107,6 +107,17 @@ EnumPropertyItem brush_image_tool_items[] = {
{0, NULL, 0, NULL, NULL}
};
+EnumPropertyItem brush_hair_tool_items[] = {
+ {HAIR_TOOL_COMB, "COMB", ICON_BRUSH_HAIR_COMB, "Comb", "Align hairs to the stroke direction"},
+ {HAIR_TOOL_CUT, "CUT", ICON_BRUSH_HAIR_CUT, "Cut", "Shorten and/or remove hairs"},
+ {HAIR_TOOL_LENGTH, "LENGTH", ICON_BRUSH_HAIR_LENGTH, "Length", "Increase hair length"},
+ {HAIR_TOOL_PUFF, "PUFF", ICON_BRUSH_HAIR_PUFF, "Puff", "Increase spacing between hairs"},
+ {HAIR_TOOL_ADD, "ADD", ICON_BRUSH_HAIR_ADD, "Add", "Add more hairs on the object"},
+ {HAIR_TOOL_SMOOTH, "SMOOTH", ICON_BRUSH_HAIR_SMOOTH, "Smooth", "Align hairs in the same direction"},
+ {HAIR_TOOL_WEIGHT, "WEIGHT", ICON_BRUSH_HAIR_WEIGHT, "Weight", "Set hair vertex weights"},
+ {0, NULL, 0, NULL, NULL}
+};
+
#ifdef RNA_RUNTIME
#include "MEM_guardedalloc.h"
@@ -395,6 +406,13 @@ static void rna_Brush_imagepaint_tool_update(Main *bmain, Scene *scene, PointerR
rna_Brush_update(bmain, scene, ptr);
}
+static void rna_Brush_hair_tool_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ Brush *br = (Brush *)ptr->data;
+ rna_Brush_reset_icon(br, "hair");
+ rna_Brush_update(bmain, scene, ptr);
+}
+
static void rna_Brush_stroke_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, scene);
@@ -877,6 +895,12 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Image Paint Tool", "");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, "rna_Brush_imagepaint_tool_update");
+ prop = RNA_def_property(srna, "hair_tool", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "hair_tool");
+ RNA_def_property_enum_items(prop, brush_hair_tool_items);
+ RNA_def_property_ui_text(prop, "Hair Tool", "");
+ RNA_def_property_update(prop, 0, "rna_Brush_hair_tool_update");
+
prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag");
RNA_def_property_enum_items(prop, prop_direction_items);
@@ -1319,6 +1343,10 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "ob_mode", OB_MODE_TEXTURE_PAINT);
RNA_def_property_ui_text(prop, "Use Texture", "Use this brush in texture paint mode");
+ prop = RNA_def_property(srna, "use_hair_edit", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "ob_mode", OB_MODE_HAIR_EDIT);
+ RNA_def_property_ui_text(prop, "Use Hair", "Use this brush in hair edit mode");
+
/* texture */
prop = RNA_def_property(srna, "texture_slot", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "BrushTextureSlot");
diff --git a/source/blender/makesrna/intern/rna_cache_library.c b/source/blender/makesrna/intern/rna_cache_library.c
new file mode 100644
index 00000000000..4c8b8835491
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_cache_library.c
@@ -0,0 +1,1049 @@
+/*
+ * ***** 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): Blender Foundation (2015).
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/makesrna/intern/rna_cache_library.c
+ * \ingroup RNA
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "DNA_cache_library_types.h"
+
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "rna_internal.h"
+
+#include "WM_types.h"
+
+EnumPropertyItem cache_library_data_type_items[] = {
+ {CACHE_TYPE_OBJECT, "OBJECT", ICON_OBJECT_DATA, "Object", "Object base properties"},
+ {CACHE_TYPE_DERIVED_MESH, "DERIVED_MESH", ICON_OUTLINER_OB_MESH, "Derived Mesh", "Mesh result from modifiers"},
+ {CACHE_TYPE_HAIR, "HAIR", ICON_PARTICLE_POINT, "Hair", "Hair parent strands"},
+ {CACHE_TYPE_HAIR_PATHS, "HAIR_PATHS", ICON_PARTICLE_PATH, "Hair Paths", "Full hair paths"},
+ {CACHE_TYPE_PARTICLES, "PARTICLES", ICON_PARTICLES, "Particles", "Emitter particles"},
+ {0, NULL, 0, NULL, NULL}
+};
+
+EnumPropertyItem cache_library_read_result_items[] = {
+ {CACHE_READ_SAMPLE_INVALID, "INVALID", ICON_ERROR, "Invalid", "No valid sample found"},
+ {CACHE_READ_SAMPLE_EXACT, "EXACT", ICON_SPACE3, "Exact", "Found sample for requested frame"},
+ {CACHE_READ_SAMPLE_INTERPOLATED, "INTERPOLATED", ICON_TRIA_DOWN_BAR, "Interpolated", "Enclosing samples found for interpolation"},
+ {CACHE_READ_SAMPLE_EARLY, "EARLY", ICON_TRIA_RIGHT_BAR, "Early", "Requested frame before the first sample"},
+ {CACHE_READ_SAMPLE_LATE, "LATE", ICON_TRIA_LEFT_BAR, "Late", "Requested frame after the last sample"},
+ {0, NULL, 0, NULL, NULL}
+};
+
+EnumPropertyItem cache_modifier_type_items[] = {
+ {eCacheModifierType_HairSimulation, "HAIR_SIMULATION", ICON_HAIR, "Hair Simulation", ""},
+ {eCacheModifierType_ForceField, "FORCE_FIELD", ICON_FORCE_FORCE, "Force Field", ""},
+ {eCacheModifierType_ShrinkWrap, "SHRINK_WRAP", ICON_MOD_SHRINKWRAP, "Shrink Wrap", ""},
+ {eCacheModifierType_StrandsKey, "STRANDS_KEY", ICON_SHAPEKEY_DATA, "Strands Key", "Shape key for strands"},
+ {eCacheModifierType_Haircut, "HAIRCUT", ICON_HAIR, "Hair Cut", "Cut strands where they intersect with an object"},
+ {0, NULL, 0, NULL, NULL}
+};
+
+#ifdef RNA_RUNTIME
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+
+#include "DNA_key_types.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+
+#include "BKE_animsys.h"
+#include "BKE_cache_library.h"
+#include "BKE_depsgraph.h"
+#include "BKE_main.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+
+/* ========================================================================= */
+
+static void rna_CacheLibrary_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ CacheLibrary *cachelib = ptr->data;
+ DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA);
+ WM_main_add_notifier(NC_WINDOW, NULL);
+}
+
+static void rna_CacheArchiveInfo_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr))
+{
+// CacheLibrary *cachelib = ptr->id.data;
+// WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL);
+}
+
+/* ========================================================================= */
+
+static void rna_CacheModifier_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr))
+{
+}
+
+#if 0 /* unused */
+static void rna_CacheModifier_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ rna_CacheModifier_update(bmain, scene, ptr);
+ DAG_relations_tag_update(bmain);
+}
+#endif
+
+
+static StructRNA *rna_CacheModifier_refine(struct PointerRNA *ptr)
+{
+ CacheModifier *md = (CacheModifier *)ptr->data;
+
+ switch ((eCacheModifier_Type)md->type) {
+ case eCacheModifierType_HairSimulation:
+ return &RNA_HairSimulationCacheModifier;
+ case eCacheModifierType_ForceField:
+ return &RNA_ForceFieldCacheModifier;
+ case eCacheModifierType_ShrinkWrap:
+ return &RNA_ShrinkWrapCacheModifier;
+ case eCacheModifierType_StrandsKey:
+ return &RNA_StrandsKeyCacheModifier;
+ case eCacheModifierType_Haircut:
+ return &RNA_HaircutCacheModifier;
+
+ /* Default */
+ case eCacheModifierType_None:
+ case NUM_CACHE_MODIFIER_TYPES:
+ return &RNA_CacheLibraryModifier;
+ }
+
+ return &RNA_CacheLibraryModifier;
+}
+
+static void rna_CacheLibraryModifier_name_set(PointerRNA *ptr, const char *value)
+{
+ CacheModifier *md = ptr->data;
+ char oldname[sizeof(md->name)];
+
+ /* make a copy of the old name first */
+ BLI_strncpy(oldname, md->name, sizeof(md->name));
+
+ /* copy the new name into the name slot */
+ BLI_strncpy_utf8(md->name, value, sizeof(md->name));
+
+ /* make sure the name is truly unique */
+ if (ptr->id.data) {
+ CacheLibrary *cachelib = ptr->id.data;
+ BKE_cache_modifier_unique_name(&cachelib->modifiers, md);
+ }
+
+ /* fix all the animation data which may link to this */
+ BKE_animdata_fix_paths_rename_all(NULL, "modifiers", oldname, md->name);
+}
+
+static char *rna_CacheLibraryModifier_path(PointerRNA *ptr)
+{
+ CacheModifier *md = ptr->data;
+ char name_esc[sizeof(md->name) * 2];
+
+ BLI_strescape(name_esc, md->name, sizeof(name_esc));
+ return BLI_sprintfN("modifiers[\"%s\"]", name_esc);
+}
+
+static CacheModifier *rna_CacheLibrary_modifier_new(CacheLibrary *cachelib, bContext *UNUSED(C), ReportList *UNUSED(reports),
+ const char *name, int type)
+{
+ return BKE_cache_modifier_add(cachelib, name, type);
+}
+
+static void rna_CacheLibrary_modifier_remove(CacheLibrary *cachelib, bContext *UNUSED(C), ReportList *UNUSED(reports), PointerRNA *md_ptr)
+{
+ CacheModifier *md = md_ptr->data;
+
+ BKE_cache_modifier_remove(cachelib, md);
+
+ RNA_POINTER_INVALIDATE(md_ptr);
+}
+
+static void rna_CacheLibrary_modifier_clear(CacheLibrary *cachelib, bContext *UNUSED(C))
+{
+ BKE_cache_modifier_clear(cachelib);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int rna_CacheLibraryModifier_mesh_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value)
+{
+ /*HairSimCacheModifier *hsmd = ptr->data;*/
+ Object *ob = value.data;
+
+ return ob->type == OB_MESH && ob->data != NULL;
+}
+
+static int rna_CacheLibraryModifier_hair_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value)
+{
+ /*HairSimCacheModifier *hsmd = ptr->data;*/
+ Object *ob = value.data;
+ ParticleSystem *psys;
+ bool has_hair_system = false;
+
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ if (psys->part && psys->part->type == PART_HAIR) {
+ has_hair_system = true;
+ break;
+ }
+ }
+ return has_hair_system;
+}
+
+static PointerRNA rna_HairSimulationCacheModifier_hair_system_get(PointerRNA *ptr)
+{
+ HairSimCacheModifier *hsmd = ptr->data;
+ ParticleSystem *psys = hsmd->object ? BLI_findlink(&hsmd->object->particlesystem, hsmd->hair_system) : NULL;
+ PointerRNA value;
+
+ RNA_pointer_create(ptr->id.data, &RNA_ParticleSystem, psys, &value);
+ return value;
+}
+
+static void rna_HairSimulationCacheModifier_hair_system_set(PointerRNA *ptr, PointerRNA value)
+{
+ HairSimCacheModifier *hsmd = ptr->data;
+ ParticleSystem *psys = value.data;
+ hsmd->hair_system = hsmd->object ? BLI_findindex(&hsmd->object->particlesystem, psys) : -1;
+}
+
+static int rna_HairSimulationCacheModifier_hair_system_poll(PointerRNA *ptr, PointerRNA value)
+{
+ HairSimCacheModifier *hsmd = ptr->data;
+ ParticleSystem *psys = value.data;
+
+ if (!hsmd->object)
+ return false;
+ if (BLI_findindex(&hsmd->object->particlesystem, psys) == -1)
+ return false;
+ if (!psys->part || psys->part->type != PART_HAIR)
+ return false;
+ return true;
+}
+
+static PointerRNA rna_ShrinkWrapCacheModifier_hair_system_get(PointerRNA *ptr)
+{
+ ShrinkWrapCacheModifier *smd = ptr->data;
+ ParticleSystem *psys = smd->object ? BLI_findlink(&smd->object->particlesystem, smd->hair_system) : NULL;
+ PointerRNA value;
+
+ RNA_pointer_create(ptr->id.data, &RNA_ParticleSystem, psys, &value);
+ return value;
+}
+
+static void rna_ShrinkWrapCacheModifier_hair_system_set(PointerRNA *ptr, PointerRNA value)
+{
+ ShrinkWrapCacheModifier *smd = ptr->data;
+ ParticleSystem *psys = value.data;
+ smd->hair_system = smd->object ? BLI_findindex(&smd->object->particlesystem, psys) : -1;
+}
+
+static int rna_ShrinkWrapCacheModifier_hair_system_poll(PointerRNA *ptr, PointerRNA value)
+{
+ ShrinkWrapCacheModifier *smd = ptr->data;
+ ParticleSystem *psys = value.data;
+
+ if (!smd->object)
+ return false;
+ if (BLI_findindex(&smd->object->particlesystem, psys) == -1)
+ return false;
+ if (!psys->part || psys->part->type != PART_HAIR)
+ return false;
+ return true;
+}
+
+static PointerRNA rna_StrandsKeyCacheModifier_hair_system_get(PointerRNA *ptr)
+{
+ StrandsKeyCacheModifier *skmd = ptr->data;
+ ParticleSystem *psys = skmd->object ? BLI_findlink(&skmd->object->particlesystem, skmd->hair_system) : NULL;
+ PointerRNA value;
+
+ RNA_pointer_create(ptr->id.data, &RNA_ParticleSystem, psys, &value);
+ return value;
+}
+
+static void rna_StrandsKeyCacheModifier_hair_system_set(PointerRNA *ptr, PointerRNA value)
+{
+ StrandsKeyCacheModifier *skmd = ptr->data;
+ ParticleSystem *psys = value.data;
+ skmd->hair_system = skmd->object ? BLI_findindex(&skmd->object->particlesystem, psys) : -1;
+}
+
+static int rna_StrandsKeyCacheModifier_hair_system_poll(PointerRNA *ptr, PointerRNA value)
+{
+ StrandsKeyCacheModifier *skmd = ptr->data;
+ ParticleSystem *psys = value.data;
+
+ if (!skmd->object)
+ return false;
+ if (BLI_findindex(&skmd->object->particlesystem, psys) == -1)
+ return false;
+ if (!psys->part || psys->part->type != PART_HAIR)
+ return false;
+ return true;
+}
+
+static void rna_StrandsKeyCacheModifier_active_shape_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+#if 0 // TODO
+ StrandsKeyCacheModifier *skmd = ptr->data;
+
+ if (scene->obedit == ob) {
+ /* exit/enter editmode to get new shape */
+ switch (ob->type) {
+ case OB_MESH:
+ EDBM_mesh_load(ob);
+ EDBM_mesh_make(scene->toolsettings, ob);
+ EDBM_mesh_normals_update(((Mesh *)ob->data)->edit_btmesh);
+ BKE_editmesh_tessface_calc(((Mesh *)ob->data)->edit_btmesh);
+ break;
+ case OB_CURVE:
+ case OB_SURF:
+ load_editNurb(ob);
+ make_editNurb(ob);
+ break;
+ case OB_LATTICE:
+ load_editLatt(ob);
+ make_editLatt(ob);
+ break;
+ }
+ }
+#endif
+
+ rna_CacheModifier_update(bmain, scene, ptr);
+}
+
+static void rna_StrandsKeyCacheModifier_active_shape_key_index_range(PointerRNA *ptr, int *min, int *max, int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ StrandsKeyCacheModifier *skmd = ptr->data;
+ Key *key = skmd->key;
+
+ *min = 0;
+ if (key) {
+ *max = BLI_listbase_count(&key->block) - 1;
+ if (*max < 0) *max = 0;
+ }
+ else {
+ *max = 0;
+ }
+}
+
+static int rna_StrandsKeyCacheModifier_active_shape_key_index_get(PointerRNA *ptr)
+{
+ StrandsKeyCacheModifier *skmd = ptr->data;
+
+ return max_ii(skmd->shapenr - 1, 0);
+}
+
+static void rna_StrandsKeyCacheModifier_active_shape_key_index_set(PointerRNA *ptr, int value)
+{
+ StrandsKeyCacheModifier *skmd = ptr->data;
+
+ skmd->shapenr = value + 1;
+}
+
+static PointerRNA rna_StrandsKeyCacheModifier_active_shape_key_get(PointerRNA *ptr)
+{
+ StrandsKeyCacheModifier *skmd = ptr->data;
+ Key *key = skmd->key;
+ KeyBlock *kb;
+ PointerRNA keyptr;
+
+ if (key == NULL)
+ return PointerRNA_NULL;
+
+ kb = BLI_findlink(&key->block, skmd->shapenr - 1);
+ RNA_pointer_create((ID *)key, &RNA_ShapeKey, kb, &keyptr);
+ return keyptr;
+}
+
+static PointerRNA rna_HaircutCacheModifier_hair_system_get(PointerRNA *ptr)
+{
+ HaircutCacheModifier *hmd = ptr->data;
+ ParticleSystem *psys = hmd->object ? BLI_findlink(&hmd->object->particlesystem, hmd->hair_system) : NULL;
+ PointerRNA value;
+
+ RNA_pointer_create(ptr->id.data, &RNA_ParticleSystem, psys, &value);
+ return value;
+}
+
+static void rna_HaircutCacheModifier_hair_system_set(PointerRNA *ptr, PointerRNA value)
+{
+ HaircutCacheModifier *hmd = ptr->data;
+ ParticleSystem *psys = value.data;
+ hmd->hair_system = hmd->object ? BLI_findindex(&hmd->object->particlesystem, psys) : -1;
+}
+
+static int rna_HaircutCacheModifier_hair_system_poll(PointerRNA *ptr, PointerRNA value)
+{
+ HaircutCacheModifier *hmd = ptr->data;
+ ParticleSystem *psys = value.data;
+
+ if (!hmd->object)
+ return false;
+ if (BLI_findindex(&hmd->object->particlesystem, psys) == -1)
+ return false;
+ if (!psys->part || psys->part->type != PART_HAIR)
+ return false;
+ return true;
+}
+
+static void rna_CacheArchiveInfoNode_bytes_size_get(PointerRNA *ptr, char *value)
+{
+ CacheArchiveInfoNode *node = ptr->data;
+ BLI_snprintf(value, MAX_NAME, "%lld", (long long int)node->bytes_size);
+}
+
+static int rna_CacheArchiveInfoNode_bytes_size_length(PointerRNA *ptr)
+{
+ char buf[MAX_NAME];
+ /* theoretically could do a dummy BLI_snprintf here, but BLI does not allow NULL buffer ... */
+ CacheArchiveInfoNode *node = ptr->data;
+ return BLI_snprintf(buf, sizeof(buf), "%lld", (long long int)node->bytes_size);
+}
+
+#else
+
+static void rna_def_hair_sim_params(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "HairSimulationParameters", NULL);
+ RNA_def_struct_sdna(srna, "HairSimParams");
+ RNA_def_struct_ui_text(srna, "Hair Simulation Parameters", "Simulation parameters for hair simulation");
+ RNA_def_struct_ui_icon(srna, ICON_HAIR);
+
+ prop = RNA_def_property(srna, "timescale", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1f, 3);
+ RNA_def_property_ui_text(prop, "Time Scale", "Simulation time scale relative to scene time");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "substeps", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 1, 80);
+ RNA_def_property_ui_text(prop, "Substeps", "Simulation steps per frame");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "effector_weights", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "EffectorWeights");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Effector Weights", "");
+
+ prop = RNA_def_property(srna, "mass", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, 10.0f);
+ RNA_def_property_ui_text(prop, "Mass", "Mass of hair vertices");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "drag", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1f, 3);
+ RNA_def_property_ui_text(prop, "Drag", "Drag simulating friction with surrounding air");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "goal_stiffness", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1f, 3);
+ RNA_def_property_float_default(prop, 0.0f);
+ RNA_def_property_ui_text(prop, "Goal Strength", "Goal spring, pulling vertices toward their rest position");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "goal_damping", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 3);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_ui_text(prop, "Goal Damping", "Damping factor of goal springs");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "use_goal_stiffness_curve", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eHairSimParams_Flag_UseGoalStiffnessCurve);
+ RNA_def_property_ui_text(prop, "Use Goal Stiffness Curve", "Use a curve to define goal stiffness along the strand");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "goal_stiffness_curve", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "goal_stiffness_mapping");
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Goal Stiffness Curve", "Stiffness of goal springs along the strand curves");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "use_goal_deflect", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eHairSimParams_Flag_UseGoalDeflect);
+ RNA_def_property_ui_text(prop, "Use Goal Deflect", "Disable goal springs inside deflectors, to avoid unstable deformations");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "use_bend_stiffness_curve", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eHairSimParams_Flag_UseBendStiffnessCurve);
+ RNA_def_property_ui_text(prop, "Use Bend Stiffness Curve", "Use a curve to define bend resistance along the strand");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "bend_stiffness_curve", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "bend_stiffness_mapping");
+ RNA_def_property_struct_type(prop, "CurveMapping");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Bend Stiffness Curve", "Resistance to bending along the strand curves");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "stretch_stiffness", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 10000.0f, 0.1f, 3);
+ RNA_def_property_float_default(prop, 10000.0f);
+ RNA_def_property_ui_text(prop, "Stretch Stiffness", "Resistance to stretching");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "stretch_damping", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 3);
+ RNA_def_property_float_default(prop, 0.1f);
+ RNA_def_property_ui_text(prop, "Stretch Damping", "Damping factor of stretch springs");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "bend_stiffness", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 1000.0f, 0.1f, 3);
+ RNA_def_property_float_default(prop, 100.0f);
+ RNA_def_property_ui_text(prop, "Bend Stiffness", "Resistance to bending of the rest shape");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "bend_damping", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 3);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_ui_text(prop, "Bend Damping", "Damping factor of bending springs");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+}
+
+static void rna_def_cache_modifier_hair_simulation(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ rna_def_hair_sim_params(brna);
+
+ srna = RNA_def_struct(brna, "HairSimulationCacheModifier", "CacheLibraryModifier");
+ RNA_def_struct_sdna(srna, "HairSimCacheModifier");
+ RNA_def_struct_ui_text(srna, "Hair Simulation Cache Modifier", "Apply hair dynamics simulation to the cache");
+ RNA_def_struct_ui_icon(srna, ICON_HAIR);
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "object");
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_CacheLibraryModifier_hair_object_poll");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Object", "Object whose cache to simulate");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "hair_system_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "hair_system");
+ RNA_def_property_ui_text(prop, "Hair System Index", "Hair system cache to simulate");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "hair_system", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_funcs(prop, "rna_HairSimulationCacheModifier_hair_system_get", "rna_HairSimulationCacheModifier_hair_system_set", NULL, "rna_HairSimulationCacheModifier_hair_system_poll");
+ RNA_def_property_struct_type(prop, "ParticleSystem");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Hair System", "Hair system cache to simulate");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "parameters", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "sim_params");
+ RNA_def_property_struct_type(prop, "HairSimulationParameters");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Simulation Parameters", "Parameters of the simulation");
+}
+
+static void rna_def_cache_modifier_force_field(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem force_type_items[] = {
+ {eForceFieldCacheModifier_Type_Deflect, "DEFLECT", ICON_FORCE_FORCE, "Deflect", "Push away from the surface"},
+ {eForceFieldCacheModifier_Type_Drag, "DRAG", ICON_FORCE_DRAG, "Drag", "Adjust velocity to the surface"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ srna = RNA_def_struct(brna, "ForceFieldCacheModifier", "CacheLibraryModifier");
+ RNA_def_struct_sdna(srna, "ForceFieldCacheModifier");
+ RNA_def_struct_ui_text(srna, "Force Field Cache Modifier", "Use an object as a force field");
+ RNA_def_struct_ui_icon(srna, ICON_FORCE_FORCE);
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "object");
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_CacheLibraryModifier_mesh_object_poll");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Object", "Object whose cache to simulate");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "force_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "type");
+ RNA_def_property_enum_items(prop, force_type_items);
+ RNA_def_property_ui_text(prop, "Force Type", "Type of force field");
+
+ prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
+ RNA_def_property_ui_range(prop, -10000.0f, 10000.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Strength", "");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "falloff", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_range(prop, 0.0f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Falloff", "");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "min_distance", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
+ RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1, 4);
+ RNA_def_property_ui_text(prop, "Minimum Distance", "");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "max_distance", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_range(prop, -FLT_MAX, FLT_MAX);
+ RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1, 4);
+ RNA_def_property_ui_text(prop, "Maximum Distance", "");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "use_double_sided", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eForceFieldCacheModifier_Flag_DoubleSided);
+ RNA_def_property_ui_text(prop, "Use Double Sided", "");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+}
+
+static void rna_def_cache_modifier_shrink_wrap(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "ShrinkWrapCacheModifier", "CacheLibraryModifier");
+ RNA_def_struct_sdna(srna, "ShrinkWrapCacheModifier");
+ RNA_def_struct_ui_text(srna, "Shrink Wrap Cache Modifier", "");
+ RNA_def_struct_ui_icon(srna, ICON_HAIR);
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "object");
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_CacheLibraryModifier_hair_object_poll");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Object", "Object whose cache to simulate");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "hair_system_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "hair_system");
+ RNA_def_property_ui_text(prop, "Hair System Index", "Hair system cache to simulate");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "hair_system", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_funcs(prop, "rna_ShrinkWrapCacheModifier_hair_system_get", "rna_ShrinkWrapCacheModifier_hair_system_set", NULL, "rna_ShrinkWrapCacheModifier_hair_system_poll");
+ RNA_def_property_struct_type(prop, "ParticleSystem");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Hair System", "Hair system cache to simulate");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "target");
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Target", "Mesh object to wrap onto");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "use_internal_target", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eShrinkWrapCacheModifier_Flag_InternalTarget);
+ RNA_def_property_ui_text(prop, "Use Internal Target", "Use a cached object from the group instead of an object in the scene");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+}
+
+static void rna_def_cache_modifier_strands_key(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "StrandsKeyCacheModifier", "CacheLibraryModifier");
+ RNA_def_struct_sdna(srna, "StrandsKeyCacheModifier");
+ RNA_def_struct_ui_text(srna, "Strands Key Cache Modifier", "");
+ RNA_def_struct_ui_icon(srna, ICON_SHAPEKEY_DATA);
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "object");
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_CacheLibraryModifier_hair_object_poll");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Object", "Object whose cache to simulate");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "hair_system_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "hair_system");
+ RNA_def_property_ui_text(prop, "Hair System Index", "Hair system cache to simulate");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "hair_system", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_funcs(prop, "rna_StrandsKeyCacheModifier_hair_system_get", "rna_StrandsKeyCacheModifier_hair_system_set", NULL, "rna_StrandsKeyCacheModifier_hair_system_poll");
+ RNA_def_property_struct_type(prop, "ParticleSystem");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Hair System", "Hair system cache to simulate");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ /* shape keys */
+ prop = RNA_def_property(srna, "shape_keys", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "key");
+ RNA_def_property_ui_text(prop, "Shape Keys", "");
+
+ prop = RNA_def_property(srna, "use_motion_state", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eStrandsKeyCacheModifier_Flag_UseMotionState);
+ RNA_def_property_ui_text(prop, "Use Motion State", "Apply the shape key to the motion state instead of the base shape");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "show_only_shape_key", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eStrandsKeyCacheModifier_Flag_ShapeLock);
+ RNA_def_property_ui_text(prop, "Shape Key Lock", "Always show the current Shape for this Object");
+ RNA_def_property_ui_icon(prop, ICON_UNPINNED, 1);
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "active_shape_key", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ShapeKey");
+ RNA_def_property_pointer_funcs(prop, "rna_StrandsKeyCacheModifier_active_shape_key_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop, "Active Shape Key", "Current shape key");
+
+ prop = RNA_def_property(srna, "active_shape_key_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "shapenr");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); /* XXX this is really unpredictable... */
+ RNA_def_property_int_funcs(prop, "rna_StrandsKeyCacheModifier_active_shape_key_index_get", "rna_StrandsKeyCacheModifier_active_shape_key_index_set",
+ "rna_StrandsKeyCacheModifier_active_shape_key_index_range");
+ RNA_def_property_ui_text(prop, "Active Shape Key Index", "Current shape key index");
+ RNA_def_property_update(prop, 0, "rna_StrandsKeyCacheModifier_active_shape_update");
+}
+
+static void rna_def_cache_modifier_haircut(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem cut_mode_items[] = {
+ {eHaircutCacheModifier_CutMode_Enter, "ENTER", 0, "Enter", "Cut strands when entering the target mesh"},
+ {eHaircutCacheModifier_CutMode_Exit, "EXIT", 0, "Exit", "Cut strands when exiting the target mesh"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ srna = RNA_def_struct(brna, "HaircutCacheModifier", "CacheLibraryModifier");
+ RNA_def_struct_sdna(srna, "HaircutCacheModifier");
+ RNA_def_struct_ui_text(srna, "Hair Cut Cache Modifier", "");
+ RNA_def_struct_ui_icon(srna, ICON_HAIR);
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "object");
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_CacheLibraryModifier_hair_object_poll");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Object", "Object whose cache to simulate");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "hair_system_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "hair_system");
+ RNA_def_property_ui_text(prop, "Hair System Index", "Hair system cache to simulate");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "hair_system", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_funcs(prop, "rna_HaircutCacheModifier_hair_system_get", "rna_HaircutCacheModifier_hair_system_set", NULL, "rna_HaircutCacheModifier_hair_system_poll");
+ RNA_def_property_struct_type(prop, "ParticleSystem");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Hair System", "Hair system cache to simulate");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "target");
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Target", "Mesh object to wrap onto");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "use_internal_target", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eHaircutCacheModifier_Flag_InternalTarget);
+ RNA_def_property_ui_text(prop, "Use Internal Target", "Use a cached object from the group instead of an object in the scene");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+
+ prop = RNA_def_property(srna, "cut_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "cut_mode");
+ RNA_def_property_enum_items(prop, cut_mode_items);
+ RNA_def_property_flag(prop, PROP_ENUM_FLAG);
+ RNA_def_property_ui_text(prop, "Cut Mode", "When to cut strands with the target");
+ RNA_def_property_update(prop, 0, "rna_CacheModifier_update");
+}
+
+static void rna_def_cache_modifier(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "CacheLibraryModifier", NULL);
+ RNA_def_struct_sdna(srna, "CacheModifier");
+ RNA_def_struct_path_func(srna, "rna_CacheLibraryModifier_path");
+ RNA_def_struct_refine_func(srna, "rna_CacheModifier_refine");
+ RNA_def_struct_ui_text(srna, "Cache Modifier", "Cache Modifier");
+ RNA_def_struct_ui_icon(srna, ICON_PHYSICS);
+
+ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "type");
+ RNA_def_property_enum_items(prop, cache_modifier_type_items);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Type", "Type of the cache modifier");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_CacheLibraryModifier_name_set");
+ RNA_def_property_ui_text(prop, "Name", "Modifier name");
+ RNA_def_property_update(prop, NC_ID | NA_RENAME, NULL);
+ RNA_def_struct_name_property(srna, prop);
+
+ rna_def_cache_modifier_hair_simulation(brna);
+ rna_def_cache_modifier_force_field(brna);
+ rna_def_cache_modifier_shrink_wrap(brna);
+ rna_def_cache_modifier_strands_key(brna);
+ rna_def_cache_modifier_haircut(brna);
+}
+
+static void rna_def_cache_library_modifiers(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "CacheLibraryModifiers");
+ srna = RNA_def_struct(brna, "CacheLibraryModifiers", NULL);
+ RNA_def_struct_sdna(srna, "CacheLibrary");
+ RNA_def_struct_ui_text(srna, "Cache Modifiers", "Collection of cache modifiers");
+
+ /* add modifier */
+ func = RNA_def_function(srna, "new", "rna_CacheLibrary_modifier_new");
+ RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func, "Add a new modifier");
+ parm = RNA_def_string(func, "name", "Name", 0, "", "New name for the modifier");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ /* modifier to add */
+ parm = RNA_def_enum(func, "type", cache_modifier_type_items, 1, "", "Modifier type to add");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ /* return type */
+ parm = RNA_def_pointer(func, "modifier", "CacheLibraryModifier", "", "Newly created modifier");
+ RNA_def_function_return(func, parm);
+
+ /* remove modifier */
+ func = RNA_def_function(srna, "remove", "rna_CacheLibrary_modifier_remove");
+ RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func, "Remove an existing modifier");
+ /* modifier to remove */
+ parm = RNA_def_pointer(func, "modifier", "CacheLibraryModifier", "", "Modifier to remove");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
+ RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
+
+ /* clear all modifiers */
+ func = RNA_def_function(srna, "clear", "rna_CacheLibrary_modifier_clear");
+ RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+ RNA_def_function_ui_description(func, "Remove all modifiers");
+}
+
+static void rna_def_cache_library(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem source_mode_items[] = {
+ {CACHE_LIBRARY_SOURCE_SCENE, "SCENE", 0, "Scene", "Use generated scene data as source"},
+ {CACHE_LIBRARY_SOURCE_CACHE, "CACHE", 0, "Cache", "Use cache data as source"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem display_mode_items[] = {
+ {CACHE_LIBRARY_DISPLAY_SOURCE, "SOURCE", 0, "Source", "Display source data unmodified"},
+ {CACHE_LIBRARY_DISPLAY_MODIFIERS, "MODIFIERS", 0, "Modifiers", "Display source data with modifiers applied"},
+ {CACHE_LIBRARY_DISPLAY_RESULT, "RESULT", 0, "Result", "Display resulting data"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ srna = RNA_def_struct(brna, "CacheLibrary", "ID");
+ RNA_def_struct_ui_text(srna, "Cache Library", "Cache Library datablock for constructing an archive of caches");
+ RNA_def_struct_ui_icon(srna, ICON_PHYSICS);
+
+ prop = RNA_def_property(srna, "input_filepath", PROP_STRING, PROP_FILEPATH);
+ RNA_def_property_string_sdna(prop, NULL, "input_filepath");
+ RNA_def_property_ui_text(prop, "Input File Path", "Path to a cache archive for reading input");
+ RNA_def_property_update(prop, 0, "rna_CacheLibrary_update");
+
+ prop = RNA_def_property(srna, "output_filepath", PROP_STRING, PROP_FILEPATH);
+ RNA_def_property_string_sdna(prop, NULL, "output_filepath");
+ RNA_def_property_ui_text(prop, "Output File Path", "Path where cache output is written");
+ RNA_def_property_update(prop, 0, "rna_CacheLibrary_update");
+
+ prop = RNA_def_property(srna, "source_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "source_mode");
+ RNA_def_property_enum_items(prop, source_mode_items);
+ RNA_def_property_ui_text(prop, "Source Mode", "Source of the cache library data");
+ RNA_def_property_update(prop, 0, "rna_CacheLibrary_update");
+
+ prop = RNA_def_property(srna, "display_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "display_mode");
+ RNA_def_property_enum_items(prop, display_mode_items);
+ RNA_def_property_ui_text(prop, "Display Mode", "What data to display in the viewport");
+ RNA_def_property_update(prop, 0, "rna_CacheLibrary_update");
+
+ prop = RNA_def_property(srna, "display_motion", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "display_flag", CACHE_LIBRARY_DISPLAY_MOTION);
+ RNA_def_property_ui_text(prop, "Display Motion", "Display motion state result from simulation, if available");
+ RNA_def_property_update(prop, 0, "rna_CacheLibrary_update");
+
+ prop = RNA_def_property(srna, "display_children", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "display_flag", CACHE_LIBRARY_DISPLAY_CHILDREN);
+ RNA_def_property_ui_text(prop, "Display Children", "Display child strands, if available");
+ RNA_def_property_update(prop, 0, "rna_CacheLibrary_update");
+
+ prop = RNA_def_property(srna, "data_types", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "data_types");
+ RNA_def_property_enum_items(prop, cache_library_data_type_items);
+ RNA_def_property_flag(prop, PROP_ENUM_FLAG);
+ RNA_def_property_ui_text(prop, "Data Types", "Types of data to store in the cache");
+
+ prop = RNA_def_property(srna, "filter_group", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "filter_group");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Filter Group", "If set, only objects in this group will be cached");
+
+ prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Description", "Description of the output archive");
+
+ /* modifiers */
+ prop = RNA_def_property(srna, "modifiers", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CacheLibraryModifier");
+ RNA_def_property_ui_text(prop, "Modifiers", "Modifiers applying to the cached data");
+ rna_def_cache_library_modifiers(brna, prop);
+
+ prop = RNA_def_property(srna, "archive_info", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CacheArchiveInfo");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Archive Info", "Information about structure and contents of the archive");
+}
+
+static void rna_def_cache_archive_info_node(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem type_items[] = {
+ {eCacheArchiveInfoNode_Type_Object, "OBJECT", 0, "Object", "Structural object node forming the hierarchy"},
+ {eCacheArchiveInfoNode_Type_ScalarProperty, "SCALAR_PROPERTY", 0, "Scalar Property", "Property with a single value per sample"},
+ {eCacheArchiveInfoNode_Type_ArrayProperty, "ARRAY_PROPERTY", 0, "Array Property", "Array property with an arbitrary number of values per sample"},
+ {eCacheArchiveInfoNode_Type_CompoundProperty, "COMPOUND_PROPERTY", 0, "Compound Property", "Compound property containing other properties"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ srna = RNA_def_struct(brna, "CacheArchiveInfoNode", NULL);
+ RNA_def_struct_ui_text(srna, "Cache Archive Info Node", "Node in the structure of a cache archive");
+
+ prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, type_items);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Type", "Type of archive node");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Name", "Name of the archive node");
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "child_nodes", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CacheArchiveInfoNode");
+ RNA_def_property_ui_text(prop, "Child Nodes", "Nested archive nodes");
+
+ prop = RNA_def_property(srna, "expand", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", eCacheArchiveInfoNode_Flag_Expand);
+ RNA_def_property_ui_text(prop, "Expand", "Show contents of the node");
+ RNA_def_property_update(prop, 0, "rna_CacheArchiveInfo_update");
+
+ /* XXX this is a 64bit integer, not supported nicely by RNA,
+ * but string encoding is sufficient for feedback
+ */
+ prop = RNA_def_property(srna, "bytes_size", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, "rna_CacheArchiveInfoNode_bytes_size_get", "rna_CacheArchiveInfoNode_bytes_size_length", NULL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Bytes Size", "Overall size of the node data in bytes");
+
+ prop = RNA_def_property(srna, "datatype", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "datatype_name");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Datatype", "Type of values stored in the property");
+
+ prop = RNA_def_property(srna, "datatype_extent", PROP_INT, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Datatype Extent", "Array extent of a single data element");
+
+ prop = RNA_def_property(srna, "samples", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "num_samples");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Samples", "Number of samples stored for the property");
+
+ prop = RNA_def_property(srna, "array_size", PROP_INT, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Array Size", "Maximum array size for any sample of the property");
+}
+
+static void rna_def_cache_archive_info(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "CacheArchiveInfo", NULL);
+ RNA_def_struct_ui_text(srna, "Cache Archive Info", "Information about structure and contents of a cache file");
+
+ prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "File Path", "Path to the cache archive");
+
+ prop = RNA_def_property(srna, "app_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Application", "Name of the application that created the archive");
+
+ prop = RNA_def_property(srna, "date_written", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Date", "Date and time when the archive was created");
+
+ prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Description", "Description of the archive");
+
+ prop = RNA_def_property(srna, "root_node", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "CacheArchiveInfoNode");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Root Node", "Root node of the archive");
+}
+
+void RNA_def_cache_library(BlenderRNA *brna)
+{
+ rna_def_cache_modifier(brna);
+ rna_def_cache_library(brna);
+ rna_def_cache_archive_info_node(brna);
+ rna_def_cache_archive_info(brna);
+}
+
+#endif
diff --git a/source/blender/makesrna/intern/rna_context.c b/source/blender/makesrna/intern/rna_context.c
index d7a679e9702..a512f1d750f 100644
--- a/source/blender/makesrna/intern/rna_context.c
+++ b/source/blender/makesrna/intern/rna_context.c
@@ -148,6 +148,7 @@ void RNA_def_context(BlenderRNA *brna)
{CTX_MODE_PAINT_VERTEX, "PAINT_VERTEX", 0, "Vertex Paint", ""},
{CTX_MODE_PAINT_TEXTURE, "PAINT_TEXTURE", 0, "Texture Paint", ""},
{CTX_MODE_PARTICLE, "PARTICLE", 0, "Particle", ""},
+ {CTX_MODE_HAIR, "HAIR", 0, "Hair", ""},
{CTX_MODE_OBJECT, "OBJECT", 0, "Object", ""},
{0, NULL, 0, NULL, NULL}
};
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index eab14be9085..62fe71fe8a5 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_cache_library(struct BlenderRNA *brna);
void RNA_def_camera(struct BlenderRNA *brna);
void RNA_def_cloth(struct BlenderRNA *brna);
void RNA_def_color(struct BlenderRNA *brna);
@@ -155,6 +156,7 @@ void RNA_def_linestyle(struct BlenderRNA *brna);
void RNA_def_main(struct BlenderRNA *brna);
void RNA_def_material(struct BlenderRNA *brna);
void RNA_def_mesh(struct BlenderRNA *brna);
+void RNA_def_mesh_sample(struct BlenderRNA *brna);
void RNA_def_meta(struct BlenderRNA *brna);
void RNA_def_modifier(struct BlenderRNA *brna);
void RNA_def_nla(struct BlenderRNA *brna);
@@ -176,6 +178,7 @@ void RNA_def_sequencer(struct BlenderRNA *brna);
void RNA_def_smoke(struct BlenderRNA *brna);
void RNA_def_space(struct BlenderRNA *brna);
void RNA_def_speaker(struct BlenderRNA *brna);
+void RNA_def_strands(struct BlenderRNA *brna);
void RNA_def_test(struct BlenderRNA *brna);
void RNA_def_text(struct BlenderRNA *brna);
void RNA_def_texture(struct BlenderRNA *brna);
@@ -264,6 +267,7 @@ void RNA_api_image(struct StructRNA *srna);
void RNA_api_lattice(struct StructRNA *srna);
void RNA_api_operator(struct StructRNA *srna);
void RNA_api_macro(struct StructRNA *srna);
+void RNA_api_widgetgroup(struct StructRNA *srna);
void RNA_api_keyconfig(struct StructRNA *srna);
void RNA_api_keyconfigs(struct StructRNA *srna);
void RNA_api_keyingset(struct StructRNA *srna);
@@ -330,6 +334,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_cache_libraries(BlenderRNA *brna, PropertyRNA *cprop);
/* ID Properties */
diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c
index 04cba835ae2..641c9a596a8 100644
--- a/source/blender/makesrna/intern/rna_key.c
+++ b/source/blender/makesrna/intern/rna_key.c
@@ -32,6 +32,7 @@
#include "DNA_key_types.h"
#include "DNA_lattice_types.h"
#include "DNA_mesh_types.h"
+#include "DNA_particle_types.h"
#include "BLI_utildefines.h"
@@ -47,9 +48,11 @@
#include <stddef.h>
+#include "DNA_cache_library_types.h"
#include "DNA_object_types.h"
#include "BKE_animsys.h"
+#include "BKE_cache_library.h"
#include "BKE_depsgraph.h"
#include "BKE_key.h"
#include "BKE_main.h"
@@ -302,7 +305,7 @@ static void rna_ShapeKey_data_begin(CollectionPropertyIterator *iter, PointerRNA
Nurb *nu;
int tot = kb->totelem, size = key->elemsize;
- if (GS(key->from->name) == ID_CU) {
+ if (key->from && key->fromtype == KEY_OWNER_CURVE) {
cu = (Curve *)key->from;
nu = cu->nurb.first;
@@ -323,7 +326,7 @@ static int rna_ShapeKey_data_length(PointerRNA *ptr)
Nurb *nu;
int tot = kb->totelem;
- if (GS(key->from->name) == ID_CU) {
+ if (key->from && key->fromtype == KEY_OWNER_CURVE) {
cu = (Curve *)key->from;
nu = cu->nurb.first;
@@ -341,7 +344,7 @@ static PointerRNA rna_ShapeKey_data_get(CollectionPropertyIterator *iter)
Curve *cu;
Nurb *nu;
- if (GS(key->from->name) == ID_CU) {
+ if (key->from && key->fromtype == KEY_OWNER_CURVE) {
cu = (Curve *)key->from;
nu = cu->nurb.first;
@@ -374,11 +377,37 @@ static void rna_Key_update_data(Main *bmain, Scene *UNUSED(scene), PointerRNA *p
{
Key *key = ptr->id.data;
Object *ob;
-
- for (ob = bmain->object.first; ob; ob = ob->id.next) {
- if (BKE_key_from_object(ob) == key) {
- DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
- WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
+ CacheLibrary *cachelib;
+
+ switch (key->fromtype) {
+ case KEY_OWNER_MESH:
+ case KEY_OWNER_CURVE:
+ case KEY_OWNER_LATTICE:
+ for (ob = bmain->object.first; ob; ob = ob->id.next) {
+ if (BKE_key_from_object(ob) == key) {
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
+ }
+ }
+ break;
+ case KEY_OWNER_PARTICLES:
+ for (ob = bmain->object.first; ob; ob = ob->id.next) {
+ ParticleSystem *psys;
+ for (psys = ob->particlesystem.first; psys; psys = psys->next) {
+ if (psys->key == key) {
+ psys->recalc |= PSYS_RECALC_REDO;
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+ WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, ob);
+ }
+ }
+ }
+ break;
+ }
+
+ for (cachelib = bmain->cache_library.first; cachelib; cachelib = cachelib->id.next) {
+ if (BKE_cache_library_uses_key(cachelib, key)) {
+ DAG_id_tag_update(&cachelib->id, OB_RECALC_DATA);
+ WM_main_add_notifier(NC_WINDOW, NULL);
}
}
}
@@ -600,6 +629,11 @@ static void rna_def_keyblock(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Vertex Group", "Vertex weight group, to blend with basis shape");
RNA_def_property_update(prop, 0, "rna_Key_update_data");
+ prop = RNA_def_property(srna, "face_map", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "facemap");
+ RNA_def_property_ui_text(prop, "Face Map", "Face Map used to initiate interpolation for this shapekey");
+ RNA_def_property_update(prop, 0, "rna_Key_update_data");
+
prop = RNA_def_property(srna, "relative_key", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "ShapeKey");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL);
diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c
index 348fa3c8302..926c5db5ae6 100644
--- a/source/blender/makesrna/intern/rna_main.c
+++ b/source/blender/makesrna/intern/rna_main.c
@@ -287,6 +287,12 @@ static void rna_Main_linestyle_begin(CollectionPropertyIterator *iter, PointerRN
rna_iterator_listbase_begin(iter, &bmain->linestyle, NULL);
}
+static void rna_Main_cachelibraries_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Main *bmain = (Main *)ptr->data;
+ rna_iterator_listbase_begin(iter, &bmain->cache_library, NULL);
+}
+
static void rna_Main_version_get(PointerRNA *ptr, int *value)
{
Main *bmain = (Main *)ptr->data;
@@ -361,6 +367,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_libraries", "CacheLibrary", "rna_Main_cachelibraries_begin", "Cache Libraries", "Cache Library datablocks", RNA_def_main_cache_libraries},
{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 8b5ed66e217..b9b3bb519ca 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -49,6 +49,8 @@
#ifdef RNA_RUNTIME
#include "BKE_main.h"
+#include "BKE_anim.h"
+#include "BKE_cache_library.h"
#include "BKE_camera.h"
#include "BKE_curve.h"
#include "BKE_DerivedMesh.h"
@@ -83,6 +85,7 @@
#include "BKE_linestyle.h"
#include "DNA_armature_types.h"
+#include "DNA_cache_library_types.h"
#include "DNA_camera_types.h"
#include "DNA_curve_types.h"
#include "DNA_lamp_types.h"
@@ -312,6 +315,61 @@ Mesh *rna_Main_meshes_new_from_object(
return BKE_mesh_new_from_object(bmain, sce, ob, apply_modifiers, settings, calc_tessface, calc_undeformed);
}
+/* copied from Mesh_getFromObject and adapted to RNA interface */
+/* settings: 1 - preview, 2 - render */
+Mesh *rna_Main_meshes_new_from_dupli(
+ Main *bmain, ReportList *reports, Scene *scene, Object *parent, DupliObject *dob,
+ int settings, int calc_tessface, int calc_undeformed)
+{
+ Mesh *mesh = NULL;
+ bool is_cached = parent->cache_library && (parent->cache_library->source_mode == CACHE_LIBRARY_SOURCE_CACHE || parent->cache_library->display_mode == CACHE_LIBRARY_DISPLAY_RESULT);
+
+ switch (dob->ob->type) {
+ case OB_FONT:
+ case OB_CURVE:
+ case OB_SURF:
+ case OB_MBALL:
+ case OB_MESH:
+ break;
+ default:
+ BKE_report(reports, RPT_ERROR, "Object does not have geometry data");
+ return NULL;
+ }
+
+ if (is_cached) {
+ float frame = (float)scene->r.cfra + scene->r.subframe;
+ bool use_render = (settings == 2);
+
+ if (!ELEM(settings, 1, 2))
+ return NULL;
+
+ if (settings == 1 && parent->dup_cache) {
+ DupliObjectData *data;
+
+ /* use dupli cache for realtime dupli data if possible */
+ data = BKE_dupli_cache_find_data(parent->dup_cache, dob->ob);
+ if (data)
+ mesh = BKE_mesh_new_from_dupli_data(bmain, data, calc_tessface, calc_undeformed);
+ }
+ else {
+ DupliObjectData data;
+
+ memset(&data, 0, sizeof(data));
+ if (BKE_cache_read_dupli_object(parent->cache_library, &data, scene, dob->ob, frame, use_render, true))
+ mesh = BKE_mesh_new_from_dupli_data(bmain, &data, calc_tessface, calc_undeformed);
+
+ BKE_dupli_object_data_clear(&data);
+ }
+ }
+
+ /* default, and fallback in case no mesh data was stored in the cache */
+ if (!mesh) {
+ mesh = BKE_mesh_new_from_object(bmain, scene, dob->ob, true, settings, calc_tessface, calc_undeformed);
+ }
+
+ return mesh;
+}
+
static void rna_Main_meshes_remove(Main *bmain, ReportList *reports, PointerRNA *mesh_ptr)
{
Mesh *mesh = mesh_ptr->data;
@@ -744,6 +802,22 @@ static void rna_Main_linestyles_remove(Main *bmain, ReportList *reports, Freesty
/* XXX python now has invalid pointer? */
}
+static CacheLibrary *rna_Main_cachelibraries_new(Main *bmain, const char *name)
+{
+ CacheLibrary *cachelib = BKE_cache_library_add(bmain, name);
+ id_us_min(&cachelib->id);
+ return cachelib;
+}
+
+static void rna_Main_cachelibraries_remove(Main *bmain, ReportList *reports, CacheLibrary *cachelib)
+{
+ if (ID_REAL_USERS(cachelib) <= 0)
+ BKE_libblock_free(bmain, cachelib);
+ else
+ BKE_reportf(reports, RPT_ERROR, "Cache library '%s' must have zero users to be removed, found %d",
+ cachelib->id.name + 2, ID_REAL_USERS(cachelib));
+}
+
/* tag functions, all the same */
static void rna_Main_cameras_tag(Main *bmain, int value) { BKE_main_id_tag_listbase(&bmain->camera, value); }
static void rna_Main_scenes_tag(Main *bmain, int value) { BKE_main_id_tag_listbase(&bmain->scene, value); }
@@ -777,6 +851,7 @@ static void rna_Main_gpencil_tag(Main *bmain, int value) { BKE_main_id_tag_listb
static void rna_Main_movieclips_tag(Main *bmain, int value) { BKE_main_id_tag_listbase(&bmain->movieclip, value); }
static void rna_Main_masks_tag(Main *bmain, int value) { BKE_main_id_tag_listbase(&bmain->mask, value); }
static void rna_Main_linestyle_tag(Main *bmain, int value) { BKE_main_id_tag_listbase(&bmain->linestyle, value); }
+static void rna_Main_cachelibraries_tag(Main *bmain, int value) { BKE_main_id_tag_listbase(&bmain->cache_library, value); }
static int rna_Main_cameras_is_updated_get(PointerRNA *ptr) { return DAG_id_type_tagged(ptr->data, ID_CA) != 0; }
static int rna_Main_scenes_is_updated_get(PointerRNA *ptr) { return DAG_id_type_tagged(ptr->data, ID_SCE) != 0; }
@@ -806,6 +881,7 @@ static int rna_Main_particles_is_updated_get(PointerRNA *ptr) { return DAG_id_ty
static int rna_Main_palettes_is_updated_get(PointerRNA *ptr) { return DAG_id_type_tagged(ptr->data, ID_PAL) != 0; }
static int rna_Main_gpencil_is_updated_get(PointerRNA *ptr) { return DAG_id_type_tagged(ptr->data, ID_GD) != 0; }
static int rna_Main_linestyle_is_updated_get(PointerRNA *ptr) { return DAG_id_type_tagged(ptr->data, ID_LS) != 0; }
+static int rna_Main_cachelibraries_is_updated_get(PointerRNA *ptr) { return DAG_id_type_tagged(ptr->data, ID_CL) != 0; }
#else
@@ -1059,6 +1135,23 @@ void RNA_def_main_meshes(BlenderRNA *brna, PropertyRNA *cprop)
"Mesh created from object, remove it if it is only used for export");
RNA_def_function_return(func, parm);
+ func = RNA_def_function(srna, "new_from_dupli", "rna_Main_meshes_new_from_dupli");
+ RNA_def_function_ui_description(func, "Add a new mesh created from dupli cache data");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func, "scene", "Scene", "", "Scene within which to evaluate modifiers");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
+ parm = RNA_def_pointer(func, "parent", "Object", "", "Duplicator parent of the object");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
+ parm = RNA_def_pointer(func, "dupli_object", "DupliObject", "", "Dupli Object to create mesh from");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
+ parm = RNA_def_enum(func, "settings", mesh_type_items, 0, "", "Modifier settings to apply");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ RNA_def_boolean(func, "calc_tessface", true, "Calculate Tessellation", "Calculate tessellation faces");
+ RNA_def_boolean(func, "calc_undeformed", false, "Calculate Undeformed", "Calculate undeformed vertex coordinates");
+ parm = RNA_def_pointer(func, "mesh", "Mesh", "",
+ "Mesh created from object, remove it if it is only used for export");
+ RNA_def_function_return(func, parm);
+
func = RNA_def_function(srna, "remove", "rna_Main_meshes_remove");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
RNA_def_function_ui_description(func, "Remove a mesh from the current blendfile");
@@ -1906,4 +1999,39 @@ void RNA_def_main_linestyles(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_property_boolean_funcs(prop, "rna_Main_linestyle_is_updated_get", NULL);
}
+void RNA_def_main_cache_libraries(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+ PropertyRNA *prop;
+
+ RNA_def_property_srna(cprop, "BlendDataCacheLibraries");
+ srna = RNA_def_struct(brna, "BlendDataCacheLibraries", NULL);
+ RNA_def_struct_sdna(srna, "Main");
+ RNA_def_struct_ui_text(srna, "Main Cache Libraries", "Collection of cache libraries");
+
+ func = RNA_def_function(srna, "tag", "rna_Main_cachelibraries_tag");
+ parm = RNA_def_boolean(func, "value", 0, "Value", "");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ func = RNA_def_function(srna, "new", "rna_Main_cachelibraries_new");
+ RNA_def_function_ui_description(func, "Add a new cache library to the main database");
+ parm = RNA_def_string(func, "name", "CacheLibrary", 0, "", "New name for the datablock");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ /* return type */
+ parm = RNA_def_pointer(func, "cachelib", "CacheLibrary", "", "New cache library datablock");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_Main_cachelibraries_remove");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func, "Remove a cache library from the current blendfile");
+ parm = RNA_def_pointer(func, "cachelib", "CacheLibrary", "", "Cache Library to remove");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+
+ 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_cachelibraries_is_updated_get", NULL);
+}
+
#endif
diff --git a/source/blender/makesrna/intern/rna_mesh_sample.c b/source/blender/makesrna/intern/rna_mesh_sample.c
new file mode 100644
index 00000000000..81a3f4bf5e4
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_mesh_sample.c
@@ -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.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/makesrna/intern/rna_mesh_sample.c
+ * \ingroup RNA
+ */
+
+#include <stdlib.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_meshdata_types.h"
+
+#include "BLI_utildefines.h"
+
+#include "BKE_mesh_sample.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_types.h"
+
+#include "rna_internal.h"
+
+#include "WM_types.h"
+
+
+#ifdef RNA_RUNTIME
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+
+
+#else
+
+static void rna_def_mesh_sample(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "MeshSample", NULL);
+ RNA_def_struct_sdna(srna, "MSurfaceSample");
+ RNA_def_struct_ui_text(srna, "Mesh Sample", "Point on a mesh that follows deformation");
+
+ prop = RNA_def_property(srna, "vertex_indices", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_sdna(prop, NULL, "orig_verts");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Vertex Indices", "Index of the mesh vertices used for interpolation");
+}
+
+void RNA_def_mesh_sample(BlenderRNA *brna)
+{
+ rna_def_mesh_sample(brna);
+}
+
+#endif
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index 087db2bedcd..394c3a3c519 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -258,6 +258,8 @@ EnumPropertyItem DT_layers_select_dst_items[] = {
#ifdef RNA_RUNTIME
+#include "BLI_listbase.h"
+
#include "DNA_particle_types.h"
#include "DNA_curve_types.h"
#include "DNA_smoke_types.h"
@@ -1065,6 +1067,43 @@ static int rna_CorrectiveSmoothModifier_is_bind_get(PointerRNA *ptr)
return (csmd->bind_coords != NULL);
}
+static int rna_ParticleInstanceModifier_particle_system_poll(PointerRNA *ptr, const PointerRNA value)
+{
+ ParticleInstanceModifierData *psmd = ptr->data;
+ ParticleSystem *psys = value.data;
+
+ if (!psmd->ob)
+ return false;
+
+ /* make sure psys is in the object */
+ return BLI_findindex(&psmd->ob->particlesystem, psys) >= 0;
+}
+
+static PointerRNA rna_ParticleInstanceModifier_particle_system_get(PointerRNA *ptr)
+{
+ ParticleInstanceModifierData *psmd = ptr->data;
+ ParticleSystem *psys;
+ PointerRNA rptr;
+
+ if (!psmd->ob)
+ return PointerRNA_NULL;
+
+ psys = BLI_findlink(&psmd->ob->particlesystem, psmd->psys - 1);
+ RNA_pointer_create((ID *)psmd->ob, &RNA_ParticleSystem, psys, &rptr);
+ return rptr;
+}
+
+static void rna_ParticleInstanceModifier_particle_system_set(PointerRNA *ptr, const PointerRNA value)
+{
+ ParticleInstanceModifierData *psmd = ptr->data;
+
+ if (!psmd->ob)
+ return;
+
+ psmd->psys = BLI_findindex(&psmd->ob->particlesystem, value.data) + 1;
+ CLAMP_MIN(psmd->psys, 1);
+}
+
#else
static PropertyRNA *rna_def_property_subdivision_common(StructRNA *srna, const char type[])
@@ -1685,6 +1724,11 @@ static void rna_def_modifier_armature(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Use Bone Envelopes", "Bind Bone envelopes to armature modifier");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+ prop = RNA_def_property(srna, "use_face_maps", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "deformflag", ARM_DEF_FACEMAPS);
+ RNA_def_property_ui_text(prop, "Use Face Maps", "Create facemap widgets with same name as bones in the armature");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
prop = RNA_def_property(srna, "use_vertex_groups", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "deformflag", ARM_DEF_VGROUP);
RNA_def_property_ui_text(prop, "Use Vertex Groups", "Bind vertex groups to armature modifier");
@@ -2467,6 +2511,12 @@ static void rna_def_modifier_particleinstance(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL}
};
+ static EnumPropertyItem particleinstance_space[] = {
+ {eParticleInstanceSpace_Local, "LOCAL", 0, "Local", "Use offset from the particle object in the instance object"},
+ {eParticleInstanceSpace_World, "WORLD", 0, "World", "Use world space offset in the instance object"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
srna = RNA_def_struct(brna, "ParticleInstanceModifier", "Modifier");
RNA_def_struct_ui_text(srna, "ParticleInstance Modifier", "Particle system instancing modifier");
RNA_def_struct_sdna(srna, "ParticleInstanceModifierData");
@@ -2481,16 +2531,30 @@ static void rna_def_modifier_particleinstance(BlenderRNA *brna)
prop = RNA_def_property(srna, "particle_system_index", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "psys");
- RNA_def_property_range(prop, 1, 10);
+ RNA_def_property_range(prop, 1, SHRT_MAX);
RNA_def_property_ui_text(prop, "Particle System Number", "");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+ prop = RNA_def_property(srna, "particle_system", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ParticleSystem");
+ RNA_def_property_pointer_funcs(prop, "rna_ParticleInstanceModifier_particle_system_get", "rna_ParticleInstanceModifier_particle_system_set",
+ NULL, "rna_ParticleInstanceModifier_particle_system_poll");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Particle System", "");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
prop = RNA_def_property(srna, "axis", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "axis");
RNA_def_property_enum_items(prop, particleinstance_axis);
RNA_def_property_ui_text(prop, "Axis", "Pole axis for rotation");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
-
+
+ prop = RNA_def_property(srna, "space", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "space");
+ RNA_def_property_enum_items(prop, particleinstance_space);
+ RNA_def_property_ui_text(prop, "Space", "Space to use for copying mesh data");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
prop = RNA_def_property(srna, "use_normal", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", eParticleInstanceFlag_Parents);
RNA_def_property_ui_text(prop, "Normal", "Create instances from normal particles");
@@ -2542,6 +2606,40 @@ static void rna_def_modifier_particleinstance(BlenderRNA *brna)
RNA_def_property_range(prop, 0.0, 1.0);
RNA_def_property_ui_text(prop, "Random Position", "Randomize position along path");
RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "rotation", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "rotation");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Rotation", "Rotation around path");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "random_rotation", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "random_rotation");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Random Rotation", "Randomize rotation around path");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "particle_amount", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Particle Amount", "Amount of particles to use for instancing");
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "particle_offset", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_text(prop, "Particle Offset", "Relative offset of particles to use for instancing, to avoid overlap of multiple instances");
+ RNA_def_property_float_default(prop, 0.0f);
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "index_layer_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "index_layer_name");
+ RNA_def_property_ui_text(prop, "Index Layer Name", "Custom data layer name for the index");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_property(srna, "value_layer_name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "value_layer_name");
+ RNA_def_property_ui_text(prop, "Value Layer Name", "Custom data layer name for the randomized value");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
}
static void rna_def_modifier_explode(BlenderRNA *brna)
@@ -2620,7 +2718,7 @@ static void rna_def_modifier_cloth(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "ClothSolverResult");
RNA_def_property_pointer_sdna(prop, NULL, "solver_result");
RNA_def_property_ui_text(prop, "Solver Result", "");
-
+
prop = RNA_def_property(srna, "point_cache", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_ui_text(prop, "Point Cache", "");
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 4df954b062b..cbdb0d2fcae 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -38,6 +38,8 @@
#include "DNA_mesh_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+#include "DNA_scene_types.h"
#include "DNA_text_types.h"
#include "DNA_texture_types.h"
@@ -61,6 +63,8 @@
#include "MEM_guardedalloc.h"
+#include "RE_render_ext.h"
+
EnumPropertyItem node_socket_in_out_items[] = {
{ SOCK_IN, "IN", 0, "Input", "" },
{ SOCK_OUT, "OUT", 0, "Output", "" },
@@ -2966,6 +2970,101 @@ static void rna_CompositorNodeScale_update(Main *bmain, Scene *scene, PointerRNA
rna_Node_update(bmain, scene, ptr);
}
+static PointerRNA rna_ShaderNodePointDensity_psys_get(PointerRNA *ptr)
+{
+ bNode *node = ptr->data;
+ NodeShaderTexPointDensity *shader_point_density = node->storage;
+ Object *ob = (Object*)node->id;
+ ParticleSystem *psys = NULL;
+ PointerRNA value;
+
+ if (ob && shader_point_density->particle_system) {
+ psys = BLI_findlink(&ob->particlesystem, shader_point_density->particle_system - 1);
+ }
+
+ RNA_pointer_create(&ob->id, &RNA_ParticleSystem, psys, &value);
+ return value;
+}
+
+static void rna_ShaderNodePointDensity_psys_set(PointerRNA *ptr, PointerRNA value)
+{
+ bNode *node = ptr->data;
+ NodeShaderTexPointDensity *shader_point_density = node->storage;
+ Object *ob = (Object*)node->id;
+
+ if (ob && value.id.data == ob) {
+ shader_point_density->particle_system = BLI_findindex(&ob->particlesystem, value.data) + 1;
+ }
+ else {
+ shader_point_density->particle_system = 0;
+ }
+}
+
+static int point_density_color_source_from_shader(NodeShaderTexPointDensity *shader_point_density)
+{
+ switch (shader_point_density->color_source) {
+ case SHD_POINTDENSITY_COLOR_CONSTANT: return TEX_PD_COLOR_CONSTANT;
+ case SHD_POINTDENSITY_COLOR_PARTAGE: return TEX_PD_COLOR_PARTAGE;
+ case SHD_POINTDENSITY_COLOR_PARTSPEED: return TEX_PD_COLOR_PARTSPEED;
+ case SHD_POINTDENSITY_COLOR_PARTVEL: return TEX_PD_COLOR_PARTVEL;
+ case SHD_POINTDENSITY_COLOR_PARTTEX: return TEX_PD_COLOR_PARTTEX;
+ default: BLI_assert(false); return TEX_PD_COLOR_CONSTANT;
+ }
+}
+
+/* TODO(sergey): This functio nassumes allocated array was passed,
+ * works fine with Cycles via C++ RNA, but fails with call from python.
+ */
+void rna_ShaderNodePointDensity_density_calc(bNode *self, Scene *scene, int *length, float **values)
+{
+ NodeShaderTexPointDensity *shader_point_density = self->storage;
+ PointDensity pd;
+
+ *length = 4 * shader_point_density->resolution *
+ shader_point_density->resolution *
+ shader_point_density->resolution;
+
+ if (*values == NULL) {
+ *values = MEM_mallocN(sizeof(float) * (*length), "point density dynamic array");
+ }
+
+ /* Create PointDensity structure from node for sampling. */
+ BKE_texture_pointdensity_init_data(&pd);
+ pd.object = (Object *)self->id;
+ pd.radius = shader_point_density->radius;
+ if (shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_PSYS) {
+ pd.source = TEX_PD_PSYS;
+ pd.psys = shader_point_density->particle_system;
+ pd.psys_cache_space = TEX_PD_OBJECTSPACE;
+ }
+ else {
+ BLI_assert(shader_point_density->point_source == SHD_POINTDENSITY_SOURCE_OBJECT);
+ pd.source = TEX_PD_OBJECT;
+ pd.ob_cache_space = TEX_PD_OBJECTSPACE;
+ }
+ pd.color_source = point_density_color_source_from_shader(shader_point_density);
+
+ /* Single-threaded sampling of the voxel domain. */
+ RE_sample_point_density(scene, &pd,
+ shader_point_density->resolution,
+ *values);
+
+ /* We're done, time to clean up. */
+ BKE_texture_pointdensity_free_data(&pd);
+}
+
+static void rna_ShaderNodeOpenVDB_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ bNodeTree *ntree = (bNodeTree *)ptr->id.data;
+ bNode *node = (bNode *)ptr->data;
+
+ ntreeUpdateOpenVDBNode(bmain, ntree, node);
+ ntreeUpdateTree(bmain, ntree);
+ WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
+
+ UNUSED_VARS(scene);
+}
+
#else
static EnumPropertyItem prop_image_layer_items[] = {
@@ -3792,6 +3891,105 @@ static void def_sh_tex_wireframe(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_sh_tex_pointdensity(StructRNA *srna)
+{
+ PropertyRNA *prop;
+ FunctionRNA *func;
+
+ static EnumPropertyItem point_source_items[] = {
+ {SHD_POINTDENSITY_SOURCE_PSYS, "PARTICLE_SYSTEM", 0, "Particle System",
+ "Generate point density from a particle system"},
+ {SHD_POINTDENSITY_SOURCE_OBJECT, "OBJECT", 0, "Object Vertices",
+ "Generate point density from an object's vertices"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static const EnumPropertyItem prop_interpolation_items[] = {
+ {SHD_INTERP_CLOSEST, "Closest", 0, "Closest",
+ "No interpolation (sample closest texel)"},
+ {SHD_INTERP_LINEAR, "Linear", 0, "Linear",
+ "Linear interpolation"},
+ {SHD_INTERP_CUBIC, "Cubic", 0, "Cubic",
+ "Cubic interpolation (CPU only)"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem space_items[] = {
+ {SHD_POINTDENSITY_SPACE_OBJECT, "OBJECT", 0, "Object Space", ""},
+ {SHD_POINTDENSITY_SPACE_WORLD, "WORLD", 0, "World Space", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem color_source_items[] = {
+ {SHD_POINTDENSITY_COLOR_CONSTANT, "CONSTANT", 0, "Constant", ""},
+ {SHD_POINTDENSITY_COLOR_PARTAGE, "PARTICLE_AGE", 0, "Particle Age",
+ "Lifetime mapped as 0.0 - 1.0 intensity"},
+ {SHD_POINTDENSITY_COLOR_PARTSPEED, "PARTICLE_SPEED", 0, "Particle Speed",
+ "Particle speed (absolute magnitude of velocity) mapped as 0.0-1.0 intensity"},
+ {SHD_POINTDENSITY_COLOR_PARTVEL, "PARTICLE_VELOCITY", 0, "Particle Velocity",
+ "XYZ velocity mapped to RGB colors"},
+ {SHD_POINTDENSITY_COLOR_PARTTEX, "PARTICLE_TEXTURE", 0, "Particle Texture", "Texture color of particles"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "id");
+ RNA_def_property_struct_type(prop, "Object");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Object", "Object to take point data from)");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ RNA_def_struct_sdna_from(srna, "NodeShaderTexPointDensity", "storage");
+
+ prop = RNA_def_property(srna, "point_source", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, point_source_items);
+ RNA_def_property_ui_text(prop, "Point Source", "Point data to use as renderable point density");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "particle_system", PROP_POINTER, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Particle System", "Particle System to render as points");
+ RNA_def_property_struct_type(prop, "ParticleSystem");
+ RNA_def_property_pointer_funcs(prop, "rna_ShaderNodePointDensity_psys_get",
+ "rna_ShaderNodePointDensity_psys_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "resolution", PROP_INT, PROP_NONE);
+ RNA_def_property_range(prop, 1, 32768);
+ RNA_def_property_ui_text(prop, "Resolution", "Resolution used by the texture holding the point density");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "radius", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "radius");
+ RNA_def_property_range(prop, 0.001, FLT_MAX);
+ RNA_def_property_ui_text(prop, "Radius", "Radius from the shaded sample to look for points within");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "space", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, space_items);
+ RNA_def_property_ui_text(prop, "Space", "Coordinate system to calculate voxels in");
+ RNA_def_property_update(prop, 0, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "interpolation", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, prop_interpolation_items);
+ RNA_def_property_ui_text(prop, "Interpolation", "Texture interpolation");
+ RNA_def_property_update(prop, 0, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "color_source", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "color_source");
+ RNA_def_property_enum_items(prop, color_source_items);
+ RNA_def_property_ui_text(prop, "Color Source", "Data to derive color results from");
+ RNA_def_property_update(prop, 0, "rna_Node_update");
+
+ func = RNA_def_function(srna, "calc_point_density", "rna_ShaderNodePointDensity_density_calc");
+ RNA_def_function_ui_description(func, "Calculate point density");
+ RNA_def_pointer(func, "scene", "Scene", "", "");
+ /* TODO, See how array size of 0 works, this shouldnt be used. */
+ prop = RNA_def_float_array(func, "rgba_values", 1, NULL, 0, 0, "", "RGBA Values", 0, 0);
+ RNA_def_property_flag(prop, PROP_DYNAMIC);
+ RNA_def_function_output(func, prop);
+}
+
static void def_glossy(StructRNA *srna)
{
PropertyRNA *prop;
@@ -3875,6 +4073,43 @@ static void def_sh_uvmap(StructRNA *srna)
RNA_def_struct_sdna_from(srna, "bNode", NULL);
}
+static void def_sh_openvdb(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem prop_openvdb_sampling[] = {
+ {NODE_VDB_SAMPLE_POINT, "POINT", 0, "Point",
+ "Nearest neighbor interpolation type"},
+ {NODE_VDB_SAMPLE_BOX, "Box", 0, "Box",
+ "Trilinear interpolation type"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static const EnumPropertyItem prop_openvdb_source[] = {
+ {NODE_VDB_SRC_FILE, "FILE", 0, "Single File", "Single vdb file"},
+ {NODE_VDB_SRC_SEQ, "SEQUENCE", 0, "File Sequence", "Multiple vdb files, as a sequence"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ RNA_def_struct_sdna_from(srna, "NodeShaderOpenVDB", "storage");
+
+ prop = RNA_def_property(srna, "filename", PROP_STRING, PROP_FILEPATH);
+ RNA_def_property_ui_text(prop, "File Path", "Path to the file to use");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNodeOpenVDB_update");
+
+ prop = RNA_def_property(srna, "sampling", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, prop_openvdb_sampling);
+ RNA_def_property_ui_text(prop, "Sampling", "Grid interpolation");
+ RNA_def_property_update(prop, 0, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "source", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, prop_openvdb_source);
+ RNA_def_property_ui_text(prop, "Source", "File Source");
+ RNA_def_property_update(prop, 0, "rna_Node_update");
+
+ RNA_def_struct_sdna_from(srna, "bNode", NULL);
+}
+
static void def_sh_uvalongstroke(StructRNA *srna)
{
PropertyRNA *prop;
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index f433c2c36a8..cc60d59ae95 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -42,6 +42,7 @@
#include "BLI_utildefines.h"
#include "BLI_listbase.h"
+#include "BKE_facemap.h"
#include "BKE_camera.h"
#include "BKE_paint.h"
#include "BKE_editmesh.h"
@@ -69,6 +70,7 @@ EnumPropertyItem object_mode_items[] = {
{OB_MODE_WEIGHT_PAINT, "WEIGHT_PAINT", ICON_WPAINT_HLT, "Weight Paint", ""},
{OB_MODE_TEXTURE_PAINT, "TEXTURE_PAINT", ICON_TPAINT_HLT, "Texture Paint", ""},
{OB_MODE_PARTICLE_EDIT, "PARTICLE_EDIT", ICON_PARTICLEMODE, "Particle Edit", ""},
+ {OB_MODE_HAIR_EDIT, "HAIR_EDIT", ICON_PARTICLEMODE, "Hair Edit", ""},
{0, NULL, 0, NULL, NULL}
};
@@ -179,12 +181,15 @@ EnumPropertyItem object_axis_unsigned_items[] = {
#include "BLI_math.h"
#include "DNA_key_types.h"
+#include "DNA_cache_library_types.h"
#include "DNA_constraint_types.h"
#include "DNA_lattice_types.h"
#include "DNA_node_types.h"
+#include "BKE_anim.h"
#include "BKE_armature.h"
#include "BKE_bullet.h"
+#include "BKE_cache_library.h"
#include "BKE_constraint.h"
#include "BKE_context.h"
#include "BKE_curve.h"
@@ -197,6 +202,7 @@ EnumPropertyItem object_axis_unsigned_items[] = {
#include "BKE_mesh.h"
#include "BKE_particle.h"
#include "BKE_scene.h"
+#include "BKE_strands.h"
#include "BKE_deform.h"
#include "ED_object.h"
@@ -622,6 +628,87 @@ void rna_object_vgroup_name_set(PointerRNA *ptr, const char *value, char *result
result[0] = '\0';
}
+static void rna_FaceMap_name_set(PointerRNA *ptr, const char *value)
+{
+ Object *ob = (Object *)ptr->id.data;
+ bFaceMap *fmap = (bFaceMap *)ptr->data;
+ BLI_strncpy_utf8(fmap->name, value, sizeof(fmap->name));
+ fmap_unique_name(fmap, ob);
+}
+
+static int rna_FaceMap_index_get(PointerRNA *ptr)
+{
+ Object *ob = (Object *)ptr->id.data;
+
+ return BLI_findindex(&ob->fmaps, ptr->data);
+}
+
+static PointerRNA rna_Object_active_face_map_get(PointerRNA *ptr)
+{
+ Object *ob = (Object *)ptr->id.data;
+ return rna_pointer_inherit_refine(ptr, &RNA_FaceMap, BLI_findlink(&ob->fmaps, ob->actfmap - 1));
+}
+
+static int rna_Object_active_face_map_index_get(PointerRNA *ptr)
+{
+ Object *ob = (Object *)ptr->id.data;
+ return ob->actfmap - 1;
+}
+
+static void rna_Object_active_face_map_index_set(PointerRNA *ptr, int value)
+{
+ Object *ob = (Object *)ptr->id.data;
+ ob->actfmap = value + 1;
+}
+
+static void rna_Object_active_face_map_index_range(PointerRNA *ptr, int *min, int *max,
+ int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ Object *ob = (Object *)ptr->id.data;
+
+ *min = 0;
+ *max = max_ii(0, BLI_listbase_count(&ob->fmaps) - 1);
+}
+
+void rna_object_fmap_name_index_get(PointerRNA *ptr, char *value, int index)
+{
+ Object *ob = (Object *)ptr->id.data;
+ bFaceMap *fmap;
+
+ fmap = BLI_findlink(&ob->fmaps, index - 1);
+
+ if (fmap) BLI_strncpy(value, fmap->name, sizeof(fmap->name));
+ else value[0] = '\0';
+}
+
+int rna_object_fmap_name_index_length(PointerRNA *ptr, int index)
+{
+ Object *ob = (Object *)ptr->id.data;
+ bFaceMap *fmap;
+
+ fmap = BLI_findlink(&ob->fmaps, index - 1);
+ return (fmap) ? strlen(fmap->name) : 0;
+}
+
+void rna_object_fmap_name_index_set(PointerRNA *ptr, const char *value, short *index)
+{
+ Object *ob = (Object *)ptr->id.data;
+ *index = fmap_name_index(ob, value) + 1;
+}
+
+void rna_object_fmap_name_set(PointerRNA *ptr, const char *value, char *result, int maxlen)
+{
+ Object *ob = (Object *)ptr->id.data;
+ bFaceMap *fmap = fmap_find_name(ob, value);
+ if (fmap) {
+ BLI_strncpy(result, value, maxlen); /* no need for BLI_strncpy_utf8, since this matches an existing group */
+ return;
+ }
+
+ result[0] = '\0';
+}
+
+
void rna_object_uvlayer_name_set(PointerRNA *ptr, const char *value, char *result, int maxlen)
{
Object *ob = (Object *)ptr->id.data;
@@ -1457,6 +1544,69 @@ static float rna_VertexGroup_weight(ID *id, bDeformGroup *dg, ReportList *report
return weight;
}
+static bFaceMap *rna_Object_fmap_new(Object *ob, const char *name)
+{
+ bFaceMap *fmap = BKE_object_facemap_add_name(ob, name);
+
+ WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob);
+
+ return fmap;
+}
+
+static void rna_Object_fmap_remove(Object *ob, ReportList *reports, PointerRNA *fmap_ptr)
+{
+ bFaceMap *fmap = fmap_ptr->data;
+ if (BLI_findindex(&ob->fmaps, fmap) == -1) {
+ BKE_reportf(reports, RPT_ERROR, "FaceMap '%s' not in object '%s'", fmap->name, ob->id.name + 2);
+ return;
+ }
+
+ BKE_object_facemap_remove(ob, fmap);
+ RNA_POINTER_INVALIDATE(fmap_ptr);
+
+ WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob);
+}
+
+
+static void rna_Object_fmap_clear(Object *ob)
+{
+ BKE_object_fmap_remove_all(ob);
+
+ WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob);
+}
+
+
+static void rna_FaceMap_face_add(ID *id, bFaceMap *fmap, ReportList *reports, int index_len,
+ int *index)
+{
+ Object *ob = (Object *)id;
+
+ if (BKE_object_is_in_editmode(ob)) {
+ BKE_report(reports, RPT_ERROR, "FaceMap.add(): cannot be called while object is in edit mode");
+ return;
+ }
+
+ while (index_len--)
+ ED_fmap_face_add(ob, fmap, *index++);
+
+ WM_main_add_notifier(NC_GEOM | ND_DATA, (ID *)ob->data);
+}
+
+static void rna_FaceMap_face_remove(ID *id, bFaceMap *fmap, ReportList *reports, int index_len, int *index)
+{
+ Object *ob = (Object *)id;
+
+ if (BKE_object_is_in_editmode(ob)) {
+ BKE_report(reports, RPT_ERROR, "FaceMap.add(): cannot be called while object is in edit mode");
+ return;
+ }
+
+ while (index_len--)
+ ED_fmap_face_remove(ob, fmap, *index++);
+
+ WM_main_add_notifier(NC_GEOM | ND_DATA, (ID *)ob->data);
+}
+
/* generic poll functions */
int rna_Lattice_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value)
{
@@ -1510,6 +1660,120 @@ static void rna_Object_lod_distance_update(Main *UNUSED(bmain), Scene *UNUSED(sc
(void)ob;
#endif
}
+
+/* settings: 1 - preview, 2 - render */
+Strands *rna_DupliObject_strands_new(DupliObject *dob, ReportList *UNUSED(reports), Scene *scene, Object *parent, ParticleSystem *psys, int settings)
+{
+ Strands *strands = NULL;
+ bool is_cached = parent->cache_library && (parent->cache_library->source_mode == CACHE_LIBRARY_SOURCE_CACHE || parent->cache_library->display_mode == CACHE_LIBRARY_DISPLAY_RESULT);
+
+ if (is_cached) {
+ float frame = (float)scene->r.cfra + scene->r.subframe;
+ bool use_render = (settings == 2);
+
+ if (!ELEM(settings, 1, 2))
+ return NULL;
+
+ if (!use_render && parent->dup_cache) {
+ DupliObjectData *data;
+
+ /* use dupli cache for realtime dupli data if possible */
+ data = BKE_dupli_cache_find_data(parent->dup_cache, dob->ob);
+ if (data) {
+ /* TODO(sergey): Consider sharing the data between viewport and
+ * render engine.
+ */
+ BKE_dupli_object_data_find_strands(data, psys->name, &strands, NULL);
+ if (strands) {
+ strands = BKE_strands_copy(strands);
+ }
+ }
+ }
+ else {
+ DupliObjectData data;
+
+ memset(&data, 0, sizeof(data));
+ if (BKE_cache_read_dupli_object(parent->cache_library, &data, scene, dob->ob, frame, use_render, true)) {
+ BKE_dupli_object_data_find_strands(&data, psys->name, &strands, NULL);
+ if (strands)
+ BKE_dupli_object_data_acquire_strands(&data, strands);
+ }
+
+ BKE_dupli_object_data_clear(&data);
+ }
+ }
+
+ return strands;
+}
+
+static void rna_DupliObject_strands_free(DupliObject *UNUSED(dob), ReportList *UNUSED(reports), PointerRNA *strands_ptr)
+{
+ Strands *strands = strands_ptr->data;
+ if (strands)
+ BKE_strands_free(strands);
+}
+
+/* settings: 1 - preview, 2 - render */
+StrandsChildren *rna_DupliObject_strands_children_new(DupliObject *dob, ReportList *UNUSED(reports), Scene *scene, Object *parent, ParticleSystem *psys, int settings)
+{
+ StrandsChildren *strands = NULL;
+ bool is_cached = parent->cache_library && (parent->cache_library->source_mode == CACHE_LIBRARY_SOURCE_CACHE || parent->cache_library->display_mode == CACHE_LIBRARY_DISPLAY_RESULT);
+
+ if (is_cached) {
+ float frame = (float)scene->r.cfra + scene->r.subframe;
+ bool use_render = (settings == 2);
+
+ if (!ELEM(settings, 1, 2))
+ return NULL;
+
+ if (!use_render && parent->dup_cache) {
+ DupliObjectData *data;
+
+ /* use dupli cache for realtime dupli data if possible */
+ data = BKE_dupli_cache_find_data(parent->dup_cache, dob->ob);
+ if (data) {
+ /* TODO(sergey): Consider sharing the data between viewport and
+ * render engine.
+ */
+ BKE_dupli_object_data_find_strands(data, psys->name, NULL, &strands);
+ if (strands) {
+ strands = BKE_strands_children_copy(strands);
+ }
+ }
+ }
+ else {
+ DupliObjectData data;
+
+ memset(&data, 0, sizeof(data));
+ if (BKE_cache_read_dupli_object(parent->cache_library, &data, scene, dob->ob, frame, use_render, true)) {
+ Strands *parents;
+ BKE_dupli_object_data_find_strands(&data, psys->name, &parents, &strands);
+ if (strands) {
+ BKE_dupli_object_data_acquire_strands_children(&data, strands);
+
+ /* Deform child strands to follow parent motion.
+ * Note that this is an optional feature for viewport/render display,
+ * strand motion is not applied to raw child data in caches.
+ */
+ if (parents)
+ BKE_strands_children_deform(strands, parents, true);
+ }
+ }
+
+ BKE_dupli_object_data_clear(&data);
+ }
+ }
+
+ return strands;
+}
+
+static void rna_DupliObject_strands_children_free(DupliObject *UNUSED(dob), ReportList *UNUSED(reports), PointerRNA *strands_ptr)
+{
+ StrandsChildren *strands = strands_ptr->data;
+ if (strands)
+ BKE_strands_children_free(strands);
+}
+
#else
static void rna_def_vertex_group(BlenderRNA *brna)
@@ -1575,6 +1839,44 @@ static void rna_def_vertex_group(BlenderRNA *brna)
RNA_def_function_return(func, prop);
}
+static void rna_def_face_map(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+ FunctionRNA *func;
+
+ srna = RNA_def_struct(brna, "FaceMap", NULL);
+ RNA_def_struct_sdna(srna, "bFaceMap");
+ RNA_def_struct_ui_text(srna, "Face Map", "Group of faces, each face can only be part of one map");
+ RNA_def_struct_ui_icon(srna, ICON_MOD_TRIANGULATE);
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_ui_text(prop, "Name", "Face map name");
+ RNA_def_struct_name_property(srna, prop);
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_FaceMap_name_set");
+ /* update data because modifiers may use [#24761] */
+ RNA_def_property_update(prop, NC_GEOM | ND_DATA | NA_RENAME, "rna_Object_internal_update_data");
+
+ prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_int_funcs(prop, "rna_FaceMap_index_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "Index", "Index number of the face map");
+
+ func = RNA_def_function(srna, "add", "rna_FaceMap_face_add");
+ RNA_def_function_ui_description(func, "Add vertices to the group");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_SELF_ID);
+ /* TODO, see how array size of 0 works, this shouldnt be used */
+ prop = RNA_def_int_array(func, "index", 1, NULL, 0, 0, "", "Index List", 0, 0);
+ RNA_def_property_flag(prop, PROP_DYNAMIC | PROP_REQUIRED);
+
+ func = RNA_def_function(srna, "remove", "rna_FaceMap_face_remove");
+ RNA_def_function_ui_description(func, "Remove a vertex from the group");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_SELF_ID);
+ /* TODO, see how array size of 0 works, this shouldnt be used */
+ prop = RNA_def_int_array(func, "index", 1, NULL, 0, 0, "", "Index List", 0, 0);
+ RNA_def_property_flag(prop, PROP_DYNAMIC | PROP_REQUIRED);
+}
+
static void rna_def_material_slot(BlenderRNA *brna)
{
StructRNA *srna;
@@ -2090,6 +2392,54 @@ static void rna_def_object_vertex_groups(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_function_ui_description(func, "Delete all vertex groups from object");
}
+/* object.vertex_groups */
+static void rna_def_object_face_maps(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+
+ PropertyRNA *prop;
+
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "FaceMaps");
+ srna = RNA_def_struct(brna, "FaceMaps", NULL);
+ RNA_def_struct_sdna(srna, "Object");
+ RNA_def_struct_ui_text(srna, "Face Maps", "Collection of face maps");
+
+ prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "FaceMap");
+ RNA_def_property_pointer_funcs(prop, "rna_Object_active_face_map_get",
+ "rna_Object_active_face_map_set", NULL, NULL);
+ RNA_def_property_ui_text(prop, "Active Face Map", "Face maps of the object");
+ RNA_def_property_update(prop, NC_GEOM | ND_DATA, "rna_Object_internal_update_data");
+
+ prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_int_sdna(prop, NULL, "actfmap");
+ RNA_def_property_int_funcs(prop, "rna_Object_active_face_map_index_get",
+ "rna_Object_active_face_map_index_set",
+ "rna_Object_active_face_map_index_range");
+ RNA_def_property_ui_text(prop, "Active Face Map Index", "Active index in face map array");
+ RNA_def_property_update(prop, NC_GEOM | ND_DATA, "rna_Object_internal_update_data");
+
+ /* face maps */ /* add_face_map */
+ func = RNA_def_function(srna, "new", "rna_Object_fmap_new");
+ RNA_def_function_ui_description(func, "Add face map to object");
+ RNA_def_string(func, "name", "Map", 0, "", "face map name"); /* optional */
+ parm = RNA_def_pointer(func, "fmap", "FaceMap", "", "New face map");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_Object_fmap_remove");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func, "Delete vertex group from object");
+ parm = RNA_def_pointer(func, "group", "FaceMap", "", "Face map to remove");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
+ RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
+
+ func = RNA_def_function(srna, "clear", "rna_Object_fmap_clear");
+ RNA_def_function_ui_description(func, "Delete all vertex groups from object");
+}
static void rna_def_object_lodlevel(BlenderRNA *brna)
{
@@ -2510,6 +2860,14 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Vertex Groups", "Vertex groups of the object");
rna_def_object_vertex_groups(brna, prop);
+
+ /* vertex groups */
+ prop = RNA_def_property(srna, "face_maps", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "fmaps", NULL);
+ RNA_def_property_struct_type(prop, "FaceMap");
+ RNA_def_property_ui_text(prop, "Face Maps", "Maps of faces of the object");
+ rna_def_object_face_maps(brna, prop);
+
/* empty */
prop = RNA_def_property(srna, "empty_draw_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "empty_drawtype");
@@ -2670,6 +3028,12 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Dupli Group", "Instance an existing group");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_dependency_update");
+ prop = RNA_def_property(srna, "cache_library", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "cache_library");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Cache Library", "Cache Library to use");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Object_dependency_update");
+
prop = RNA_def_property(srna, "dupli_frames_start", PROP_INT, PROP_NONE | PROP_UNIT_TIME);
RNA_def_property_int_sdna(prop, NULL, "dupsta");
RNA_def_property_range(prop, MINAFRAME, MAXFRAME);
@@ -2748,6 +3112,11 @@ static void rna_def_object(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Draw All Edges", "Display all edges for mesh objects");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+ prop = RNA_def_property(srna, "show_wire_color", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "dtx", OB_DRAW_WIRECOLOR);
+ RNA_def_property_ui_text(prop, "Draw Wire Color", "Use custom wire color");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
prop = RNA_def_property(srna, "show_transparent", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "dtx", OB_DRAWTRANSP);
RNA_def_property_ui_text(prop, "Draw Transparent",
@@ -2826,6 +3195,14 @@ static void rna_def_dupli_object(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ static EnumPropertyItem strand_settings_items[] = {
+ {1, "PREVIEW", 0, "Preview", "Apply preview settings"},
+ {2, "RENDER", 0, "Render", "Apply render settings"},
+ {0, NULL, 0, NULL, NULL}
+ };
srna = RNA_def_struct(brna, "DupliObject", NULL);
RNA_def_struct_sdna(srna, "DupliObject");
@@ -2874,6 +3251,60 @@ static void rna_def_dupli_object(BlenderRNA *brna)
RNA_def_property_enum_items(prop, dupli_items);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Dupli Type", "Duplicator type that generated this dupli object");
+
+ func = RNA_def_function(srna, "strands_new", "rna_DupliObject_strands_new");
+ RNA_def_function_ui_description(func, "Add new strands created from dupli cache data");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func, "scene", "Scene", "", "Scene within which to evaluate modifiers");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
+ parm = RNA_def_pointer(func, "parent", "Object", "", "Duplicator parent of the object");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
+ parm = RNA_def_pointer(func, "particle_system", "ParticleSystem", "", "Particle System");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
+ parm = RNA_def_enum(func, "settings", strand_settings_items, 0, "", "Modifier settings to apply");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm = RNA_def_pointer(func, "strands", "Strands", "",
+ "Strands created from object, remove it if it is only used for export");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "strands_free", "rna_DupliObject_strands_free");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func, "Free strands data");
+ parm = RNA_def_pointer(func, "strands", "Strands", "", "Strands to remove");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
+ RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
+
+ func = RNA_def_function(srna, "strands_children_new", "rna_DupliObject_strands_children_new");
+ RNA_def_function_ui_description(func, "Add new strands created from dupli cache data");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ parm = RNA_def_pointer(func, "scene", "Scene", "", "Scene within which to evaluate modifiers");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
+ parm = RNA_def_pointer(func, "parent", "Object", "", "Duplicator parent of the object");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
+ parm = RNA_def_pointer(func, "particle_system", "ParticleSystem", "", "Particle System");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
+ parm = RNA_def_enum(func, "settings", strand_settings_items, 0, "", "Modifier settings to apply");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm = RNA_def_pointer(func, "strands", "StrandsChildren", "",
+ "Strands created from object, remove it if it is only used for export");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "strands_children_free", "rna_DupliObject_strands_children_free");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func, "Free strands data");
+ parm = RNA_def_pointer(func, "strands", "StrandsChildren", "", "Strands to remove");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
+ RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
+}
+
+static void rna_def_dupli_object_data(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ /*PropertyRNA *prop;*/
+
+ srna = RNA_def_struct(brna, "DupliObjectData", NULL);
+ RNA_def_struct_sdna(srna, "DupliObjectData");
+ RNA_def_struct_ui_text(srna, "Object Duplicate Data", "Override of object data for duplis");
}
static void rna_def_object_base(BlenderRNA *brna)
@@ -2920,8 +3351,10 @@ void RNA_def_object(BlenderRNA *brna)
rna_def_object_game_settings(brna);
rna_def_object_base(brna);
rna_def_vertex_group(brna);
+ rna_def_face_map(brna);
rna_def_material_slot(brna);
rna_def_dupli_object(brna);
+ rna_def_dupli_object_data(brna);
RNA_define_animate_sdna(true);
rna_def_object_lodlevel(brna);
}
diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c
index 55f559c0222..541971ea748 100644
--- a/source/blender/makesrna/intern/rna_object_api.c
+++ b/source/blender/makesrna/intern/rna_object_api.c
@@ -224,6 +224,18 @@ static void rna_Object_free_duplilist(Object *ob)
}
}
+static PointerRNA rna_Object_find_dupli_cache(Object *ob, Object *dupob)
+{
+ DupliObjectData *data = NULL;
+ PointerRNA ptr;
+
+ if (ob->dup_cache)
+ data = BKE_dupli_cache_find_data(ob->dup_cache, dupob);
+
+ RNA_pointer_create((ID *)ob, &RNA_DupliObjectData, data, &ptr);
+ return ptr;
+}
+
static PointerRNA rna_Object_shape_key_add(Object *ob, bContext *C, ReportList *reports,
const char *name, int from_mix)
{
@@ -466,6 +478,12 @@ static int rna_Object_update_from_editmode(Object *ob)
}
return false;
}
+
+static void rna_Object_cache_release(Object *object, int free_smoke_sim)
+{
+ BKE_object_free_caches(object, free_smoke_sim != 0);
+}
+
#else /* RNA_RUNTIME */
void RNA_api_object(StructRNA *srna)
@@ -572,6 +590,14 @@ void RNA_api_object(StructRNA *srna)
func = RNA_def_function(srna, "dupli_list_clear", "rna_Object_free_duplilist");
RNA_def_function_ui_description(func, "Free the list of dupli objects");
+ func = RNA_def_function(srna, "find_dupli_cache", "rna_Object_find_dupli_cache");
+ RNA_def_function_ui_description(func, "Find cached data for a dupli object");
+ parm = RNA_def_pointer(func, "object", "Object", "", "Object data to look up");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
+ parm = RNA_def_pointer(func, "data", "DupliObjectData", "", "Cached object data");
+ RNA_def_property_flag(parm, PROP_RNAPTR);
+ RNA_def_function_return(func, parm);
+
/* Armature */
func = RNA_def_function(srna, "find_armature", "modifiers_isDeformedByArmature");
RNA_def_function_ui_description(func, "Find armature influencing this object as a parent or via a modifier");
@@ -688,7 +714,8 @@ void RNA_api_object(StructRNA *srna)
parm = RNA_def_boolean(func, "result", 0, "", "Success");
RNA_def_function_return(func, parm);
- func = RNA_def_function(srna, "cache_release", "BKE_object_free_caches");
+ func = RNA_def_function(srna, "cache_release", "rna_Object_cache_release");
+ RNA_def_boolean(func, "free_smoke_sim", 0, "", "Free baked smoke simulation data");
RNA_def_function_ui_description(func, "Release memory used by caches associated with this object. Intended to be used by render engines only");
}
diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c
index 75becb341b9..21f53f86d90 100644
--- a/source/blender/makesrna/intern/rna_object_force.c
+++ b/source/blender/makesrna/intern/rna_object_force.c
@@ -1337,6 +1337,11 @@ static void rna_def_field(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Use Max", "Use a maximum distance for the field to work");
RNA_def_property_update(prop, 0, "rna_FieldSettings_update");
+ prop = RNA_def_property(srna, "use_signed_distance", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", PFIELD_USE_SIGNED_DISTANCE);
+ RNA_def_property_ui_text(prop, "Use Signed Distance", "Use negative distance on the interior of surface shapes");
+ RNA_def_property_update(prop, 0, "rna_FieldSettings_update");
+
prop = RNA_def_property(srna, "use_radial_min", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", PFIELD_USEMINR);
RNA_def_property_ui_text(prop, "Use Min", "Use a minimum radial distance for the field's fall-off");
diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c
index c0ce8f21870..a6bbbfcb8b4 100644
--- a/source/blender/makesrna/intern/rna_particle.c
+++ b/source/blender/makesrna/intern/rna_particle.c
@@ -35,6 +35,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_cloth_types.h"
+#include "DNA_key_types.h"
#include "DNA_particle_types.h"
#include "DNA_object_force.h"
#include "DNA_object_types.h"
@@ -97,9 +98,12 @@ static EnumPropertyItem part_draw_as_items[] = {
#ifdef RNA_RUNTIME
static EnumPropertyItem part_hair_draw_as_items[] = {
- {PART_DRAW_NOT, "NONE", 0, "None", ""},
- {PART_DRAW_REND, "RENDER", 0, "Rendered", ""},
- {PART_DRAW_PATH, "PATH", 0, "Path", ""},
+ {PART_DRAW_NOT, "NONE", 0, "None", "No hair drawing"},
+ {PART_DRAW_REND, "RENDER", 0, "Rendered", "Approximate render result in the viewport"},
+ {PART_DRAW_PATH, "PATH", 0, "Path", "Show path of hair particles"},
+#ifdef USE_PARTICLE_HULL_DRAWING
+ {PART_DRAW_HULL, "HULL", 0, "Hull", "Show convex hull of child particle paths"},
+#endif
{0, NULL, 0, NULL, NULL}
};
#endif
@@ -127,6 +131,7 @@ static EnumPropertyItem part_hair_ren_as_items[] = {
#ifdef RNA_RUNTIME
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BKE_context.h"
@@ -137,12 +142,17 @@ static EnumPropertyItem part_hair_ren_as_items[] = {
#include "BKE_DerivedMesh.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_effect.h"
+#include "BKE_key.h"
#include "BKE_material.h"
#include "BKE_modifier.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
#include "BKE_texture.h"
+#include "RNA_access.h"
+
+#include "ED_particle.h"
+
/* use for object space hair get/set */
static void rna_ParticleHairKey_location_object_info(PointerRNA *ptr, ParticleSystemModifierData **psmd_pt,
ParticleData **pa_pt)
@@ -735,6 +745,7 @@ static void rna_Particle_hair_dynamics(Main *bmain, Scene *scene, PointerRNA *pt
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
}
+
static PointerRNA rna_particle_settings_get(PointerRNA *ptr)
{
ParticleSystem *psys = (ParticleSystem *)ptr->data;
@@ -763,6 +774,64 @@ static void rna_particle_settings_set(PointerRNA *ptr, PointerRNA value)
psys->recalc |= PSYS_RECALC_TYPE;
}
}
+
+static void rna_Particle_active_shape_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ Object *ob = ptr->id.data;
+ ParticleSystem *psys = ptr->data;
+
+ if (PE_shapekey_load(ob, psys)) {
+ WM_main_add_notifier(NC_SCENE | ND_PARTICLE | NS_MODE_PARTICLE, ptr->id.data);
+ }
+
+ rna_Particle_redo(bmain, scene, ptr);
+}
+
+static void rna_Particle_active_shape_key_index_range(PointerRNA *ptr, int *min, int *max,
+ int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ ParticleSystem *psys = ptr->data;
+ Key *key = psys->key;
+
+ *min = 0;
+ if (key) {
+ *max = BLI_listbase_count(&key->block) - 1;
+ if (*max < 0) *max = 0;
+ }
+ else {
+ *max = 0;
+ }
+}
+
+static int rna_Particle_active_shape_key_index_get(PointerRNA *ptr)
+{
+ ParticleSystem *psys = ptr->data;
+
+ return MAX2(psys->shapenr - 1, 0);
+}
+
+static void rna_Particle_active_shape_key_index_set(PointerRNA *ptr, int value)
+{
+ ParticleSystem *psys = ptr->data;
+
+ psys->shapenr = value + 1;
+}
+
+static PointerRNA rna_Particle_active_shape_key_get(PointerRNA *ptr)
+{
+ ParticleSystem *psys = ptr->data;
+ Key *key = psys->key;
+ KeyBlock *kb;
+ PointerRNA keyptr;
+
+ if (key == NULL)
+ return PointerRNA_NULL;
+
+ kb = BLI_findlink(&key->block, psys->shapenr - 1);
+ RNA_pointer_create((ID *)key, &RNA_ShapeKey, kb, &keyptr);
+ return keyptr;
+}
+
static void rna_Particle_abspathtime_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
ParticleSettings *settings = (ParticleSettings *)ptr->data;
@@ -1834,6 +1903,16 @@ static void rna_def_particle_settings_mtex(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Length", "Affect the child hair length");
RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+ prop = RNA_def_property(srna, "use_map_shapekey", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_SHAPEKEY);
+ RNA_def_property_ui_text(prop, "Shape Key", "Affect the blend factor of a hair shape key");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "use_map_particle_color", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "mapto", PAMAP_COLOR);
+ RNA_def_property_ui_text(prop, "Color", "Affect the particle color");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
/* influence factors */
prop = RNA_def_property(srna, "time_factor", PROP_FLOAT, PROP_NONE);
@@ -1915,6 +1994,23 @@ static void rna_def_particle_settings_mtex(BlenderRNA *brna)
RNA_def_property_ui_range(prop, 0, 1, 10, 3);
RNA_def_property_ui_text(prop, "Rough Factor", "Amount texture affects child roughness");
RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "shapekey_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "shapefac");
+ RNA_def_property_ui_range(prop, 0, 1, 10, 3);
+ RNA_def_property_ui_text(prop, "Shape Key Factor", "Amount texture affects shape key blend value");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "shapekey", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "shapekey");
+ RNA_def_property_ui_text(prop, "Shape Key", "Name of the shape key affected by the texture");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "particle_color_factor", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "pacolfac");
+ RNA_def_property_ui_range(prop, 0, 1, 10, 3);
+ RNA_def_property_ui_text(prop, "Particle Color Factor", "Amount texture affects particle color");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
}
static void rna_def_particle_settings(BlenderRNA *brna)
@@ -2033,6 +2129,10 @@ static void rna_def_particle_settings(BlenderRNA *brna)
{PART_DRAW_COL_MAT, "MATERIAL", 0, "Material", ""},
{PART_DRAW_COL_VEL, "VELOCITY", 0, "Velocity", ""},
{PART_DRAW_COL_ACC, "ACCELERATION", 0, "Acceleration", ""},
+#ifdef USE_PARTICLE_HULL_DRAWING
+ {PART_DRAW_COL_PARENT, "PARENT", 0, "Parent", ""},
+#endif
+ {PART_DRAW_COL_TEX, "TEXTURE", 0, "Texture", ""},
{0, NULL, 0, NULL, NULL}
};
@@ -2861,6 +2961,20 @@ static void rna_def_particle_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Clump Noise Size", "Size of clump noise");
RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+ prop = RNA_def_property(srna, "clump_noise_random", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "clump_noise_random");
+ RNA_def_property_range(prop, -100000.0f, 100000.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 10.0f, 0.1f, 3);
+ RNA_def_property_ui_text(prop, "Clump Noise Random", "Random offset of clump noise");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
+ prop = RNA_def_property(srna, "clump_noise_random_size", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "clump_noise_random_size");
+ RNA_def_property_range(prop, 0.00001f, 100000.0f);
+ RNA_def_property_ui_range(prop, 0.01f, 10.0f, 0.1f, 3);
+ RNA_def_property_ui_text(prop, "Clump Noise Random Size", "Size of clump noise offset");
+ RNA_def_property_update(prop, 0, "rna_Particle_redo_child");
+
/* kink */
prop = RNA_def_property(srna, "kink_amplitude", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "kink_amp");
@@ -3253,6 +3367,30 @@ static void rna_def_particle_system(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Hair Dynamics", "Enable hair dynamics using cloth simulation");
RNA_def_property_update(prop, 0, "rna_Particle_hair_dynamics");
+ prop = RNA_def_property(srna, "hair_preview_factor", PROP_FLOAT, PROP_PERCENTAGE);
+ RNA_def_property_float_sdna(prop, NULL, "hair_preview_factor");
+ RNA_def_property_range(prop, 0.0f, 100.0f);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Preview Factor", "Part of hair particles to use for simulation preview");
+ RNA_def_property_update(prop, 0, "rna_Particle_reset");
+
+ prop = RNA_def_property(srna, "shape_keys", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "key");
+ RNA_def_property_ui_text(prop, "Shape Keys", "");
+
+ prop = RNA_def_property(srna, "active_shape_key", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "ShapeKey");
+ RNA_def_property_pointer_funcs(prop, "rna_Particle_active_shape_key_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop, "Active Shape Key", "Current shape key");
+
+ prop = RNA_def_property(srna, "active_shape_key_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "shapenr");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_int_funcs(prop, "rna_Particle_active_shape_key_index_get", "rna_Particle_active_shape_key_index_set",
+ "rna_Particle_active_shape_key_index_range");
+ RNA_def_property_ui_text(prop, "Active Shape Key Index", "Current shape key index");
+ RNA_def_property_update(prop, 0, "rna_Particle_active_shape_update");
+
prop = RNA_def_property(srna, "cloth", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "clmd");
RNA_def_property_struct_type(prop, "ClothModifier");
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 23103206387..97cb00b7872 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -2114,6 +2114,10 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "particle");
RNA_def_property_ui_text(prop, "Particle Edit", "");
+ prop = RNA_def_property(srna, "hair_edit", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "hair_edit");
+ RNA_def_property_ui_text(prop, "Hair Edit", "");
+
prop = RNA_def_property(srna, "use_uv_sculpt", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_uv_sculpt", 1);
RNA_def_property_ui_text(prop, "UV Sculpt", "Enable brush for UV sculpting");
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index 7b30aa84cfb..acde663a799 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -77,6 +77,8 @@ EnumPropertyItem symmetrize_direction_items[] = {
#include "BKE_context.h"
#include "BKE_DerivedMesh.h"
+#include "BKE_editstrands.h"
+#include "BKE_effect.h"
#include "BKE_pointcache.h"
#include "BKE_particle.h"
#include "BKE_depsgraph.h"
@@ -221,6 +223,8 @@ static int rna_Brush_mode_poll(PointerRNA *ptr, PointerRNA value)
mode = OB_MODE_VERTEX_PAINT;
else if (ptr->data == ts->wpaint)
mode = OB_MODE_WEIGHT_PAINT;
+ else if (ptr->data == &ts->hair_edit)
+ mode = OB_MODE_HAIR_EDIT;
return brush->ob_mode & mode;
}
@@ -359,6 +363,30 @@ static int rna_ImaPaint_detect_data(ImagePaintSettings *imapaint)
{
return imapaint->missing_data == 0;
}
+
+/* ==== Hair Edit ==== */
+
+static char *rna_HairEdit_path(PointerRNA *UNUSED(ptr))
+{
+ return BLI_strdup("tool_settings.hair_edit");
+}
+
+static void rna_HairEdit_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr))
+{
+ Object *ob = OBACT;
+
+ if (ob)
+ DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+}
+
+static void rna_HairEdit_brush_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ HairEditSettings *settings = ptr->data;
+ Brush *brush = settings->brush;
+ BKE_paint_invalidate_overlay_all();
+ WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush);
+}
+
#else
static void rna_def_paint_curve(BlenderRNA *brna)
@@ -911,6 +939,10 @@ static void rna_def_particle_edit(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Puff Volume",
"Apply puff to unselected end-points (helps maintain hair volume when puffing root)");
+ prop = RNA_def_property(srna, "use_add_stroke", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", PE_BRUSH_DATA_ADD_SINGLE);
+ RNA_def_property_ui_text(prop, "Add Stroke", "Add multiple particles per brush stroke");
+
prop = RNA_def_property(srna, "length_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "invert");
RNA_def_property_enum_items(prop, length_mode);
@@ -923,6 +955,41 @@ static void rna_def_particle_edit(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Curve", "");
}
+static void rna_def_hair_edit(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem select_mode_items[] = {
+ {HAIR_SELECT_STRAND, "STRAND", ICON_PARTICLE_PATH, "Strand", "Strand edit mode"},
+ {HAIR_SELECT_VERTEX, "VERTEX", ICON_PARTICLE_POINT, "Vertex", "Vertex select mode"},
+ {HAIR_SELECT_TIP, "TIP", ICON_PARTICLE_TIP, "Tip", "Tip select mode"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ srna = RNA_def_struct(brna, "HairEdit", NULL);
+ RNA_def_struct_sdna(srna, "HairEditSettings");
+ RNA_def_struct_path_func(srna, "rna_HairEdit_path");
+ RNA_def_struct_ui_text(srna, "Hair Edit", "Settings for hair editing mode");
+
+ prop = RNA_def_property(srna, "brush", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Brush_mode_poll");
+ RNA_def_property_ui_text(prop, "Brush", "Active Brush");
+ RNA_def_property_update(prop, 0, "rna_HairEdit_brush_update");
+
+ prop = RNA_def_property(srna, "select_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_bitflag_sdna(prop, NULL, "select_mode");
+ RNA_def_property_enum_items(prop, select_mode_items);
+ RNA_def_property_ui_text(prop, "Selection Mode", "Hair selection mode");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_HairEdit_update");
+
+ prop = RNA_def_property(srna, "shape_object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Shape Object", "Outer shape to use for tools");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_HairEdit_update");
+}
+
void RNA_def_sculpt_paint(BlenderRNA *brna)
{
/* *** Non-Animated *** */
@@ -934,6 +1001,7 @@ void RNA_def_sculpt_paint(BlenderRNA *brna)
rna_def_vertex_paint(brna);
rna_def_image_paint(brna);
rna_def_particle_edit(brna);
+ rna_def_hair_edit(brna);
RNA_define_animate_sdna(true);
}
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index d62509e5ab5..1cb728c7b9e 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -1900,7 +1900,12 @@ static void rna_def_scene(BlenderRNA *brna)
RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Camera_object_poll");
RNA_def_property_ui_text(prop, "Camera Override", "Override the scenes active camera");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_update");
-
+
+ prop = RNA_def_property(srna, "use_sequence", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SCENE_STRIPS);
+ RNA_def_property_ui_text(prop, "Use Sequence", "Use scenes sequence strips directly, instead of rendering");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_update");
+
prop = RNA_def_property(srna, "use_grease_pencil", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_SCENE_NO_GPENCIL);
RNA_def_property_ui_text(prop, "Use Grease Pencil", "Show Grease Pencil strokes in OpenGL previews");
diff --git a/source/blender/makesrna/intern/rna_smoke.c b/source/blender/makesrna/intern/rna_smoke.c
index 539f3c192be..d1786dab8ca 100644
--- a/source/blender/makesrna/intern/rna_smoke.c
+++ b/source/blender/makesrna/intern/rna_smoke.c
@@ -53,6 +53,8 @@
#include "BKE_depsgraph.h"
#include "BKE_particle.h"
+#include "BLI_math.h"
+
#include "smoke_API.h"
@@ -316,8 +318,99 @@ static void rna_SmokeFlow_uvlayer_set(PointerRNA *ptr, const char *value)
rna_object_uvlayer_name_set(ptr, value, flow->uvlayer_name, sizeof(flow->uvlayer_name));
}
+static PointerRNA rna_SmokeModifier_active_openvdb_cache_get(PointerRNA *ptr)
+{
+ SmokeDomainSettings *sds = (SmokeDomainSettings *)ptr->data;
+ OpenVDBCache *cache = NULL;
+
+ cache = BKE_openvdb_get_current_cache(sds);
+ return rna_pointer_inherit_refine(ptr, &RNA_OpenVDBCache, cache);
+}
+
+static void rna_SmokeModifier_active_openvdb_cache_index_range(PointerRNA *ptr, int *min, int *max,
+ int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ SmokeDomainSettings *sds = (SmokeDomainSettings *)ptr->data;
+ *min = 0;
+ *max = max_ii(0, BLI_listbase_count(&sds->vdb_caches) - 1);
+}
+
+static int rna_SmokeModifier_active_openvdb_cache_index_get(PointerRNA *ptr)
+{
+ SmokeDomainSettings *sds = (SmokeDomainSettings *)ptr->data;
+ OpenVDBCache *cache = (OpenVDBCache *)sds->vdb_caches.first;
+ int i = 0;
+
+ for (; cache; cache = cache->next, i++) {
+ if (cache->flags & VDB_CACHE_CURRENT)
+ return i;
+ }
+ return 0;
+}
+
+static void rna_SmokeModifier_active_openvdb_cache_index_set(struct PointerRNA *ptr, int value)
+{
+ SmokeDomainSettings *sds = (SmokeDomainSettings *)ptr->data;
+ OpenVDBCache *cache = (OpenVDBCache *)sds->vdb_caches.first;
+ int i = 0;
+
+ for (; cache; cache = cache->next, i++) {
+ if (i == value)
+ cache->flags |= VDB_CACHE_CURRENT;
+ else
+ cache->flags &= ~VDB_CACHE_CURRENT;
+ }
+}
+
+
#else
+static void rna_def_openvdb_cache(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem prop_compression_items[] = {
+ { VDB_COMPRESSION_ZIP, "ZIP", 0, "Zip", "Slow and effective compression" },
+#ifdef WITH_OPENVDB_BLOSC
+ { VDB_COMPRESSION_BLOSC, "BLOSC", 0, "Blosc", "Multithreaded compression, almost similar in size and quality as 'Zip'" },
+#endif
+ { VDB_COMPRESSION_NONE, "NONE", 0, "None", "Do not use any compression" },
+ { 0, NULL, 0, NULL, NULL }
+ };
+
+ srna = RNA_def_struct(brna, "OpenVDBCache", NULL);
+ RNA_def_struct_ui_text(srna, "OpenVDB cache", "OpenVDB cache");
+
+ prop = RNA_def_property(srna, "frame_start", PROP_INT, PROP_TIME);
+ RNA_def_property_int_sdna(prop, NULL, "startframe");
+ RNA_def_property_range(prop, -MAXFRAME, MAXFRAME);
+ RNA_def_property_ui_range(prop, 1, MAXFRAME, 1, 1);
+ RNA_def_property_ui_text(prop, "Start", "Frame on which the simulation starts");
+
+ prop = RNA_def_property(srna, "frame_end", PROP_INT, PROP_TIME);
+ RNA_def_property_int_sdna(prop, NULL, "endframe");
+ RNA_def_property_range(prop, 1, MAXFRAME);
+ RNA_def_property_ui_text(prop, "End", "Frame on which the simulation stops");
+
+ prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_DIRPATH);
+ RNA_def_property_string_sdna(prop, NULL, "path");
+ RNA_def_property_ui_text(prop, "File Path", "Cache file path");
+// RNA_def_property_update(prop, NC_OBJECT, "rna_Cache_idname_change");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "name");
+ RNA_def_property_ui_text(prop, "Name", "Cache name");
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "compression", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "compression");
+ RNA_def_property_enum_items(prop, prop_compression_items);
+ RNA_def_property_ui_text(prop, "File Compression",
+ "Select what type of compression to use when writing the files");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_reset");
+}
+
static void rna_def_smoke_domain_settings(BlenderRNA *brna)
{
StructRNA *srna;
@@ -463,6 +556,14 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
RNA_def_property_enum_items(prop, smoke_cache_comp_items);
RNA_def_property_ui_text(prop, "Cache Compression", "Compression method to be used");
+ prop = RNA_def_property(srna, "point_cache_offset", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "point_cache_offset");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_range(prop, -10000, 10000);
+ RNA_def_property_ui_range(prop, -10000, 10000, 1, -1);
+ RNA_def_property_ui_text(prop, "Point Cache Offset", "Offset to add to cached frames");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_update");
+
prop = RNA_def_property(srna, "collision_extents", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "border_collisions");
RNA_def_property_enum_items(prop, smoke_domain_colli_items);
@@ -601,6 +702,37 @@ static void rna_def_smoke_domain_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Threshold",
"Maximum amount of fluid cell can contain before it is considered empty");
RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_resetCache");
+
+ /* display */
+ prop = RNA_def_property(srna, "display_thickness", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "display_thickness");
+ RNA_def_property_range(prop, 0.001f, 1000.0f);
+ RNA_def_property_ui_range(prop, 0.1f, 10.0f, 0.1, 3);
+ RNA_def_property_ui_text(prop, "Thickness", "Thickness of smoke drawing in the viewport");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, NULL);
+
+ prop = RNA_def_property(srna, "use_openvdb", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_openvdb", 1);
+ RNA_def_property_ui_text(prop, "Use OpenVDB", "Use OpenVDB to cache the simulation");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, NULL);
+
+ prop = RNA_def_property(srna, "cache", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "vdb_caches", NULL);
+ RNA_def_property_struct_type(prop, "OpenVDBCache");
+
+ prop = RNA_def_property(srna, "active_openvdb_cache", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "OpenVDBCache");
+ RNA_def_property_pointer_funcs(prop, "rna_SmokeModifier_active_openvdb_cache_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop, "Active OpenVDB cache", "");
+
+ prop = RNA_def_property(srna, "active_openvdb_cache_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_funcs(prop, "rna_SmokeModifier_active_openvdb_cache_index_get",
+ "rna_SmokeModifier_active_openvdb_cache_index_set",
+ "rna_SmokeModifier_active_openvdb_cache_index_range");
+ RNA_def_property_ui_text(prop, "Active OpenVDB cache Index", "");
+
+ rna_def_openvdb_cache(brna);
}
static void rna_def_smoke_flow_settings(BlenderRNA *brna)
@@ -732,6 +864,11 @@ static void rna_def_smoke_flow_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Set Size", "Set particle size in simulation cells or use nearest cell");
RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_reset");
+ prop = RNA_def_property(srna, "use_particle_texture_color", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flags", MOD_SMOKE_FLOW_USE_PART_TEXCOLOR);
+ RNA_def_property_ui_text(prop, "Set Texture Color", "Set particle texture color in simulation cells");
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Smoke_reset");
+
prop = RNA_def_property(srna, "subframes", PROP_INT, PROP_NONE);
RNA_def_property_range(prop, 0, 50);
RNA_def_property_ui_range(prop, 0, 10, 1, -1);
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 20a6bd67dea..76535955929 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -2484,6 +2484,11 @@ static void rna_def_space_view3d(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "World Background", "Display world colors in the background");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+ prop = RNA_def_property(srna, "show_motionpaths", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag3", V3D_HIDE_MOTIONPATHS);
+ RNA_def_property_ui_text(prop, "Motion Paths", "Display animation motion paths -even in only render mode");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
prop = RNA_def_property(srna, "use_occlude_geometry", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_ZBUF_SELECT);
RNA_def_property_ui_text(prop, "Occlude Geometry", "Limit selection to visible (clipped with depth buffer)");
@@ -2608,6 +2613,11 @@ static void rna_def_space_view3d(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Show 3D Marker Names", "Show names for reconstructed tracks objects");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+ prop = RNA_def_property(srna, "use_wire_color", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag2", V3D_WIRE_COLOR_NOCUSTOM);
+ RNA_def_property_ui_text(prop, "Color Wire", "Draw wireframes using object color");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
prop = RNA_def_property(srna, "use_matcap", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag2", V3D_SOLID_MATCAP);
RNA_def_property_ui_text(prop, "Matcap", "Active Objects draw images mapped on normals, enhancing Solid Draw Mode");
@@ -3077,6 +3087,11 @@ static void rna_def_space_sequencer(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Show Metadata", "Show metadata of first visible strip");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+ prop = RNA_def_property(srna, "show_info", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_NO_INFO);
+ RNA_def_property_ui_text(prop, "Show Strip Info", "Show source info on the strip");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+
prop = RNA_def_property(srna, "show_seconds", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_DRAWFRAMES);
RNA_def_property_ui_text(prop, "Show Seconds", "Show timing in seconds not frames");
@@ -3134,10 +3149,22 @@ static void rna_def_space_sequencer(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Overlay Type", "Overlay draw type");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
- prop = RNA_def_property(srna, "show_backdrop", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "draw_flag", SEQ_DRAW_BACKDROP);
- RNA_def_property_ui_text(prop, "Use Backdrop", "Display result under strips");
+ prop = RNA_def_property(srna, "show_overdrop", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "draw_flag", SEQ_DRAW_OVERDROP);
+ RNA_def_property_ui_text(prop, "Use Overdrop", "Display result under strips");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL);
+
+ prop = RNA_def_property(srna, "overdrop_zoom", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_default(prop, 1.0f);
+ RNA_def_property_range(prop, 0.01f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.01, 100, 1, 2);
+ RNA_def_property_ui_text(prop, "Overdrop Zoom", "Overdrop zoom factor");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
+
+ prop = RNA_def_property(srna, "overdrop_offset", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_array(prop, 2);
+ RNA_def_property_ui_text(prop, "Overdrop Offset", "Overdrop offset");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
prop = RNA_def_property(srna, "show_strip_offset", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "draw_flag", SEQ_DRAW_OFFSET_EXT);
@@ -3497,6 +3524,23 @@ static void rna_def_space_graph(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Auto Normalization",
"Automatically recalculate curve normalization on every curve edit");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL);
+
+ prop = RNA_def_property(srna, "show_backdrop", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SIPO_DRAW_BACKDROP);
+ RNA_def_property_ui_text(prop, "Show Backdrop", "Draw a backdrop showing the content of the 3D View");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL);
+
+ prop = RNA_def_property(srna, "backdrop_camera", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Camera_object_poll");
+ RNA_def_property_ui_text(prop, "Backdrop Camera", "The camera that is used to project the backdrop");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL);
+
+ prop = RNA_def_property(srna, "backdrop_opacity", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "backdrop_opacity");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_ui_text(prop, "Backdrop Opacity", "Opacity of the backdrop");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_GRAPH, NULL);
}
static void rna_def_space_nla(BlenderRNA *brna)
@@ -3744,6 +3788,11 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Show Hidden", "Show hidden dot files");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+ prop = RNA_def_property(srna, "collapse_seq", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", FILE_COLLAPSE_IMAGES);
+ RNA_def_property_ui_text(prop, "Collapse Image Sequences", "Collapse image sequences");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+
prop = RNA_def_property(srna, "sort_method", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "sort");
RNA_def_property_enum_items(prop, file_sort_items);
@@ -4173,21 +4222,15 @@ static void rna_def_space_node(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
prop = RNA_def_property(srna, "backdrop_zoom", PROP_FLOAT, PROP_NONE);
- RNA_def_property_float_sdna(prop, NULL, "zoom");
RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_range(prop, 0.01f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.01, 100, 1, 2);
RNA_def_property_ui_text(prop, "Backdrop Zoom", "Backdrop zoom factor");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
- prop = RNA_def_property(srna, "backdrop_x", PROP_FLOAT, PROP_NONE);
- RNA_def_property_float_sdna(prop, NULL, "xof");
- RNA_def_property_ui_text(prop, "Backdrop X", "Backdrop X offset");
- RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
-
- prop = RNA_def_property(srna, "backdrop_y", PROP_FLOAT, PROP_NONE);
- RNA_def_property_float_sdna(prop, NULL, "yof");
- RNA_def_property_ui_text(prop, "Backdrop Y", "Backdrop Y offset");
+ prop = RNA_def_property(srna, "backdrop_offset", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_array(prop, 2);
+ RNA_def_property_ui_text(prop, "Backdrop Offset", "Backdrop offset");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL);
prop = RNA_def_property(srna, "backdrop_channels", PROP_ENUM, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_strands.c b/source/blender/makesrna/intern/rna_strands.c
new file mode 100644
index 00000000000..f6154fd1e01
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_strands.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file blender/makesrna/intern/rna_strands.c
+ * \ingroup RNA
+ */
+
+#include <stdlib.h>
+
+#include "BLI_utildefines.h"
+
+#include "RNA_define.h"
+#include "RNA_access.h"
+
+#include "rna_internal.h"
+
+#include "WM_types.h"
+
+#ifdef RNA_RUNTIME
+
+#include "BLI_math.h"
+
+#include "BKE_strands.h"
+#include "BKE_report.h"
+
+static int rna_Strands_has_motion_state_get(PointerRNA *ptr)
+{
+ Strands *strands = ptr->data;
+ return (bool)(strands->state != NULL);
+}
+
+static int rna_StrandsChildCurve_render_size_get(PointerRNA *ptr)
+{
+ StrandsChildCurve *curve = ptr->data;
+ return curve->cutoff < 0.0f ? curve->numverts : min_ii(curve->numverts, (int)ceilf(curve->cutoff) + 1);
+}
+
+static void rna_StrandsChildren_curve_uvs_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ StrandsChildren *strands = ptr->data;
+ rna_iterator_array_begin(iter, strands->curve_uvs, sizeof(StrandsChildCurveUV), strands->totcurves * strands->numuv, false, NULL);
+}
+
+static int rna_StrandsChildren_curve_uvs_length(PointerRNA *ptr)
+{
+ StrandsChildren *strands = ptr->data;
+ return strands->totcurves * strands->numuv;
+}
+
+static int rna_StrandsChildren_curve_uvs_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr)
+{
+ StrandsChildren *strands = ptr->data;
+ if (index >= 0 && index < strands->totcurves * strands->numuv) {
+ RNA_pointer_create(ptr->id.data, &RNA_StrandsChildCurveUV, strands->curve_uvs + index, r_ptr);
+ return true;
+ }
+ else {
+ RNA_pointer_create(ptr->id.data, &RNA_StrandsChildCurveUV, NULL, r_ptr);
+ return false;
+ }
+}
+
+static void rna_StrandsChildren_curve_vcols_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ StrandsChildren *strands = ptr->data;
+ rna_iterator_array_begin(iter, strands->curve_vcols, sizeof(StrandsChildCurveVCol), strands->totcurves * strands->numvcol, false, NULL);
+}
+
+static int rna_StrandsChildren_curve_vcols_length(PointerRNA *ptr)
+{
+ StrandsChildren *strands = ptr->data;
+ return strands->totcurves * strands->numvcol;
+}
+
+static int rna_StrandsChildren_curve_vcols_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr)
+{
+ StrandsChildren *strands = ptr->data;
+ if (index >= 0 && index < strands->totcurves * strands->numvcol) {
+ RNA_pointer_create(ptr->id.data, &RNA_StrandsChildCurveVCol, strands->curve_vcols + index, r_ptr);
+ return true;
+ }
+ else {
+ RNA_pointer_create(ptr->id.data, &RNA_StrandsChildCurveVCol, NULL, r_ptr);
+ return false;
+ }
+}
+
+#else
+
+static void rna_def_strands_curve(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "StrandsCurve", NULL);
+ RNA_def_struct_sdna(srna, "StrandsCurve");
+ RNA_def_struct_ui_text(srna, "Strand Curve", "Strand curve");
+
+ prop = RNA_def_property(srna, "size", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "numverts");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Size", "Number of vertices of the curve");
+
+ /* same as "size", defined for consistency */
+ prop = RNA_def_property(srna, "render_size", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "numverts");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Render Size", "Number of vertices of the curve for rendering based on cutoff length");
+}
+
+static void rna_def_strands_vertex(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "StrandsVertex", NULL);
+ RNA_def_struct_sdna(srna, "StrandsVertex");
+ RNA_def_struct_ui_text(srna, "Strand Vertex", "Strand vertex");
+
+ prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_float_sdna(prop, NULL, "co");
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Location", "");
+}
+
+static void rna_def_strands_motion_state(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "StrandsMotionState", NULL);
+ RNA_def_struct_sdna(srna, "StrandsMotionState");
+ RNA_def_struct_ui_text(srna, "Strand Vertex Motion State", "Physical motion state of a vertex");
+
+ prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_float_sdna(prop, NULL, "co");
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Location", "");
+}
+
+static void rna_def_strands(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "Strands", NULL);
+ RNA_def_struct_sdna(srna, "Strands");
+ RNA_def_struct_ui_text(srna, "Strands", "Strand geometry to represent hair and similar linear structures");
+
+ prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "curves", "totcurves");
+ RNA_def_property_struct_type(prop, "StrandsCurve");
+ RNA_def_property_ui_text(prop, "Strand Curves", "");
+
+ prop = RNA_def_property(srna, "vertices", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "verts", "totverts");
+ RNA_def_property_struct_type(prop, "StrandsVertex");
+ RNA_def_property_ui_text(prop, "Strand Vertex", "");
+
+ prop = RNA_def_property(srna, "has_motion_state", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop, "rna_Strands_has_motion_state_get", NULL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Has Motion State", "Strands have physical motion data associated with vertices");
+
+ prop = RNA_def_property(srna, "motion_state", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "state", "totverts");
+ RNA_def_property_struct_type(prop, "StrandsMotionState");
+ RNA_def_property_ui_text(prop, "Strand Motion State", "");
+}
+
+static void rna_def_strands_child_curve(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "StrandsChildCurve", NULL);
+ RNA_def_struct_sdna(srna, "StrandsChildCurve");
+ RNA_def_struct_ui_text(srna, "Strand Child Curve", "Strand child curve");
+
+ prop = RNA_def_property(srna, "size", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "numverts");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Size", "Number of vertices of the curve");
+
+ prop = RNA_def_property(srna, "render_size", PROP_INT, PROP_NONE);
+ RNA_def_property_int_funcs(prop, "rna_StrandsChildCurve_render_size_get", NULL, NULL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Render Size", "Number of vertices of the curve for rendering based on cutoff length");
+
+ prop = RNA_def_property(srna, "cutoff", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "cutoff");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Cutoff", "Curve parameter at which the curve is cut short for rendering");
+}
+
+static void rna_def_strands_child_curve_uv(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "StrandsChildCurveUV", NULL);
+ RNA_def_struct_sdna(srna, "StrandsChildCurveUV");
+ RNA_def_struct_ui_text(srna, "Strand Child Curve UV", "UV data for child strand curves");
+
+ prop = RNA_def_property(srna, "uv", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_array(prop, 2);
+ RNA_def_property_ui_text(prop, "UV", "");
+}
+
+static void rna_def_strands_child_curve_vcol(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "StrandsChildCurveVCol", NULL);
+ RNA_def_struct_sdna(srna, "StrandsChildCurveVCol");
+ RNA_def_struct_ui_text(srna, "Strand Child Curve Vertex Color", "Vertex color data for child strand curves");
+
+ prop = RNA_def_property(srna, "vcol", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_ui_text(prop, "Vertex Color", "");
+}
+
+static void rna_def_strands_child_vertex(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "StrandsChildVertex", NULL);
+ RNA_def_struct_sdna(srna, "StrandsChildVertex");
+ RNA_def_struct_ui_text(srna, "Strand Child Vertex", "Strand child vertex");
+
+ prop = RNA_def_property(srna, "location", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_float_sdna(prop, NULL, "co");
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Location", "");
+}
+
+static void rna_def_strands_children(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "StrandsChildren", NULL);
+ RNA_def_struct_sdna(srna, "StrandsChildren");
+ RNA_def_struct_ui_text(srna, "Child Strands", "Strand geometry to represent hair and similar linear structures");
+
+ prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "curves", "totcurves");
+ RNA_def_property_struct_type(prop, "StrandsChildCurve");
+ RNA_def_property_ui_text(prop, "Strand Child Curves", "");
+
+ prop = RNA_def_property(srna, "curve_uvs", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_funcs(prop, "rna_StrandsChildren_curve_uvs_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get",
+ "rna_StrandsChildren_curve_uvs_length", "rna_StrandsChildren_curve_uvs_lookup_int", NULL, NULL);
+ RNA_def_property_struct_type(prop, "StrandsChildCurveUV");
+ RNA_def_property_ui_text(prop, "Strand Child Curves UV", "");
+
+ prop = RNA_def_property(srna, "num_curve_uv_layers", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "numuv");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "UV Layers", "Number of UV layers");
+
+ prop = RNA_def_property(srna, "curve_vcols", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_funcs(prop, "rna_StrandsChildren_curve_vcols_begin", "rna_iterator_array_next", "rna_iterator_array_end", "rna_iterator_array_get",
+ "rna_StrandsChildren_curve_vcols_length", "rna_StrandsChildren_curve_vcols_lookup_int", NULL, NULL);
+ RNA_def_property_struct_type(prop, "StrandsChildCurveVCol");
+ RNA_def_property_ui_text(prop, "Strand Child Curves Vertex Colors", "");
+
+ prop = RNA_def_property(srna, "num_curve_vcol_layers", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "numvcol");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Vertex Color Layers", "Number of Vertex Color layers");
+
+ prop = RNA_def_property(srna, "vertices", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "verts", "totverts");
+ RNA_def_property_struct_type(prop, "StrandsChildVertex");
+ RNA_def_property_ui_text(prop, "Strand Child Vertex", "");
+}
+
+void RNA_def_strands(BlenderRNA *brna)
+{
+ rna_def_strands_curve(brna);
+ rna_def_strands_vertex(brna);
+ rna_def_strands_motion_state(brna);
+ rna_def_strands(brna);
+ rna_def_strands_child_curve(brna);
+ rna_def_strands_child_curve_uv(brna);
+ rna_def_strands_child_curve_vcol(brna);
+ rna_def_strands_child_vertex(brna);
+ rna_def_strands_children(brna);
+}
+
+#endif
diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c
index 726744782ed..88f164193d4 100644
--- a/source/blender/makesrna/intern/rna_texture.c
+++ b/source/blender/makesrna/intern/rna_texture.c
@@ -1674,6 +1674,7 @@ static void rna_def_texture_pointdensity(BlenderRNA *brna)
{TEX_PD_COLOR_PARTSPEED, "PARTICLE_SPEED", 0, "Particle Speed",
"Particle speed (absolute magnitude of velocity) mapped as 0.0-1.0 intensity"},
{TEX_PD_COLOR_PARTVEL, "PARTICLE_VELOCITY", 0, "Particle Velocity", "XYZ velocity mapped to RGB colors"},
+ {TEX_PD_COLOR_PARTTEX, "PARTICLE_TEXTURE", 0, "Particle Texture", "Texture color of particles"},
{0, NULL, 0, NULL, NULL}
};
diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c
index d75966f6599..59bee5bce8c 100644
--- a/source/blender/makesrna/intern/rna_ui_api.c
+++ b/source/blender/makesrna/intern/rna_ui_api.c
@@ -911,6 +911,19 @@ 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);
+
+ /* cache library item */
+ func = RNA_def_function(srna, "template_cache_library_item", "uiTemplateCacheLibraryItem");
+ RNA_def_function_ui_description(func, "Cache Library Item");
+ RNA_def_function_flag(func, FUNC_USE_CONTEXT);
+ RNA_def_pointer(func, "cachelib", "CacheLibrary", "Cache Library", "Cache library containing the item");
+ RNA_def_pointer(func, "object", "Object", "Object", "Object to cache");
+ parm = RNA_def_enum(func, "datatype", cache_library_data_type_items, 0, "Data Type", "Type of cached data");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ RNA_def_int(func, "index", -1, -1, INT_MAX, "Index", "Index of cached data", -1, INT_MAX);
+ RNA_def_boolean(func, "enabled", true, "Enabled", "Enable the item");
+ parm = RNA_def_pointer(func, "layout", "UILayout", "", "Sub-layout to put items in");
+ RNA_def_function_return(func, parm);
}
#endif
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 9604f643149..988b726f43e 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -3473,6 +3473,16 @@ static void rna_def_userdef_view(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Manipulator", "Use 3D transform manipulator");
RNA_def_property_update(prop, 0, "rna_userdef_show_manipulator_update");
+ prop = RNA_def_property(srna, "shaded_widgets", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "tw_flag", V3D_SHADED_WIDGETS);
+ RNA_def_property_ui_text(prop, "Shaded Widgets", "3D shading for widgets");
+ RNA_def_property_update(prop, 0, "rna_userdef_update");
+
+ prop = RNA_def_property(srna, "widgets_3d", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "tw_flag", V3D_3D_WIDGETS);
+ RNA_def_property_ui_text(prop, "3D widgets", "Widgets size stays constant in world space");
+ RNA_def_property_update(prop, 0, "rna_userdef_update");
+
prop = RNA_def_property(srna, "manipulator_size", PROP_INT, PROP_PIXEL);
RNA_def_property_int_sdna(prop, NULL, "tw_size");
RNA_def_property_range(prop, 10, 200);
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index 4446f5d9a2b..c23b56582b6 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -30,6 +30,7 @@
#include "DNA_space_types.h"
#include "DNA_userdef_types.h"
#include "DNA_windowmanager_types.h"
+#include "DNA_widget_types.h"
#include "BLI_utildefines.h"
@@ -522,6 +523,26 @@ static PointerRNA rna_Operator_properties_get(PointerRNA *ptr)
return rna_pointer_inherit_refine(ptr, op->type->srna, op->properties);
}
+static void rna_WidgetGroup_name_get(PointerRNA *ptr, char *value)
+{
+ wmWidgetGroup *wgroup = ptr->data;
+ strcpy(value, "Dummy_XXX" /*wgroup->type->name*/);
+ (void)wgroup;
+}
+
+static int rna_WidgetGroup_name_length(PointerRNA *ptr)
+{
+ wmWidgetGroup *wgroup = ptr->data;
+ return strlen("Dummy_XXX" /*wgroup->type->name*/);
+ (void)wgroup;
+}
+
+static int rna_WidgetGroup_has_reports_get(PointerRNA *ptr)
+{
+ wmWidgetGroup *wgroup = ptr->data;
+ return (wgroup->reports && wgroup->reports->list.first);
+}
+
static PointerRNA rna_OperatorMacro_properties_get(PointerRNA *ptr)
{
wmOperatorTypeMacro *otmacro = (wmOperatorTypeMacro *)ptr->data;
@@ -1387,6 +1408,180 @@ static void rna_Operator_bl_description_set(PointerRNA *ptr, const char *value)
assert(!"setting the bl_description on a non-builtin operator");
}
+#ifdef WITH_PYTHON
+static void rna_WidgetGroup_unregister(struct Main *bmain, StructRNA *type)
+{
+ //const char *idname;
+ wmWidgetGroupType *wgrouptype = RNA_struct_blender_type_get(type);
+ //wmWindowManager *wm;
+ //wmWidgetMapType *wmap = NULL;
+
+ if (!wgrouptype)
+ return;
+
+ WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL);
+
+ //RNA_struct_free_extension(type, &wgrouptype->ext);
+
+ WM_widgetgrouptype_unregister(NULL, bmain, wgrouptype);
+ //WM_operatortype_remove_ptr(ot);
+
+ /* not to be confused with the RNA_struct_free that WM_operatortype_remove calls, they are 2 different srna's */
+ RNA_struct_free(&BLENDER_RNA, type);
+}
+
+static int widgetgroup_poll(const bContext *C, wmWidgetGroupType *wgrouptype)
+{
+
+ extern FunctionRNA rna_WidgetGroup_poll_func;
+
+ PointerRNA ptr;
+ ParameterList list;
+ FunctionRNA *func;
+ void *ret;
+ int visible;
+
+ RNA_pointer_create(NULL, wgrouptype->ext.srna, NULL, &ptr); /* dummy */
+ func = &rna_WidgetGroup_poll_func; /* RNA_struct_find_function(&ptr, "poll"); */
+
+ RNA_parameter_list_create(&list, &ptr, func);
+ RNA_parameter_set_lookup(&list, "context", &C);
+ wgrouptype->ext.call((bContext *)C, &ptr, func, &list);
+
+ RNA_parameter_get_lookup(&list, "visible", &ret);
+ visible = *(int *)ret;
+
+ RNA_parameter_list_free(&list);
+
+ return visible;
+}
+
+static void widgetgroup_draw(const bContext *C, wmWidgetGroup *wgroup)
+{
+ extern FunctionRNA rna_WidgetGroup_draw_func;
+
+ PointerRNA wgroup_ptr;
+ ParameterList list;
+ FunctionRNA *func;
+
+ RNA_pointer_create(NULL, wgroup->type->ext.srna, wgroup, &wgroup_ptr);
+ func = &rna_WidgetGroup_draw_func; /* RNA_struct_find_function(&wgroupr, "draw"); */
+
+ RNA_parameter_list_create(&list, &wgroup_ptr, func);
+ RNA_parameter_set_lookup(&list, "context", &C);
+ wgroup->type->ext.call((bContext *)C, &wgroup_ptr, func, &list);
+
+ RNA_parameter_list_free(&list);
+}
+#if 0
+
+/* same as exec(), but call cancel */
+static void operator_cancel(bContext *C, wmWidgetGroup *op)
+{
+ extern FunctionRNA rna_WidgetGroup_cancel_func;
+
+ PointerRNA opr;
+ ParameterList list;
+ FunctionRNA *func;
+
+ RNA_pointer_create(NULL, op->type->ext.srna, op, &opr);
+ func = &rna_WidgetGroup_cancel_func; /* RNA_struct_find_function(&opr, "cancel"); */
+
+ RNA_parameter_list_create(&list, &opr, func);
+ RNA_parameter_set_lookup(&list, "context", &C);
+ op->type->ext.call(C, &opr, func, &list);
+
+ RNA_parameter_list_free(&list);
+}
+#endif
+
+void widgetgroup_wrapper(wmWidgetGroupType *ot, void *userdata);
+
+static char _widgetgroup_idname[OP_MAX_TYPENAME];
+//static char _widgetgroup_name[OP_MAX_TYPENAME];
+//static char _widgetgroup_descr[RNA_DYN_DESCR_MAX];
+//static char _widgetgroup_ctxt[RNA_DYN_DESCR_MAX];
+static StructRNA *rna_WidgetGroup_register(Main *bmain, ReportList *reports, void *data, const char *identifier,
+ StructValidateFunc validate, StructCallbackFunc call, StructFreeFunc free)
+{
+
+ wmWidgetGroupType *wgrouptype, dummywgt = {NULL};
+ wmWidgetGroup dummywg = {NULL};
+ PointerRNA wgptr;
+ int have_function[2];
+
+ /* setup dummy widgetgroup & widgetgroup type to store static properties in */
+ dummywg.type = &dummywgt;
+ RNA_pointer_create(NULL, &RNA_WidgetGroup, &dummywg, &wgptr);
+
+ /* clear in case they are left unset */
+ _widgetgroup_idname[0] = '\0';
+
+ /* validate the python class */
+ if (validate(&wgptr, data, have_function) != 0)
+ return NULL;
+
+ if (strlen(identifier) >= sizeof(dummywgt.idname)) {
+ BKE_reportf(reports, RPT_ERROR, "Registering widgetgroup class: '%s' is too long, maximum length is %d",
+ identifier, (int)sizeof(dummywgt.idname));
+ return NULL;
+ }
+
+ /* check if the area supports widgets */
+ if (!WM_widgetmaptype_find(dummywgt.mapidname ,dummywgt.spaceid, dummywgt.regionid, dummywgt.is_3d, false)) {
+ BKE_reportf(reports, RPT_ERROR, "Area type does not support widgets");
+ return NULL;
+ }
+
+#if 0
+ /* check if we have registered this widgetgroup type before, and remove it */
+ {
+ //wmWidgetGroupType *ot = WM_widgetgrouptype_find(dummywgt.idname, true);
+ if (ot && ot->ext.srna)
+ rna_WidgetGroup_unregister(bmain, ot->ext.srna);
+ }
+
+#endif
+ /* XXX, this doubles up with the widgetgroup name [#29666]
+ * for now just remove from dir(bpy.types) */
+
+ /* create a new widgetgroup type */
+ dummywgt.ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, dummywgt.idname, &RNA_WidgetGroup);
+ RNA_def_struct_flag(dummywgt.ext.srna, STRUCT_NO_IDPROPERTIES); /* widgetgroup properties are registered separately */
+ dummywgt.ext.data = data;
+ dummywgt.ext.call = call;
+ dummywgt.ext.free = free;
+
+ dummywgt.poll = (have_function[0]) ? widgetgroup_poll : NULL;
+ dummywgt.draw = (have_function[1]) ? widgetgroup_draw : NULL;
+
+ wgrouptype = WM_widgetgrouptype_new(dummywgt.poll, dummywgt.draw, bmain, dummywgt.mapidname, dummywgt.spaceid, dummywgt.regionid, dummywgt.is_3d);
+ memcpy(wgrouptype, &dummywgt, sizeof(dummywgt));
+
+ /* update while blender is running */
+ WM_main_add_notifier(NC_SCREEN | NA_EDITED, NULL);
+
+ dummywgt.ext.srna = RNA_def_struct_ptr(&BLENDER_RNA, dummywgt.idname, &RNA_WidgetGroup);
+
+ return dummywgt.ext.srna;
+}
+
+//RNA_struct_blender_type_set(pt->ext.srna, pt);
+
+static void **rna_WidgetGroup_instance(PointerRNA *ptr)
+{
+ wmWidgetGroup *wgroup = ptr->data;
+ return &wgroup->py_instance;
+}
+
+static StructRNA *rna_WidgetGroup_refine(PointerRNA *wgroup_ptr)
+{
+ wmWidgetGroup *wgroup = wgroup_ptr->data;
+ return (wgroup->type && wgroup->type->ext.srna) ? wgroup->type->ext.srna : &RNA_WidgetGroup;
+}
+
+#endif
+
static void rna_KeyMapItem_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
wmKeyMapItem *kmi = ptr->data;
@@ -1635,7 +1830,62 @@ static void rna_def_operator_filelist_element(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_IDPROPERTY);
RNA_def_property_ui_text(prop, "Name", "Name of a file or directory within a file list");
}
-
+
+static void rna_def_widgetgroup(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "WidgetGroup", NULL);
+ RNA_def_struct_ui_text(srna, "WidgetGroup", "Storage of an operator being executed, or registered after execution");
+ RNA_def_struct_sdna(srna, "wmWidgetGroup");
+ RNA_def_struct_refine_func(srna, "rna_WidgetGroup_refine");
+#ifdef WITH_PYTHON
+ RNA_def_struct_register_funcs(srna, "rna_WidgetGroup_register", "rna_WidgetGroup_unregister", "rna_WidgetGroup_instance");
+#endif
+ RNA_def_struct_translation_context(srna, BLF_I18NCONTEXT_OPERATOR_DEFAULT);
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_string_funcs(prop, "rna_WidgetGroup_name_get", "rna_WidgetGroup_name_length", NULL);
+ RNA_def_property_ui_text(prop, "Name", "");
+
+ prop = RNA_def_property(srna, "has_reports", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* this is 'virtual' property */
+ RNA_def_property_boolean_funcs(prop, "rna_WidgetGroup_has_reports_get", NULL);
+ RNA_def_property_ui_text(prop, "Has Reports",
+ "WidgetGroup has a set of reports (warnings and errors) from last execution");
+
+ /* Registration */
+ prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "type->idname");
+ /* RNA_def_property_clear_flag(prop, PROP_EDITABLE); */
+ RNA_def_property_flag(prop, PROP_REGISTER);
+ RNA_def_struct_name_property(srna, prop);
+
+ prop = RNA_def_property(srna, "bl_space_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "type->spaceid");
+ RNA_def_property_enum_items(prop, space_type_items);
+ RNA_def_property_flag(prop, PROP_REGISTER);
+ RNA_def_property_ui_text(prop, "Space type", "The space where the panel is going to be used in");
+
+ prop = RNA_def_property(srna, "bl_region_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "type->regionid");
+ RNA_def_property_enum_items(prop, region_type_items);
+ RNA_def_property_flag(prop, PROP_REGISTER);
+ RNA_def_property_ui_text(prop, "Region Type", "The region where the panel is going to be used in");
+
+#if 0
+ prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "type->flag");
+ RNA_def_property_enum_items(prop, operator_flag_items);
+ RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG);
+ RNA_def_property_ui_text(prop, "Options", "Options for this operator type");
+#endif
+
+ RNA_api_widgetgroup(srna);
+}
+
static void rna_def_event(BlenderRNA *brna)
{
StructRNA *srna;
@@ -2217,6 +2467,7 @@ void RNA_def_wm(BlenderRNA *brna)
rna_def_operator_filelist_element(brna);
rna_def_macro_operator(brna);
rna_def_operator_type_macro(brna);
+ rna_def_widgetgroup(brna);
rna_def_event(brna);
rna_def_timer(brna);
rna_def_popupmenu(brna);
diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c
index e819d331124..2e0bd8f491a 100644
--- a/source/blender/makesrna/intern/rna_wm_api.c
+++ b/source/blender/makesrna/intern/rna_wm_api.c
@@ -616,6 +616,39 @@ void RNA_api_macro(StructRNA *srna)
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
}
+void RNA_api_widgetgroup(StructRNA *srna)
+{
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+#if 0
+ /* utility, not for registering */
+ func = RNA_def_function(srna, "report", "rna_Operator_report");
+ parm = RNA_def_enum_flag(func, "type", wm_report_items, 0, "Type", "");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm = RNA_def_string(func, "message", NULL, 0, "Report Message", "");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+#endif
+
+
+ /* Registration */
+
+ /* poll */
+ func = RNA_def_function(srna, "poll", NULL);
+ RNA_def_function_ui_description(func, "Test if the operator can be called or not");
+ RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_REGISTER_OPTIONAL);
+ RNA_def_function_return(func, RNA_def_boolean(func, "visible", 1, "", ""));
+ parm = RNA_def_pointer(func, "context", "Context", "", "");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
+
+ /* draw */
+ func = RNA_def_function(srna, "draw", NULL);
+ RNA_def_function_ui_description(func, "Draw function for the operator");
+ RNA_def_function_flag(func, FUNC_REGISTER);
+ parm = RNA_def_pointer(func, "context", "Context", "", "");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
+}
+
void RNA_api_keyconfig(StructRNA *UNUSED(srna))
{
/* FunctionRNA *func; */
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index fde0c773ef0..5306f6d34ba 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -34,6 +34,7 @@ set(INC
../makesdna
../makesrna
../bmesh
+ ../pointcache
../render/extern/include
../../../intern/elbeem/extern
../../../intern/guardedalloc
diff --git a/source/blender/modifiers/SConscript b/source/blender/modifiers/SConscript
index a7d17609ab7..761342b79d6 100644
--- a/source/blender/modifiers/SConscript
+++ b/source/blender/modifiers/SConscript
@@ -45,6 +45,7 @@ incs = [
'../makesrna',
'../blenkernel',
'../gpu',
+ '../pointcache',
env['BF_ZLIB_INC'],
]
diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c
index cb6234d50b7..d0e93dff01f 100644
--- a/source/blender/modifiers/intern/MOD_particleinstance.c
+++ b/source/blender/modifiers/intern/MOD_particleinstance.c
@@ -40,6 +40,7 @@
#include "BLI_math.h"
#include "BLI_listbase.h"
#include "BLI_rand.h"
+#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BKE_cdderivedmesh.h"
@@ -62,7 +63,12 @@ static void initData(ModifierData *md)
pimd->psys = 1;
pimd->position = 1.0f;
pimd->axis = 2;
-
+ pimd->space = eParticleInstanceSpace_Local;
+ pimd->particle_amount = 1.0f;
+ pimd->particle_offset = 0.0f;
+
+ BLI_strncpy(pimd->index_layer_name, "particle_index", sizeof(pimd->index_layer_name));
+ BLI_strncpy(pimd->value_layer_name, "particle_value", sizeof(pimd->value_layer_name));
}
static void copyData(ModifierData *md, ModifierData *target)
{
@@ -147,39 +153,44 @@ static void foreachObjectLink(ModifierData *md, Object *ob,
walk(userData, ob, &pimd->ob);
}
-static int particle_skip(ParticleInstanceModifierData *pimd, ParticleSystem *psys, int p)
+static bool particle_skip(ParticleInstanceModifierData *pimd, ParticleSystem *psys, int p)
{
+ const bool between = (psys->part->childtype == PART_CHILD_FACES);
ParticleData *pa;
+ int totpart, randp, minp, maxp;
- if (pimd->flag & eParticleInstanceFlag_Parents) {
- if (p >= psys->totpart) {
- if (psys->part->childtype == PART_CHILD_PARTICLES) {
- pa = psys->particles + (psys->child + p - psys->totpart)->parent;
- }
- else {
- pa = NULL;
- }
- }
- else {
- pa = psys->particles + p;
- }
+ if (p >= psys->totpart) {
+ ChildParticle *cpa = psys->child + (p - psys->totpart);
+ pa = psys->particles + (between? cpa->pa[0]: cpa->parent);
}
else {
- if (psys->part->childtype == PART_CHILD_PARTICLES) {
- pa = psys->particles + (psys->child + p)->parent;
- }
- else {
- pa = NULL;
- }
+ pa = psys->particles + p;
}
if (pa) {
- if (pa->alive == PARS_UNBORN && (pimd->flag & eParticleInstanceFlag_Unborn) == 0) return 1;
- if (pa->alive == PARS_ALIVE && (pimd->flag & eParticleInstanceFlag_Alive) == 0) return 1;
- if (pa->alive == PARS_DEAD && (pimd->flag & eParticleInstanceFlag_Dead) == 0) return 1;
+ if (pa->alive == PARS_UNBORN && (pimd->flag & eParticleInstanceFlag_Unborn) == 0) return true;
+ if (pa->alive == PARS_ALIVE && (pimd->flag & eParticleInstanceFlag_Alive) == 0) return true;
+ if (pa->alive == PARS_DEAD && (pimd->flag & eParticleInstanceFlag_Dead) == 0) return true;
}
- return 0;
+ totpart = psys->totpart + psys->totchild;
+
+ /* TODO make randomization optional? */
+ randp = (int)(psys_frand(psys, 3578 + p) * totpart) % totpart;
+
+ minp = (int)(totpart * pimd->particle_offset) % (totpart+1);
+ maxp = (int)(totpart * (pimd->particle_offset + pimd->particle_amount)) % (totpart+1);
+
+ if (maxp > minp) {
+ return randp < minp || randp >= maxp;
+ }
+ else if (maxp < minp) {
+ return randp < minp && randp >= maxp;
+ }
+ else
+ return true;
+
+ return false;
}
static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
@@ -195,11 +206,17 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
MLoop *mloop, *orig_mloop;
MVert *mvert, *orig_mvert;
int totvert, totpoly, totloop /* , totedge */;
- int maxvert, maxpoly, maxloop, totpart = 0, first_particle = 0;
+ int maxvert, maxpoly, maxloop, part_end = 0, part_start;
int k, p, p_skip;
short track = ob->trackflag % 3, trackneg, axis = pimd->axis;
float max_co = 0.0, min_co = 0.0, temp_co[3];
float *size = NULL;
+ float spacemat[4][4];
+ int *cd_index = NULL;
+ float *cd_value = NULL;
+ const bool use_parents = pimd->flag & eParticleInstanceFlag_Parents;
+ const bool use_children = pimd->flag & eParticleInstanceFlag_Children;
+ bool between;
trackneg = ((ob->trackflag > 2) ? 1 : 0);
@@ -217,25 +234,26 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
return derivedData;
}
- if (pimd->flag & eParticleInstanceFlag_Parents)
- totpart += psys->totpart;
- if (pimd->flag & eParticleInstanceFlag_Children) {
- if (totpart == 0)
- first_particle = psys->totpart;
- totpart += psys->totchild;
- }
+ part_start = use_parents ? 0 : psys->totpart;
+
+ part_end = 0;
+ if (use_parents)
+ part_end += psys->totpart;
+ if (use_children)
+ part_end += psys->totchild;
- if (totpart == 0)
+ if (part_end == 0)
return derivedData;
sim.scene = md->scene;
sim.ob = pimd->ob;
sim.psys = psys;
sim.psmd = psys_get_modifier(pimd->ob, psys);
+ between = (psys->part->childtype == PART_CHILD_FACES);
if (pimd->flag & eParticleInstanceFlag_UseSize) {
float *si;
- si = size = MEM_callocN(totpart * sizeof(float), "particle size array");
+ si = size = MEM_callocN(part_end * sizeof(float), "particle size array");
if (pimd->flag & eParticleInstanceFlag_Parents) {
for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++, si++)
@@ -251,6 +269,21 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
}
}
+ switch (pimd->space) {
+ case eParticleInstanceSpace_World:
+ /* particle states are in world space already */
+ unit_m4(spacemat);
+ break;
+ case eParticleInstanceSpace_Local:
+ /* get particle states in the particle object's local space */
+ invert_m4_m4(spacemat, pimd->ob->obmat);
+ break;
+ default:
+ /* should not happen */
+ BLI_assert(false);
+ break;
+ }
+
totvert = dm->getNumVerts(dm);
totpoly = dm->getNumPolys(dm);
totloop = dm->getNumLoops(dm);
@@ -261,7 +294,7 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
maxpoly = 0;
maxloop = 0;
- for (p = 0; p < totpart; p++) {
+ for (p = part_start; p < part_end; p++) {
if (particle_skip(pimd, psys, p))
continue;
@@ -290,9 +323,18 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
mloop = result->getLoopArray(result);
orig_mloop = dm->getLoopArray(dm);
- for (p = 0, p_skip = 0; p < totpart; p++) {
+ /* create customdata layer for particle index storage */
+ if (pimd->index_layer_name[0] != '\0')
+ cd_index = CustomData_add_layer_named(&result->vertData, CD_PROP_INT, CD_CALLOC,
+ NULL, maxvert, pimd->index_layer_name);
+ if (pimd->value_layer_name[0] != '\0')
+ cd_value = CustomData_add_layer_named(&result->vertData, CD_PROP_FLT, CD_CALLOC,
+ NULL, maxvert, pimd->value_layer_name);
+
+ for (p = part_start, p_skip = 0; p < part_end; p++) {
float prev_dir[3];
float frame[4]; /* frame orientation quaternion */
+ float p_random = psys_frand(psys, 77091 + 283*p);
/* skip particle? */
if (particle_skip(pimd, psys, p))
@@ -302,12 +344,18 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
for (k = 0; k < totvert; k++) {
ParticleKey state;
MVert *inMV;
- MVert *mv = mvert + p_skip * totvert + k;
+ int vindex = p_skip * totvert + k;
+ MVert *mv = mvert + vindex;
inMV = orig_mvert + k;
DM_copy_vert_data(dm, result, k, p_skip * totvert + k, 1);
*mv = *inMV;
+ if (cd_index)
+ cd_index[vindex] = p;
+ if (cd_value)
+ cd_value[vindex] = p_random;
+
/*change orientation based on object trackflag*/
copy_v3_v3(temp_co, mv->co);
mv->co[axis] = temp_co[track];
@@ -335,7 +383,7 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
mv->co[axis] = 0.0;
}
- psys_get_particle_on_path(&sim, first_particle + p, &state, 1);
+ psys_get_particle_on_path(&sim, p, &state, 1);
normalize_v3(state.vel);
@@ -344,17 +392,26 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
float hairmat[4][4];
float mat[3][3];
- if (first_particle + p < psys->totpart)
- pa = psys->particles + first_particle + p;
+ if (p < psys->totpart)
+ pa = psys->particles + p;
else {
ChildParticle *cpa = psys->child + (p - psys->totpart);
- pa = psys->particles + cpa->parent;
+ pa = psys->particles + (between? cpa->pa[0]: cpa->parent);
}
psys_mat_hair_to_global(sim.ob, sim.psmd->dm, sim.psys->part->from, pa, hairmat);
copy_m3_m4(mat, hairmat);
/* to quaternion */
mat3_to_quat(frame, mat);
+ if (pimd->rotation > 0.0f || pimd->random_rotation > 0.0f) {
+ float angle = 2.0f*M_PI * (pimd->rotation + pimd->random_rotation * (psys_frand(psys, 19957323 + p) - 0.5f));
+ float eul[3] = { 0.0f, 0.0f, angle };
+ float rot[4];
+
+ eul_to_quat(rot, eul);
+ mul_qt_qtqt(frame, frame, rot);
+ }
+
/* note: direction is same as normal vector currently,
* but best to keep this separate so the frame can be
* rotated later if necessary
@@ -391,13 +448,15 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
}
else {
state.time = -1.0;
- psys_get_particle_state(&sim, first_particle + p, &state, 1);
+ psys_get_particle_state(&sim, p, &state, 1);
}
mul_qt_v3(state.rot, mv->co);
if (pimd->flag & eParticleInstanceFlag_UseSize)
mul_v3_fl(mv->co, size[p]);
add_v3_v3(mv->co, state.co);
+
+ mul_m4_v3(spacemat, mv->co);
}
/* create polys and loops */
diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c
index 3d998f2d95a..31ff8dee562 100644
--- a/source/blender/modifiers/intern/MOD_surface.c
+++ b/source/blender/modifiers/intern/MOD_surface.c
@@ -111,6 +111,7 @@ static void deformVerts(ModifierData *md, Object *ob,
float *vec;
MVert *x, *v;
+ DM_ensure_tessface(surmd->dm);
CDDM_apply_vert_coords(surmd->dm, vertexCos);
CDDM_calc_normals(surmd->dm);
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index c95daffe557..037ce3c06e8 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -182,6 +182,7 @@ set(SRC
shader/nodes/node_shader_mix_shader.c
shader/nodes/node_shader_normal_map.c
shader/nodes/node_shader_object_info.c
+ shader/nodes/node_shader_openvdb.c
shader/nodes/node_shader_hair_info.c
shader/nodes/node_shader_output_lamp.c
shader/nodes/node_shader_output_material.c
@@ -200,6 +201,7 @@ set(SRC
shader/nodes/node_shader_tex_magic.c
shader/nodes/node_shader_tex_musgrave.c
shader/nodes/node_shader_tex_noise.c
+ shader/nodes/node_shader_tex_pointdensity.c
shader/nodes/node_shader_tex_sky.c
shader/nodes/node_shader_tex_voronoi.c
shader/nodes/node_shader_tex_wave.c
@@ -287,4 +289,12 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_OPENVDB)
+ add_definitions(-DWITH_OPENVDB)
+ add_definitions(-DOPENVDB_USE_BLOSC)
+ list(APPEND INC
+ ../../../intern/openvdb
+ )
+endif()
+
blender_add_lib(bf_nodes "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h
index 595a3b12bc6..183e4c92416 100644
--- a/source/blender/nodes/NOD_shader.h
+++ b/source/blender/nodes/NOD_shader.h
@@ -75,6 +75,7 @@ void register_node_type_sh_sepxyz(void);
void register_node_type_sh_combxyz(void);
void register_node_type_sh_hue_sat(void);
void register_node_type_sh_tex_brick(void);
+void register_node_type_sh_tex_pointdensity(void);
void register_node_type_sh_attribute(void);
void register_node_type_sh_geometry(void);
@@ -115,6 +116,7 @@ void register_node_type_sh_mix_shader(void);
void register_node_type_sh_add_shader(void);
void register_node_type_sh_uvmap(void);
void register_node_type_sh_uvalongstroke(void);
+void register_node_type_sh_openvdb(void);
void register_node_type_sh_output_lamp(void);
void register_node_type_sh_output_material(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index b33bec51c2c..47808fbec77 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -116,6 +116,7 @@ DefNode( ShaderNode, SH_NODE_TEX_MUSGRAVE, def_sh_tex_musgrave, "TE
DefNode( ShaderNode, SH_NODE_TEX_VORONOI, def_sh_tex_voronoi, "TEX_VORONOI", TexVoronoi, "Voronoi Texture", "" )
DefNode( ShaderNode, SH_NODE_TEX_CHECKER, def_sh_tex_checker, "TEX_CHECKER", TexChecker, "Checker Texture", "" )
DefNode( ShaderNode, SH_NODE_TEX_BRICK, def_sh_tex_brick, "TEX_BRICK", TexBrick, "Brick Texture", "" )
+DefNode( ShaderNode, SH_NODE_TEX_POINTDENSITY, def_sh_tex_pointdensity,"TEX_POINTDENSITY", TexPointDensity, "Point Density", "" )
DefNode( ShaderNode, SH_NODE_TEX_COORD, def_sh_tex_coord, "TEX_COORD", TexCoord, "Texture Coordinate","" )
DefNode( ShaderNode, SH_NODE_VECT_TRANSFORM, def_sh_vect_transform, "VECT_TRANSFORM", VectorTransform, "Vector Transform", "" )
DefNode( ShaderNode, SH_NODE_SEPHSV, 0, "SEPHSV", SeparateHSV, "Separate HSV", "" )
@@ -124,6 +125,7 @@ DefNode( ShaderNode, SH_NODE_UVMAP, def_sh_uvmap, "UV
DefNode( ShaderNode, SH_NODE_UVALONGSTROKE, def_sh_uvalongstroke, "UVALONGSTROKE", UVAlongStroke, "UV Along Stroke", "" )
DefNode( ShaderNode, SH_NODE_SEPXYZ, 0, "SEPXYZ", SeparateXYZ, "Separate XYZ", "" )
DefNode( ShaderNode, SH_NODE_COMBXYZ, 0, "COMBXYZ", CombineXYZ, "Combine XYZ", "" )
+DefNode( ShaderNode, SH_NODE_OPENVDB, def_sh_openvdb, "OPENVDB", OpenVDB, "OpenVDB Volume", "" )
DefNode( CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" )
DefNode( CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" )
diff --git a/source/blender/nodes/SConscript b/source/blender/nodes/SConscript
index aed846355c3..a41be4bfa82 100644
--- a/source/blender/nodes/SConscript
+++ b/source/blender/nodes/SConscript
@@ -75,6 +75,10 @@ if env['WITH_BF_COMPOSITOR']:
if env['WITH_BF_FREESTYLE']:
defs.append('WITH_FREESTYLE')
+if env['WITH_BF_OPENVDB']:
+ defs.append('WITH_OPENVDB')
+ incs += ' #/intern/openvdb'
+
env.BlenderLib ( libname = 'bf_nodes', sources = sources, includes = Split(incs), defines = defs, libtype=['core','player'], priority = [190,105] )
env.BlenderLib ( libname = 'bf_cmpnodes', sources = cmpsources, includes = Split(incs), defines = defs, libtype=['core','player'], priority = [175,101] )
env.BlenderLib ( libname = 'bf_shdnodes', sources = shdsources, includes = Split(incs), defines = defs, libtype=['core','player'], priority = [175,101] )
diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.c
index 19e93447608..3a957cedf6b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_image.c
+++ b/source/blender/nodes/composite/nodes/node_composite_image.c
@@ -200,7 +200,7 @@ static void cmp_node_image_add_multilayer_outputs(bNodeTree *ntree, bNode *node,
}
}
-static void cmp_node_image_create_outputs(bNodeTree *ntree, bNode *node)
+static void cmp_node_image_create_outputs(void *UNUSED(userdata), bNodeTree *ntree, bNode *node)
{
Image *ima = (Image *)node->id;
if (ima) {
@@ -244,80 +244,9 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, bNode *node)
cmp_node_image_add_render_pass_outputs(ntree, node, RRES_OUT_IMAGE | RRES_OUT_ALPHA);
}
-static bNodeSocket *cmp_node_image_output_find_match(bNode *UNUSED(node), bNodeSocket *newsock, ListBase *oldsocklist)
-{
- bNodeSocket *sock;
-
- for (sock = oldsocklist->first; sock; sock = sock->next)
- if (STREQ(sock->name, newsock->name))
- return sock;
- return NULL;
-}
-
-static bNodeSocket *cmp_node_image_output_relink(bNode *node, bNodeSocket *oldsock, int oldindex)
-{
- bNodeSocket *sock;
-
- /* first try to find matching socket name */
- for (sock = node->outputs.first; sock; sock = sock->next)
- if (STREQ(sock->name, oldsock->name))
- return sock;
-
- /* no matching name, simply link to same index */
- return BLI_findlink(&node->outputs, oldindex);
-}
-
-static void cmp_node_image_sync_output(bNode *UNUSED(node), bNodeSocket *UNUSED(newsock), bNodeSocket *UNUSED(oldsock))
-{
- /* pass */
-}
-
-/* XXX make this into a generic socket verification function for dynamic socket replacement (multilayer, groups, static templates) */
static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node)
{
- bNodeSocket *newsock, *oldsock, *oldsock_next;
- ListBase oldsocklist;
- int oldindex;
- bNodeLink *link;
-
- /* store current nodes in oldsocklist, then clear socket list */
- oldsocklist = node->outputs;
- BLI_listbase_clear(&node->outputs);
-
- /* XXX make callback */
- cmp_node_image_create_outputs(ntree, node);
-
- for (newsock = node->outputs.first; newsock; newsock = newsock->next) {
- /* XXX make callback */
- oldsock = cmp_node_image_output_find_match(node, newsock, &oldsocklist);
- if (oldsock) {
- /* XXX make callback */
- cmp_node_image_sync_output(node, newsock, oldsock);
- }
- }
-
- /* move links to new socket */
- for (oldsock = oldsocklist.first, oldindex = 0; oldsock; oldsock = oldsock->next, ++oldindex) {
- newsock = cmp_node_image_output_relink(node, oldsock, oldindex);
-
- if (newsock) {
- for (link = ntree->links.first; link; link = link->next) {
- if (link->fromsock == oldsock)
- link->fromsock = newsock;
- }
- }
- }
-
- /* delete old sockets
- * XXX oldsock is not actually in the node->outputs list any more,
- * but the nodeRemoveSocket function works anyway. In future this
- * should become part of the core code, so can take care of this behavior.
- */
- for (oldsock = oldsocklist.first; oldsock; oldsock = oldsock_next) {
- oldsock_next = oldsock->next;
- MEM_freeN(oldsock->storage);
- nodeRemoveSocket(ntree, node, oldsock);
- }
+ nodeSyncOutputs(ntree, node, cmp_node_image_create_outputs, NULL, NULL);
}
static void cmp_node_image_update(bNodeTree *ntree, bNode *node)
diff --git a/source/blender/nodes/shader/nodes/node_shader_openvdb.c b/source/blender/nodes/shader/nodes/node_shader_openvdb.c
new file mode 100644
index 00000000000..0b8b9968e5c
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_openvdb.c
@@ -0,0 +1,146 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "../node_shader_util.h"
+
+#ifdef WITH_OPENVDB
+# include "openvdb_capi.h"
+#endif
+
+static bNodeSocketTemplate sh_node_openvdb_in[] = {
+ {SOCK_VECTOR, 1, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE},
+ {-1, 0, ""}
+};
+
+static void node_shader_init_openvdb(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeShaderOpenVDB *vdb = MEM_callocN(sizeof(NodeShaderOpenVDB), "NodeShaderOpenVDB");
+ node->storage = vdb;
+}
+
+static void node_shader_free_openvdb(bNode *node)
+{
+ NodeShaderOpenVDB *vdb = node->storage;
+ if (vdb) {
+ BLI_freelistN(&vdb->grid_info);
+ MEM_freeN(vdb);
+ }
+}
+
+static void node_shader_copy_openvdb(bNodeTree *UNUSED(dest_ntree), bNode *dst_node, bNode *src_node)
+{
+ dst_node->storage = MEM_dupallocN(src_node->storage);
+ if (dst_node->storage) {
+ NodeShaderOpenVDB *src_vdb = src_node->storage;
+ NodeShaderOpenVDB *dst_vdb = dst_node->storage;
+
+ BLI_duplicatelist(&dst_vdb->grid_info, &src_vdb->grid_info);
+ }
+}
+
+#ifdef WITH_OPENVDB
+static void node_openvdb_get_info(void *userdata, const char *name, const char *value_type, bool is_color)
+{
+ NodeShaderOpenVDB *vdb = userdata;
+ OpenVDBGridInfo *info = MEM_callocN(sizeof(OpenVDBGridInfo), "openvdb grid info");
+
+ BLI_strncpy(info->name, name, sizeof(info->name));
+ if (STREQ(value_type, "float"))
+ info->type = OPENVDB_TYPE_FLOAT;
+ else if (STREQ(value_type, "vec3s")) {
+ if (is_color)
+ info->type = OPENVDB_TYPE_COLOR;
+ else
+ info->type = OPENVDB_TYPE_VEC3;
+ }
+ else
+ info->type = OPENVDB_TYPE_UNKNOWN;
+
+ info->flag = 0;
+
+ BLI_addtail(&vdb->grid_info, info);
+}
+
+static void node_openvdb_create_sockets(void *userdata, bNodeTree *ntree, bNode *node)
+{
+ Main *bmain = userdata;
+ NodeShaderOpenVDB *vdb = node->storage;
+ OpenVDBGridInfo *info;
+ char *filename;
+
+ if (!vdb) {
+ return;
+ }
+
+ filename = &vdb->filename[0];
+
+ if (BLI_path_is_rel(filename)) {
+ BLI_path_abs(filename, bmain->name);
+ }
+
+ BLI_freelistN(&vdb->grid_info);
+ OpenVDB_get_grid_info(filename, node_openvdb_get_info, vdb);
+
+ for (info = vdb->grid_info.first; info; info = info->next) {
+ switch (info->type) {
+ case OPENVDB_TYPE_FLOAT:
+ nodeAddStaticSocket(ntree, node, SOCK_OUT, SOCK_FLOAT, PROP_NONE, NULL, info->name);
+ break;
+ case OPENVDB_TYPE_VEC3:
+ nodeAddStaticSocket(ntree, node, SOCK_OUT, SOCK_VECTOR, PROP_NONE, NULL, info->name);
+ break;
+ case OPENVDB_TYPE_COLOR:
+ nodeAddStaticSocket(ntree, node, SOCK_OUT, SOCK_RGBA, PROP_NONE, NULL, info->name);
+ break;
+ }
+ }
+}
+
+void ntreeUpdateOpenVDBNode(Main *bmain, bNodeTree *ntree, bNode *node)
+{
+ nodeSyncOutputs(ntree, node, node_openvdb_create_sockets, NULL, bmain);
+}
+#else
+void ntreeUpdateOpenVDBNode(Main *bmain, bNodeTree *ntree, bNode *node)
+{
+ UNUSED_VARS(bmain, ntree, node);
+}
+#endif
+
+void register_node_type_sh_openvdb(void)
+{
+ static bNodeType ntype;
+
+ sh_node_type_base(&ntype, SH_NODE_OPENVDB, "OpenVDB Volume", NODE_CLASS_INPUT, 0);
+ node_type_compatibility(&ntype, NODE_NEW_SHADING);
+ node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
+ node_type_socket_templates(&ntype, sh_node_openvdb_in, NULL);
+ node_type_init(&ntype, node_shader_init_openvdb);
+ node_type_storage(&ntype, "NodeShaderOpenVDB", node_shader_free_openvdb, node_shader_copy_openvdb);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.c b/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.c
new file mode 100644
index 00000000000..3cdbfacb95c
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_pointdensity.c
@@ -0,0 +1,75 @@
+/*
+ * ***** 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) 2015 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Sergey Sharybin.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "../node_shader_util.h"
+
+/* **************** OUTPUT ******************** */
+
+static bNodeSocketTemplate sh_node_tex_pointdensity_in[] = {
+ {SOCK_VECTOR, 1, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE},
+ {-1, 0, ""}
+};
+
+static bNodeSocketTemplate sh_node_tex_pointdensity_out[] = {
+ {SOCK_FLOAT, 0, N_("Density"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f},
+ {SOCK_RGBA, 0, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
+ {-1, 0, ""}
+};
+
+static void node_shader_init_tex_pointdensity(bNodeTree *UNUSED(ntree),
+ bNode *node)
+{
+ NodeShaderTexPointDensity *point_density =
+ MEM_callocN(sizeof(NodeShaderTexPointDensity), "new pd node");
+ point_density->resolution = 100;
+ point_density->radius = 0.3f;
+ point_density->space = SHD_POINTDENSITY_SPACE_OBJECT;
+ node->storage = point_density;
+}
+
+/* node type definition */
+void register_node_type_sh_tex_pointdensity(void)
+{
+ static bNodeType ntype;
+
+ sh_node_type_base(&ntype,
+ SH_NODE_TEX_POINTDENSITY,
+ "Point Density",
+ NODE_CLASS_TEXTURE,
+ 0);
+ node_type_compatibility(&ntype, NODE_NEW_SHADING);
+ node_type_socket_templates(&ntype,
+ sh_node_tex_pointdensity_in,
+ sh_node_tex_pointdensity_out);
+ node_type_init(&ntype, node_shader_init_tex_pointdensity);
+ node_type_storage(&ntype,
+ "NodeShaderTexPointDensity",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/physics/BPH_mass_spring.h b/source/blender/physics/BPH_mass_spring.h
index de5fdd2b81d..db355c18f2e 100644
--- a/source/blender/physics/BPH_mass_spring.h
+++ b/source/blender/physics/BPH_mass_spring.h
@@ -40,7 +40,10 @@ struct Implicit_Data;
struct Object;
struct ClothModifierData;
struct ListBase;
+struct Strands;
+struct HairSimParams;
struct VoxelData;
+struct CacheEffector;
typedef enum eMassSpringSolverStatus {
BPH_SOLVER_SUCCESS = (1 << 0),
@@ -51,14 +54,19 @@ typedef enum eMassSpringSolverStatus {
struct Implicit_Data *BPH_mass_spring_solver_create(int numverts, int numsprings);
void BPH_mass_spring_solver_free(struct Implicit_Data *id);
+int BPH_mass_spring_solver_numvert(struct Implicit_Data *id);
int BPH_cloth_solver_init(struct Object *ob, struct ClothModifierData *clmd);
void BPH_cloth_solver_free(struct ClothModifierData *clmd);
int BPH_cloth_solve(struct Object *ob, float frame, struct ClothModifierData *clmd, struct ListBase *effectors);
-void BKE_cloth_solver_set_positions(struct ClothModifierData *clmd);
+void BPH_cloth_solver_set_positions(struct ClothModifierData *clmd);
bool BPH_cloth_solver_get_texture_data(struct Object *ob, struct ClothModifierData *clmd, struct VoxelData *vd);
+struct Implicit_Data *BPH_strands_solver_create(struct Strands *strands, struct HairSimParams *params);
+bool BPH_strands_solve(struct Strands *strands, float mat[4][4], struct Implicit_Data *id, struct HairSimParams *params, float frame, float frame_prev, struct Scene *scene,
+ struct ListBase *effectors, struct CacheEffector *cache_effectors, int tot_cache_effectors);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/physics/BPH_strands.h b/source/blender/physics/BPH_strands.h
new file mode 100644
index 00000000000..068c47f1c4c
--- /dev/null
+++ b/source/blender/physics/BPH_strands.h
@@ -0,0 +1,44 @@
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BPH_STRANDS_H__
+#define __BPH_STRANDS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct Object;
+struct BMEditStrands;
+
+void BPH_strands_solve_constraints(struct Object *ob, struct BMEditStrands *es, float (*orig)[3]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/blender/physics/CMakeLists.txt b/source/blender/physics/CMakeLists.txt
index 08ff1faca49..12080567585 100644
--- a/source/blender/physics/CMakeLists.txt
+++ b/source/blender/physics/CMakeLists.txt
@@ -28,6 +28,7 @@ set(INC
intern
../blenlib
../blenkernel
+ ../bmesh
../imbuf
../makesdna
../../../intern/guardedalloc
@@ -46,8 +47,10 @@ set(SRC
intern/implicit_blender.c
intern/implicit_eigen.cpp
intern/eigen_utils.h
+ intern/strands.cpp
BPH_mass_spring.h
+ BPH_strands.h
)
blender_add_lib(bf_physics "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/physics/SConscript b/source/blender/physics/SConscript
index c8165886cab..4156cecd1a3 100644
--- a/source/blender/physics/SConscript
+++ b/source/blender/physics/SConscript
@@ -35,6 +35,7 @@ incs = [
'intern',
'../blenlib',
'../blenkernel',
+ '../bmesh',
'../imbuf',
'../makesdna',
'../../../intern/guardedalloc',
diff --git a/source/blender/physics/intern/BPH_mass_spring.cpp b/source/blender/physics/intern/BPH_mass_spring.cpp
index ff211e7906a..b0c0f4fa4df 100644
--- a/source/blender/physics/intern/BPH_mass_spring.cpp
+++ b/source/blender/physics/intern/BPH_mass_spring.cpp
@@ -1,1117 +1,1724 @@
-/*
- * ***** 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) Blender Foundation
- * All rights reserved.
- *
- * The Original Code is: all of this file.
- *
- * Contributor(s): Lukas Toenne
- *
- * ***** END GPL LICENSE BLOCK *****
- */
-
-/** \file blender/physics/intern/BPH_mass_spring.cpp
- * \ingroup bph
- */
-
-extern "C" {
-#include "MEM_guardedalloc.h"
-
-#include "DNA_cloth_types.h"
-#include "DNA_scene_types.h"
-#include "DNA_object_force.h"
-#include "DNA_object_types.h"
-#include "DNA_meshdata_types.h"
-#include "DNA_modifier_types.h"
-
-#include "BLI_math.h"
-#include "BLI_linklist.h"
-#include "BLI_utildefines.h"
-
-#include "BKE_cloth.h"
-#include "BKE_collision.h"
-#include "BKE_effect.h"
-}
-
-#include "BPH_mass_spring.h"
-#include "implicit.h"
-
-static float I3[3][3] = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}};
-
-/* Number of off-diagonal non-zero matrix blocks.
- * Basically there is one of these for each vertex-vertex interaction.
- */
-static int cloth_count_nondiag_blocks(Cloth *cloth)
-{
- LinkNode *link;
- int nondiag = 0;
-
- for (link = cloth->springs; link; link = link->next) {
- ClothSpring *spring = (ClothSpring *)link->link;
- switch (spring->type) {
- case CLOTH_SPRING_TYPE_BENDING_ANG:
- /* angular bending combines 3 vertices */
- nondiag += 3;
- break;
-
- default:
- /* all other springs depend on 2 vertices only */
- nondiag += 1;
- break;
- }
- }
-
- return nondiag;
-}
-
-int BPH_cloth_solver_init(Object *UNUSED(ob), ClothModifierData *clmd)
-{
- Cloth *cloth = clmd->clothObject;
- ClothVertex *verts = cloth->verts;
- const float ZERO[3] = {0.0f, 0.0f, 0.0f};
- Implicit_Data *id;
- unsigned int i, nondiag;
-
- nondiag = cloth_count_nondiag_blocks(cloth);
- cloth->implicit = id = BPH_mass_spring_solver_create(cloth->numverts, nondiag);
-
- for (i = 0; i < cloth->numverts; i++) {
- BPH_mass_spring_set_vertex_mass(id, i, verts[i].mass);
- }
-
- for (i = 0; i < cloth->numverts; i++) {
- BPH_mass_spring_set_motion_state(id, i, verts[i].x, ZERO);
- }
-
- return 1;
-}
-
-void BPH_cloth_solver_free(ClothModifierData *clmd)
-{
- Cloth *cloth = clmd->clothObject;
-
- if (cloth->implicit) {
- BPH_mass_spring_solver_free(cloth->implicit);
- cloth->implicit = NULL;
- }
-}
-
-void BKE_cloth_solver_set_positions(ClothModifierData *clmd)
-{
- Cloth *cloth = clmd->clothObject;
- ClothVertex *verts = cloth->verts;
- unsigned int numverts = cloth->numverts, i;
- ClothHairData *cloth_hairdata = clmd->hairdata;
- Implicit_Data *id = cloth->implicit;
-
- for (i = 0; i < numverts; i++) {
- if (cloth_hairdata) {
- ClothHairData *root = &cloth_hairdata[i];
- BPH_mass_spring_set_rest_transform(id, i, root->rot);
- }
- else
- BPH_mass_spring_set_rest_transform(id, i, I3);
-
- BPH_mass_spring_set_motion_state(id, i, verts[i].x, verts[i].v);
- }
-}
-
-static bool collision_response(ClothModifierData *clmd, CollisionModifierData *collmd, CollPair *collpair, float dt, float restitution, float r_impulse[3])
-{
- Cloth *cloth = clmd->clothObject;
- int index = collpair->ap1;
- bool result = false;
-
- float v1[3], v2_old[3], v2_new[3], v_rel_old[3], v_rel_new[3];
- float epsilon2 = BLI_bvhtree_getepsilon(collmd->bvhtree);
-
- float margin_distance = (float)collpair->distance - epsilon2;
- float mag_v_rel;
-
- zero_v3(r_impulse);
-
- if (margin_distance > 0.0f)
- return false; /* XXX tested before already? */
-
- /* only handle static collisions here */
- if ( collpair->flag & COLLISION_IN_FUTURE )
- return false;
-
- /* velocity */
- copy_v3_v3(v1, cloth->verts[index].v);
- collision_get_collider_velocity(v2_old, v2_new, collmd, collpair);
- /* relative velocity = velocity of the cloth point relative to the collider */
- sub_v3_v3v3(v_rel_old, v1, v2_old);
- sub_v3_v3v3(v_rel_new, v1, v2_new);
- /* normal component of the relative velocity */
- mag_v_rel = dot_v3v3(v_rel_old, collpair->normal);
-
- /* only valid when moving toward the collider */
- if (mag_v_rel < -ALMOST_ZERO) {
- float v_nor_old, v_nor_new;
- float v_tan_old[3], v_tan_new[3];
- float bounce, repulse;
-
- /* Collision response based on
- * "Simulating Complex Hair with Robust Collision Handling" (Choe, Choi, Ko, ACM SIGGRAPH 2005)
- * http://graphics.snu.ac.kr/publications/2005-choe-HairSim/Choe_2005_SCA.pdf
- */
-
- v_nor_old = mag_v_rel;
- v_nor_new = dot_v3v3(v_rel_new, collpair->normal);
-
- madd_v3_v3v3fl(v_tan_old, v_rel_old, collpair->normal, -v_nor_old);
- madd_v3_v3v3fl(v_tan_new, v_rel_new, collpair->normal, -v_nor_new);
-
- bounce = -v_nor_old * restitution;
-
- repulse = -margin_distance / dt; /* base repulsion velocity in normal direction */
- /* XXX this clamping factor is quite arbitrary ...
- * not sure if there is a more scientific approach, but seems to give good results
- */
- CLAMP(repulse, 0.0f, 4.0f * bounce);
-
- if (margin_distance < -epsilon2) {
- mul_v3_v3fl(r_impulse, collpair->normal, max_ff(repulse, bounce) - v_nor_new);
- }
- else {
- bounce = 0.0f;
- mul_v3_v3fl(r_impulse, collpair->normal, repulse - v_nor_new);
- }
-
- result = true;
- }
-
- return result;
-}
-
-/* Init constraint matrix
- * This is part of the modified CG method suggested by Baraff/Witkin in
- * "Large Steps in Cloth Simulation" (Siggraph 1998)
- */
-static void cloth_setup_constraints(ClothModifierData *clmd, ColliderContacts *contacts, int totcolliders, float dt)
-{
- Cloth *cloth = clmd->clothObject;
- Implicit_Data *data = cloth->implicit;
- ClothVertex *verts = cloth->verts;
- int numverts = cloth->numverts;
- int i, j, v;
-
- const float ZERO[3] = {0.0f, 0.0f, 0.0f};
-
- BPH_mass_spring_clear_constraints(data);
-
- for (v = 0; v < numverts; v++) {
- if (verts[v].flags & CLOTH_VERT_FLAG_PINNED) {
- /* pinned vertex constraints */
- BPH_mass_spring_add_constraint_ndof0(data, v, ZERO); /* velocity is defined externally */
- }
-
- verts[v].impulse_count = 0;
- }
-
- for (i = 0; i < totcolliders; ++i) {
- ColliderContacts *ct = &contacts[i];
- for (j = 0; j < ct->totcollisions; ++j) {
- CollPair *collpair = &ct->collisions[j];
-// float restitution = (1.0f - clmd->coll_parms->damping) * (1.0f - ct->ob->pd->pdef_sbdamp);
- float restitution = 0.0f;
- int v = collpair->face1;
- float impulse[3];
-
- /* pinned verts handled separately */
- if (verts[v].flags & CLOTH_VERT_FLAG_PINNED)
- continue;
-
- /* XXX cheap way of avoiding instability from multiple collisions in the same step
- * this should eventually be supported ...
- */
- if (verts[v].impulse_count > 0)
- continue;
-
- /* calculate collision response */
- if (!collision_response(clmd, ct->collmd, collpair, dt, restitution, impulse))
- continue;
-
- BPH_mass_spring_add_constraint_ndof2(data, v, collpair->normal, impulse);
- ++verts[v].impulse_count;
- }
- }
-}
-
-/* computes where the cloth would be if it were subject to perfectly stiff edges
- * (edge distance constraints) in a lagrangian solver. then add forces to help
- * guide the implicit solver to that state. this function is called after
- * collisions*/
-static int UNUSED_FUNCTION(cloth_calc_helper_forces)(Object *UNUSED(ob), ClothModifierData *clmd, float (*initial_cos)[3], float UNUSED(step), float dt)
-{
- Cloth *cloth= clmd->clothObject;
- float (*cos)[3] = (float (*)[3])MEM_callocN(sizeof(float)*3*cloth->numverts, "cos cloth_calc_helper_forces");
- float *masses = (float *)MEM_callocN(sizeof(float)*cloth->numverts, "cos cloth_calc_helper_forces");
- LinkNode *node;
- ClothSpring *spring;
- ClothVertex *cv;
- int i, steps;
-
- cv = cloth->verts;
- for (i=0; i<cloth->numverts; i++, cv++) {
- copy_v3_v3(cos[i], cv->tx);
-
- if (cv->goal == 1.0f || len_squared_v3v3(initial_cos[i], cv->tx) != 0.0f) {
- masses[i] = 1e+10;
- }
- else {
- masses[i] = cv->mass;
- }
- }
-
- steps = 55;
- for (i=0; i<steps; i++) {
- for (node=cloth->springs; node; node=node->next) {
- /* ClothVertex *cv1, *cv2; */ /* UNUSED */
- int v1, v2;
- float len, c, l, vec[3];
-
- spring = (ClothSpring *)node->link;
- if (spring->type != CLOTH_SPRING_TYPE_STRUCTURAL && spring->type != CLOTH_SPRING_TYPE_SHEAR)
- continue;
-
- v1 = spring->ij; v2 = spring->kl;
- /* cv1 = cloth->verts + v1; */ /* UNUSED */
- /* cv2 = cloth->verts + v2; */ /* UNUSED */
- len = len_v3v3(cos[v1], cos[v2]);
-
- sub_v3_v3v3(vec, cos[v1], cos[v2]);
- normalize_v3(vec);
-
- c = (len - spring->restlen);
- if (c == 0.0f)
- continue;
-
- l = c / ((1.0f / masses[v1]) + (1.0f / masses[v2]));
-
- mul_v3_fl(vec, -(1.0f / masses[v1]) * l);
- add_v3_v3(cos[v1], vec);
-
- sub_v3_v3v3(vec, cos[v2], cos[v1]);
- normalize_v3(vec);
-
- mul_v3_fl(vec, -(1.0f / masses[v2]) * l);
- add_v3_v3(cos[v2], vec);
- }
- }
-
- cv = cloth->verts;
- for (i=0; i<cloth->numverts; i++, cv++) {
- float vec[3];
-
- /*compute forces*/
- sub_v3_v3v3(vec, cos[i], cv->tx);
- mul_v3_fl(vec, cv->mass*dt*20.0f);
- add_v3_v3(cv->tv, vec);
- //copy_v3_v3(cv->tx, cos[i]);
- }
-
- MEM_freeN(cos);
- MEM_freeN(masses);
-
- return 1;
-}
-
-BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s, float time)
-{
- Cloth *cloth = clmd->clothObject;
- ClothSimSettings *parms = clmd->sim_parms;
- Implicit_Data *data = cloth->implicit;
- ClothVertex *verts = cloth->verts;
-
- bool no_compress = parms->flags & CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS;
-
- zero_v3(s->f);
- zero_m3(s->dfdx);
- zero_m3(s->dfdv);
-
- s->flags &= ~CLOTH_SPRING_FLAG_NEEDED;
-
- // calculate force of structural + shear springs
- if ((s->type & CLOTH_SPRING_TYPE_STRUCTURAL) || (s->type & CLOTH_SPRING_TYPE_SHEAR) || (s->type & CLOTH_SPRING_TYPE_SEWING) ) {
-#ifdef CLOTH_FORCE_SPRING_STRUCTURAL
- float k, scaling;
-
- s->flags |= CLOTH_SPRING_FLAG_NEEDED;
-
- scaling = parms->structural + s->stiffness * fabsf(parms->max_struct - parms->structural);
- k = scaling / (parms->avg_spring_len + FLT_EPSILON);
-
- if (s->type & CLOTH_SPRING_TYPE_SEWING) {
- // TODO: verify, half verified (couldn't see error)
- // sewing springs usually have a large distance at first so clamp the force so we don't get tunnelling through colission objects
- BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, parms->max_sewing, s->f, s->dfdx, s->dfdv);
- }
- else {
- BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, 0.0f, s->f, s->dfdx, s->dfdv);
- }
-#endif
- }
- else if (s->type & CLOTH_SPRING_TYPE_GOAL) {
-#ifdef CLOTH_FORCE_SPRING_GOAL
- float goal_x[3], goal_v[3];
- float k, scaling;
-
- s->flags |= CLOTH_SPRING_FLAG_NEEDED;
-
- // current_position = xold + t * (newposition - xold)
- interp_v3_v3v3(goal_x, verts[s->ij].xold, verts[s->ij].xconst, time);
- sub_v3_v3v3(goal_v, verts[s->ij].xconst, verts[s->ij].xold); // distance covered over dt==1
-
- scaling = parms->goalspring + s->stiffness * fabsf(parms->max_struct - parms->goalspring);
- k = verts[s->ij].goal * scaling / (parms->avg_spring_len + FLT_EPSILON);
-
- BPH_mass_spring_force_spring_goal(data, s->ij, goal_x, goal_v, k, parms->goalfrict * 0.01f, s->f, s->dfdx, s->dfdv);
-#endif
- }
- else if (s->type & CLOTH_SPRING_TYPE_BENDING) { /* calculate force of bending springs */
-#ifdef CLOTH_FORCE_SPRING_BEND
- float kb, cb, scaling;
-
- s->flags |= CLOTH_SPRING_FLAG_NEEDED;
-
- scaling = parms->bending + s->stiffness * fabsf(parms->max_bend - parms->bending);
- kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
-
- scaling = parms->bending_damping;
- cb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
-
- BPH_mass_spring_force_spring_bending(data, s->ij, s->kl, s->restlen, kb, cb, s->f, s->dfdx, s->dfdv);
-#endif
- }
- else if (s->type & CLOTH_SPRING_TYPE_BENDING_ANG) {
-#ifdef CLOTH_FORCE_SPRING_BEND
- float kb, cb, scaling;
-
- s->flags |= CLOTH_SPRING_FLAG_NEEDED;
-
- /* XXX WARNING: angular bending springs for hair apply stiffness factor as an overall factor, unlike cloth springs!
- * this is crap, but needed due to cloth/hair mixing ...
- * max_bend factor is not even used for hair, so ...
- */
- scaling = s->stiffness * parms->bending;
- kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
-
- scaling = parms->bending_damping;
- cb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
-
- /* XXX assuming same restlen for ij and jk segments here, this can be done correctly for hair later */
- BPH_mass_spring_force_spring_bending_angular(data, s->ij, s->kl, s->mn, s->target, kb, cb);
-
-#if 0
- {
- float x_kl[3], x_mn[3], v[3], d[3];
-
- BPH_mass_spring_get_motion_state(data, s->kl, x_kl, v);
- BPH_mass_spring_get_motion_state(data, s->mn, x_mn, v);
-
- BKE_sim_debug_data_add_dot(clmd->debug_data, x_kl, 0.9, 0.9, 0.9, "target", 7980, s->kl);
- BKE_sim_debug_data_add_line(clmd->debug_data, x_kl, x_mn, 0.8, 0.8, 0.8, "target", 7981, s->kl);
-
- copy_v3_v3(d, s->target);
- BKE_sim_debug_data_add_vector(clmd->debug_data, x_kl, d, 0.8, 0.8, 0.2, "target", 7982, s->kl);
-
-// copy_v3_v3(d, s->target_ij);
-// BKE_sim_debug_data_add_vector(clmd->debug_data, x, d, 1, 0.4, 0.4, "target", 7983, s->kl);
- }
-#endif
-#endif
- }
-}
-
-static void hair_get_boundbox(ClothModifierData *clmd, float gmin[3], float gmax[3])
-{
- Cloth *cloth = clmd->clothObject;
- Implicit_Data *data = cloth->implicit;
- unsigned int numverts = cloth->numverts;
- int i;
-
- INIT_MINMAX(gmin, gmax);
- for (i = 0; i < numverts; i++) {
- float x[3];
- BPH_mass_spring_get_motion_state(data, i, x, NULL);
- DO_MINMAX(x, gmin, gmax);
- }
-}
-
-static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), ListBase *effectors, float time)
-{
- /* Collect forces and derivatives: F, dFdX, dFdV */
- Cloth *cloth = clmd->clothObject;
- Implicit_Data *data = cloth->implicit;
- unsigned int i = 0;
- float drag = clmd->sim_parms->Cvi * 0.01f; /* viscosity of air scaled in percent */
- float gravity[3] = {0.0f, 0.0f, 0.0f};
- MFace *mfaces = cloth->mfaces;
- unsigned int numverts = cloth->numverts;
- ClothVertex *vert;
-
-#ifdef CLOTH_FORCE_GRAVITY
- /* global acceleration (gravitation) */
- if (clmd->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
- /* scale gravity force */
- mul_v3_v3fl(gravity, clmd->scene->physics_settings.gravity, 0.001f * clmd->sim_parms->effector_weights->global_gravity);
- }
- vert = cloth->verts;
- for (i = 0; i < cloth->numverts; i++, vert++) {
- BPH_mass_spring_force_gravity(data, i, vert->mass, gravity);
- }
-#endif
-
- /* cloth_calc_volume_force(clmd); */
-
-#ifdef CLOTH_FORCE_DRAG
- BPH_mass_spring_force_drag(data, drag);
-#endif
-
- /* handle external forces like wind */
- if (effectors) {
- /* cache per-vertex forces to avoid redundant calculation */
- float (*winvec)[3] = (float (*)[3])MEM_callocN(sizeof(float) * 3 * numverts, "effector forces");
- for (i = 0; i < cloth->numverts; i++) {
- float x[3], v[3];
- EffectedPoint epoint;
-
- BPH_mass_spring_get_motion_state(data, i, x, v);
- pd_point_from_loc(clmd->scene, x, v, i, &epoint);
- pdDoEffectors(effectors, NULL, clmd->sim_parms->effector_weights, &epoint, winvec[i], NULL);
- }
-
- for (i = 0; i < cloth->numfaces; i++) {
- MFace *mf = &mfaces[i];
- BPH_mass_spring_force_face_wind(data, mf->v1, mf->v2, mf->v3, mf->v4, winvec);
- }
-
- /* Hair has only edges */
- if (cloth->numfaces == 0) {
-#if 0
- ClothHairData *hairdata = clmd->hairdata;
- ClothHairData *hair_ij, *hair_kl;
-
- for (LinkNode *link = cloth->springs; link; link = link->next) {
- ClothSpring *spring = (ClothSpring *)link->link;
- if (spring->type == CLOTH_SPRING_TYPE_STRUCTURAL) {
- if (hairdata) {
- hair_ij = &hairdata[spring->ij];
- hair_kl = &hairdata[spring->kl];
- BPH_mass_spring_force_edge_wind(data, spring->ij, spring->kl, hair_ij->radius, hair_kl->radius, winvec);
- }
- else
- BPH_mass_spring_force_edge_wind(data, spring->ij, spring->kl, 1.0f, 1.0f, winvec);
- }
- }
-#else
- ClothHairData *hairdata = clmd->hairdata;
-
- vert = cloth->verts;
- for (i = 0; i < cloth->numverts; i++, vert++) {
- if (hairdata) {
- ClothHairData *hair = &hairdata[i];
- BPH_mass_spring_force_vertex_wind(data, i, hair->radius, winvec);
- }
- else
- BPH_mass_spring_force_vertex_wind(data, i, 1.0f, winvec);
- }
- }
-#endif
-
- MEM_freeN(winvec);
- }
-
- // calculate spring forces
- for (LinkNode *link = cloth->springs; link; link = link->next) {
- ClothSpring *spring = (ClothSpring *)link->link;
- // only handle active springs
- if (!(spring->flags & CLOTH_SPRING_FLAG_DEACTIVATE))
- cloth_calc_spring_force(clmd, spring, time);
- }
-}
-
-/* returns vertexes' motion state */
-BLI_INLINE void cloth_get_grid_location(Implicit_Data *data, float cell_scale, const float cell_offset[3],
- int index, float x[3], float v[3])
-{
- BPH_mass_spring_get_position(data, index, x);
- BPH_mass_spring_get_new_velocity(data, index, v);
-
- mul_v3_fl(x, cell_scale);
- add_v3_v3(x, cell_offset);
-}
-
-/* returns next spring forming a continous hair sequence */
-BLI_INLINE LinkNode *hair_spring_next(LinkNode *spring_link)
-{
- ClothSpring *spring = (ClothSpring *)spring_link->link;
- LinkNode *next = spring_link->next;
- if (next) {
- ClothSpring *next_spring = (ClothSpring *)next->link;
- if (next_spring->type == CLOTH_SPRING_TYPE_STRUCTURAL && next_spring->kl == spring->ij)
- return next;
- }
- return NULL;
-}
-
-/* XXX this is nasty: cloth meshes do not explicitly store
- * the order of hair segments!
- * We have to rely on the spring build function for now,
- * which adds structural springs in reverse order:
- * (3,4), (2,3), (1,2)
- * This is currently the only way to figure out hair geometry inside this code ...
- */
-static LinkNode *cloth_continuum_add_hair_segments(HairGrid *grid, const float cell_scale, const float cell_offset[3], Cloth *cloth, LinkNode *spring_link)
-{
- Implicit_Data *data = cloth->implicit;
- LinkNode *next_spring_link = NULL; /* return value */
- ClothSpring *spring1, *spring2, *spring3;
- // ClothVertex *verts = cloth->verts;
- // ClothVertex *vert3, *vert4;
- float x1[3], v1[3], x2[3], v2[3], x3[3], v3[3], x4[3], v4[3];
- float dir1[3], dir2[3], dir3[3];
-
- spring1 = NULL;
- spring2 = NULL;
- spring3 = (ClothSpring *)spring_link->link;
-
- zero_v3(x1); zero_v3(v1);
- zero_v3(dir1);
- zero_v3(x2); zero_v3(v2);
- zero_v3(dir2);
-
- // vert3 = &verts[spring3->kl];
- cloth_get_grid_location(data, cell_scale, cell_offset, spring3->kl, x3, v3);
- // vert4 = &verts[spring3->ij];
- cloth_get_grid_location(data, cell_scale, cell_offset, spring3->ij, x4, v4);
- sub_v3_v3v3(dir3, x4, x3);
- normalize_v3(dir3);
-
- while (spring_link) {
- /* move on */
- spring1 = spring2;
- spring2 = spring3;
-
- // vert3 = vert4;
-
- copy_v3_v3(x1, x2); copy_v3_v3(v1, v2);
- copy_v3_v3(x2, x3); copy_v3_v3(v2, v3);
- copy_v3_v3(x3, x4); copy_v3_v3(v3, v4);
-
- copy_v3_v3(dir1, dir2);
- copy_v3_v3(dir2, dir3);
-
- /* read next segment */
- next_spring_link = spring_link->next;
- spring_link = hair_spring_next(spring_link);
-
- if (spring_link) {
- spring3 = (ClothSpring *)spring_link->link;
- // vert4 = &verts[spring3->ij];
- cloth_get_grid_location(data, cell_scale, cell_offset, spring3->ij, x4, v4);
- sub_v3_v3v3(dir3, x4, x3);
- normalize_v3(dir3);
- }
- else {
- spring3 = NULL;
- // vert4 = NULL;
- zero_v3(x4); zero_v3(v4);
- zero_v3(dir3);
- }
-
- BPH_hair_volume_add_segment(grid, x1, v1, x2, v2, x3, v3, x4, v4,
- spring1 ? dir1 : NULL,
- dir2,
- spring3 ? dir3 : NULL);
- }
-
- return next_spring_link;
-}
-
-static void cloth_continuum_fill_grid(HairGrid *grid, Cloth *cloth)
-{
-#if 0
- Implicit_Data *data = cloth->implicit;
- int numverts = cloth->numverts;
- ClothVertex *vert;
- int i;
-
- for (i = 0, vert = cloth->verts; i < numverts; i++, vert++) {
- float x[3], v[3];
-
- cloth_get_vertex_motion_state(data, vert, x, v);
- BPH_hair_volume_add_vertex(grid, x, v);
- }
-#else
- LinkNode *link;
- float cellsize, gmin[3], cell_scale, cell_offset[3];
-
- /* scale and offset for transforming vertex locations into grid space
- * (cell size is 0..1, gmin becomes origin)
- */
- BPH_hair_volume_grid_geometry(grid, &cellsize, NULL, gmin, NULL);
- cell_scale = cellsize > 0.0f ? 1.0f / cellsize : 0.0f;
- mul_v3_v3fl(cell_offset, gmin, cell_scale);
- negate_v3(cell_offset);
-
- link = cloth->springs;
- while (link) {
- ClothSpring *spring = (ClothSpring *)link->link;
- if (spring->type == CLOTH_SPRING_TYPE_STRUCTURAL)
- link = cloth_continuum_add_hair_segments(grid, cell_scale, cell_offset, cloth, link);
- else
- link = link->next;
- }
-#endif
- BPH_hair_volume_normalize_vertex_grid(grid);
-}
-
-static void cloth_continuum_step(ClothModifierData *clmd, float dt)
-{
- ClothSimSettings *parms = clmd->sim_parms;
- Cloth *cloth = clmd->clothObject;
- Implicit_Data *data = cloth->implicit;
- int numverts = cloth->numverts;
- ClothVertex *vert;
-
- const float fluid_factor = 0.95f; /* blend between PIC and FLIP methods */
- float smoothfac = parms->velocity_smooth;
- /* XXX FIXME arbitrary factor!!! this should be based on some intuitive value instead,
- * like number of hairs per cell and time decay instead of "strength"
- */
- float density_target = parms->density_target;
- float density_strength = parms->density_strength;
- float gmin[3], gmax[3];
- int i;
-
- /* clear grid info */
- zero_v3_int(clmd->hair_grid_res);
- zero_v3(clmd->hair_grid_min);
- zero_v3(clmd->hair_grid_max);
- clmd->hair_grid_cellsize = 0.0f;
-
- hair_get_boundbox(clmd, gmin, gmax);
-
- /* gather velocities & density */
- if (smoothfac > 0.0f || density_strength > 0.0f) {
- HairGrid *grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_cell_size, gmin, gmax);
-
- cloth_continuum_fill_grid(grid, cloth);
-
- /* main hair continuum solver */
- BPH_hair_volume_solve_divergence(grid, dt, density_target, density_strength);
-
- for (i = 0, vert = cloth->verts; i < numverts; i++, vert++) {
- float x[3], v[3], nv[3];
-
- /* calculate volumetric velocity influence */
- BPH_mass_spring_get_position(data, i, x);
- BPH_mass_spring_get_new_velocity(data, i, v);
-
- BPH_hair_volume_grid_velocity(grid, x, v, fluid_factor, nv);
-
- interp_v3_v3v3(nv, v, nv, smoothfac);
-
- /* apply on hair data */
- BPH_mass_spring_set_new_velocity(data, i, nv);
- }
-
- /* store basic grid info in the modifier data */
- BPH_hair_volume_grid_geometry(grid, &clmd->hair_grid_cellsize, clmd->hair_grid_res, clmd->hair_grid_min, clmd->hair_grid_max);
-
-#if 0 /* DEBUG hair velocity vector field */
- {
- const int size = 64;
- int i, j;
- float offset[3], a[3], b[3];
- const int axis = 0;
- const float shift = 0.0f;
-
- copy_v3_v3(offset, clmd->hair_grid_min);
- zero_v3(a);
- zero_v3(b);
-
- offset[axis] = shift * clmd->hair_grid_cellsize;
- a[(axis+1) % 3] = clmd->hair_grid_max[(axis+1) % 3] - clmd->hair_grid_min[(axis+1) % 3];
- b[(axis+2) % 3] = clmd->hair_grid_max[(axis+2) % 3] - clmd->hair_grid_min[(axis+2) % 3];
-
- BKE_sim_debug_data_clear_category(clmd->debug_data, "grid velocity");
- for (j = 0; j < size; ++j) {
- for (i = 0; i < size; ++i) {
- float x[3], v[3], gvel[3], gvel_smooth[3], gdensity;
-
- madd_v3_v3v3fl(x, offset, a, (float)i / (float)(size-1));
- madd_v3_v3fl(x, b, (float)j / (float)(size-1));
- zero_v3(v);
-
- BPH_hair_volume_grid_interpolate(grid, x, &gdensity, gvel, gvel_smooth, NULL, NULL);
-
-// BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity, 0.7, 0.3, 1, "grid density", i, j, 3111);
- if (!is_zero_v3(gvel) || !is_zero_v3(gvel_smooth)) {
- float dvel[3];
- sub_v3_v3v3(dvel, gvel_smooth, gvel);
-// BKE_sim_debug_data_add_vector(clmd->debug_data, x, gvel, 0.4, 0, 1, "grid velocity", i, j, 3112);
-// BKE_sim_debug_data_add_vector(clmd->debug_data, x, gvel_smooth, 0.6, 1, 1, "grid velocity", i, j, 3113);
- BKE_sim_debug_data_add_vector(clmd->debug_data, x, dvel, 0.4, 1, 0.7, "grid velocity", i, j, 3114);
-#if 0
- if (gdensity > 0.0f) {
- float col0[3] = {0.0, 0.0, 0.0};
- float col1[3] = {0.0, 1.0, 0.0};
- float col[3];
-
- interp_v3_v3v3(col, col0, col1, CLAMPIS(gdensity * clmd->sim_parms->density_strength, 0.0, 1.0));
-// BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity * clmd->sim_parms->density_strength, 0, 1, 0.4, "grid velocity", i, j, 3115);
-// BKE_sim_debug_data_add_dot(clmd->debug_data, x, col[0], col[1], col[2], "grid velocity", i, j, 3115);
- BKE_sim_debug_data_add_circle(clmd->debug_data, x, 0.01f, col[0], col[1], col[2], "grid velocity", i, j, 3115);
- }
-#endif
- }
- }
- }
- }
-#endif
-
- BPH_hair_volume_free_vertex_grid(grid);
- }
-}
-
-#if 0
-static void cloth_calc_volume_force(ClothModifierData *clmd)
-{
- ClothSimSettings *parms = clmd->sim_parms;
- Cloth *cloth = clmd->clothObject;
- Implicit_Data *data = cloth->implicit;
- int numverts = cloth->numverts;
- ClothVertex *vert;
-
- /* 2.0f is an experimental value that seems to give good results */
- float smoothfac = 2.0f * parms->velocity_smooth;
- float collfac = 2.0f * parms->collider_friction;
- float pressfac = parms->pressure;
- float minpress = parms->pressure_threshold;
- float gmin[3], gmax[3];
- int i;
-
- hair_get_boundbox(clmd, gmin, gmax);
-
- /* gather velocities & density */
- if (smoothfac > 0.0f || pressfac > 0.0f) {
- HairVertexGrid *vertex_grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_res, gmin, gmax);
-
- vert = cloth->verts;
- for (i = 0; i < numverts; i++, vert++) {
- float x[3], v[3];
-
- if (vert->solver_index < 0) {
- copy_v3_v3(x, vert->x);
- copy_v3_v3(v, vert->v);
- }
- else {
- BPH_mass_spring_get_motion_state(data, vert->solver_index, x, v);
- }
- BPH_hair_volume_add_vertex(vertex_grid, x, v);
- }
- BPH_hair_volume_normalize_vertex_grid(vertex_grid);
-
- vert = cloth->verts;
- for (i = 0; i < numverts; i++, vert++) {
- float x[3], v[3], f[3], dfdx[3][3], dfdv[3][3];
-
- if (vert->solver_index < 0)
- continue;
-
- /* calculate volumetric forces */
- BPH_mass_spring_get_motion_state(data, vert->solver_index, x, v);
- BPH_hair_volume_vertex_grid_forces(vertex_grid, x, v, smoothfac, pressfac, minpress, f, dfdx, dfdv);
- /* apply on hair data */
- BPH_mass_spring_force_extern(data, vert->solver_index, f, dfdx, dfdv);
- }
-
- BPH_hair_volume_free_vertex_grid(vertex_grid);
- }
-}
-#endif
-
-/* old collision stuff for cloth, use for continuity
- * until a good replacement is ready
- */
-static void cloth_collision_solve_extra(Object *ob, ClothModifierData *clmd, ListBase *effectors, float frame, float step, float dt)
-{
- Cloth *cloth = clmd->clothObject;
- Implicit_Data *id = cloth->implicit;
- ClothVertex *verts = cloth->verts;
- int numverts = cloth->numverts;
- const float spf = (float)clmd->sim_parms->stepsPerFrame / clmd->sim_parms->timescale;
-
- bool do_extra_solve;
- int i;
-
- if (!(clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED))
- return;
- if (!clmd->clothObject->bvhtree)
- return;
-
- // update verts to current positions
- for (i = 0; i < numverts; i++) {
- BPH_mass_spring_get_new_position(id, i, verts[i].tx);
-
- sub_v3_v3v3(verts[i].tv, verts[i].tx, verts[i].txold);
- copy_v3_v3(verts[i].v, verts[i].tv);
- }
-
-#if 0 /* unused */
- for (i=0, cv=cloth->verts; i<cloth->numverts; i++, cv++) {
- copy_v3_v3(initial_cos[i], cv->tx);
- }
-#endif
-
- // call collision function
- // TODO: check if "step" or "step+dt" is correct - dg
- do_extra_solve = cloth_bvh_objcollision(ob, clmd, step / clmd->sim_parms->timescale, dt / clmd->sim_parms->timescale);
-
- // copy corrected positions back to simulation
- for (i = 0; i < numverts; i++) {
- float curx[3];
- BPH_mass_spring_get_position(id, i, curx);
- // correct velocity again, just to be sure we had to change it due to adaptive collisions
- sub_v3_v3v3(verts[i].tv, verts[i].tx, curx);
- }
-
- if (do_extra_solve) {
-// cloth_calc_helper_forces(ob, clmd, initial_cos, step/clmd->sim_parms->timescale, dt/clmd->sim_parms->timescale);
-
- for (i = 0; i < numverts; i++) {
-
- float newv[3];
-
- if ((clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) && (verts [i].flags & CLOTH_VERT_FLAG_PINNED))
- continue;
-
- BPH_mass_spring_set_new_position(id, i, verts[i].tx);
- mul_v3_v3fl(newv, verts[i].tv, spf);
- BPH_mass_spring_set_new_velocity(id, i, newv);
- }
- }
-
- // X = Xnew;
- BPH_mass_spring_apply_result(id);
-
- if (do_extra_solve) {
- ImplicitSolverResult result;
-
- /* initialize forces to zero */
- BPH_mass_spring_clear_forces(id);
-
- // calculate forces
- cloth_calc_force(clmd, frame, effectors, step);
-
- // calculate new velocity and position
- BPH_mass_spring_solve_velocities(id, dt, &result);
-// cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame);
-
- /* note: positions are advanced only once in the main solver step! */
-
- BPH_mass_spring_apply_result(id);
- }
-}
-
-static void cloth_clear_result(ClothModifierData *clmd)
-{
- ClothSolverResult *sres = clmd->solver_result;
-
- sres->status = 0;
- sres->max_error = sres->min_error = sres->avg_error = 0.0f;
- sres->max_iterations = sres->min_iterations = 0;
- sres->avg_iterations = 0.0f;
-}
-
-static void cloth_record_result(ClothModifierData *clmd, ImplicitSolverResult *result, int steps)
-{
- ClothSolverResult *sres = clmd->solver_result;
-
- if (sres->status) { /* already initialized ? */
- /* error only makes sense for successful iterations */
- if (result->status == BPH_SOLVER_SUCCESS) {
- sres->min_error = min_ff(sres->min_error, result->error);
- sres->max_error = max_ff(sres->max_error, result->error);
- sres->avg_error += result->error / (float)steps;
- }
-
- sres->min_iterations = min_ii(sres->min_iterations, result->iterations);
- sres->max_iterations = max_ii(sres->max_iterations, result->iterations);
- sres->avg_iterations += (float)result->iterations / (float)steps;
- }
- else {
- /* error only makes sense for successful iterations */
- if (result->status == BPH_SOLVER_SUCCESS) {
- sres->min_error = sres->max_error = result->error;
- sres->avg_error += result->error / (float)steps;
- }
-
- sres->min_iterations = sres->max_iterations = result->iterations;
- sres->avg_iterations += (float)result->iterations / (float)steps;
- }
-
- sres->status |= result->status;
-}
-
-int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *effectors)
-{
- /* Hair currently is a cloth sim in disguise ...
- * Collision detection and volumetrics work differently then.
- * Bad design, TODO
- */
- const bool is_hair = (clmd->hairdata != NULL);
-
- unsigned int i=0;
- float step=0.0f, tf=clmd->sim_parms->timescale;
- Cloth *cloth = clmd->clothObject;
- ClothVertex *verts = cloth->verts/*, *cv*/;
- unsigned int numverts = cloth->numverts;
- float dt = clmd->sim_parms->timescale / clmd->sim_parms->stepsPerFrame;
- Implicit_Data *id = cloth->implicit;
- ColliderContacts *contacts = NULL;
- int totcolliders = 0;
-
- BKE_sim_debug_data_clear_category("collision");
-
- if (!clmd->solver_result)
- clmd->solver_result = (ClothSolverResult *)MEM_callocN(sizeof(ClothSolverResult), "cloth solver result");
- cloth_clear_result(clmd);
-
- if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) { /* do goal stuff */
- for (i = 0; i < numverts; i++) {
- // update velocities with constrained velocities from pinned verts
- if (verts [i].flags & CLOTH_VERT_FLAG_PINNED) {
- float v[3];
- sub_v3_v3v3(v, verts[i].xconst, verts[i].xold);
- // mul_v3_fl(v, clmd->sim_parms->stepsPerFrame);
- BPH_mass_spring_set_velocity(id, i, v);
- }
- }
- }
-
- while (step < tf) {
- ImplicitSolverResult result;
-
- /* copy velocities for collision */
- for (i = 0; i < numverts; i++) {
- BPH_mass_spring_get_motion_state(id, i, NULL, verts[i].tv);
- copy_v3_v3(verts[i].v, verts[i].tv);
- }
-
- if (is_hair) {
- /* determine contact points */
- if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) {
- cloth_find_point_contacts(ob, clmd, 0.0f, tf, &contacts, &totcolliders);
- }
-
- /* setup vertex constraints for pinned vertices and contacts */
- cloth_setup_constraints(clmd, contacts, totcolliders, dt);
- }
- else {
- /* setup vertex constraints for pinned vertices */
- cloth_setup_constraints(clmd, NULL, 0, dt);
- }
-
- /* initialize forces to zero */
- BPH_mass_spring_clear_forces(id);
-
- // damping velocity for artistic reasons
- // this is a bad way to do it, should be removed imo - lukas_t
- if (clmd->sim_parms->vel_damping != 1.0f) {
- for (i = 0; i < numverts; i++) {
- float v[3];
- BPH_mass_spring_get_motion_state(id, i, NULL, v);
- mul_v3_fl(v, clmd->sim_parms->vel_damping);
- BPH_mass_spring_set_velocity(id, i, v);
- }
- }
-
- // calculate forces
- cloth_calc_force(clmd, frame, effectors, step);
-
- // calculate new velocity and position
- BPH_mass_spring_solve_velocities(id, dt, &result);
- cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame);
-
- if (is_hair) {
- cloth_continuum_step(clmd, dt);
- }
-
- BPH_mass_spring_solve_positions(id, dt);
-
- if (!is_hair) {
- cloth_collision_solve_extra(ob, clmd, effectors, frame, step, dt);
- }
-
- BPH_mass_spring_apply_result(id);
-
- /* move pinned verts to correct position */
- for (i = 0; i < numverts; i++) {
- if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) {
- if (verts[i].flags & CLOTH_VERT_FLAG_PINNED) {
- float x[3];
- interp_v3_v3v3(x, verts[i].xold, verts[i].xconst, step + dt);
- BPH_mass_spring_set_position(id, i, x);
- }
- }
-
- BPH_mass_spring_get_motion_state(id, i, verts[i].txold, NULL);
- }
-
- /* free contact points */
- if (contacts) {
- cloth_free_contacts(contacts, totcolliders);
- }
-
- step += dt;
- }
-
- /* copy results back to cloth data */
- for (i = 0; i < numverts; i++) {
- BPH_mass_spring_get_motion_state(id, i, verts[i].x, verts[i].v);
- copy_v3_v3(verts[i].txold, verts[i].x);
- }
-
- return 1;
-}
-
-bool BPH_cloth_solver_get_texture_data(Object *UNUSED(ob), ClothModifierData *clmd, VoxelData *vd)
-{
- Cloth *cloth = clmd->clothObject;
- HairGrid *grid;
- float gmin[3], gmax[3];
-
- if (!clmd->clothObject || !clmd->clothObject->implicit)
- return false;
-
- hair_get_boundbox(clmd, gmin, gmax);
-
- grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_cell_size, gmin, gmax);
- cloth_continuum_fill_grid(grid, cloth);
-
- BPH_hair_volume_get_texture_data(grid, vd);
-
- BPH_hair_volume_free_vertex_grid(grid);
-
- return true;
-}
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/physics/intern/BPH_mass_spring.cpp
+ * \ingroup bph
+ */
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "DNA_cache_library_types.h"
+#include "DNA_cloth_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_object_force.h"
+#include "DNA_object_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+
+#include "BLI_math.h"
+#include "BLI_linklist.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_cache_library.h"
+#include "BKE_cloth.h"
+#include "BKE_collision.h"
+#include "BKE_colortools.h"
+#include "BKE_effect.h"
+#include "BKE_strands.h"
+}
+
+#include "BPH_mass_spring.h"
+#include "implicit.h"
+
+static float I3[3][3] = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}};
+
+/* Number of off-diagonal non-zero matrix blocks.
+ * Basically there is one of these for each vertex-vertex interaction.
+ */
+static int cloth_count_nondiag_blocks(Cloth *cloth)
+{
+ LinkNode *link;
+ int nondiag = 0;
+
+ for (link = cloth->springs; link; link = link->next) {
+ ClothSpring *spring = (ClothSpring *)link->link;
+
+ switch (spring->type) {
+ case CLOTH_SPRING_TYPE_BENDING_ANG:
+ /* angular bending combines 3 vertices */
+ nondiag += 3;
+ break;
+
+ default:
+ /* all other springs depend on 2 vertices only */
+ nondiag += 1;
+ break;
+ }
+ }
+
+ return nondiag;
+}
+
+static struct Implicit_Data *cloth_solver_init_data(Cloth *cloth)
+{
+ int totvert = cloth->numverts;
+
+ if (cloth->implicit && totvert != BPH_mass_spring_solver_numvert(cloth->implicit)) {
+ BPH_mass_spring_solver_free(cloth->implicit);
+ cloth->implicit = NULL;
+ }
+
+ if (!cloth->implicit) {
+ int nondiag = cloth_count_nondiag_blocks(cloth);
+ cloth->implicit = BPH_mass_spring_solver_create(totvert, nondiag);
+ }
+
+ return cloth->implicit;
+}
+
+int BPH_cloth_solver_init(Object *UNUSED(ob), ClothModifierData *clmd)
+{
+ Cloth *cloth = clmd->clothObject;
+ ClothVertex *vert;
+ const float ZERO[3] = {0.0f, 0.0f, 0.0f};
+ Implicit_Data *id = cloth_solver_init_data(cloth);
+ unsigned int i;
+
+ vert = cloth->verts;
+ for (i = 0; i < cloth->numverts; ++i, ++vert) {
+ BPH_mass_spring_set_vertex_mass(id, i, vert->mass);
+ BPH_mass_spring_set_motion_state(id, i, vert->x, ZERO);
+ }
+
+ return 1;
+}
+
+void BPH_cloth_solver_free(ClothModifierData *clmd)
+{
+ Cloth *cloth = clmd->clothObject;
+
+ if (cloth->implicit) {
+ BPH_mass_spring_solver_free(cloth->implicit);
+ cloth->implicit = NULL;
+ }
+}
+
+void BPH_cloth_solver_set_positions(ClothModifierData *clmd)
+{
+ Cloth *cloth = clmd->clothObject;
+ ClothVertex *vert;
+ unsigned int numverts = cloth->numverts, i;
+ ClothHairData *cloth_hairdata = clmd->hairdata;
+ Implicit_Data *id = cloth_solver_init_data(cloth);
+
+ vert = cloth->verts;
+ for (i = 0; i < numverts; ++i, ++vert) {
+ if (cloth_hairdata) {
+ ClothHairData *root = &cloth_hairdata[i];
+ BPH_mass_spring_set_rest_transform(id, i, root->rot);
+ }
+ else
+ BPH_mass_spring_set_rest_transform(id, i, I3);
+
+ BPH_mass_spring_set_motion_state(id, i, vert->x, vert->v);
+ }
+}
+
+static bool collision_response(ClothModifierData *clmd, CollisionModifierData *collmd, CollPair *collpair, float dt, float restitution, float r_impulse[3])
+{
+ Cloth *cloth = clmd->clothObject;
+ int index = collpair->ap1;
+ bool result = false;
+
+ float v1[3], v2_old[3], v2_new[3], v_rel_old[3], v_rel_new[3];
+ float epsilon2 = BLI_bvhtree_getepsilon(collmd->bvhtree);
+
+ float margin_distance = (float)collpair->distance - epsilon2;
+ float mag_v_rel;
+
+ zero_v3(r_impulse);
+
+ if (margin_distance > 0.0f)
+ return false; /* XXX tested before already? */
+
+ /* only handle static collisions here */
+ if ( collpair->flag & COLLISION_IN_FUTURE )
+ return false;
+
+ /* velocity */
+ copy_v3_v3(v1, cloth->verts[index].v);
+ collision_get_collider_velocity(v2_old, v2_new, collmd, collpair);
+ /* relative velocity = velocity of the cloth point relative to the collider */
+ sub_v3_v3v3(v_rel_old, v1, v2_old);
+ sub_v3_v3v3(v_rel_new, v1, v2_new);
+ /* normal component of the relative velocity */
+ mag_v_rel = dot_v3v3(v_rel_old, collpair->normal);
+
+ /* only valid when moving toward the collider */
+ if (mag_v_rel < -ALMOST_ZERO) {
+ float v_nor_old, v_nor_new;
+ float v_tan_old[3], v_tan_new[3];
+ float bounce, repulse;
+
+ /* Collision response based on
+ * "Simulating Complex Hair with Robust Collision Handling" (Choe, Choi, Ko, ACM SIGGRAPH 2005)
+ * http://graphics.snu.ac.kr/publications/2005-choe-HairSim/Choe_2005_SCA.pdf
+ */
+
+ v_nor_old = mag_v_rel;
+ v_nor_new = dot_v3v3(v_rel_new, collpair->normal);
+
+ madd_v3_v3v3fl(v_tan_old, v_rel_old, collpair->normal, -v_nor_old);
+ madd_v3_v3v3fl(v_tan_new, v_rel_new, collpair->normal, -v_nor_new);
+
+ bounce = -v_nor_old * restitution;
+
+ repulse = -margin_distance / dt; /* base repulsion velocity in normal direction */
+ /* XXX this clamping factor is quite arbitrary ...
+ * not sure if there is a more scientific approach, but seems to give good results
+ */
+ CLAMP(repulse, 0.0f, 4.0f * bounce);
+
+ if (margin_distance < -epsilon2) {
+ mul_v3_v3fl(r_impulse, collpair->normal, max_ff(repulse, bounce) - v_nor_new);
+ }
+ else {
+ bounce = 0.0f;
+ mul_v3_v3fl(r_impulse, collpair->normal, repulse - v_nor_new);
+ }
+
+ result = true;
+ }
+
+ return result;
+}
+
+/* Init constraint matrix
+ * This is part of the modified CG method suggested by Baraff/Witkin in
+ * "Large Steps in Cloth Simulation" (Siggraph 1998)
+ */
+static void cloth_setup_constraints(ClothModifierData *clmd, ColliderContacts *contacts, int totcolliders, float dt)
+{
+ Cloth *cloth = clmd->clothObject;
+ Implicit_Data *data = cloth->implicit;
+ ClothVertex *vert;
+ int numverts = cloth->numverts;
+ int i, j, v;
+
+ const float ZERO[3] = {0.0f, 0.0f, 0.0f};
+
+ BPH_mass_spring_clear_constraints(data);
+
+ vert = cloth->verts;
+ for (v = 0; v < numverts; ++v, ++vert) {
+ if (vert->flags & CLOTH_VERT_FLAG_PINNED) {
+ /* pinned vertex constraints */
+ BPH_mass_spring_add_constraint_ndof0(data, v, ZERO); /* velocity is defined externally */
+ }
+
+ vert->impulse_count = 0;
+ }
+
+ for (i = 0; i < totcolliders; ++i) {
+ ColliderContacts *ct = &contacts[i];
+ for (j = 0; j < ct->totcollisions; ++j) {
+ CollPair *collpair = &ct->collisions[j];
+ int v = collpair->face1;
+ float impulse[3];
+
+// float restitution = (1.0f - clmd->coll_parms->damping) * (1.0f - ct->ob->pd->pdef_sbdamp);
+ float restitution = 0.0f;
+
+ vert = &cloth->verts[v];
+ /* pinned verts handled separately */
+ if (vert->flags & CLOTH_VERT_FLAG_PINNED)
+ continue;
+
+ /* XXX cheap way of avoiding instability from multiple collisions in the same step
+ * this should eventually be supported ...
+ */
+ if (vert->impulse_count > 0)
+ continue;
+
+ /* calculate collision response */
+ if (!collision_response(clmd, ct->collmd, collpair, dt, restitution, impulse))
+ continue;
+
+ BPH_mass_spring_add_constraint_ndof2(data, i, collpair->normal, impulse);
+ ++vert->impulse_count;
+ }
+ }
+}
+
+/* computes where the cloth would be if it were subject to perfectly stiff edges
+ * (edge distance constraints) in a lagrangian solver. then add forces to help
+ * guide the implicit solver to that state. this function is called after
+ * collisions*/
+static int UNUSED_FUNCTION(cloth_calc_helper_forces)(Object *UNUSED(ob), ClothModifierData *clmd, float (*initial_cos)[3], float UNUSED(step), float dt)
+{
+ Cloth *cloth= clmd->clothObject;
+ float (*cos)[3] = (float (*)[3])MEM_callocN(sizeof(float)*3*cloth->numverts, "cos cloth_calc_helper_forces");
+ float *masses = (float *)MEM_callocN(sizeof(float)*cloth->numverts, "cos cloth_calc_helper_forces");
+ LinkNode *node;
+ ClothSpring *spring;
+ ClothVertex *vert;
+ int i, steps;
+
+ vert = cloth->verts;
+ for (i=0; i<cloth->numverts; i++, vert++) {
+ copy_v3_v3(cos[i], vert->tx);
+
+ if (vert->goal == 1.0f || len_squared_v3v3(initial_cos[i], vert->tx) != 0.0f) {
+ masses[i] = 1e+10;
+ }
+ else {
+ masses[i] = vert->mass;
+ }
+ }
+
+ steps = 55;
+ for (i=0; i<steps; i++) {
+ for (node=cloth->springs; node; node=node->next) {
+ /* ClothVertex *cv1, *cv2; */ /* UNUSED */
+ int v1, v2;
+ float len, c, l, vec[3];
+
+ spring = (ClothSpring *)node->link;
+ if (spring->type != CLOTH_SPRING_TYPE_STRUCTURAL && spring->type != CLOTH_SPRING_TYPE_SHEAR)
+ continue;
+
+ v1 = spring->ij; v2 = spring->kl;
+ /* cv1 = cloth->verts + v1; */ /* UNUSED */
+ /* cv2 = cloth->verts + v2; */ /* UNUSED */
+ len = len_v3v3(cos[v1], cos[v2]);
+
+ sub_v3_v3v3(vec, cos[v1], cos[v2]);
+ normalize_v3(vec);
+
+ c = (len - spring->restlen);
+ if (c == 0.0f)
+ continue;
+
+ l = c / ((1.0f / masses[v1]) + (1.0f / masses[v2]));
+
+ mul_v3_fl(vec, -(1.0f / masses[v1]) * l);
+ add_v3_v3(cos[v1], vec);
+
+ sub_v3_v3v3(vec, cos[v2], cos[v1]);
+ normalize_v3(vec);
+
+ mul_v3_fl(vec, -(1.0f / masses[v2]) * l);
+ add_v3_v3(cos[v2], vec);
+ }
+ }
+
+ vert = cloth->verts;
+ for (i=0; i<cloth->numverts; i++, vert++) {
+ float vec[3];
+
+ /*compute forces*/
+ sub_v3_v3v3(vec, cos[i], vert->tx);
+ mul_v3_fl(vec, vert->mass*dt*20.0f);
+ add_v3_v3(vert->tv, vec);
+ //copy_v3_v3(cv->tx, cos[i]);
+ }
+
+ MEM_freeN(cos);
+ MEM_freeN(masses);
+
+ return 1;
+}
+
+BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s, float time)
+{
+ Cloth *cloth = clmd->clothObject;
+ ClothSimSettings *parms = clmd->sim_parms;
+ Implicit_Data *data = cloth->implicit;
+ ClothVertex *verts = cloth->verts;
+
+ bool no_compress = parms->flags & CLOTH_SIMSETTINGS_FLAG_NO_SPRING_COMPRESS;
+
+ zero_v3(s->f);
+ zero_m3(s->dfdx);
+ zero_m3(s->dfdv);
+
+ s->flags &= ~CLOTH_SPRING_FLAG_NEEDED;
+
+ // calculate force of structural + shear springs
+ if ((s->type & CLOTH_SPRING_TYPE_STRUCTURAL) || (s->type & CLOTH_SPRING_TYPE_SHEAR) || (s->type & CLOTH_SPRING_TYPE_SEWING) ) {
+#ifdef CLOTH_FORCE_SPRING_STRUCTURAL
+ float k, scaling;
+
+ s->flags |= CLOTH_SPRING_FLAG_NEEDED;
+
+ scaling = parms->structural + s->stiffness * fabsf(parms->max_struct - parms->structural);
+ k = scaling / (parms->avg_spring_len + FLT_EPSILON);
+
+ if (s->type & CLOTH_SPRING_TYPE_SEWING) {
+ // TODO: verify, half verified (couldn't see error)
+ // sewing springs usually have a large distance at first so clamp the force so we don't get tunnelling through colission objects
+ BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, parms->max_sewing, s->f, s->dfdx, s->dfdv);
+ }
+ else {
+ BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->Cdis, no_compress, 0.0f, s->f, s->dfdx, s->dfdv);
+ }
+#endif
+ }
+ else if (s->type & CLOTH_SPRING_TYPE_GOAL) {
+#ifdef CLOTH_FORCE_SPRING_GOAL
+ float goal_x[3], goal_v[3];
+ float k, scaling;
+
+ s->flags |= CLOTH_SPRING_FLAG_NEEDED;
+
+ // current_position = xold + t * (newposition - xold)
+ interp_v3_v3v3(goal_x, verts[s->ij].xold, verts[s->ij].xconst, time);
+ sub_v3_v3v3(goal_v, verts[s->ij].xconst, verts[s->ij].xold); // distance covered over dt==1
+
+ scaling = parms->goalspring + s->stiffness * fabsf(parms->max_struct - parms->goalspring);
+ k = verts[s->ij].goal * scaling / (parms->avg_spring_len + FLT_EPSILON);
+
+ BPH_mass_spring_force_spring_goal(data, s->ij, goal_x, goal_v, k, parms->goalfrict * 0.01f, s->f, s->dfdx, s->dfdv);
+#endif
+ }
+ else if (s->type & CLOTH_SPRING_TYPE_BENDING) { /* calculate force of bending springs */
+#ifdef CLOTH_FORCE_SPRING_BEND
+ float kb, cb, scaling;
+
+ s->flags |= CLOTH_SPRING_FLAG_NEEDED;
+
+ scaling = parms->bending + s->stiffness * fabsf(parms->max_bend - parms->bending);
+ kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
+
+ scaling = parms->bending_damping;
+ cb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
+
+ BPH_mass_spring_force_spring_bending(data, s->ij, s->kl, s->restlen, kb, cb, s->f, s->dfdx, s->dfdv);
+#endif
+ }
+ else if (s->type & CLOTH_SPRING_TYPE_BENDING_ANG) {
+#ifdef CLOTH_FORCE_SPRING_BEND
+ float kb, cb, scaling;
+
+ s->flags |= CLOTH_SPRING_FLAG_NEEDED;
+
+ /* XXX WARNING: angular bending springs for hair apply stiffness factor as an overall factor, unlike cloth springs!
+ * this is crap, but needed due to cloth/hair mixing ...
+ * max_bend factor is not even used for hair, so ...
+ */
+ scaling = s->stiffness * parms->bending;
+ kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
+
+ scaling = parms->bending_damping;
+ cb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
+
+ /* XXX assuming same restlen for ij and jk segments here, this can be done correctly for hair later */
+ BPH_mass_spring_force_spring_bending_angular(data, s->ij, s->kl, s->mn, s->target, kb, cb, NULL, NULL, NULL);
+
+#if 0
+ {
+ float x_kl[3], x_mn[3], v[3], d[3];
+
+ BPH_mass_spring_get_motion_state(data, s->kl, x_kl, v);
+ BPH_mass_spring_get_motion_state(data, s->mn, x_mn, v);
+
+ BKE_sim_debug_data_add_dot(clmd->debug_data, x_kl, 0.9, 0.9, 0.9, "target", 7980, s->kl);
+ BKE_sim_debug_data_add_line(clmd->debug_data, x_kl, x_mn, 0.8, 0.8, 0.8, "target", 7981, s->kl);
+
+ copy_v3_v3(d, s->target);
+ BKE_sim_debug_data_add_vector(clmd->debug_data, x_kl, d, 0.8, 0.8, 0.2, "target", 7982, s->kl);
+
+// copy_v3_v3(d, s->target_ij);
+// BKE_sim_debug_data_add_vector(clmd->debug_data, x, d, 1, 0.4, 0.4, "target", 7983, s->kl);
+ }
+#endif
+#endif
+ }
+}
+
+static void hair_get_boundbox(ClothModifierData *clmd, float gmin[3], float gmax[3])
+{
+ Cloth *cloth = clmd->clothObject;
+ Implicit_Data *data = cloth->implicit;
+ unsigned int numverts = cloth->numverts;
+ ClothVertex *vert;
+ int i;
+
+ INIT_MINMAX(gmin, gmax);
+ vert = cloth->verts;
+ for (i = 0; i < numverts; i++, vert++) {
+ float x[3];
+ BPH_mass_spring_get_motion_state(data, i, x, NULL);
+ DO_MINMAX(x, gmin, gmax);
+ }
+}
+
+static void cloth_calc_force(ClothModifierData *clmd, float UNUSED(frame), ListBase *effectors, float time)
+{
+ /* Collect forces and derivatives: F, dFdX, dFdV */
+ Cloth *cloth = clmd->clothObject;
+ Implicit_Data *data = cloth->implicit;
+ unsigned int i = 0;
+ float drag = clmd->sim_parms->Cvi * 0.01f; /* viscosity of air scaled in percent */
+ float gravity[3] = {0.0f, 0.0f, 0.0f};
+ MFace *mfaces = cloth->mfaces;
+ unsigned int numverts = cloth->numverts;
+ ClothVertex *vert;
+
+#ifdef CLOTH_FORCE_GRAVITY
+ /* global acceleration (gravitation) */
+ if (clmd->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
+ /* scale gravity force */
+ mul_v3_v3fl(gravity, clmd->scene->physics_settings.gravity, 0.001f * clmd->sim_parms->effector_weights->global_gravity);
+ }
+ vert = cloth->verts;
+ for (i = 0; i < cloth->numverts; i++, vert++) {
+ BPH_mass_spring_force_gravity(data, i, vert->mass, gravity);
+ }
+#endif
+
+ /* cloth_calc_volume_force(clmd); */
+
+#ifdef CLOTH_FORCE_DRAG
+ BPH_mass_spring_force_drag(data, drag);
+#endif
+
+ /* handle external forces like wind */
+ if (effectors) {
+ /* cache per-vertex forces to avoid redundant calculation */
+ float (*winvec)[3] = (float (*)[3])MEM_callocN(sizeof(float) * 3 * numverts, "effector forces");
+
+ vert = cloth->verts;
+ for (i = 0; i < cloth->numverts; i++, vert++) {
+ float x[3], v[3];
+ EffectedPoint epoint;
+
+ BPH_mass_spring_get_motion_state(data, i, x, v);
+ pd_point_from_loc(clmd->scene, x, v, i, &epoint);
+ pdDoEffectors(effectors, NULL, clmd->sim_parms->effector_weights, &epoint, winvec[i], NULL);
+ }
+
+ for (i = 0; i < cloth->numfaces; i++) {
+ MFace *mf = &mfaces[i];
+ BPH_mass_spring_force_face_wind(data, mf->v1, mf->v2, mf->v3, mf->v4, winvec);
+ }
+
+ /* Hair has only edges */
+ if (cloth->numfaces == 0) {
+ const float density = 0.01f; /* XXX arbitrary value, corresponds to effect of air density */
+#if 0
+ ClothHairData *hairdata = clmd->hairdata;
+ ClothHairData *hair_ij, *hair_kl;
+
+ for (LinkNode *link = cloth->springs; link; link = link->next) {
+ ClothSpring *spring = (ClothSpring *)link->link;
+ if (spring->type == CLOTH_SPRING_TYPE_STRUCTURAL) {
+ ClothVertex *verts = cloth->verts;
+ int si_ij = verts[spring->ij].solver_index;
+ int si_kl = verts[spring->kl].solver_index;
+
+ if (si_ij < 0 || si_kl < 0)
+ continue;
+
+ if (hairdata) {
+ hair_ij = &hairdata[spring->ij];
+ hair_kl = &hairdata[spring->kl];
+ BPH_mass_spring_force_edge_wind(data, si_ij, si_kl, hair_ij->radius, hair_kl->radius, winvec);
+ }
+ else
+ BPH_mass_spring_force_edge_wind(data, si_ij, si_kl, 1.0f, 1.0f, winvec);
+ }
+ }
+#else
+ ClothHairData *hairdata = clmd->hairdata;
+
+ vert = cloth->verts;
+ for (i = 0; i < cloth->numverts; i++, vert++) {
+ if (hairdata) {
+ ClothHairData *hair = &hairdata[i];
+ BPH_mass_spring_force_vertex_wind(data, i, hair->radius * density, winvec);
+ }
+ else
+ BPH_mass_spring_force_vertex_wind(data, i, density, winvec);
+ }
+#endif
+ }
+
+ MEM_freeN(winvec);
+ }
+
+ // calculate spring forces
+ for (LinkNode *link = cloth->springs; link; link = link->next) {
+ ClothSpring *spring = (ClothSpring *)link->link;
+ // only handle active springs
+ if (!(spring->flags & CLOTH_SPRING_FLAG_DEACTIVATE))
+ cloth_calc_spring_force(clmd, spring, time);
+ }
+}
+
+/* returns vertexes' motion state */
+BLI_INLINE void cloth_get_grid_location(Implicit_Data *data, float cell_scale, const float cell_offset[3],
+ int index, float x[3], float v[3])
+{
+ BPH_mass_spring_get_position(data, index, x);
+ BPH_mass_spring_get_new_velocity(data, index, v);
+
+ mul_v3_fl(x, cell_scale);
+ add_v3_v3(x, cell_offset);
+}
+
+/* returns next spring forming a continous hair sequence */
+BLI_INLINE LinkNode *hair_spring_next(LinkNode *spring_link)
+{
+ ClothSpring *spring = (ClothSpring *)spring_link->link;
+ LinkNode *next = spring_link->next;
+ if (next) {
+ ClothSpring *next_spring = (ClothSpring *)next->link;
+ if (next_spring->type == CLOTH_SPRING_TYPE_STRUCTURAL && next_spring->kl == spring->ij)
+ return next;
+ }
+ return NULL;
+}
+
+/* XXX this is nasty: cloth meshes do not explicitly store
+ * the order of hair segments!
+ * We have to rely on the spring build function for now,
+ * which adds structural springs in reverse order:
+ * (3,4), (2,3), (1,2)
+ * This is currently the only way to figure out hair geometry inside this code ...
+ */
+static LinkNode *cloth_continuum_add_hair_segments(HairGrid *grid, const float cell_scale, const float cell_offset[3], Cloth *cloth, LinkNode *spring_link)
+{
+ Implicit_Data *data = cloth->implicit;
+ LinkNode *next_spring_link = NULL; /* return value */
+ ClothSpring *spring1, *spring2, *spring3;
+ float x1[3], v1[3], x2[3], v2[3], x3[3], v3[3], x4[3], v4[3];
+ float dir1[3], dir2[3], dir3[3];
+
+ spring1 = NULL;
+ spring2 = NULL;
+ spring3 = (ClothSpring *)spring_link->link;
+
+ zero_v3(x1); zero_v3(v1);
+ zero_v3(dir1);
+ zero_v3(x2); zero_v3(v2);
+ zero_v3(dir2);
+
+ cloth_get_grid_location(data, cell_scale, cell_offset, spring3->kl, x3, v3);
+ cloth_get_grid_location(data, cell_scale, cell_offset, spring3->ij, x4, v4);
+ sub_v3_v3v3(dir3, x4, x3);
+ normalize_v3(dir3);
+
+ while (spring_link) {
+ /* move on */
+ spring1 = spring2;
+ spring2 = spring3;
+
+ copy_v3_v3(x1, x2); copy_v3_v3(v1, v2);
+ copy_v3_v3(x2, x3); copy_v3_v3(v2, v3);
+ copy_v3_v3(x3, x4); copy_v3_v3(v3, v4);
+
+ copy_v3_v3(dir1, dir2);
+ copy_v3_v3(dir2, dir3);
+
+ /* read next segment */
+ next_spring_link = spring_link->next;
+ spring_link = hair_spring_next(spring_link);
+
+ if (spring_link) {
+ spring3 = (ClothSpring *)spring_link->link;
+ cloth_get_grid_location(data, cell_scale, cell_offset, spring3->ij, x4, v4);
+ sub_v3_v3v3(dir3, x4, x3);
+ normalize_v3(dir3);
+ }
+ else {
+ spring3 = NULL;
+ zero_v3(x4); zero_v3(v4);
+ zero_v3(dir3);
+ }
+
+ BPH_hair_volume_add_segment(grid, x1, v1, x2, v2, x3, v3, x4, v4,
+ spring1 ? dir1 : NULL,
+ dir2,
+ spring3 ? dir3 : NULL);
+ }
+
+ return next_spring_link;
+}
+
+static void cloth_continuum_fill_grid(HairGrid *grid, Cloth *cloth)
+{
+#if 0
+ Implicit_Data *data = cloth->implicit;
+ int numverts = cloth->numverts;
+ ClothVertex *vert;
+ int i;
+
+ for (i = 0, vert = cloth->verts; i < numverts; i++, vert++) {
+ float x[3], v[3];
+
+ cloth_get_vertex_motion_state(data, vert, x, v);
+ BPH_hair_volume_add_vertex(grid, x, v);
+ }
+#else
+ LinkNode *link;
+ float cellsize, gmin[3], cell_scale, cell_offset[3];
+
+ /* scale and offset for transforming vertex locations into grid space
+ * (cell size is 0..1, gmin becomes origin)
+ */
+ BPH_hair_volume_grid_geometry(grid, &cellsize, NULL, gmin, NULL);
+ cell_scale = cellsize > 0.0f ? 1.0f / cellsize : 0.0f;
+ mul_v3_v3fl(cell_offset, gmin, cell_scale);
+ negate_v3(cell_offset);
+
+ link = cloth->springs;
+ while (link) {
+ ClothSpring *spring = (ClothSpring *)link->link;
+ if (spring->type == CLOTH_SPRING_TYPE_STRUCTURAL)
+ link = cloth_continuum_add_hair_segments(grid, cell_scale, cell_offset, cloth, link);
+ else
+ link = link->next;
+ }
+#endif
+ BPH_hair_volume_normalize_vertex_grid(grid);
+}
+
+static void cloth_continuum_step(ClothModifierData *clmd, float dt)
+{
+ ClothSimSettings *parms = clmd->sim_parms;
+ Cloth *cloth = clmd->clothObject;
+ Implicit_Data *data = cloth->implicit;
+ int numverts = cloth->numverts;
+ ClothVertex *vert;
+
+ const float fluid_factor = 0.95f; /* blend between PIC and FLIP methods */
+ float smoothfac = parms->velocity_smooth;
+ /* XXX FIXME arbitrary factor!!! this should be based on some intuitive value instead,
+ * like number of hairs per cell and time decay instead of "strength"
+ */
+ float density_target = parms->density_target;
+ float density_strength = parms->density_strength;
+ float gmin[3], gmax[3];
+ int i;
+
+ /* clear grid info */
+ zero_v3_int(clmd->hair_grid_res);
+ zero_v3(clmd->hair_grid_min);
+ zero_v3(clmd->hair_grid_max);
+ clmd->hair_grid_cellsize = 0.0f;
+
+ hair_get_boundbox(clmd, gmin, gmax);
+
+ /* gather velocities & density */
+ if (smoothfac > 0.0f || density_strength > 0.0f) {
+ HairGrid *grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_cell_size, gmin, gmax);
+
+ cloth_continuum_fill_grid(grid, cloth);
+
+ /* main hair continuum solver */
+ BPH_hair_volume_solve_divergence(grid, dt, density_target, density_strength);
+
+ for (i = 0, vert = cloth->verts; i < numverts; i++, vert++) {
+ float x[3], v[3], nv[3];
+
+ /* calculate volumetric velocity influence */
+ BPH_mass_spring_get_position(data, i, x);
+ BPH_mass_spring_get_new_velocity(data, i, v);
+
+ BPH_hair_volume_grid_velocity(grid, x, v, fluid_factor, nv);
+
+ interp_v3_v3v3(nv, v, nv, smoothfac);
+
+ /* apply on hair data */
+ BPH_mass_spring_set_new_velocity(data, i, nv);
+ }
+
+ /* store basic grid info in the modifier data */
+ BPH_hair_volume_grid_geometry(grid, &clmd->hair_grid_cellsize, clmd->hair_grid_res, clmd->hair_grid_min, clmd->hair_grid_max);
+
+#if 0 /* DEBUG hair velocity vector field */
+ {
+ const int size = 64;
+ int i, j;
+ float offset[3], a[3], b[3];
+ const int axis = 0;
+ const float shift = 0.0f;
+
+ copy_v3_v3(offset, clmd->hair_grid_min);
+ zero_v3(a);
+ zero_v3(b);
+
+ offset[axis] = shift * clmd->hair_grid_cellsize;
+ a[(axis+1) % 3] = clmd->hair_grid_max[(axis+1) % 3] - clmd->hair_grid_min[(axis+1) % 3];
+ b[(axis+2) % 3] = clmd->hair_grid_max[(axis+2) % 3] - clmd->hair_grid_min[(axis+2) % 3];
+
+ BKE_sim_debug_data_clear_category(clmd->debug_data, "grid velocity");
+ for (j = 0; j < size; ++j) {
+ for (i = 0; i < size; ++i) {
+ float x[3], v[3], gvel[3], gvel_smooth[3], gdensity;
+
+ madd_v3_v3v3fl(x, offset, a, (float)i / (float)(size-1));
+ madd_v3_v3fl(x, b, (float)j / (float)(size-1));
+ zero_v3(v);
+
+ BPH_hair_volume_grid_interpolate(grid, x, &gdensity, gvel, gvel_smooth, NULL, NULL);
+
+// BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity, 0.7, 0.3, 1, "grid density", i, j, 3111);
+ if (!is_zero_v3(gvel) || !is_zero_v3(gvel_smooth)) {
+ float dvel[3];
+ sub_v3_v3v3(dvel, gvel_smooth, gvel);
+// BKE_sim_debug_data_add_vector(clmd->debug_data, x, gvel, 0.4, 0, 1, "grid velocity", i, j, 3112);
+// BKE_sim_debug_data_add_vector(clmd->debug_data, x, gvel_smooth, 0.6, 1, 1, "grid velocity", i, j, 3113);
+ BKE_sim_debug_data_add_vector(clmd->debug_data, x, dvel, 0.4, 1, 0.7, "grid velocity", i, j, 3114);
+#if 0
+ if (gdensity > 0.0f) {
+ float col0[3] = {0.0, 0.0, 0.0};
+ float col1[3] = {0.0, 1.0, 0.0};
+ float col[3];
+
+ interp_v3_v3v3(col, col0, col1, CLAMPIS(gdensity * clmd->sim_parms->density_strength, 0.0, 1.0));
+// BKE_sim_debug_data_add_circle(clmd->debug_data, x, gdensity * clmd->sim_parms->density_strength, 0, 1, 0.4, "grid velocity", i, j, 3115);
+// BKE_sim_debug_data_add_dot(clmd->debug_data, x, col[0], col[1], col[2], "grid velocity", i, j, 3115);
+ BKE_sim_debug_data_add_circle(clmd->debug_data, x, 0.01f, col[0], col[1], col[2], "grid velocity", i, j, 3115);
+ }
+#endif
+ }
+ }
+ }
+ }
+#endif
+
+ BPH_hair_volume_free_vertex_grid(grid);
+ }
+}
+
+/* old collision stuff for cloth, use for continuity
+ * until a good replacement is ready
+ */
+static void cloth_collision_solve_extra(Object *ob, ClothModifierData *clmd, ListBase *effectors, float frame, float step, float dt)
+{
+ Cloth *cloth = clmd->clothObject;
+ Implicit_Data *id = cloth->implicit;
+ ClothVertex *verts = cloth->verts;
+ int numverts = cloth->numverts;
+ const float spf = (float)clmd->sim_parms->stepsPerFrame / clmd->sim_parms->timescale;;
+
+ bool do_extra_solve;
+ int i;
+
+ if (!(clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED))
+ return;
+ if (!clmd->clothObject->bvhtree)
+ return;
+
+ // update verts to current positions
+ for (i = 0; i < numverts; i++) {
+ BPH_mass_spring_get_new_position(id, i, verts[i].tx);
+
+ sub_v3_v3v3(verts[i].tv, verts[i].tx, verts[i].txold);
+ copy_v3_v3(verts[i].v, verts[i].tv);
+ }
+
+#if 0 /* unused */
+ for (i=0, cv=cloth->verts; i<cloth->numverts; i++, cv++) {
+ copy_v3_v3(initial_cos[i], cv->tx);
+ }
+#endif
+
+ // call collision function
+ // TODO: check if "step" or "step+dt" is correct - dg
+ do_extra_solve = cloth_bvh_objcollision(ob, clmd, step / clmd->sim_parms->timescale, dt / clmd->sim_parms->timescale);
+
+ // copy corrected positions back to simulation
+ for (i = 0; i < numverts; i++) {
+ float curx[3];
+ BPH_mass_spring_get_position(id, i, curx);
+ // correct velocity again, just to be sure we had to change it due to adaptive collisions
+ sub_v3_v3v3(verts[i].tv, verts[i].tx, curx);
+ }
+
+ if (do_extra_solve) {
+// cloth_calc_helper_forces(ob, clmd, initial_cos, step/clmd->sim_parms->timescale, dt/clmd->sim_parms->timescale);
+
+ for (i = 0; i < numverts; i++) {
+
+ float newv[3];
+
+ if ((clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) && (verts [i].flags & CLOTH_VERT_FLAG_PINNED))
+ continue;
+
+ BPH_mass_spring_set_new_position(id, i, verts[i].tx);
+ mul_v3_v3fl(newv, verts[i].tv, spf);
+ BPH_mass_spring_set_new_velocity(id, i, newv);
+ }
+ }
+
+ // X = Xnew;
+ BPH_mass_spring_apply_result(id);
+
+ if (do_extra_solve) {
+ ImplicitSolverResult result;
+
+ /* initialize forces to zero */
+ BPH_mass_spring_clear_forces(id);
+
+ // calculate forces
+ cloth_calc_force(clmd, frame, effectors, step);
+
+ // calculate new velocity and position
+ BPH_mass_spring_solve_velocities(id, dt, &result);
+// cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame);
+
+ /* note: positions are advanced only once in the main solver step! */
+
+ BPH_mass_spring_apply_result(id);
+ }
+}
+
+static void cloth_clear_result(ClothModifierData *clmd)
+{
+ ClothSolverResult *sres = clmd->solver_result;
+
+ sres->status = 0;
+ sres->max_error = sres->min_error = sres->avg_error = 0.0f;
+ sres->max_iterations = sres->min_iterations = 0;
+ sres->avg_iterations = 0.0f;
+}
+
+static void cloth_record_result(ClothModifierData *clmd, ImplicitSolverResult *result, int steps)
+{
+ ClothSolverResult *sres = clmd->solver_result;
+
+ if (sres->status) { /* already initialized ? */
+ /* error only makes sense for successful iterations */
+ if (result->status == BPH_SOLVER_SUCCESS) {
+ sres->min_error = min_ff(sres->min_error, result->error);
+ sres->max_error = max_ff(sres->max_error, result->error);
+ sres->avg_error += result->error / (float)steps;
+ }
+
+ sres->min_iterations = min_ii(sres->min_iterations, result->iterations);
+ sres->max_iterations = max_ii(sres->max_iterations, result->iterations);
+ sres->avg_iterations += (float)result->iterations / (float)steps;
+ }
+ else {
+ /* error only makes sense for successful iterations */
+ if (result->status == BPH_SOLVER_SUCCESS) {
+ sres->min_error = sres->max_error = result->error;
+ sres->avg_error += result->error / (float)steps;
+ }
+
+ sres->min_iterations = sres->max_iterations = result->iterations;
+ sres->avg_iterations += (float)result->iterations / (float)steps;
+ }
+
+ sres->status |= result->status;
+}
+
+int BPH_cloth_solve(Object *ob, float frame, ClothModifierData *clmd, ListBase *effectors)
+{
+ /* Hair currently is a cloth sim in disguise ...
+ * Collision detection and volumetrics work differently then.
+ * Bad design, TODO
+ */
+ const bool is_hair = (clmd->hairdata != NULL);
+
+ unsigned int i=0;
+ float step=0.0f, tf=clmd->sim_parms->timescale;
+ Cloth *cloth = clmd->clothObject;
+ ClothVertex *verts = cloth->verts, *vert;
+ unsigned int numverts = cloth->numverts;
+ float dt = clmd->sim_parms->timescale / clmd->sim_parms->stepsPerFrame;
+ Implicit_Data *id = cloth->implicit;
+ ColliderContacts *contacts = NULL;
+ int totcolliders = 0;
+
+ BKE_sim_debug_data_clear_category("collision");
+
+ if (!clmd->solver_result)
+ clmd->solver_result = (ClothSolverResult *)MEM_callocN(sizeof(ClothSolverResult), "cloth solver result");
+ cloth_clear_result(clmd);
+
+ if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) { /* do goal stuff */
+ vert = verts;
+ for (i = 0; i < numverts; i++, vert++) {
+ // update velocities with constrained velocities from pinned verts
+ if (vert->flags & CLOTH_VERT_FLAG_PINNED) {
+ float v[3];
+
+ sub_v3_v3v3(v, verts[i].xconst, verts[i].xold);
+ // mul_v3_fl(v, clmd->sim_parms->stepsPerFrame);
+ BPH_mass_spring_set_velocity(id, i, v);
+ }
+ }
+ }
+
+ while (step < tf) {
+ ImplicitSolverResult result;
+
+ /* copy velocities for collision */
+ vert = verts;
+ for (i = 0; i < numverts; i++, vert++) {
+ BPH_mass_spring_get_motion_state(id, i, NULL, verts[i].tv);
+ copy_v3_v3(verts[i].v, verts[i].tv);
+ }
+
+ if (is_hair) {
+ /* determine contact points */
+ if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) {
+ cloth_find_point_contacts(ob, clmd, 0.0f, tf, &contacts, &totcolliders);
+ }
+
+ /* setup vertex constraints for pinned vertices and contacts */
+ cloth_setup_constraints(clmd, contacts, totcolliders, dt);
+ }
+ else {
+ /* setup vertex constraints for pinned vertices */
+ cloth_setup_constraints(clmd, NULL, 0, dt);
+ }
+
+ /* initialize forces to zero */
+ BPH_mass_spring_clear_forces(id);
+
+ // damping velocity for artistic reasons
+ // this is a bad way to do it, should be removed imo - lukas_t
+ if (clmd->sim_parms->vel_damping != 1.0f) {
+ vert = verts;
+ for (i = 0; i < numverts; i++, vert++) {
+ float v[3];
+ BPH_mass_spring_get_motion_state(id, i, NULL, v);
+ mul_v3_fl(v, clmd->sim_parms->vel_damping);
+ BPH_mass_spring_set_velocity(id, i, v);
+ }
+ }
+
+ // calculate forces
+ cloth_calc_force(clmd, frame, effectors, step);
+
+ // calculate new velocity and position
+ BPH_mass_spring_solve_velocities(id, dt, &result);
+ cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame);
+
+ if (is_hair) {
+ cloth_continuum_step(clmd, dt);
+ }
+
+ BPH_mass_spring_solve_positions(id, dt);
+
+ if (!is_hair) {
+ cloth_collision_solve_extra(ob, clmd, effectors, frame, step, dt);
+ }
+
+ BPH_mass_spring_apply_result(id);
+
+ /* move pinned verts to correct position */
+ vert = verts;
+ for (i = 0; i < numverts; i++, vert++) {
+ if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL) {
+ if (vert->flags & CLOTH_VERT_FLAG_PINNED) {
+ float x[3];
+ interp_v3_v3v3(x, verts[i].xold, verts[i].xconst, step + dt);
+ BPH_mass_spring_set_position(id, i, x);
+ }
+ }
+
+ BPH_mass_spring_get_motion_state(id, i, verts[i].txold, NULL);
+ }
+
+ /* free contact points */
+ if (contacts) {
+ cloth_free_contacts(contacts, totcolliders);
+ }
+
+ step += dt;
+ }
+
+ /* copy results back to cloth data */
+ vert = verts;
+ for (i = 0; i < numverts; i++, vert++) {
+ BPH_mass_spring_get_motion_state(id, i, vert->x, vert->v);
+ copy_v3_v3(vert->txold, vert->x);
+ }
+
+ return 1;
+}
+
+bool BPH_cloth_solver_get_texture_data(Object *UNUSED(ob), ClothModifierData *clmd, VoxelData *vd)
+{
+ Cloth *cloth = clmd->clothObject;
+ HairGrid *grid;
+ float gmin[3], gmax[3];
+
+ if (!clmd->clothObject || !clmd->clothObject->implicit)
+ return false;
+
+ hair_get_boundbox(clmd, gmin, gmax);
+
+ grid = BPH_hair_volume_create_vertex_grid(clmd->sim_parms->voxel_cell_size, gmin, gmax);
+ cloth_continuum_fill_grid(grid, cloth);
+
+ BPH_hair_volume_get_texture_data(grid, vd);
+
+ BPH_hair_volume_free_vertex_grid(grid);
+
+ return true;
+}
+
+/* ========================================================================= */
+
+struct Implicit_Data *BPH_strands_solver_create(struct Strands *strands, struct HairSimParams *params)
+{
+ static float I3[3][3] = { {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f} };
+
+ struct Implicit_Data *id;
+ int numverts = strands->totverts;
+ int numcurves = strands->totcurves;
+ int numgoalverts = numverts - numcurves; /* extra virtual vertices to act as goal spring targets */
+ int numedges = max_ii(numverts - numcurves, 0);
+ int numbends = max_ii(numverts - 2*numcurves, 0);
+
+ /* goal springs: 1 per vertex, except roots
+ * stretch springs: 1 per edge
+ * bending sprints: 3 per bend // XXX outdated, 1 is enough
+ */
+ int numsprings = numgoalverts + numedges + 3*numbends;
+ int i;
+
+ id = BPH_mass_spring_solver_create(numverts + numgoalverts, numsprings);
+
+ for (i = 0; i < numverts; i++) {
+ float mass = params->mass;
+ BPH_mass_spring_set_vertex_mass(id, i, mass);
+ }
+ for (i = numverts; i < numverts + numgoalverts; i++) {
+ BPH_mass_spring_set_vertex_mass(id, i, 1.0f);
+ }
+
+ for (i = 0; i < numverts; i++) {
+ BPH_mass_spring_set_rest_transform(id, i, I3);
+ }
+ for (i = numverts; i < numverts + numgoalverts; i++) {
+ BPH_mass_spring_set_rest_transform(id, i, I3);
+ }
+
+ return id;
+}
+
+/* Init constraint matrix
+ * This is part of the modified CG method suggested by Baraff/Witkin in
+ * "Large Steps in Cloth Simulation" (Siggraph 1998)
+ */
+static void strands_setup_constraints(Strands *strands, Implicit_Data *data, ColliderContacts *UNUSED(contacts), int UNUSED(totcolliders), float UNUSED(dt))
+{
+ static const float ZERO[3] = { 0.0f, 0.0f, 0.0f };
+
+ BPH_mass_spring_clear_constraints(data);
+
+ StrandIterator it_strand;
+ for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ int index = BKE_strand_iter_vertex_offset(strands, &it_strand);
+
+ /* pin strand roots */
+ BPH_mass_spring_add_constraint_ndof0(data, index, ZERO); /* velocity is defined externally */
+
+// StrandVertexIterator it_vert;
+// for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) {
+// }
+ }
+
+ /* pin virtual goal targets */
+ {
+ int goalstart = strands->totverts;
+ int goalend = goalstart + strands->totverts - strands->totcurves;
+ for (int i = goalstart; i < goalend; ++i) {
+ BPH_mass_spring_add_constraint_ndof0(data, i, ZERO); /* velocity is defined externally */
+ }
+ }
+
+#if 0
+ for (i = 0; i < totcolliders; ++i) {
+ ColliderContacts *ct = &contacts[i];
+ for (j = 0; j < ct->totcollisions; ++j) {
+ CollPair *collpair = &ct->collisions[j];
+// float restitution = (1.0f - clmd->coll_parms->damping) * (1.0f - ct->ob->pd->pdef_sbdamp);
+ float restitution = 0.0f;
+ int v = collpair->face1;
+ float impulse[3];
+
+ /* pinned verts handled separately */
+ if (verts[v].flags & CLOTH_VERT_FLAG_PINNED)
+ continue;
+
+ /* XXX cheap way of avoiding instability from multiple collisions in the same step
+ * this should eventually be supported ...
+ */
+ if (verts[v].impulse_count > 0)
+ continue;
+
+ /* calculate collision response */
+ if (!collision_response(clmd, ct->collmd, collpair, dt, restitution, impulse))
+ continue;
+
+ BPH_mass_spring_add_constraint_ndof2(data, v, collpair->normal, impulse);
+ ++verts[v].impulse_count;
+ }
+ }
+#endif
+}
+
+/* stretch forces are created between 2 vertices of each segment */
+static void strands_calc_curve_stretch_forces(Strands *strands, float UNUSED(space[4][4]), HairSimParams *params, Implicit_Data *data, StrandIterator *it_strand)
+{
+ StrandEdgeIterator it_edge;
+
+ for (BKE_strand_edge_iter_init(&it_edge, it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge)) {
+ int vi = BKE_strand_edge_iter_vertex0_offset(strands, &it_edge);
+ int vj = BKE_strand_edge_iter_vertex1_offset(strands, &it_edge);
+ float restlen = len_v3v3(it_edge.vertex0->co, it_edge.vertex1->co);
+
+ float stiffness = params->stretch_stiffness;
+ float damping = stiffness * params->stretch_damping;
+ float f[3];
+ BPH_mass_spring_force_spring_linear(data, vi, vj, restlen, stiffness, damping, true, 0.0f, f, NULL, NULL);
+ }
+}
+
+static float strands_bending_stiffness(Strands *UNUSED(strands), HairSimParams *params, StrandsVertex *UNUSED(vert), float t)
+{
+ float weight;
+
+ if (params->flag & eHairSimParams_Flag_UseBendStiffnessCurve)
+ weight = curvemapping_evaluateF(params->bend_stiffness_mapping, 0, t);
+ else
+ weight = 1.0f;
+ CLAMP(weight, 0.0f, 1.0f);
+
+ return params->bend_stiffness * weight;
+}
+
+/* bending forces aim to restore the rest shape of each strand locally */
+static void strands_calc_curve_bending_forces(Strands *strands, float space[4][4], HairSimParams *params, Implicit_Data *data, StrandIterator *it_strand)
+{
+ StrandBendIterator it_bend;
+
+ float length = 0.0f;
+ StrandEdgeIterator it_edge;
+ for (BKE_strand_edge_iter_init(&it_edge, it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge))
+ length += len_v3v3(it_edge.vertex1->co, it_edge.vertex0->co);
+ float length_inv = length > 0.0f ? 1.0f / length : 0.0f;
+
+ float t = 0.0f;
+ BKE_strand_bend_iter_init(&it_bend, it_strand);
+ if (!BKE_strand_bend_iter_valid(&it_bend))
+ return;
+
+ /* The 'mat' matrix (here: A) contains the relative transform between the local rest and motion state coordinate systems.
+ * In the beginning both systems are the root matrix R, so the relative transform is the unit matrix.
+ *
+ * A = M_state * M_rest^T
+ * = R * R^T
+ * = I
+ *
+ * With each bend the matrices are rotated along the curvature, described by matrix B^T. Since we are only
+ * interested in the combined transform however, the resulting operation becomes
+ *
+ * A' = M_state' * M_rest'
+ * = (B_state^T * M_state) * (B_rest^T * M_rest)^T
+ * = B_state^T * M_state * M_rest^T * B_rest
+ * = B_state^T * A * B_rest
+ *
+ * The target vector is originally the direction of the first segment. For each bend, the target vector
+ * is the _previous_ segment's direction, i.e. the target vector is rotated by B with a 1-step delay.
+ *
+ * The target vector in the current motion state system for each segment could thus be calculated by multiplying
+ *
+ * t_state = M * t_rest
+ *
+ * but using the edge vector directly is more practical.
+ *
+ */
+ float mat[3][3];
+// float Mrest[3][3], Mstate[3][3];
+
+ { /* initialize using the first edge deviation from the rest direction */
+ float edge_rest[3], edge_state[3], rot[3][3];
+ sub_v3_v3v3(edge_rest, it_strand->verts[1].co, it_strand->verts[0].co);
+ sub_v3_v3v3(edge_state, it_strand->state[1].co, it_strand->state[0].co);
+ normalize_v3(edge_rest);
+ normalize_v3(edge_state);
+ rotation_between_vecs_to_mat3(rot, edge_rest, edge_state);
+
+ copy_m3_m3(mat, rot);
+// copy_m3_m3(Mrest, it_strand->curve->root_matrix);
+// mul_m3_m3m3(Mstate, rot, Mrest);
+ }
+
+ { /* apply force */
+ /* Note: applying forces to the first segment is necessary to equalize forces on the root,
+ * otherwise energy gets introduced at the root and can destabilize the simulation.
+ */
+ float target[3];
+ sub_v3_v3v3(target, it_strand->verts[1].co, it_strand->verts[0].co);
+ mul_mat3_m4_v3(space, target); /* to solver space (world space) */
+
+ float target_state[3];
+ mul_v3_m3v3(target_state, mat, target);
+
+ const float stiffness = strands_bending_stiffness(strands, params, it_bend.vertex0, t * length_inv);
+ const float damping = stiffness * params->bend_damping;
+
+ int vroot = BKE_strand_bend_iter_vertex0_offset(strands, &it_bend); /* root velocity used as goal velocity */
+ int vj = BKE_strand_bend_iter_vertex1_offset(strands, &it_bend);
+ float goal[3], rootvel[3];
+ mul_v3_m4v3(goal, space, it_strand->verts[1].co);
+ BPH_mass_spring_get_velocity(data, vroot, rootvel);
+ BPH_mass_spring_force_spring_goal(data, vj, goal, rootvel, stiffness, damping, NULL, NULL, NULL);
+ }
+
+ do {
+ float restlen = len_v3v3(it_bend.vertex1->co, it_bend.vertex0->co);
+ t += restlen;
+
+ { /* advance the coordinate frame */
+ float rotrest[3][3], rotrest_inv[3][3], rotstate[3][3], rotstate_inv[3][3];
+ BKE_strand_bend_iter_transform_rest(&it_bend, rotrest);
+ BKE_strand_bend_iter_transform_state(&it_bend, rotstate);
+ transpose_m3_m3(rotrest_inv, rotrest);
+ transpose_m3_m3(rotstate_inv, rotstate);
+
+// mul_m3_m3m3(Mrest, rotrest_inv, Mrest);
+// mul_m3_m3m3(Mstate, rotstate_inv, Mstate);
+
+ mul_m3_m3m3(mat, mat, rotrest);
+ mul_m3_m3m3(mat, rotstate_inv, mat);
+ }
+
+ { /* apply force */
+ float target[3];
+ sub_v3_v3v3(target, it_bend.vertex1->co, it_bend.vertex0->co);
+ mul_mat3_m4_v3(space, target); /* to solver space (world space) */
+
+ float target_state[3];
+ mul_v3_m3v3(target_state, mat, target);
+
+ const float stiffness = strands_bending_stiffness(strands, params, it_bend.vertex1, t * length_inv);
+ const float damping = stiffness * params->bend_damping;
+
+ int vi = BKE_strand_bend_iter_vertex0_offset(strands, &it_bend);
+ int vj = BKE_strand_bend_iter_vertex1_offset(strands, &it_bend);
+ int vk = BKE_strand_bend_iter_vertex2_offset(strands, &it_bend);
+ float f[3];
+ BPH_mass_spring_force_spring_bending_angular(data, vi, vj, vk, target_state, stiffness, damping, f, NULL, NULL);
+
+#if 0 /* debug */
+ {
+ float mscale = 0.1f;
+
+ float x[3];
+ BPH_mass_spring_get_position(data, vj, x);
+ BKE_sim_debug_data_add_vector(x, target, 0,0,1, "hairsim", 2598, vi, vj, vk);
+ BKE_sim_debug_data_add_vector(x, target_state, 0.4,0.4,1, "hairsim", 2599, vi, vj, vk);
+
+ float mr[3][3];
+ copy_m3_m3(mr, Mrest);
+ mul_m3_fl(mr, mscale);
+ BKE_sim_debug_data_add_vector(x, mr[0], 0.7,0.0,0.0, "hairsim", 1957, vi, vj, vk);
+ BKE_sim_debug_data_add_vector(x, mr[1], 0.0,0.7,0.0, "hairsim", 1958, vi, vj, vk);
+ BKE_sim_debug_data_add_vector(x, mr[2], 0.0,0.0,0.7, "hairsim", 1959, vi, vj, vk);
+
+ float ms[3][3];
+ copy_m3_m3(ms, Mstate);
+ mul_m3_fl(ms, mscale);
+ BKE_sim_debug_data_add_vector(x, ms[0], 1.0,0.4,0.4, "hairsim", 1857, vi, vj, vk);
+ BKE_sim_debug_data_add_vector(x, ms[1], 0.4,1.0,0.4, "hairsim", 1858, vi, vj, vk);
+ BKE_sim_debug_data_add_vector(x, ms[2], 0.4,0.4,1.0, "hairsim", 1859, vi, vj, vk);
+ }
+#endif
+ }
+
+ BKE_strand_bend_iter_next(&it_bend);
+ } while (BKE_strand_bend_iter_valid(&it_bend));
+}
+
+static float strands_goal_stiffness(Strands *UNUSED(strands), HairSimParams *params, StrandsVertex *vert, float t)
+{
+ /* XXX There is no possibility of tweaking them in linked data currently,
+ * so the original workflow of painting weights in particle edit mode is virtually useless.
+ */
+ float weight;
+
+ if (params->flag & eHairSimParams_Flag_UseGoalStiffnessCurve)
+ weight = curvemapping_evaluateF(params->goal_stiffness_mapping, 0, t);
+ else
+ weight = vert->weight;
+ CLAMP(weight, 0.0f, 1.0f);
+
+ return params->goal_stiffness * weight;
+}
+
+static bool strands_deflector_filter(void *UNUSED(data), CacheEffector *eff)
+{
+ return eff->type == eCacheEffector_Type_Deflect;
+}
+
+static bool strands_test_deflector(StrandsVertex *UNUSED(vertex), int index, CacheEffector *cache_effectors, int tot_cache_effectors, Implicit_Data *data)
+{
+ CacheEffectorPoint point;
+ point.index = index;
+ BPH_mass_spring_get_motion_state(data, index, point.x, point.v);
+
+ CacheEffectorResult result;
+ if (BKE_cache_effectors_eval_ex(cache_effectors, tot_cache_effectors, &point, &result, strands_deflector_filter, NULL) > 0) {
+ return true;
+ }
+
+ return false;
+}
+
+/* goal forces pull vertices toward their rest position */
+static void strands_calc_vertex_goal_forces(Strands *strands, float UNUSED(space[4][4]), HairSimParams *params, CacheEffector *cache_effectors, int tot_cache_effectors,
+ Implicit_Data *data, StrandIterator *it_strand)
+{
+ const int goalstart = strands->totverts;
+ StrandEdgeIterator it_edge;
+
+ float length = 0.0f;
+ for (BKE_strand_edge_iter_init(&it_edge, it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge))
+ length += len_v3v3(it_edge.vertex1->co, it_edge.vertex0->co);
+ float length_inv = length > 0.0f ? 1.0f / length : 0.0f;
+
+ float t = 0.0f;
+ for (BKE_strand_edge_iter_init(&it_edge, it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge)) {
+ int vj = BKE_strand_edge_iter_vertex1_offset(strands, &it_edge);
+ int numroots = it_strand->index + 1; /* roots don't have goal verts, skip this many */
+ int goalj = goalstart + vj - numroots;
+
+ if (params->flag & eHairSimParams_Flag_UseGoalDeflect) {
+ if (strands_test_deflector(it_edge.vertex1, vj, cache_effectors, tot_cache_effectors, data))
+ continue;
+ }
+
+ float restlen = len_v3v3(it_edge.vertex1->co, it_edge.vertex0->co);
+ t += restlen;
+
+ float stiffness = strands_goal_stiffness(strands, params, it_edge.vertex1, t * length_inv);
+ float damping = stiffness * params->goal_damping;
+
+ float f[3];
+ BPH_mass_spring_force_spring_linear(data, vj, goalj, 0.0f, stiffness, damping, true, 0.0f, f, NULL, NULL);
+ }
+
+#if 0
+ StrandEdgeIterator it_edge;
+
+ float rootvel[3];
+ BPH_mass_spring_get_velocity(data, BKE_strand_iter_vertex_offset(strands, it_strand), rootvel);
+
+ float length = 0.0f;
+ for (BKE_strand_edge_iter_init(&it_edge, it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge))
+ length += len_v3v3(it_edge.vertex1->co, it_edge.vertex0->co);
+ float length_inv = length > 0.0f ? 1.0f / length : 0.0f;
+
+ float t = 0.0f;
+ for (BKE_strand_edge_iter_init(&it_edge, it_strand); BKE_strand_edge_iter_valid(&it_edge); BKE_strand_edge_iter_next(&it_edge)) {
+ int vj = BKE_strand_edge_iter_vertex1_offset(strands, &it_edge);
+ t += len_v3v3(it_edge.vertex1->co, it_edge.vertex0->co);
+
+ float stiffness = strands_goal_stiffness(strands, params, it_edge.vertex1, t * length_inv);
+ float damping = stiffness * params->goal_damping;
+
+ float goal[3];
+ mul_v3_m4v3(goal, space, it_edge.vertex1->co);
+
+ float f[3];
+ BPH_mass_spring_force_spring_goal(data, vj, goal, rootvel, stiffness, damping, f, NULL, NULL);
+ }
+#endif
+}
+
+/* calculates internal forces for a single strand curve */
+static void strands_calc_curve_forces(Strands *strands, float space[4][4], HairSimParams *params, CacheEffector *cache_effectors, int tot_cache_effectors,
+ Implicit_Data *data, StrandIterator *it_strand)
+{
+ strands_calc_curve_stretch_forces(strands, space, params, data, it_strand);
+ strands_calc_curve_bending_forces(strands, space, params, data, it_strand);
+ strands_calc_vertex_goal_forces(strands, space, params, cache_effectors, tot_cache_effectors, data, it_strand);
+}
+
+/* Collect forces and derivatives: F, dFdX, dFdV */
+static void strands_calc_force(Strands *strands, float space[4][4], HairSimParams *params, Implicit_Data *data, float UNUSED(frame), Scene *scene,
+ ListBase *effectors, CacheEffector *cache_effectors, int tot_cache_effectors)
+{
+ unsigned int numverts = strands->totverts;
+
+ int i = 0;
+ float gravity[3] = {0.0f, 0.0f, 0.0f};
+
+ /* global acceleration (gravitation) */
+ if (scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY) {
+ /* scale gravity force */
+ mul_v3_v3fl(gravity, scene->physics_settings.gravity, params->effector_weights->global_gravity);
+ }
+ for (i = 0; i < numverts; i++) {
+ float mass = params->mass;
+ BPH_mass_spring_force_gravity(data, i, mass, gravity);
+ }
+
+ BPH_mass_spring_force_drag(data, params->drag);
+
+ /* handle external forces like wind */
+ if (effectors || (cache_effectors && tot_cache_effectors > 0)) {
+ /* cache per-vertex forces to avoid redundant calculation */
+ float (*ext_forces)[3] = (float (*)[3])MEM_callocN(sizeof(float) * 3 * numverts, "effector forces");
+
+ if (effectors) {
+ for (i = 0; i < numverts; ++i) {
+ float x[3], v[3];
+ EffectedPoint epoint;
+
+ BPH_mass_spring_get_motion_state(data, i, x, v);
+ pd_point_from_loc(scene, x, v, i, &epoint);
+ pdDoEffectors(effectors, NULL, params->effector_weights, &epoint, ext_forces[i], NULL);
+ }
+ }
+
+ if (cache_effectors && tot_cache_effectors > 0) {
+ for (i = 0; i < numverts; ++i) {
+ CacheEffectorPoint point;
+ point.index = i;
+ BPH_mass_spring_get_motion_state(data, i, point.x, point.v);
+
+ CacheEffectorResult result;
+ if (BKE_cache_effectors_eval(cache_effectors, tot_cache_effectors, &point, &result) > 0) {
+ add_v3_v3(ext_forces[i], result.f);
+ }
+ }
+ }
+
+ for (i = 0; i < numverts; ++i) {
+ BPH_mass_spring_force_vertex_wind(data, i, 1.0f, ext_forces);
+ }
+
+ MEM_freeN(ext_forces);
+ }
+
+ /* spring forces */
+ StrandIterator it_strand;
+ for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ strands_calc_curve_forces(strands, space, params, cache_effectors, tot_cache_effectors, data, &it_strand);
+ }
+}
+
+/* calculates the velocity of strand roots using the new rest location (verts->co) and the current motion state */
+static void strands_calc_root_velocity(Strands *strands, float mat[4][4], Implicit_Data *data, float timestep)
+{
+ StrandIterator it_strand;
+ for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ if (it_strand.curve->numverts > 0) {
+ int index = BKE_strand_iter_vertex_offset(strands, &it_strand);
+
+ float vel[3];
+ sub_v3_v3v3(vel, it_strand.verts[0].co, it_strand.state[0].co);
+ mul_v3_fl(vel, 1.0f/timestep);
+ mul_mat3_m4_v3(mat, vel);
+
+ BPH_mass_spring_set_velocity(data, index, vel);
+ }
+ }
+}
+
+/* calculates the location of strand roots using the new rest location (verts->co) and the current motion state */
+static void strands_calc_root_location(Strands *strands, float mat[4][4], Implicit_Data *data, float step)
+{
+ StrandIterator it_strand;
+ for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ if (it_strand.curve->numverts > 0) {
+ int index = BKE_strand_iter_vertex_offset(strands, &it_strand);
+
+ float co[3];
+ interp_v3_v3v3(co, it_strand.state[0].co, it_strand.verts[0].co, step);
+ mul_m4_v3(mat, co);
+
+ BPH_mass_spring_set_position(data, index, co);
+ }
+ }
+}
+
+/* calculates the location of virtual goal vertices */
+static void strands_calc_goal_velocities(Strands *strands, float mat[4][4], Implicit_Data *data, float timestep)
+{
+ const int goalstart = strands->totverts;
+ int goalindex = goalstart;
+
+ StrandIterator it_strand;
+ for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ StrandVertexIterator it_vert;
+ for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) {
+ /* skip root */
+ if (it_vert.index == 0)
+ continue;
+
+ int index = BKE_strand_vertex_iter_vertex_offset(strands, &it_vert);
+
+ float vel[3];
+ sub_v3_v3v3(vel, strands->verts[index].co, strands->state[index].co);
+ mul_v3_fl(vel, 1.0f/timestep);
+ mul_mat3_m4_v3(mat, vel);
+
+ BPH_mass_spring_set_velocity(data, goalindex, vel);
+
+ ++goalindex;
+ }
+ }
+}
+
+/* calculates the location of virtual goal vertices */
+static void strands_calc_goal_locations(Strands *strands, float mat[4][4], Implicit_Data *data, float step)
+{
+ const int goalstart = strands->totverts;
+ int goalindex = goalstart;
+
+ StrandIterator it_strand;
+ for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ StrandVertexIterator it_vert;
+ for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) {
+ /* skip root */
+ if (it_vert.index == 0)
+ continue;
+
+ int index = BKE_strand_vertex_iter_vertex_offset(strands, &it_vert);
+
+ float co[3];
+ interp_v3_v3v3(co, strands->state[index].co, strands->verts[index].co, step);
+ mul_m4_v3(mat, co);
+
+ BPH_mass_spring_set_position(data, goalindex, co);
+
+ ++goalindex;
+ }
+ }
+}
+
+/* XXX Do we need to take fictitious forces from the moving and/or accelerated frame of reference into account?
+ * This would mean we pass not only the basic world transform mat, but also linear/angular velocity and acceleration.
+ */
+bool BPH_strands_solve(Strands *strands, float mat[4][4], Implicit_Data *id, HairSimParams *params, float frame, float frame_prev, Scene *scene,
+ ListBase *effectors, CacheEffector *cache_effectors, int tot_cache_effectors)
+{
+ if (params->timescale == 0.0f || params->substeps < 1)
+ return false;
+
+ float timestep = (FRA2TIME(frame) - FRA2TIME(frame_prev)) * params->timescale;
+ float dstep = 1.0f / params->substeps;
+ float dtime = timestep * dstep;
+ int numverts = strands->totverts;
+
+ int i;
+ ColliderContacts *contacts = NULL;
+ int totcolliders = 0;
+
+ float imat[4][4];
+ invert_m4_m4(imat, mat);
+
+// if (!clmd->solver_result)
+// clmd->solver_result = (ClothSolverResult *)MEM_callocN(sizeof(ClothSolverResult), "cloth solver result");
+// cloth_clear_result(clmd);
+
+ /* initialize solver data */
+ for (i = 0; i < numverts; i++) {
+ float wco[3], wvel[3];
+ copy_v3_v3(wco, strands->state[i].co);
+ copy_v3_v3(wvel, strands->state[i].vel);
+ mul_m4_v3(mat, wco);
+ mul_mat3_m4_v3(mat, wvel);
+ BPH_mass_spring_set_motion_state(id, i, wco, wvel);
+ }
+ strands_calc_root_velocity(strands, mat, id, timestep);
+
+ strands_calc_goal_locations(strands, mat, id, 0.0f);
+ strands_calc_goal_velocities(strands, mat, id, timestep);
+
+ for (float step = 0.0f; step < 1.0f; step += dstep) {
+ ImplicitSolverResult result;
+
+#if 0
+ /* determine contact points */
+ if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) {
+ cloth_find_point_contacts(ob, clmd, 0.0f, tf, &contacts, &totcolliders);
+ }
+#endif
+
+ /* setup vertex constraints for pinned vertices and contacts */
+ strands_setup_constraints(strands, id, contacts, totcolliders, dtime);
+
+ /* initialize forces to zero */
+ BPH_mass_spring_clear_forces(id);
+
+ // calculate forces
+ strands_calc_force(strands, mat, params, id, frame, scene, effectors, cache_effectors, tot_cache_effectors);
+
+ // calculate new velocity and position
+ BPH_mass_spring_solve_velocities(id, dtime, &result);
+// cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame);
+
+#if 0
+ if (is_hair) {
+ cloth_continuum_step(clmd, dtime);
+ }
+#endif
+
+ BPH_mass_spring_solve_positions(id, dtime);
+
+ BPH_mass_spring_apply_result(id);
+
+ /* move pinned verts to correct position */
+ strands_calc_root_location(strands, mat, id, step + dstep);
+ strands_calc_goal_locations(strands, mat, id, step + dstep);
+
+#if 0
+ /* free contact points */
+ if (contacts) {
+ cloth_free_contacts(contacts, totcolliders);
+ }
+#endif
+
+ }
+
+ /* copy results back to strand data */
+ for (i = 0; i < numverts; i++) {
+ float co[3], vel[3];
+ BPH_mass_spring_get_motion_state(id, i, co, vel);
+ mul_m4_v3(imat, co);
+ mul_mat3_m4_v3(imat, vel);
+ copy_v3_v3(strands->state[i].co, co);
+ copy_v3_v3(strands->state[i].vel, vel);
+ }
+
+ return true;
+}
diff --git a/source/blender/physics/intern/eigen_utils.h b/source/blender/physics/intern/eigen_utils.h
index e4a4f306aeb..ebfed7cb690 100644
--- a/source/blender/physics/intern/eigen_utils.h
+++ b/source/blender/physics/intern/eigen_utils.h
@@ -39,6 +39,7 @@
#endif
#include <Eigen/Sparse>
+#include <Eigen/SVD>
#include <Eigen/src/Core/util/DisableStupidWarnings.h>
#ifdef __GNUC__
@@ -113,6 +114,7 @@ public:
};
typedef Eigen::VectorXf lVector;
+typedef Eigen::VectorXf VectorX;
/* Extension of dense Eigen vectors,
* providing 3-float block access for blenlib math functions
@@ -146,6 +148,7 @@ public:
typedef Eigen::Triplet<Scalar> Triplet;
typedef std::vector<Triplet> TripletList;
+typedef Eigen::MatrixXf MatrixX;
typedef Eigen::SparseMatrix<Scalar> lMatrix;
/* Constructor type that provides more convenient handling of Eigen triplets
@@ -201,6 +204,33 @@ typedef Eigen::ConjugateGradient<lMatrix, Eigen::Lower, Eigen::DiagonalPrecondit
using Eigen::ComputationInfo;
+template <typename MatrixType>
+BLI_INLINE MatrixType pseudo_inverse(const MatrixType& mat, double epsilon = std::numeric_limits<double>::epsilon())
+{
+ typedef Eigen::JacobiSVD<MatrixType> SVD;
+// typedef typename SVD::SingularValuesType SingularValues;
+
+ SVD svd(mat, Eigen::ComputeThinU | Eigen::ComputeThinV);
+
+ double tolerance = epsilon * std::max(mat.cols(), mat.rows()) * svd.singularValues().array().abs()(0);
+ return svd.matrixV() * (svd.singularValues().array().abs() > tolerance).select(svd.singularValues().array().inverse(), 0).matrix().asDiagonal() * svd.matrixU().adjoint();
+
+// const double pinvtoler=1.e-6; // choose your tolerance wisely!
+// SingularValues singularValues_inv = svd.singularValues();
+// for (long i = 0; i < mat.cols(); ++i) {
+// if (svd.singularValues(i) > pinvtoler )
+// singularValues_inv(i) = 1.0 / svd.singularValues(i);
+// else singularValues_inv(i) = 0;
+// }
+
+// return (svd.matrixV() * singularValues_inv.asDiagonal() * svd.matrixU().transpose());
+}
+
+BLI_INLINE void print_matrix_elem(float v)
+{
+ printf("%-8.3f", v);
+}
+
BLI_INLINE void print_lvector(const lVector3f &v)
{
for (int i = 0; i < v.rows(); ++i) {
@@ -221,7 +251,7 @@ BLI_INLINE void print_lmatrix(const lMatrix &m)
if (i > 0 && i % 3 == 0)
printf(" ");
- implicit_print_matrix_elem(m.coeff(j, i));
+ print_matrix_elem(m.coeff(j, i));
}
printf("\n");
}
diff --git a/source/blender/physics/intern/implicit.h b/source/blender/physics/intern/implicit.h
index dba1cd11ae0..79cf35ffb49 100644
--- a/source/blender/physics/intern/implicit.h
+++ b/source/blender/physics/intern/implicit.h
@@ -67,11 +67,6 @@ typedef struct ImplicitSolverResult {
float error;
} ImplicitSolverResult;
-BLI_INLINE void implicit_print_matrix_elem(float v)
-{
- printf("%-8.3f", v);
-}
-
void BPH_mass_spring_set_vertex_mass(struct Implicit_Data *data, int index, float mass);
void BPH_mass_spring_set_rest_transform(struct Implicit_Data *data, int index, float rot[3][3]);
@@ -80,6 +75,7 @@ void BPH_mass_spring_set_position(struct Implicit_Data *data, int index, const f
void BPH_mass_spring_set_velocity(struct Implicit_Data *data, int index, const float v[3]);
void BPH_mass_spring_get_motion_state(struct Implicit_Data *data, int index, float x[3], float v[3]);
void BPH_mass_spring_get_position(struct Implicit_Data *data, int index, float x[3]);
+void BPH_mass_spring_get_velocity(struct Implicit_Data *data, int index, float v[3]);
/* access to modified motion state during solver step */
void BPH_mass_spring_get_new_position(struct Implicit_Data *data, int index, float x[3]);
@@ -111,7 +107,7 @@ void BPH_mass_spring_force_face_wind(struct Implicit_Data *data, int v1, int v2,
/* Wind force, acting on an edge */
void BPH_mass_spring_force_edge_wind(struct Implicit_Data *data, int v1, int v2, float radius1, float radius2, const float (*winvec)[3]);
/* Wind force, acting on a vertex */
-void BPH_mass_spring_force_vertex_wind(struct Implicit_Data *data, int v, float radius, const float (*winvec)[3]);
+void BPH_mass_spring_force_vertex_wind(struct Implicit_Data *data, int v, float factor, const float (*winvec)[3]);
/* Linear spring force between two points */
bool BPH_mass_spring_force_spring_linear(struct Implicit_Data *data, int i, int j, float restlen,
float stiffness, float damping, bool no_compress, float clamp_force,
@@ -122,7 +118,8 @@ bool BPH_mass_spring_force_spring_bending(struct Implicit_Data *data, int i, int
float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]);
/* Angular bending force based on local target vectors */
bool BPH_mass_spring_force_spring_bending_angular(struct Implicit_Data *data, int i, int j, int k,
- const float target[3], float stiffness, float damping);
+ const float target[3], float stiffness, float damping,
+ float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3]);
/* Global goal spring */
bool BPH_mass_spring_force_spring_goal(struct Implicit_Data *data, int i, const float goal_x[3], const float goal_v[3],
float stiffness, float damping,
diff --git a/source/blender/physics/intern/implicit_blender.c b/source/blender/physics/intern/implicit_blender.c
index 5b1d83a3eef..a2dd1ea76b4 100644
--- a/source/blender/physics/intern/implicit_blender.c
+++ b/source/blender/physics/intern/implicit_blender.c
@@ -324,9 +324,7 @@ static void print_sparse_matrix(fmatrix3x3 *m)
}
}
}
-#endif
-#if 0
static void print_lvector(lfVector *v, int numverts)
{
int i;
@@ -339,9 +337,7 @@ static void print_lvector(lfVector *v, int numverts)
printf("%f,\n", v[i][2]);
}
}
-#endif
-#if 0
static void print_bfmatrix(fmatrix3x3 *m)
{
int tot = m[0].vcount + m[0].scount;
@@ -377,7 +373,7 @@ static void print_bfmatrix(fmatrix3x3 *m)
if (i > 0 && i % 3 == 0)
printf(" ");
- implicit_print_matrix_elem(t[i + j * size]);
+ print_matrix_elem(t[i + j * size]);
}
printf("\n");
}
@@ -754,6 +750,14 @@ void BPH_mass_spring_solver_free(Implicit_Data *id)
MEM_freeN(id);
}
+int BPH_mass_spring_solver_numvert(Implicit_Data *id)
+{
+ if (id)
+ return id->A[0].vcount;
+ else
+ return 0;
+}
+
/* ==== Transformation from/to root reference frames ==== */
BLI_INLINE void world_to_root_v3(Implicit_Data *data, int index, float r[3], const float v[3])
@@ -1225,6 +1229,11 @@ void BPH_mass_spring_get_position(struct Implicit_Data *data, int index, float x
root_to_world_v3(data, index, x, data->X[index]);
}
+void BPH_mass_spring_get_velocity(struct Implicit_Data *data, int index, float v[3])
+{
+ root_to_world_v3(data, index, v, data->V[index]);
+}
+
void BPH_mass_spring_get_new_position(struct Implicit_Data *data, int index, float x[3])
{
root_to_world_v3(data, index, x, data->Xnew[index]);
@@ -1503,15 +1512,13 @@ void BPH_mass_spring_force_edge_wind(Implicit_Data *data, int v1, int v2, float
add_v3_v3(data->F[v2], f);
}
-void BPH_mass_spring_force_vertex_wind(Implicit_Data *data, int v, float UNUSED(radius), const float (*winvec)[3])
+void BPH_mass_spring_force_vertex_wind(Implicit_Data *data, int v, float factor, const float (*winvec)[3])
{
- const float density = 0.01f; /* XXX arbitrary value, corresponds to effect of air density */
-
float wind[3];
float f[3];
world_to_root_v3(data, v, wind, winvec[v]);
- mul_v3_v3fl(f, wind, density);
+ mul_v3_v3fl(f, wind, factor);
add_v3_v3(data->F[v], f);
}
@@ -1519,26 +1526,19 @@ BLI_INLINE void dfdx_spring(float to[3][3], const float dir[3], float length, fl
{
// dir is unit length direction, rest is spring's restlength, k is spring constant.
//return ( (I-outerprod(dir, dir))*Min(1.0f, rest/length) - I) * -k;
- outerproduct(to, dir, dir);
- sub_m3_m3m3(to, I, to);
+ if (L > ALMOST_ZERO) {
+ outerproduct(to, dir, dir);
+ sub_m3_m3m3(to, I, to);
+
+ mul_m3_fl(to, (L / length));
+ }
+ else
+ zero_m3(to);
- mul_m3_fl(to, (L/length));
sub_m3_m3m3(to, to, I);
mul_m3_fl(to, k);
}
-/* unused */
-#if 0
-BLI_INLINE void dfdx_damp(float to[3][3], const float dir[3], float length, const float vel[3], float rest, float damping)
-{
- // inner spring damping vel is the relative velocity of the endpoints.
- // return (I-outerprod(dir, dir)) * (-damping * -(dot(dir, vel)/Max(length, rest)));
- mul_fvectorT_fvector(to, dir, dir);
- sub_fmatrix_fmatrix(to, I, to);
- mul_fmatrix_S(to, (-damping * -(dot_v3v3(dir, vel)/MAX2(length, rest))));
-}
-#endif
-
BLI_INLINE void dfdv_damp(float to[3][3], const float dir[3], float damping)
{
// derivative of force wrt velocity
@@ -1548,7 +1548,7 @@ BLI_INLINE void dfdv_damp(float to[3][3], const float dir[3], float damping)
BLI_INLINE float fb(float length, float L)
{
- float x = length / L;
+ float x = L > ALMOST_ZERO ? length / L : 0.0f;
float xx = x * x;
float xxx = xx * x;
float xxxx = xxx * x;
@@ -1557,7 +1557,7 @@ BLI_INLINE float fb(float length, float L)
BLI_INLINE float fbderiv(float length, float L)
{
- float x = length/L;
+ float x = L > ALMOST_ZERO ? length / L : 0.0f;
float xx = x * x;
float xxx = xx * x;
return (-46.164f * xxx + 102.579f * xx - 78.166f * x + 23.116f);
@@ -1854,7 +1854,8 @@ BLI_INLINE void spring_angbend_estimate_dfdv(Implicit_Data *data, int i, int j,
* See "Artistic Simulation of Curly Hair" (Pixar technical memo #12-03a)
*/
bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data, int i, int j, int k,
- const float target[3], float stiffness, float damping)
+ const float target[3], float stiffness, float damping,
+ float r_f[3], float r_dfdx[3][3], float r_dfdv[3][3])
{
float goal[3];
float fj[3], fk[3];
@@ -1991,6 +1992,13 @@ bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data, int i, in
add_m3_m3m3(data->dFdX[block_ik].m, data->dFdX[block_ik].m, dfk_dxi);
#endif
+ if (r_f)
+ copy_v3_v3(r_f, fj);
+ if (r_dfdx)
+ copy_m3_m3(r_dfdx, dfj_dxj);
+ if (r_dfdv)
+ copy_m3_m3(r_dfdx, dfj_dvj);
+
return true;
}
diff --git a/source/blender/physics/intern/implicit_eigen.cpp b/source/blender/physics/intern/implicit_eigen.cpp
index 0ea8ccb0a49..69feb3c19e2 100644
--- a/source/blender/physics/intern/implicit_eigen.cpp
+++ b/source/blender/physics/intern/implicit_eigen.cpp
@@ -269,7 +269,7 @@ static void print_lmatrix(const lMatrix &m)
if (i > 0 && i % 3 == 0)
printf(" ");
- implicit_print_matrix_elem(m.coeff(j, i));
+ print_matrix_elem(m.coeff(j, i));
}
printf("\n");
}
diff --git a/source/blender/physics/intern/strands.cpp b/source/blender/physics/intern/strands.cpp
new file mode 100644
index 00000000000..f8bba6a615c
--- /dev/null
+++ b/source/blender/physics/intern/strands.cpp
@@ -0,0 +1,472 @@
+/*
+ * ***** 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) Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Lukas Toenne
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/physics/intern/strands.c
+ * \ingroup bke
+ */
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+
+#include "DNA_customdata_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_bvhutils.h"
+#include "BKE_customdata.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_editstrands.h"
+#include "BKE_effect.h"
+#include "BKE_mesh_sample.h"
+
+#include "bmesh.h"
+}
+
+#include "BPH_strands.h"
+
+#include "eigen_utils.h"
+
+/* === constraints === */
+
+static bool strand_get_root_vectors(BMEditStrands *edit, BMVert *root, float loc[3], float nor[3], float tang[3])
+{
+ BMesh *bm = edit->bm;
+ DerivedMesh *root_dm = edit->root_dm;
+ MSurfaceSample root_sample;
+
+ if (!CustomData_has_layer(&bm->vdata, CD_MSURFACE_SAMPLE))
+ return false;
+
+ BM_elem_meshsample_data_named_get(&bm->vdata, root, CD_MSURFACE_SAMPLE, CD_HAIR_ROOT_LOCATION, &root_sample);
+ return BKE_mesh_sample_eval(root_dm, &root_sample, loc, nor, tang);
+}
+
+static int strand_count_vertices(BMVert *root)
+{
+ BMVert *v;
+ BMIter iter;
+
+ int len = 0;
+ BM_ITER_STRANDS_ELEM(v, &iter, root, BM_VERTS_OF_STRAND) {
+ ++len;
+ }
+ return len;
+}
+
+static int UNUSED_FUNCTION(strands_get_max_length)(BMEditStrands *edit)
+{
+ BMVert *root;
+ BMIter iter;
+ int maxlen = 0;
+
+ BM_ITER_STRANDS(root, &iter, edit->bm, BM_STRANDS_OF_MESH) {
+ int len = strand_count_vertices(root);
+ if (len > maxlen)
+ maxlen = len;
+ }
+ return maxlen;
+}
+
+static void strands_apply_root_locations(BMEditStrands *edit)
+{
+ BMVert *root;
+ BMIter iter;
+
+ if (!edit->root_dm)
+ return;
+
+ BM_ITER_STRANDS(root, &iter, edit->bm, BM_STRANDS_OF_MESH) {
+ float loc[3], nor[3], tang[3];
+
+ if (strand_get_root_vectors(edit, root, loc, nor, tang)) {
+ copy_v3_v3(root->co, loc);
+ }
+ }
+}
+
+static void strands_adjust_segment_lengths(BMesh *bm)
+{
+ BMVert *root;
+ BMIter iter;
+
+ BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) {
+ BMVert *v, *vprev = NULL;
+ BMIter iter_strand;
+ BM_ITER_STRANDS_ELEM(v, &iter_strand, root, BM_VERTS_OF_STRAND) {
+ if (vprev) {
+ float base_length = BM_elem_float_data_named_get(&bm->vdata, vprev, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH);
+ float dist[3];
+ float length;
+
+ sub_v3_v3v3(dist, v->co, vprev->co);
+ length = len_v3(dist);
+ if (length > 0.0f)
+ madd_v3_v3v3fl(v->co, vprev->co, dist, base_length / length);
+ }
+ vprev = v;
+ }
+ }
+}
+
+static void strands_vertex_relax(BMesh *bm, float relax_factor, const BMVert *vprev, const BMVert *v, const BMVert *vnext, float relax[3])
+{
+ float D_pos[3], D_neg[3];
+
+ if (vprev) {
+ sub_v3_v3v3(D_neg, vprev->co, v->co);
+ float len = len_v3(D_neg);
+ if (len > 0.0f) {
+ float Lprev = BM_elem_float_data_named_get(&bm->vdata, (void *)vprev, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH);
+ mul_v3_fl(D_neg, (1.0f - Lprev / len) * relax_factor);
+ }
+ else
+ zero_v3(D_neg);
+ }
+ else
+ zero_v3(D_neg);
+
+ if (vnext) {
+ sub_v3_v3v3(D_pos, vnext->co, v->co);
+ float len = len_v3(D_pos);
+ if (len > 0.0f) {
+ float L = BM_elem_float_data_named_get(&bm->vdata, (void *)v, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH);
+ mul_v3_fl(D_pos, (1.0f - L / len) * relax_factor);
+ }
+ else
+ zero_v3(D_pos);
+ }
+ else
+ zero_v3(D_pos);
+
+ add_v3_v3v3(relax, D_neg, D_pos);
+}
+
+/* single relaxation iteration, must be repeated totkey times for complete relaxation */
+static void strands_relax(BMesh *bm, BMVert *root, bool skip_first)
+{
+ const int numvert = BM_strands_keys_count(root);
+ const float relax_factor = numvert > 0 ? 1.0f / numvert : 0.0f;
+
+ BMVert *vert_next, *vert = NULL, *vert_prev = NULL;
+ float relax_prev[3], relax[3];
+ BMIter iter;
+
+ BM_ITER_STRANDS_ELEM(vert_next, &iter, root, BM_VERTS_OF_STRAND) {
+
+ if (vert) {
+ /* note: relaxation is applied *after* calculating the next segment, so vertex location can be modified safely */
+ strands_vertex_relax(bm, relax_factor, vert_prev, vert, vert_next, relax);
+
+ /* don't modify fixed root */
+ if (vert_prev && !(skip_first && vert_prev == root)) {
+ add_v3_v3(vert_prev->co, relax_prev);
+ }
+ }
+
+ vert_prev = vert;
+ vert = vert_next;
+ copy_v3_v3(relax_prev, relax);
+ }
+
+ /* last segment */
+ {
+ /* don't modify fixed root */
+ if (vert_prev && !(skip_first && vert_prev == root)) {
+ add_v3_v3(vert_prev->co, relax_prev);
+ }
+ }
+}
+
+/* try to find a nice solution to keep distances between neighboring keys */
+/* XXX Stub implementation ported from particles:
+ * Successively relax each segment starting from the root,
+ * repeat this for every vertex (O(n^2))
+ * This should be replaced by a more advanced method using a least-squares
+ * error metric with length and root location constraints (IK solver)
+ */
+static void strands_solve_edge_relaxation(BMEditStrands *edit)
+{
+ BMesh *bm = edit->bm;
+ BMVert *root;
+ BMIter iter;
+
+ if (!edit)
+ return;
+// if (!(pset->flag & PE_KEEP_LENGTHS)) // XXX TODO
+// return;
+
+ BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) {
+ BMVert *vj;
+ BMIter iterj;
+ int j;
+
+ BM_ITER_STRANDS_ELEM_INDEX(vj, &iterj, root, BM_VERTS_OF_STRAND, j) {
+
+ if (j < 1)
+ continue;
+
+ /* XXX particles use PE_LOCK_FIRST option */
+ bool skip_first = true;
+
+ strands_relax(bm, root, skip_first);
+ }
+ }
+}
+
+typedef struct IKTarget {
+ BMVert *vertex;
+ float weight;
+} IKTarget;
+
+static int strand_find_ik_targets(BMVert *root, IKTarget *targets)
+{
+ BMVert *v;
+ BMIter iter;
+ int k, index;
+
+ index = 0;
+ BM_ITER_STRANDS_ELEM_INDEX(v, &iter, root, BM_VERTS_OF_STRAND, k) {
+ /* XXX TODO allow multiple targets and do weight calculation here */
+ if (BM_strands_vert_is_tip(v)) {
+ IKTarget *target = &targets[index];
+ target->vertex = v;
+ target->weight = 1.0f;
+ ++index;
+ }
+ }
+
+ return index;
+}
+
+static void calc_jacobian_entry(Object *ob, BMEditStrands * /*edit*/, IKTarget *target, int index_target, int index_angle,
+ const float point[3], const float axis1[3], const float axis2[3], MatrixX &J)
+{
+ float (*obmat)[4] = ob->obmat;
+
+ float dist[3], jac1[3], jac2[3];
+
+ sub_v3_v3v3(dist, target->vertex->co, point);
+
+ cross_v3_v3v3(jac1, axis1, dist);
+ cross_v3_v3v3(jac2, axis2, dist);
+
+ for (int i = 0; i < 3; ++i) {
+ J.coeffRef(index_target + i, index_angle + 0) = jac1[i];
+ J.coeffRef(index_target + i, index_angle + 1) = jac2[i];
+ }
+
+#if 1
+ {
+ float wco[3], wdir[3];
+
+ mul_v3_m4v3(wco, obmat, point);
+
+ mul_v3_m4v3(wdir, obmat, jac1);
+ BKE_sim_debug_data_add_vector(wco, wdir, 1,1,0, "strands", index_angle, 1);
+ mul_v3_m4v3(wdir, obmat, jac2);
+ BKE_sim_debug_data_add_vector(wco, wdir, 0,1,1, "strands", index_angle + 1, 2);
+ }
+#endif
+}
+
+static MatrixX strand_calc_target_jacobian(Object *ob, BMEditStrands *edit, BMVert *root, int numjoints, IKTarget *targets, int numtargets)
+{
+ BMVert *v, *vprev;
+ BMIter iter_strand;
+ int k;
+
+ float loc[3], axis[3], dir[3];
+
+ MatrixX J(3 * numtargets, 2 * numjoints);
+ if (!strand_get_root_vectors(edit, root, loc, dir, axis)) {
+ return J;
+ }
+
+ BM_ITER_STRANDS_ELEM_INDEX(v, &iter_strand, root, BM_VERTS_OF_STRAND, k) {
+ float dirprev[3];
+
+ if (k > 0) {
+ float rot[3][3];
+
+ copy_v3_v3(dirprev, dir);
+ sub_v3_v3v3(dir, v->co, vprev->co);
+ normalize_v3(dir);
+
+ rotation_between_vecs_to_mat3(rot, dirprev, dir);
+ mul_m3_v3(rot, axis);
+ }
+
+ calc_jacobian_entry(ob, edit, &targets[0], 0, 2*k, v->co, axis, dir, J);
+
+#if 0
+ {
+ float (*obmat)[4] = ob->obmat;
+ float wco[3], wdir[3];
+
+ mul_v3_m4v3(wco, obmat, v->co);
+
+ mul_v3_m4v3(wdir, obmat, axis);
+ BKE_sim_debug_data_add_vector(edit->debug_data, wco, wdir, 1,0,0, "strands", BM_elem_index_get(v), 1);
+ mul_v3_m4v3(wdir, obmat, dir);
+ BKE_sim_debug_data_add_vector(edit->debug_data, wco, wdir, 0,1,0, "strands", BM_elem_index_get(v), 2);
+ cross_v3_v3v3(wdir, axis, dir);
+ mul_m4_v3(obmat, wdir);
+ BKE_sim_debug_data_add_vector(edit->debug_data, wco, wdir, 0,0,1, "strands", BM_elem_index_get(v), 3);
+ }
+#endif
+
+ vprev = v;
+ }
+
+ return J;
+}
+
+static VectorX strand_angles_to_loc(Object * /*ob*/, BMEditStrands *edit, BMVert *root, int numjoints, const VectorX &angles)
+{
+ BMVert *v, *vprev;
+ BMIter iter_strand;
+ int k;
+
+ float loc[3], axis[3], dir[3];
+ float mat_theta[3][3], mat_phi[3][3];
+
+ if (!strand_get_root_vectors(edit, root, loc, dir, axis))
+ return VectorX();
+
+ VectorX result(3*numjoints);
+
+ BM_ITER_STRANDS_ELEM_INDEX(v, &iter_strand, root, BM_VERTS_OF_STRAND, k) {
+ float dirprev[3];
+
+ if (k > 0) {
+ const float base_length = BM_elem_float_data_named_get(&edit->bm->vdata, v, CD_PROP_FLT, CD_HAIR_SEGMENT_LENGTH);
+ float rot[3][3];
+
+ copy_v3_v3(dirprev, dir);
+ sub_v3_v3v3(dir, v->co, vprev->co);
+ normalize_v3(dir);
+
+ rotation_between_vecs_to_mat3(rot, dirprev, dir);
+ mul_m3_v3(rot, axis);
+
+ /* apply rotations from previous joint on the vertex */
+ float vec[3];
+ mul_v3_v3fl(vec, dir, base_length);
+
+ mul_m3_v3(mat_theta, vec);
+ mul_m3_v3(mat_phi, vec);
+ add_v3_v3v3(&result.coeffRef(3*k), &result.coeff(3*(k-1)), vec);
+ }
+ else {
+ copy_v3_v3(&result.coeffRef(3*k), v->co);
+ }
+
+ float theta = angles[2*k + 0];
+ float phi = angles[2*k + 1];
+ axis_angle_normalized_to_mat3(mat_theta, axis, theta);
+ axis_angle_normalized_to_mat3(mat_phi, dir, phi);
+
+ vprev = v;
+ }
+
+ return result;
+}
+
+static void UNUSED_FUNCTION(strand_apply_ik_result)(Object *UNUSED(ob), BMEditStrands *UNUSED(edit), BMVert *root, const VectorX &solution)
+{
+ BMVert *v;
+ BMIter iter_strand;
+ int k;
+
+ BM_ITER_STRANDS_ELEM_INDEX(v, &iter_strand, root, BM_VERTS_OF_STRAND, k) {
+ copy_v3_v3(v->co, &solution.coeff(3*k));
+ }
+}
+
+static void strands_solve_inverse_kinematics(Object *ob, BMEditStrands *edit, float (*orig)[3])
+{
+ BMesh *bm = edit->bm;
+
+ BMVert *root;
+ BMIter iter;
+
+ BM_ITER_STRANDS(root, &iter, bm, BM_STRANDS_OF_MESH) {
+ int numjoints = strand_count_vertices(root);
+ if (numjoints <= 0)
+ continue;
+
+ IKTarget targets[1]; /* XXX placeholder, later should be allocated to max. strand length */
+ int numtargets = strand_find_ik_targets(root, targets);
+
+ MatrixX J = strand_calc_target_jacobian(ob, edit, root, numjoints, targets, numtargets);
+ MatrixX Jinv = pseudo_inverse(J, 1.e-6);
+
+ VectorX x(3 * numtargets);
+ for (int i = 0; i < numtargets; ++i) {
+ sub_v3_v3v3(&x.coeffRef(3*i), targets[i].vertex->co, orig[i]);
+ /* TODO calculate deviation of vertices from their origin (whatever that is) */
+// x[3*i + 0] = 0.0f;
+// x[3*i + 1] = 0.0f;
+// x[3*i + 2] = 0.0f;
+ }
+ VectorX angles = Jinv * x;
+ VectorX solution = strand_angles_to_loc(ob, edit, root, numjoints, angles);
+
+// strand_apply_ik_result(ob, edit, root, solution);
+
+#if 1
+ {
+ BMVert *v;
+ BMIter iter_strand;
+ int k;
+ float wco[3];
+
+ BM_ITER_STRANDS_ELEM_INDEX(v, &iter_strand, root, BM_VERTS_OF_STRAND, k) {
+ mul_v3_m4v3(wco, ob->obmat, &solution.coeff(3*k));
+ BKE_sim_debug_data_add_circle(wco, 0.05f, 1,0,1, "strands", k, BM_elem_index_get(root), 2344);
+ }
+ }
+#endif
+ }
+}
+
+void BPH_strands_solve_constraints(Object *ob, BMEditStrands *edit, float (*orig)[3])
+{
+ strands_apply_root_locations(edit);
+
+ if (true) {
+ strands_solve_edge_relaxation(edit);
+ }
+ else {
+ if (orig)
+ strands_solve_inverse_kinematics(ob, edit, orig);
+ }
+
+ strands_adjust_segment_lengths(edit->bm);
+}
diff --git a/source/blender/pointcache/CMakeLists.txt b/source/blender/pointcache/CMakeLists.txt
new file mode 100644
index 00000000000..2898785c11e
--- /dev/null
+++ b/source/blender/pointcache/CMakeLists.txt
@@ -0,0 +1,66 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2013, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ intern
+ util
+ ../blenkernel
+ ../blenlib
+ ../makesdna
+ ../makesrna
+ ../../../intern/guardedalloc
+)
+
+set(INC_SYS
+)
+
+set(SRC
+ intern/ptc_types.h
+ intern/ptc_types.cpp
+ intern/reader.h
+ intern/reader.cpp
+ intern/writer.h
+ intern/writer.cpp
+
+ util/util_error_handler.h
+ util/util_error_handler.cpp
+ util/util_task.cpp
+ util/util_task.h
+ util/util_thread.h
+ util/util_types.h
+
+ PTC_api.h
+ PTC_api.cpp
+)
+
+if(NOT WITH_CPP11)
+ list(APPEND INC_SYS ${BOOST_INCLUDE_DIR})
+endif()
+
+if(WITH_ALEMBIC)
+ add_definitions(-DWITH_PTC_ALEMBIC)
+ add_subdirectory(alembic)
+endif()
+
+blender_add_lib(bf_pointcache "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/pointcache/PTC_api.cpp b/source/blender/pointcache/PTC_api.cpp
new file mode 100644
index 00000000000..767d4451120
--- /dev/null
+++ b/source/blender/pointcache/PTC_api.cpp
@@ -0,0 +1,423 @@
+/*
+ * ***** 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.
+ *
+ * Copyright 2013, Blender Foundation.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "PTC_api.h"
+
+#include "util/util_error_handler.h"
+
+#include "reader.h"
+#include "writer.h"
+
+#include "ptc_types.h"
+
+extern "C" {
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "DNA_listBase.h"
+#include "DNA_modifier_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_modifier.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+
+#include "RNA_access.h"
+}
+
+using namespace PTC;
+
+class StubFactory : public Factory {
+ const std::string &get_default_extension() { static std::string ext = ""; return ext; }
+ WriterArchive *open_writer_archive(double /*fps*/, float /*start_frame*/, const std::string &/*name*/, PTCArchiveResolution /*resolutions*/,
+ const char */*app_name*/, const char */*description*/, const struct tm */*time*/, struct IDProperty */*metadata*/, ErrorHandler */*error_handler*/) { return NULL; }
+ ReaderArchive *open_reader_archive(double /*fps*/, float /*start_frame*/, const std::string &/*name*/, ErrorHandler * /*error_handler*/) { return NULL; }
+ void slice(ReaderArchive * /*in*/, WriterArchive * /*out*/, struct ListBase * /*slices*/) {}
+ Writer *create_writer_object(const std::string &/*name*/, Scene */*scene*/, Object */*ob*/) { return NULL; }
+ Reader *create_reader_object(const std::string &/*name*/, Object */*ob*/) { return NULL; }
+ Writer *create_writer_group(const std::string &/*name*/, Group */*group*/) { return NULL; }
+ Reader *create_reader_group(const std::string &/*name*/, Group */*group*/) { return NULL; }
+ Writer *create_writer_cloth(const std::string &/*name*/, Object */*ob*/, ClothModifierData */*clmd*/) { return NULL; }
+ Reader *create_reader_cloth(const std::string &/*name*/, Object */*ob*/, ClothModifierData */*clmd*/) { return NULL; }
+ Writer *create_writer_derived_mesh(const std::string &/*name*/, Object */*ob*/, DerivedMesh **/*dm_ptr*/) { return NULL; }
+ Reader *create_reader_derived_mesh(const std::string &/*name*/, Object */*ob*/) { return NULL; }
+ Writer *create_writer_derived_final_realtime(const std::string &/*name*/, Object */*ob*/) { return NULL; }
+ Writer *create_writer_derived_final_render(const std::string &/*name*/, Scene */*scene*/, Object */*ob*/, DerivedMesh **/*render_dm_ptr*/) { return NULL; }
+ Writer *create_writer_dupligroup(const std::string &/*name*/, EvaluationContext */*eval_ctx*/, Scene */*scene*/, Group */*group*/, CacheLibrary */*cachelib*/) { return NULL; }
+ Writer *create_writer_duplicache(const std::string &/*name*/, Group */*group*/, DupliCache */*dupcache*/, int /*datatypes*/, bool /*do_sim_debug*/) { return NULL; }
+ Reader *create_reader_duplicache(const std::string &/*name*/, Group */*group*/, DupliCache */*dupcache*/, bool /*read_strands_motion*/, bool /*read_strands_children*/, bool /*do_sim_debug*/) { return NULL; }
+ Reader *create_reader_duplicache_object(const std::string &/*name*/, Object */*ob*/, DupliObjectData */*data*/, bool /*read_strands_motion*/, bool /*read_strands_children*/) { return NULL; }
+};
+
+#ifndef WITH_PTC_ALEMBIC
+void PTC_alembic_init()
+{
+ static StubFactory stub_factory;
+ PTC::Factory::alembic = &stub_factory;
+}
+#endif
+
+void PTC_error_handler_std(void)
+{
+ ErrorHandler::clear_default_handler();
+}
+
+void PTC_error_handler_callback(PTCErrorCallback cb, void *userdata)
+{
+ ErrorHandler::set_default_handler(new CallbackErrorHandler(cb, userdata));
+}
+
+static ReportType report_type_from_error_level(PTCErrorLevel level)
+{
+ switch (level) {
+ case PTC_ERROR_NONE: return RPT_DEBUG;
+ case PTC_ERROR_INFO: return RPT_INFO;
+ case PTC_ERROR_WARNING: return RPT_WARNING;
+ case PTC_ERROR_CRITICAL: return RPT_ERROR;
+ }
+ return RPT_ERROR;
+}
+
+static void error_handler_reports_cb(void *vreports, PTCErrorLevel level, const char *message)
+{
+ ReportList *reports = (ReportList *)vreports;
+
+ BKE_report(reports, report_type_from_error_level(level), message);
+}
+
+void PTC_error_handler_reports(struct ReportList *reports)
+{
+ ErrorHandler::set_default_handler(new CallbackErrorHandler(error_handler_reports_cb, reports));
+}
+
+static void error_handler_modifier_cb(void *vmd, PTCErrorLevel UNUSED(level), const char *message)
+{
+ ModifierData *md = (ModifierData *)vmd;
+
+ modifier_setError(md, "%s", message);
+}
+
+void PTC_error_handler_modifier(struct ModifierData *md)
+{
+ ErrorHandler::set_default_handler(new CallbackErrorHandler(error_handler_modifier_cb, md));
+}
+
+
+const char *PTC_get_default_archive_extension(void)
+{
+ return PTC::Factory::alembic->get_default_extension().c_str();
+}
+
+PTCWriterArchive *PTC_open_writer_archive(double fps, float start_frame, const char *path, PTCArchiveResolution resolutions,
+ const char *app_name, const char *description, const struct tm *time, struct IDProperty *metadata)
+{
+ return (PTCWriterArchive *)PTC::Factory::alembic->open_writer_archive(fps, start_frame, path, resolutions, app_name, description, time, metadata, NULL);
+}
+
+void PTC_close_writer_archive(PTCWriterArchive *_archive)
+{
+ PTC::WriterArchive *archive = (PTC::WriterArchive *)_archive;
+ delete archive;
+}
+
+void PTC_writer_archive_use_render(PTCWriterArchive *_archive, bool enable)
+{
+ PTC::WriterArchive *archive = (PTC::WriterArchive *)_archive;
+ archive->use_render(enable);
+}
+
+PTCReaderArchive *PTC_open_reader_archive(Scene *scene, const char *path)
+{
+ double fps = FPS;
+ float start_frame = scene->r.sfra;
+ return PTC_open_reader_archive_ex(fps, start_frame, path);
+}
+
+PTCReaderArchive *PTC_open_reader_archive_ex(double fps, float start_frame, const char *path)
+{
+ return (PTCReaderArchive *)PTC::Factory::alembic->open_reader_archive(fps, start_frame, path, NULL);
+}
+
+void PTC_close_reader_archive(PTCReaderArchive *_archive)
+{
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ delete archive;
+}
+
+PTCArchiveResolution PTC_reader_archive_get_resolutions(PTCReaderArchive *_archive)
+{
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ return archive->get_resolutions();
+}
+
+void PTC_reader_archive_use_render(PTCReaderArchive *_archive, bool enable)
+{
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ archive->use_render(enable);
+}
+
+bool PTC_reader_archive_get_frame_range(PTCReaderArchive *_archive, int *start_frame, int *end_frame)
+{
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ return archive->get_frame_range(*start_frame, *end_frame);
+}
+
+void PTC_writer_init(PTCWriter *_writer, PTCWriterArchive *_archive)
+{
+ PTC::Writer *writer = (PTC::Writer *)_writer;
+ PTC::WriterArchive *archive = (PTC::WriterArchive *)_archive;
+ writer->init(archive);
+}
+
+void PTC_writer_create_refs(PTCWriter *_writer)
+{
+ PTC::Writer *writer = (PTC::Writer *)_writer;
+ writer->create_refs();
+}
+
+void PTC_reader_init(PTCReader *_reader, PTCReaderArchive *_archive)
+{
+ PTC::Reader *reader = (PTC::Reader *)_reader;
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ reader->init(archive);
+}
+
+/* ========================================================================= */
+
+void PTC_writer_free(PTCWriter *_writer)
+{
+ PTC::Writer *writer = (PTC::Writer *)_writer;
+ delete writer;
+}
+
+void PTC_write_sample(struct PTCWriter *_writer)
+{
+ PTC::Writer *writer = (PTC::Writer *)_writer;
+ writer->write_sample();
+}
+
+
+void PTC_reader_free(PTCReader *_reader)
+{
+ PTC::Reader *reader = (PTC::Reader *)_reader;
+ delete reader;
+}
+
+bool PTC_reader_get_frame_range(PTCReader *_reader, int *start_frame, int *end_frame)
+{
+ PTC::Reader *reader = (PTC::Reader *)_reader;
+ int sfra, efra;
+ if (reader->get_frame_range(sfra, efra)) {
+ if (start_frame) *start_frame = sfra;
+ if (end_frame) *end_frame = efra;
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+PTCReadSampleResult PTC_read_sample(PTCReader *_reader, float frame)
+{
+ PTC::Reader *reader = (PTC::Reader *)_reader;
+ return reader->read_sample(frame);
+}
+
+PTCReadSampleResult PTC_test_sample(PTCReader *_reader, float frame)
+{
+ PTC::Reader *reader = (PTC::Reader *)_reader;
+ return reader->test_sample(frame);
+}
+
+void PTC_get_archive_info_stream(PTCReaderArchive *_archive, void (*stream)(void *, const char *), void *userdata)
+{
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ archive->get_info_stream(stream, userdata);
+}
+
+void PTC_get_archive_info(PTCReaderArchive *_archive, struct CacheArchiveInfo *info, IDProperty *metadata)
+{
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ archive->get_info(info, metadata);
+}
+
+void PTC_get_archive_info_nodes(PTCReaderArchive *_archive, struct CacheArchiveInfo *info, bool calc_bytes_size)
+{
+ PTC::ReaderArchive *archive = (PTC::ReaderArchive *)_archive;
+ archive->get_info_nodes(info, calc_bytes_size);
+}
+
+void PTC_archive_slice(PTCReaderArchive *_in, PTCWriterArchive *_out, struct ListBase *slices)
+{
+ PTC::ReaderArchive *in = (PTC::ReaderArchive *)_in;
+ PTC::WriterArchive *out = (PTC::WriterArchive *)_out;
+
+ PTC::Factory::alembic->slice(in, out, slices);
+}
+
+
+PTCWriter *PTC_writer_dupligroup(const char *name, struct EvaluationContext *eval_ctx, struct Scene *scene, struct Group *group, struct CacheLibrary *cachelib)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_dupligroup(name, eval_ctx, scene, group, cachelib);
+}
+
+PTCWriter *PTC_writer_duplicache(const char *name, struct Group *group, struct DupliCache *dupcache, int datatypes, bool do_sim_debug)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_duplicache(name, group, dupcache, datatypes, do_sim_debug);
+}
+
+PTCReader *PTC_reader_duplicache(const char *name, struct Group *group, struct DupliCache *dupcache,
+ bool read_strands_motion, bool read_strands_children, bool read_sim_debug)
+{
+ return (PTCReader *)PTC::Factory::alembic->create_reader_duplicache(name, group, dupcache,
+ read_strands_motion, read_strands_children, read_sim_debug);
+}
+
+PTCReader *PTC_reader_duplicache_object(const char *name, struct Object *ob, struct DupliObjectData *data,
+ bool read_strands_motion, bool read_strands_children)
+{
+ return (PTCReader *)PTC::Factory::alembic->create_reader_duplicache_object(name, ob, data, read_strands_motion, read_strands_children);
+}
+
+
+/* get writer/reader from RNA type */
+PTCWriter *PTC_writer_from_rna(Scene */*scene*/, PointerRNA */*ptr*/)
+{
+#if 0
+#if 0
+ if (RNA_struct_is_a(ptr->type, &RNA_ParticleSystem)) {
+ Object *ob = (Object *)ptr->id.data;
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+ return PTC_writer_particles_combined(scene, ob, psys);
+ }
+#endif
+ if (RNA_struct_is_a(ptr->type, &RNA_ClothModifier)) {
+ Object *ob = (Object *)ptr->id.data;
+ ClothModifierData *clmd = (ClothModifierData *)ptr->data;
+ return PTC_writer_cloth(scene, ob, clmd);
+ }
+#endif
+ return NULL;
+}
+
+PTCReader *PTC_reader_from_rna(Scene */*scene*/, PointerRNA */*ptr*/)
+{
+#if 0
+ if (RNA_struct_is_a(ptr->type, &RNA_ParticleSystem)) {
+ Object *ob = (Object *)ptr->id.data;
+ ParticleSystem *psys = (ParticleSystem *)ptr->data;
+ /* XXX particles are bad ...
+ * this can be either the actual particle cache or the hair dynamics cache,
+ * which is actually the cache of the internal cloth modifier
+ */
+ bool use_cloth_cache = psys->part->type == PART_HAIR && (psys->flag & PSYS_HAIR_DYNAMICS);
+ if (use_cloth_cache && psys->clmd)
+ return PTC_reader_cloth(scene, ob, psys->clmd);
+ else
+ return PTC_reader_particles(scene, ob, psys);
+ }
+ if (RNA_struct_is_a(ptr->type, &RNA_ClothModifier)) {
+ Object *ob = (Object *)ptr->id.data;
+ ClothModifierData *clmd = (ClothModifierData *)ptr->data;
+ return PTC_reader_cloth(scene, ob, clmd);
+ }
+#endif
+ return NULL;
+}
+
+
+/* ==== CLOTH ==== */
+
+PTCWriter *PTC_writer_cloth(const char *name, Object *ob, ClothModifierData *clmd)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_cloth(name, ob, clmd);
+}
+
+PTCReader *PTC_reader_cloth(const char *name, Object *ob, ClothModifierData *clmd)
+{
+ return (PTCReader *)PTC::Factory::alembic->create_reader_cloth(name, ob, clmd);
+}
+
+
+/* ==== MESH ==== */
+
+PTCWriter *PTC_writer_derived_mesh(const char *name, Object *ob, DerivedMesh **dm_ptr)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_derived_mesh(name, ob, dm_ptr);
+}
+
+PTCReader *PTC_reader_derived_mesh(const char *name, Object *ob)
+{
+ return (PTCReader *)PTC::Factory::alembic->create_reader_derived_mesh(name, ob);
+}
+
+struct DerivedMesh *PTC_reader_derived_mesh_acquire_result(PTCReader *_reader)
+{
+ DerivedMeshReader *reader = (DerivedMeshReader *)_reader;
+ return reader->acquire_result();
+}
+
+void PTC_reader_derived_mesh_discard_result(PTCReader *_reader)
+{
+ DerivedMeshReader *reader = (DerivedMeshReader *)_reader;
+ reader->discard_result();
+}
+
+
+PTCWriter *PTC_writer_derived_final_realtime(const char *name, Object *ob)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_derived_final_realtime(name, ob);
+}
+
+PTCWriter *PTC_writer_derived_final_render(const char *name, Scene *scene, Object *ob, DerivedMesh **render_dm_ptr)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_derived_final_render(name, scene, ob, render_dm_ptr);
+}
+
+
+/* ==== OBJECT ==== */
+
+PTCWriter *PTC_writer_object(const char *name, Scene *scene, Object *ob)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_object(name, scene, ob);
+}
+
+PTCReader *PTC_reader_object(const char *name, Object *ob)
+{
+ return (PTCReader *)PTC::Factory::alembic->create_reader_object(name, ob);
+}
+
+
+/* ==== GROUP ==== */
+
+PTCWriter *PTC_writer_group(const char *name, Group *group)
+{
+ return (PTCWriter *)PTC::Factory::alembic->create_writer_group(name, group);
+}
+
+PTCReader *PTC_reader_group(const char *name, Group *group)
+{
+ return (PTCReader *)PTC::Factory::alembic->create_writer_group(name, group);
+}
diff --git a/source/blender/pointcache/PTC_api.h b/source/blender/pointcache/PTC_api.h
new file mode 100644
index 00000000000..e164cd5d177
--- /dev/null
+++ b/source/blender/pointcache/PTC_api.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __PTC_API_H__
+#define __PTC_API_H__
+
+#include "util/util_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct tm;
+
+struct Main;
+struct Scene;
+struct EvaluationContext;
+struct ListBase;
+struct PointerRNA;
+struct ReportList;
+struct CacheArchiveInfo;
+
+struct DupliCache;
+struct ClothModifierData;
+struct DerivedMesh;
+struct Group;
+struct IDProperty;
+struct ModifierData;
+struct Object;
+struct ParticleSystem;
+struct SoftBody;
+
+struct PTCWriterArchive;
+struct PTCReaderArchive;
+struct PTCWriter;
+struct PTCReader;
+
+void PTC_alembic_init(void);
+
+/*** Error Handling ***/
+void PTC_error_handler_std(void);
+void PTC_error_handler_callback(PTCErrorCallback cb, void *userdata);
+void PTC_error_handler_reports(struct ReportList *reports);
+void PTC_error_handler_modifier(struct ModifierData *md);
+
+/*** Archive ***/
+
+const char *PTC_get_default_archive_extension(void);
+
+struct PTCWriterArchive *PTC_open_writer_archive(double fps, float start_frame, const char *path, PTCArchiveResolution resolutions,
+ const char *app_name, const char *description, const struct tm *time, struct IDProperty *metadata);
+void PTC_close_writer_archive(struct PTCWriterArchive *archive);
+void PTC_writer_archive_use_render(struct PTCWriterArchive *archive, bool enable);
+
+struct PTCReaderArchive *PTC_open_reader_archive(struct Scene *scene, const char *path);
+struct PTCReaderArchive *PTC_open_reader_archive_ex(double fps, float start_frame, const char *path);
+void PTC_close_reader_archive(struct PTCReaderArchive *archive);
+PTCArchiveResolution PTC_reader_archive_get_resolutions(struct PTCReaderArchive *archive);
+void PTC_reader_archive_use_render(struct PTCReaderArchive *archive, bool enable);
+bool PTC_reader_archive_get_frame_range(struct PTCReaderArchive *_archive, int *start_frame, int *end_frame);
+
+void PTC_writer_init(struct PTCWriter *writer, struct PTCWriterArchive *archive);
+void PTC_writer_create_refs(struct PTCWriter *writer);
+void PTC_reader_init(struct PTCReader *reader, struct PTCReaderArchive *archive);
+
+/*** Reader/Writer Interface ***/
+
+void PTC_writer_free(struct PTCWriter *writer);
+void PTC_write_sample(struct PTCWriter *writer);
+
+void PTC_reader_free(struct PTCReader *reader);
+bool PTC_reader_get_frame_range(struct PTCReader *reader, int *start_frame, int *end_frame);
+PTCReadSampleResult PTC_read_sample(struct PTCReader *reader, float frame);
+PTCReadSampleResult PTC_test_sample(struct PTCReader *reader, float frame);
+
+void PTC_get_archive_info_stream(struct PTCReaderArchive *archive, void (*stream)(void *, const char *), void *userdata);
+void PTC_get_archive_info(struct PTCReaderArchive *_archive, struct CacheArchiveInfo *info, struct IDProperty *metadata);
+void PTC_get_archive_info_nodes(struct PTCReaderArchive *_archive, struct CacheArchiveInfo *info, bool calc_bytes_size);
+
+typedef struct CacheSlice {
+ struct CacheSlice *next, *prev;
+ int start, end;
+} CacheSlice;
+
+void PTC_archive_slice(struct PTCReaderArchive *in, struct PTCWriterArchive *out, struct ListBase *slices);
+
+struct PTCWriter *PTC_writer_dupligroup(const char *name, struct EvaluationContext *eval_ctx, struct Scene *scene, struct Group *group, struct CacheLibrary *cachelib);
+struct PTCWriter *PTC_writer_duplicache(const char *name, struct Group *group, struct DupliCache *dupcache, int datatypes, bool do_sim_debug);
+
+struct PTCReader *PTC_reader_duplicache(const char *name, struct Group *group, struct DupliCache *dupcache,
+ bool read_strands_motion, bool read_strands_children, bool read_sim_debug);
+struct PTCReader *PTC_reader_duplicache_object(const char *name, struct Object *ob, struct DupliObjectData *data,
+ bool read_strands_motion, bool read_strands_children);
+
+/* get writer/reader from RNA type */
+struct PTCWriter *PTC_writer_from_rna(struct Scene *scene, struct PointerRNA *ptr);
+struct PTCReader *PTC_reader_from_rna(struct Scene *scene, struct PointerRNA *ptr);
+
+/* Object */
+struct PTCWriter *PTC_writer_object(const char *name, struct Scene *scene, struct Object *ob);
+struct PTCReader *PTC_reader_object(const char *name, struct Object *ob);
+
+/* Group */
+struct PTCWriter *PTC_writer_group(const char *name, struct Group *group);
+struct PTCReader *PTC_reader_group(const char *name, struct Group *group);
+
+/* Cloth */
+struct PTCWriter *PTC_writer_cloth(const char *name, struct Object *ob, struct ClothModifierData *clmd);
+struct PTCReader *PTC_reader_cloth(const char *name, struct Object *ob, struct ClothModifierData *clmd);
+
+struct PTCWriter *PTC_writer_derived_mesh(const char *name, struct Object *ob, struct DerivedMesh **dm_ptr);
+struct PTCReader *PTC_reader_derived_mesh(const char *name, struct Object *ob);
+struct DerivedMesh *PTC_reader_derived_mesh_acquire_result(struct PTCReader *reader);
+void PTC_reader_derived_mesh_discard_result(struct PTCReader *reader);
+
+struct PTCWriter *PTC_writer_derived_final_realtime(const char *name, struct Object *ob);
+struct PTCWriter *PTC_writer_derived_final_render(const char *name, struct Scene *scene, struct Object *ob, struct DerivedMesh **render_dm_ptr);
+
+#ifdef __cplusplus
+} /* extern C */
+#endif
+
+#endif /* __PTC_API_H__ */
diff --git a/source/blender/pointcache/SConscript b/source/blender/pointcache/SConscript
new file mode 100644
index 00000000000..f94bbd43668
--- /dev/null
+++ b/source/blender/pointcache/SConscript
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+#
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2014, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): Lukas Toenne.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+Import ('env')
+
+sources = env.Glob('intern/*.cpp') + env.Glob('util/*.cpp') + env.Glob('*.cpp')
+
+incs = [
+ '.',
+ 'intern',
+ 'util',
+ '../blenkernel',
+ '../blenlib',
+ '../makesdna',
+ '../makesrna',
+ '../../../intern/guardedalloc',
+ ]
+
+defs = []
+
+if not env['WITH_BF_CPP11']:
+ incs.append(env['BF_BOOST_INC'])
+
+if env['WITH_BF_INTERNATIONAL']:
+ defs.append('WITH_INTERNATIONAL')
+
+if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'):
+ incs.append(env['BF_PTHREADS_INC'])
+
+if env['WITH_BF_ALEMBIC']:
+ defs.append('WITH_PTC_ALEMBIC')
+ SConscript(['alembic/SConscript'])
+
+env.BlenderLib('bf_pointcache', sources, incs, defines=defs, libtype=['core','player'], priority=[902,902])
diff --git a/source/blender/pointcache/alembic/CMakeLists.txt b/source/blender/pointcache/alembic/CMakeLists.txt
new file mode 100644
index 00000000000..d7391428d5d
--- /dev/null
+++ b/source/blender/pointcache/alembic/CMakeLists.txt
@@ -0,0 +1,75 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2013, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ .
+ ../
+ ../intern
+ ../util
+ ../../blenkernel
+ ../../blenlib
+ ../../makesdna
+ ../../makesrna
+ ../../../../intern/guardedalloc
+)
+
+set(INC_SYS
+ ${ALEMBIC_INCLUDE_DIRS}
+ ${OPENEXR_INCLUDE_DIR}/OpenEXR
+)
+
+set(SRC
+ alembic.cpp
+ alembic.h
+
+ abc_frame_mapper.cpp
+ abc_frame_mapper.h
+ abc_info.cpp
+ abc_interpolate.cpp
+ abc_interpolate.h
+ abc_reader.cpp
+ abc_reader.h
+ abc_schema.h
+ abc_split.cpp
+ abc_writer.cpp
+ abc_writer.h
+
+ abc_cloth.cpp
+ abc_cloth.h
+ abc_customdata.cpp
+ abc_customdata.h
+ abc_group.cpp
+ abc_group.h
+ abc_mesh.cpp
+ abc_mesh.h
+ abc_object.cpp
+ abc_object.h
+ abc_particles.cpp
+ abc_particles.h
+ abc_simdebug.cpp
+ abc_simdebug.h
+)
+
+add_definitions(-DWITH_ALEMBIC)
+
+blender_add_lib(bf_pointcache_alembic "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/pointcache/alembic/SConscript b/source/blender/pointcache/alembic/SConscript
new file mode 100644
index 00000000000..b9da2df57c2
--- /dev/null
+++ b/source/blender/pointcache/alembic/SConscript
@@ -0,0 +1,62 @@
+#!/usr/bin/env python
+#
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2014, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): Lukas Toenne.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+Import ('env')
+
+sources = env.Glob('*.cpp')
+
+incs = [
+ '.',
+ '../',
+ '../intern',
+ '../util',
+ '../../blenkernel',
+ '../../blenlib',
+ '../../makesdna',
+ '../../makesrna',
+ '../../../../intern/guardedalloc',
+ ]
+
+incs += Split(env['BF_OPENEXR_INC'])
+
+defs = []
+
+if env['WITH_BF_INTERNATIONAL']:
+ defs.append('WITH_INTERNATIONAL')
+
+if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'):
+ incs.append(env['BF_PTHREADS_INC'])
+
+incs.append(env['BF_OPENEXR_INC'])
+incs.append(env['BF_ALEMBIC_INC'])
+
+if not env['WITH_BF_CPP11']:
+ incs.append(env['BF_BOOST_INC'])
+
+defs.append('WITH_ALEMBIC')
+
+env.BlenderLib('bf_pointcache_alembic', sources, incs, defines=defs, libtype=['core','player'], priority=[901, 901])
diff --git a/source/blender/pointcache/alembic/abc_cloth.cpp b/source/blender/pointcache/alembic/abc_cloth.cpp
new file mode 100644
index 00000000000..a722bc2b725
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_cloth.cpp
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "abc_cloth.h"
+
+extern "C" {
+#include "BLI_math.h"
+
+#include "DNA_cloth_types.h"
+#include "DNA_object_types.h"
+#include "DNA_modifier_types.h"
+
+#include "BKE_cloth.h"
+}
+
+#include "PTC_api.h"
+
+namespace PTC {
+
+using namespace Abc;
+using namespace AbcGeom;
+
+AbcClothWriter::AbcClothWriter(const std::string &name, Object *ob, ClothModifierData *clmd) :
+ ClothWriter(ob, clmd, name)
+{
+ set_error_handler(new ModifierErrorHandler(&clmd->modifier));
+}
+
+AbcClothWriter::~AbcClothWriter()
+{
+}
+
+void AbcClothWriter::init_abc(OObject parent)
+{
+ if (m_points)
+ return;
+
+ m_points = OPoints(parent, m_name, abc_archive()->frame_sampling_index());
+
+ OPointsSchema &schema = m_points.getSchema();
+ OCompoundProperty geom_params = schema.getArbGeomParams();
+
+ m_param_velocities = OV3fGeomParam(geom_params, "velocities", false, kVaryingScope, 1, 0);
+ m_param_goal_positions = OP3fGeomParam(geom_params, "goal_positions", false, kVaryingScope, 1, 0);
+}
+
+static V3fArraySample create_sample_velocities(Cloth *cloth, std::vector<V3f> &data)
+{
+ ClothVertex *vert;
+ int i, totvert = cloth->numverts;
+
+ data.reserve(totvert);
+ for (i = 0, vert = cloth->verts; i < totvert; ++i, ++vert) {
+ float *co = vert->v;
+ data.push_back(V3f(co[0], co[1], co[2]));
+ }
+
+ return V3fArraySample(data);
+}
+
+static P3fArraySample create_sample_goal_positions(Cloth *cloth, std::vector<V3f> &data)
+{
+ ClothVertex *vert;
+ int i, totvert = cloth->numverts;
+
+ data.reserve(totvert);
+ for (i = 0, vert = cloth->verts; i < totvert; ++i, ++vert) {
+ float *co = vert->xconst;
+ data.push_back(V3f(co[0], co[1], co[2]));
+ }
+
+ return P3fArraySample(data);
+}
+
+void AbcClothWriter::write_sample()
+{
+ if (!m_points)
+ return;
+
+ Cloth *cloth = m_clmd->clothObject;
+ if (!cloth)
+ return;
+
+ OPointsSchema &schema = m_points.getSchema();
+
+ int totpoint = cloth->numverts;
+ ClothVertex *vert;
+ int i;
+
+ /* XXX TODO only needed for the first frame/sample */
+ std::vector<Util::uint64_t> ids;
+ ids.reserve(totpoint);
+ for (i = 0, vert = cloth->verts; i < totpoint; ++i, ++vert)
+ ids.push_back(i);
+
+ std::vector<V3f> positions;
+ positions.reserve(totpoint);
+ for (i = 0, vert = cloth->verts; i < totpoint; ++i, ++vert) {
+ float *co = vert->x;
+ positions.push_back(V3f(co[0], co[1], co[2]));
+ }
+
+ std::vector<V3f> velocities_buffer;
+ std::vector<V3f> goal_positions_buffer;
+ V3fArraySample velocities = create_sample_velocities(cloth, velocities_buffer);
+ P3fArraySample goal_positions = create_sample_goal_positions(cloth, goal_positions_buffer);
+
+ OPointsSchema::Sample sample = OPointsSchema::Sample(V3fArraySample(positions), UInt64ArraySample(ids));
+ schema.set(sample);
+
+ m_param_velocities.set(OV3fGeomParam::Sample(velocities, kVaryingScope));
+ m_param_goal_positions.set(OP3fGeomParam::Sample(goal_positions, kVaryingScope));
+}
+
+
+AbcClothReader::AbcClothReader(const std::string &name, Object *ob, ClothModifierData *clmd) :
+ ClothReader(ob, clmd, name)
+{
+ set_error_handler(new ModifierErrorHandler(&clmd->modifier));
+}
+
+AbcClothReader::~AbcClothReader()
+{
+}
+
+void AbcClothReader::init_abc(IObject object)
+{
+ if (m_points)
+ return;
+ m_points = IPoints(object, kWrapExisting);
+
+ IPointsSchema &schema = m_points.getSchema();
+ ICompoundProperty geom_params = schema.getArbGeomParams();
+
+ m_param_velocities = IV3fGeomParam(geom_params, "velocities", 0);
+ m_param_goal_positions = IP3fGeomParam(geom_params, "goal_positions", 0);
+}
+
+static void apply_sample_positions(Cloth *cloth, P3fArraySamplePtr sample)
+{
+ ClothVertex *vert;
+ int i, totvert = cloth->numverts;
+
+ BLI_assert(sample->size() == totvert);
+
+ const V3f *data = sample->get();
+ for (i = 0, vert = cloth->verts; i < totvert; ++i, ++vert) {
+ const V3f &co = data[i];
+ copy_v3_v3(vert->x, co.getValue());
+ }
+}
+
+static void apply_sample_velocities(Cloth *cloth, V3fArraySamplePtr sample)
+{
+ ClothVertex *vert;
+ int i, totvert = cloth->numverts;
+
+ BLI_assert(sample->size() == totvert);
+
+ const V3f *data = sample->get();
+ for (i = 0, vert = cloth->verts; i < totvert; ++i, ++vert) {
+ const V3f &vel = data[i];
+ copy_v3_v3(vert->v, vel.getValue());
+ }
+}
+
+static void apply_sample_goal_positions(Cloth *cloth, P3fArraySamplePtr sample)
+{
+ ClothVertex *vert;
+ int i, totvert = cloth->numverts;
+
+ BLI_assert(sample->size() == totvert);
+
+ const V3f *data = sample->get();
+ for (i = 0, vert = cloth->verts; i < totvert; ++i, ++vert) {
+ const V3f &co = data[i];
+ copy_v3_v3(vert->xconst, co.getValue());
+ }
+}
+
+PTCReadSampleResult AbcClothReader::read_sample_abc(chrono_t time)
+{
+ Cloth *cloth = m_clmd->clothObject;
+
+ if (!m_points)
+ return PTC_READ_SAMPLE_INVALID;
+
+ IPointsSchema &schema = m_points.getSchema();
+ if (schema.getNumSamples() == 0)
+ return PTC_READ_SAMPLE_INVALID;
+
+ ISampleSelector ss = get_frame_sample_selector(time);
+
+ IPointsSchema::Sample sample;
+ schema.get(sample, ss);
+
+ P3fArraySamplePtr positions = sample.getPositions();
+
+ V3fArraySamplePtr velocities;
+ if (m_param_velocities && m_param_velocities.getNumSamples() > 0) {
+ IV3fGeomParam::Sample sample_velocities;
+ m_param_velocities.getExpanded(sample_velocities, ss);
+ velocities = sample_velocities.getVals();
+ }
+
+ P3fArraySamplePtr goal_positions;
+ if (m_param_goal_positions && m_param_goal_positions.getNumSamples() > 0) {
+ IP3fGeomParam::Sample sample_goal_positions;
+ m_param_goal_positions.getExpanded(sample_goal_positions, ss);
+ goal_positions = sample_goal_positions.getVals();
+ }
+
+ apply_sample_positions(cloth, positions);
+ if (velocities)
+ apply_sample_velocities(cloth, velocities);
+ if (goal_positions)
+ apply_sample_goal_positions(cloth, goal_positions);
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_cloth.h b/source/blender/pointcache/alembic/abc_cloth.h
new file mode 100644
index 00000000000..9b30b183a6c
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_cloth.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_ABC_CLOTH_H
+#define PTC_ABC_CLOTH_H
+
+#include <Alembic/AbcGeom/IPoints.h>
+#include <Alembic/AbcGeom/OPoints.h>
+
+#include "ptc_types.h"
+
+#include "abc_reader.h"
+#include "abc_schema.h"
+#include "abc_writer.h"
+
+struct Object;
+struct ClothModifierData;
+
+namespace PTC {
+
+class AbcClothWriter : public ClothWriter, public AbcWriter {
+public:
+ AbcClothWriter(const std::string &name, Object *ob, ClothModifierData *clmd);
+ ~AbcClothWriter();
+
+ void init_abc(Abc::OObject parent);
+
+ void write_sample();
+
+private:
+ AbcGeom::OPoints m_points;
+ AbcGeom::OV3fGeomParam m_param_velocities;
+ AbcGeom::OP3fGeomParam m_param_goal_positions;
+};
+
+class AbcClothReader : public ClothReader, public AbcReader {
+public:
+ AbcClothReader(const std::string &name, Object *ob, ClothModifierData *clmd);
+ ~AbcClothReader();
+
+ void init_abc(Abc::IObject parent);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+private:
+ AbcGeom::IPoints m_points;
+ AbcGeom::IV3fGeomParam m_param_velocities;
+ AbcGeom::IP3fGeomParam m_param_goal_positions;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_CLOTH_H */
diff --git a/source/blender/pointcache/alembic/abc_customdata.cpp b/source/blender/pointcache/alembic/abc_customdata.cpp
new file mode 100644
index 00000000000..77a9ea73a5b
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_customdata.cpp
@@ -0,0 +1,804 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <sstream>
+
+#include <Alembic/AbcGeom/IGeomParam.h>
+#include <Alembic/AbcGeom/OGeomParam.h>
+
+#include "abc_customdata.h"
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_customdata_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_customdata.h"
+}
+
+namespace PTC {
+
+using namespace Abc;
+using namespace AbcGeom;
+
+/* DEBUG */
+BLI_INLINE void print_writer_compound(OCompoundProperty &prop)
+{
+ CompoundPropertyWriterPtr ptr = prop.getPtr()->asCompoundPtr();
+ printf("compound %s: [%p] (%d)\n", ptr->getName().c_str(), ptr.get(), (int)ptr->getNumProperties());
+ for (int i = 0; i < ptr->getNumProperties(); ++i) {
+ printf(" %d: [%p]\n", i, prop.getProperty(i).getPtr().get());
+ printf(" %s\n", prop.getProperty(i).getName().c_str());
+ }
+}
+
+/* ========================================================================= */
+
+template <CustomDataType CDTYPE>
+static void write_sample(CustomDataWriter */*writer*/, OCompoundProperty &/*parent*/, const std::string &/*name*/, void */*data*/, int /*num_data*/)
+{
+ /* no implementation available, should not happen */
+ printf("ERROR: CustomData type %s has no write_sample implementation\n", CustomData_layertype_name((int)CDTYPE));
+}
+
+template <>
+void write_sample<CD_MDEFORMVERT>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent);
+
+ OInt32ArrayProperty totweight_prop = writer->add_array_property<OInt32ArrayProperty>(name + ":totweight", prop);
+ OInt32ArrayProperty flag_prop = writer->add_array_property<OInt32ArrayProperty>(name + ":flag", prop);
+ OInt32ArrayProperty def_nr_prop = writer->add_array_property<OInt32ArrayProperty>(name + ":def_nr", prop);
+ OFloatArrayProperty weight_prop = writer->add_array_property<OFloatArrayProperty>(name + ":weight", prop);
+
+ MDeformVert *mdef = (MDeformVert *)data;
+
+ /* sum all totweight for the sample size */
+ int num_mdefweight = 0;
+ for (int i = 0; i < num_data; ++i)
+ num_mdefweight += mdef[i].totweight;
+
+ std::vector<int32_t> totweight_data;
+ std::vector<int32_t> flag_data;
+ std::vector<int32_t> def_nr_data;
+ std::vector<float> weight_data;
+ totweight_data.reserve(num_data);
+ flag_data.reserve(num_data);
+ def_nr_data.reserve(num_mdefweight);
+ weight_data.reserve(num_mdefweight);
+
+ for (int i = 0; i < num_data; ++i) {
+ totweight_data.push_back(mdef->totweight);
+ flag_data.push_back(mdef->flag);
+
+ MDeformWeight *mw = mdef->dw;
+ for (int j = 0; j < mdef->totweight; ++j) {
+ def_nr_data.push_back(mw->def_nr);
+ weight_data.push_back(mw->weight);
+
+ ++mw;
+ }
+
+ ++mdef;
+ }
+
+ totweight_prop.set(Int32ArraySample(totweight_data));
+ flag_prop.set(Int32ArraySample(flag_data));
+ def_nr_prop.set(Int32ArraySample(def_nr_data));
+ weight_prop.set(FloatArraySample(weight_data));
+}
+
+template <>
+void write_sample<CD_MTFACE>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void */*data*/, int /*num_data*/)
+{
+ /* XXX this is a dummy layer, to have access to active render layers etc. */
+ writer->add_compound_property<OCompoundProperty>(name, parent);
+}
+
+template <>
+void write_sample<CD_MCOL>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OC4fArrayProperty prop = writer->add_array_property<OC4fArrayProperty>(name, parent);
+
+ MCol *mcol = (MCol *)data;
+
+ std::vector<C4f> mcol_data;
+ mcol_data.reserve(num_data);
+ for (int i = 0; i < num_data; ++i) {
+ unsigned char icol[4] = {mcol->r, mcol->g, mcol->b, mcol->a};
+ C4f fcol;
+ rgba_uchar_to_float(fcol.getValue(), icol);
+ mcol_data.push_back(fcol);
+
+ ++mcol;
+ }
+ prop.set(OC4fArrayProperty::sample_type(mcol_data));
+}
+
+template <>
+void write_sample<CD_ORIGINDEX>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OInt32ArrayProperty prop = writer->add_array_property<OInt32ArrayProperty>(name, parent);
+
+ prop.set(OInt32ArrayProperty::sample_type((int *)data, num_data));
+}
+
+template <>
+void write_sample<CD_NORMAL>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ ON3fArrayProperty prop = writer->add_array_property<ON3fArrayProperty>(name, parent);
+
+ prop.set(ON3fArrayProperty::sample_type((N3f *)data, num_data));
+}
+
+template <>
+void write_sample<CD_ORIGSPACE>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent);
+
+ OV2fArrayProperty uv_prop[4];
+ uv_prop[0] = writer->add_array_property<OV2fArrayProperty>(name + ":uv0", prop);
+ uv_prop[1] = writer->add_array_property<OV2fArrayProperty>(name + ":uv1", prop);
+ uv_prop[2] = writer->add_array_property<OV2fArrayProperty>(name + ":uv2", prop);
+ uv_prop[3] = writer->add_array_property<OV2fArrayProperty>(name + ":uv3", prop);
+
+ OrigSpaceFace *ospace = (OrigSpaceFace *)data;
+ std::vector<V2f> uv_data[4];
+ uv_data[0].reserve(num_data);
+ uv_data[1].reserve(num_data);
+ uv_data[2].reserve(num_data);
+ uv_data[3].reserve(num_data);
+ for (int i = 0; i < num_data; ++i) {
+ uv_data[0].push_back(V2f(ospace->uv[0][0], ospace->uv[0][1]));
+ uv_data[1].push_back(V2f(ospace->uv[1][0], ospace->uv[1][1]));
+ uv_data[2].push_back(V2f(ospace->uv[2][0], ospace->uv[2][1]));
+ uv_data[3].push_back(V2f(ospace->uv[3][0], ospace->uv[3][1]));
+
+ ++ospace;
+ }
+ uv_prop[0].set(V2fArraySample(uv_data[0]));
+ uv_prop[1].set(V2fArraySample(uv_data[1]));
+ uv_prop[2].set(V2fArraySample(uv_data[2]));
+ uv_prop[3].set(V2fArraySample(uv_data[3]));
+}
+
+template <>
+void write_sample<CD_ORCO>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OV3fArrayProperty prop = writer->add_array_property<OV3fArrayProperty>(name, parent);
+
+ prop.set(OV3fArrayProperty::sample_type((V3f *)data, num_data));
+}
+
+template <>
+void write_sample<CD_MTEXPOLY>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void */*data*/, int /*num_data*/)
+{
+ /* XXX this is a dummy layer, to have access to active render layers etc. */
+ writer->add_compound_property<OCompoundProperty>(name, parent);
+}
+
+template <>
+void write_sample<CD_MLOOPUV>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent);
+
+ OV2fArrayProperty prop_uv = writer->add_array_property<OV2fArrayProperty>(name + ":uv", prop);
+ OInt32ArrayProperty prop_flag = writer->add_array_property<OInt32ArrayProperty>(name + ":flag", prop);
+
+ MLoopUV *loop_uv = (MLoopUV *)data;
+ std::vector<V2f> uv_data;
+ std::vector<int32_t> flag_data;
+ uv_data.reserve(num_data);
+ flag_data.reserve(num_data);
+ for (int i = 0; i < num_data; ++i) {
+ uv_data.push_back(V2f(loop_uv->uv[0], loop_uv->uv[1]));
+ flag_data.push_back(loop_uv->flag);
+
+ ++loop_uv;
+ }
+ prop_uv.set(V2fArraySample(uv_data));
+ prop_flag.set(Int32ArraySample(flag_data));
+}
+
+template <>
+void write_sample<CD_MLOOPCOL>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent);
+
+ OC4fArrayProperty prop_col = writer->add_array_property<OC4fArrayProperty>(name + ":color", prop);
+
+ MLoopCol *loop_col = (MLoopCol *)data;
+ std::vector<C4f> col_data;
+ col_data.reserve(num_data);
+ for (int i = 0; i < num_data; ++i) {
+ col_data.push_back(C4f(loop_col->r, loop_col->g, loop_col->b, loop_col->a));
+
+ ++loop_col;
+ }
+ prop_col.set(C4fArraySample(col_data));
+}
+
+template <>
+void write_sample<CD_ORIGSPACE_MLOOP>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent);
+
+ OV2fArrayProperty prop_uv = writer->add_array_property<OV2fArrayProperty>(name + ":uv", prop);
+
+ OrigSpaceLoop *ospaceloop = (OrigSpaceLoop *)data;
+ std::vector<V2f> uv_data;
+ uv_data.reserve(num_data);
+ for (int i = 0; i < num_data; ++i) {
+ uv_data.push_back(V2f(ospaceloop->uv[0], ospaceloop->uv[1]));
+
+ ++ospaceloop;
+ }
+ prop_uv.set(V2fArraySample(uv_data));
+}
+
+template <>
+void write_sample<CD_MSURFACE_SAMPLE>(CustomDataWriter *writer, OCompoundProperty &parent, const std::string &name, void *data, int num_data)
+{
+ OCompoundProperty prop = writer->add_compound_property<OCompoundProperty>(name, parent);
+
+ OUInt32ArrayProperty prop_orig_verts = writer->add_array_property<OUInt32ArrayProperty>(name + ":orig_verts", prop);
+ OFloatArrayProperty prop_orig_weights = writer->add_array_property<OFloatArrayProperty>(name + ":orig_weights", prop);
+ OInt32ArrayProperty prop_orig_poly = writer->add_array_property<OInt32ArrayProperty>(name + ":orig_poly", prop);
+ OUInt32ArrayProperty prop_orig_loops = writer->add_array_property<OUInt32ArrayProperty>(name + ":orig_loops", prop);
+
+ MSurfaceSample *surf = (MSurfaceSample *)data;
+ std::vector<uint32_t> orig_verts_data;
+ std::vector<float32_t> orig_weights_data;
+ std::vector<int32_t> orig_poly_data;
+ std::vector<uint32_t> orig_loops_data;
+ orig_verts_data.reserve(num_data * 3);
+ orig_weights_data.reserve(num_data * 3);
+ orig_poly_data.reserve(num_data);
+ orig_loops_data.reserve(num_data * 3);
+ for (int i = 0; i < num_data; ++i) {
+ orig_verts_data.push_back(surf->orig_verts[0]);
+ orig_verts_data.push_back(surf->orig_verts[1]);
+ orig_verts_data.push_back(surf->orig_verts[2]);
+ orig_weights_data.push_back(surf->orig_weights[0]);
+ orig_weights_data.push_back(surf->orig_weights[1]);
+ orig_weights_data.push_back(surf->orig_weights[2]);
+ orig_poly_data.push_back(surf->orig_poly);
+ orig_loops_data.push_back(surf->orig_loops[0]);
+ orig_loops_data.push_back(surf->orig_loops[1]);
+ orig_loops_data.push_back(surf->orig_loops[2]);
+
+ ++surf;
+ }
+ prop_orig_verts.set(UInt32ArraySample(orig_verts_data));
+ prop_orig_weights.set(FloatArraySample(orig_weights_data));
+ prop_orig_poly.set(Int32ArraySample(orig_poly_data));
+ prop_orig_loops.set(UInt32ArraySample(orig_loops_data));
+}
+
+/* ------------------------------------------------------------------------- */
+
+template <CustomDataType CDTYPE>
+static PTCReadSampleResult read_sample(CustomDataReader */*reader*/, ICompoundProperty &/*parent*/, const ISampleSelector &/*ss*/, const std::string &/*name*/, void */*data*/, int /*num_data*/)
+{
+ /* no implementation available, should not happen */
+ printf("ERROR: CustomData type %s has no read_sample implementation\n", CustomData_layertype_name((int)CDTYPE));
+ return PTC_READ_SAMPLE_INVALID;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_MDEFORMVERT>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent);
+
+ IInt32ArrayProperty totweight_prop = reader->add_array_property<IInt32ArrayProperty>(name + ":totweight", prop);
+ IInt32ArrayProperty flag_prop = reader->add_array_property<IInt32ArrayProperty>(name + ":flag", prop);
+ IInt32ArrayProperty def_nr_prop = reader->add_array_property<IInt32ArrayProperty>(name + ":def_nr", prop);
+ IFloatArrayProperty weight_prop = reader->add_array_property<IFloatArrayProperty>(name + ":weight", prop);
+
+ Int32ArraySamplePtr sample_totweight = totweight_prop.getValue(ss);
+ Int32ArraySamplePtr sample_flag = flag_prop.getValue(ss);
+ Int32ArraySamplePtr sample_def_nr = def_nr_prop.getValue(ss);
+ FloatArraySamplePtr sample_weight = weight_prop.getValue(ss);
+
+ if (sample_totweight->size() != num_data ||
+ sample_flag->size() != num_data)
+ {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ const int32_t *data_totweight = (const int32_t *)sample_totweight->getData();
+ const int32_t *data_flag = (const int32_t *)sample_flag->getData();
+ const int32_t *data_def_nr = (const int32_t *)sample_def_nr->getData();
+ const float *data_weight = (const float *)sample_weight->getData();
+
+ MDeformVert *mdef = (MDeformVert *)data;
+ for (int i = 0; i < num_data; ++i) {
+
+ mdef->totweight = *data_totweight;
+ mdef->flag = *data_flag;
+
+ MDeformWeight *mw = mdef->dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * mdef->totweight, "deformWeight");
+ for (int j = 0; j < mdef->totweight; ++j) {
+ mw->def_nr = *data_def_nr;
+ mw->weight = *data_weight;
+
+ ++data_def_nr;
+ ++data_weight;
+ ++mw;
+ }
+
+ ++data_totweight;
+ ++data_flag;
+ ++mdef;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_MTFACE>(CustomDataReader */*reader*/, ICompoundProperty &/*parent*/, const ISampleSelector &/*ss*/, const std::string &/*name*/, void */*data*/, int /*num_data*/)
+{
+ /* XXX this is a dummy layer, to have access to active render layers etc. */
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_MCOL>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ IC4fArrayProperty prop = reader->add_array_property<IC4fArrayProperty>(name, parent);
+
+ C4fArraySamplePtr sample = prop.getValue(ss);
+
+ if (sample->size() != num_data)
+ return PTC_READ_SAMPLE_INVALID;
+
+ MCol *mcol = (MCol *)data;
+ C4f *data_mcol = (C4f *)sample->getData();
+ for (int i = 0; i < num_data; ++i) {
+ unsigned char icol[4];
+ rgba_float_to_uchar(icol, data_mcol->getValue());
+ mcol->r = icol[0];
+ mcol->g = icol[1];
+ mcol->b = icol[2];
+ mcol->a = icol[3];
+
+ ++data_mcol;
+ ++mcol;
+ }
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_ORIGINDEX>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ IInt32ArrayProperty prop = reader->add_array_property<IInt32ArrayProperty>(name, parent);
+
+ Int32ArraySamplePtr sample = prop.getValue(ss);
+
+ if (sample->size() != num_data)
+ return PTC_READ_SAMPLE_INVALID;
+
+ memcpy(data, sample->getData(), sizeof(int32_t) * num_data);
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_NORMAL>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ IN3fArrayProperty prop = reader->add_array_property<IN3fArrayProperty>(name, parent);
+
+ N3fArraySamplePtr sample = prop.getValue(ss);
+
+ if (sample->size() != num_data)
+ return PTC_READ_SAMPLE_INVALID;
+
+ memcpy(data, sample->getData(), sizeof(N3f) * num_data);
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_ORIGSPACE>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent);
+
+ IV2fArrayProperty uv_prop[4];
+ uv_prop[0] = reader->add_array_property<IV2fArrayProperty>(name + ":uv0", prop);
+ uv_prop[1] = reader->add_array_property<IV2fArrayProperty>(name + ":uv1", prop);
+ uv_prop[2] = reader->add_array_property<IV2fArrayProperty>(name + ":uv2", prop);
+ uv_prop[3] = reader->add_array_property<IV2fArrayProperty>(name + ":uv3", prop);
+
+ V2fArraySamplePtr sample0 = uv_prop[0].getValue(ss);
+ V2fArraySamplePtr sample1 = uv_prop[1].getValue(ss);
+ V2fArraySamplePtr sample2 = uv_prop[2].getValue(ss);
+ V2fArraySamplePtr sample3 = uv_prop[3].getValue(ss);
+
+ if (sample0->size() != num_data ||
+ sample1->size() != num_data ||
+ sample2->size() != num_data ||
+ sample3->size() != num_data)
+ {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ OrigSpaceFace *ospace = (OrigSpaceFace *)data;
+ const V2f *data0 = (const V2f *)sample0->getData();
+ const V2f *data1 = (const V2f *)sample1->getData();
+ const V2f *data2 = (const V2f *)sample2->getData();
+ const V2f *data3 = (const V2f *)sample3->getData();
+ for (int i = 0; i < num_data; ++i) {
+ copy_v2_v2(ospace->uv[0], data0->getValue());
+ copy_v2_v2(ospace->uv[1], data1->getValue());
+ copy_v2_v2(ospace->uv[2], data2->getValue());
+ copy_v2_v2(ospace->uv[3], data3->getValue());
+
+ ++data0;
+ ++data1;
+ ++data2;
+ ++data3;
+ ++ospace;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_ORCO>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ IV3fArrayProperty prop = reader->add_array_property<IV3fArrayProperty>(name, parent);
+
+ V3fArraySamplePtr sample = prop.getValue(ss);
+
+ if (sample->size() != num_data)
+ return PTC_READ_SAMPLE_INVALID;
+
+ memcpy(data, sample->getData(), sizeof(V3f) * num_data);
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_MTEXPOLY>(CustomDataReader */*reader*/, ICompoundProperty &/*parent*/, const ISampleSelector &/*ss*/, const std::string &/*name*/, void */*data*/, int /*num_data*/)
+{
+ /* XXX this is a dummy layer, to have access to active render layers etc. */
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_MLOOPUV>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent);
+
+ IV2fArrayProperty uv_prop = reader->add_array_property<IV2fArrayProperty>(name + ":uv", prop);
+ IInt32ArrayProperty flag_prop = reader->add_array_property<IInt32ArrayProperty>(name + ":flag", prop);
+
+ V2fArraySamplePtr uv_sample = uv_prop.getValue(ss);
+ Int32ArraySamplePtr flag_sample = flag_prop.getValue(ss);
+
+ if (uv_sample->size() != num_data || flag_sample->size() != num_data)
+ return PTC_READ_SAMPLE_INVALID;
+
+ MLoopUV *loop_uv = (MLoopUV *)data;
+ const V2f *uv_data = (const V2f *)uv_sample->getData();
+ const int32_t *flag_data = (const int32_t *)flag_sample->getData();
+ for (int i = 0; i < num_data; ++i) {
+ copy_v2_v2(loop_uv->uv, uv_data->getValue());
+ loop_uv->flag = *flag_data;
+
+ ++uv_data;
+ ++flag_data;
+ ++loop_uv;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_MLOOPCOL>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent);
+
+ IC4fArrayProperty col_prop = reader->add_array_property<IC4fArrayProperty>(name + ":color", prop);
+
+ C4fArraySamplePtr col_sample = col_prop.getValue(ss);
+
+ if (col_sample->size() != num_data)
+ return PTC_READ_SAMPLE_INVALID;
+
+ MLoopCol *loop_col = (MLoopCol *)data;
+ const C4f *col_data = (const C4f *)col_sample->getData();
+ for (int i = 0; i < num_data; ++i) {
+ loop_col->r = col_data->r;
+ loop_col->g = col_data->g;
+ loop_col->b = col_data->b;
+ loop_col->a = col_data->a;
+
+ ++col_data;
+ ++loop_col;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_ORIGSPACE_MLOOP>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent);
+
+ IV2fArrayProperty uv_prop = reader->add_array_property<IV2fArrayProperty>(name + ":uv", prop);
+
+ V2fArraySamplePtr sample = uv_prop.getValue(ss);
+
+ if (sample->size() != num_data)
+ return PTC_READ_SAMPLE_INVALID;
+
+ OrigSpaceLoop *ospace = (OrigSpaceLoop *)data;
+ const V2f *sample_data = (const V2f *)sample->getData();
+ for (int i = 0; i < num_data; ++i) {
+ copy_v2_v2(ospace->uv, sample_data->getValue());
+
+ ++sample_data;
+ ++ospace;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+template <>
+PTCReadSampleResult read_sample<CD_MSURFACE_SAMPLE>(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, const std::string &name, void *data, int num_data)
+{
+ ICompoundProperty prop = reader->add_compound_property<ICompoundProperty>(name, parent);
+
+ IUInt32ArrayProperty orig_verts_prop = reader->add_array_property<IUInt32ArrayProperty>(name + ":orig_verts", prop);
+ IFloatArrayProperty orig_weights_prop = reader->add_array_property<IFloatArrayProperty>(name + ":orig_weights", prop);
+ IInt32ArrayProperty orig_poly_prop = reader->add_array_property<IInt32ArrayProperty>(name + ":orig_poly", prop);
+ IUInt32ArrayProperty orig_loops_prop = reader->add_array_property<IUInt32ArrayProperty>(name + ":orig_loops", prop);
+
+ UInt32ArraySamplePtr orig_verts_sample = orig_verts_prop.getValue(ss);
+ FloatArraySamplePtr orig_weights_sample = orig_weights_prop.getValue(ss);
+ Int32ArraySamplePtr orig_poly_sample = orig_poly_prop.getValue(ss);
+ UInt32ArraySamplePtr orig_loops_sample = orig_loops_prop.getValue(ss);
+
+ if (orig_verts_sample->size() != num_data*3 ||
+ orig_weights_sample->size() != num_data*3 ||
+ orig_poly_sample->size() != num_data ||
+ orig_loops_sample->size() != num_data*3)
+ return PTC_READ_SAMPLE_INVALID;
+
+ MSurfaceSample *surf = (MSurfaceSample *)data;
+ const uint32_t *orig_verts_data = (const uint32_t *)orig_verts_sample->getData();
+ const float32_t *orig_weights_data = (const float32_t *)orig_weights_sample->getData();
+ const int32_t *orig_poly_data = (const int32_t *)orig_poly_sample->getData();
+ const uint32_t *orig_loops_data = (const uint32_t *)orig_loops_sample->getData();
+ for (int i = 0; i < num_data; ++i) {
+ surf->orig_verts[0] = orig_verts_data[0];
+ surf->orig_verts[1] = orig_verts_data[1];
+ surf->orig_verts[2] = orig_verts_data[2];
+ surf->orig_weights[0] = orig_weights_data[0];
+ surf->orig_weights[1] = orig_weights_data[1];
+ surf->orig_weights[2] = orig_weights_data[2];
+ surf->orig_poly = *orig_poly_data;
+ surf->orig_loops[0] = orig_loops_data[0];
+ surf->orig_loops[1] = orig_loops_data[1];
+ surf->orig_loops[2] = orig_loops_data[2];
+
+ orig_verts_data += 3;
+ orig_weights_data += 3;
+ orig_poly_data += 1;
+ orig_loops_data += 3;
+ ++surf;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+/* ========================================================================= */
+
+/* recursive template that handles dispatch by CD layer type */
+template <int CDTYPE>
+BLI_INLINE void write_sample_call(CustomDataWriter *writer, OCompoundProperty &parent, CustomDataType type, const std::string &name, void *data, int num_data)
+{
+ if (type == CDTYPE)
+ write_sample<(CustomDataType)CDTYPE>(writer, parent, name, data, num_data);
+ else
+ write_sample_call<CDTYPE + 1>(writer, parent, type, name, data, num_data);
+}
+
+/* terminator specialization */
+template <>
+void write_sample_call<CD_NUMTYPES>(CustomDataWriter */*writer*/, OCompoundProperty &/*parent*/, CustomDataType /*type*/, const std::string &/*name*/, void */*data*/, int /*num_data*/)
+{
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* recursive template that handles dispatch by CD layer type */
+template <int CDTYPE>
+BLI_INLINE PTCReadSampleResult read_sample_call(CustomDataReader *reader, ICompoundProperty &parent, const ISampleSelector &ss, CustomDataType type, const std::string &name, void *data, int num_data)
+{
+ if (type == CDTYPE)
+ return read_sample<(CustomDataType)CDTYPE>(reader, parent, ss, name, data, num_data);
+ else
+ return read_sample_call<CDTYPE + 1>(reader, parent, ss, type, name, data, num_data);
+}
+
+/* terminator specialization */
+template <>
+PTCReadSampleResult read_sample_call<CD_NUMTYPES>(CustomDataReader */*reader*/, ICompoundProperty &/*parent*/, const ISampleSelector &/*ss*/, CustomDataType /*type*/, const std::string &/*name*/, void */*data*/, int /*num_data*/)
+{
+ return PTC_READ_SAMPLE_INVALID;
+}
+
+/* ========================================================================= */
+
+CustomDataWriter::CustomDataWriter(const std::string &name, CustomDataMask cdmask) :
+ m_name(name),
+ m_cdmask(cdmask)
+{
+}
+
+CustomDataWriter::~CustomDataWriter()
+{
+ for (LayerPropsMap::iterator it = m_layer_props.begin(); it != m_layer_props.end(); ++it) {
+ BasePropertyWriterPtr prop = it->second;
+ if (prop)
+ prop.reset();
+ }
+}
+
+void CustomDataWriter::init(TimeSamplingPtr time_sampling)
+{
+ m_time_sampling = time_sampling;
+}
+
+/* unique property name based on either layer name or index */
+std::string CustomDataWriter::cdtype_to_name(CustomData *cdata, CustomDataType type, int n)
+{
+ const char *layertype_name = CustomData_layertype_name(type);
+ const char *layer_name = CustomData_get_layer_name(cdata, type, n);
+ std::string name;
+ if (layer_name && layer_name[0] != '\0') {
+ name = m_name + ":" + std::string(layertype_name) + ":S" + std::string(layer_name);
+ }
+ else {
+ std::stringstream ss; ss << n;
+ name = m_name + ":" + std::string(layertype_name) + ":N" + ss.str();
+ }
+ return name;
+}
+
+/* parse property name to CD layer name based on S or N prefix for named/unnamed layers */
+void CustomDataReader::cdtype_from_name(CustomData */*cdata*/, const std::string &name, int type, int *n, char *layer_name, int max_layer_name)
+{
+ const char *layertype_name = CustomData_layertype_name(type);
+ /* We can safely assume all properties in the compound share the correct prefix
+ * <m_name>:<layertype_name>:
+ * The layertype_name is only prepended to avoid name collisions
+ */
+ const size_t start = m_name.size() + 1 + strlen(layertype_name) + 1;
+
+ if (name.size() <= start) {
+ printf("ERROR: invalid CustomData layer property name '%s'\n", name.c_str());
+ *n = -1;
+ layer_name[0] = '\0';
+ }
+ else if (name[start] == 'S') {
+ /* named layer */
+ *n = -1;
+ BLI_strncpy(layer_name, name.c_str() + start + 1, max_layer_name);
+ }
+ else if (name[start] == 'N') {
+ /* unnamed layer */
+ std::istringstream ss(name.c_str() + start + 1);
+ ss >> (*n);
+ layer_name[0] = '\0';
+ }
+ else {
+ *n = -1;
+ layer_name[0] = '\0';
+ }
+}
+
+void CustomDataWriter::write_sample(CustomData *cdata, int num_data, OCompoundProperty &parent)
+{
+ /* compound property for all CD layers in the CustomData instance */
+ m_props = add_compound_property<OCompoundProperty>(m_name, parent);
+
+ for (int type = 0; type < CD_NUMTYPES; ++type) {
+ CustomDataMask mask = (1ull << type);
+ /* only use specified types */
+ if (!(mask & m_cdmask))
+ continue;
+
+ const char *layertype_name = CustomData_layertype_name(type);
+ int num = CustomData_number_of_layers(cdata, type);
+
+ bool has_props = false;
+ OCompoundProperty layertype_props;
+ for (int n = 0; n < num; ++n) {
+ /* compound for all CD layers of the same type */
+ if (!has_props) {
+ has_props = true;
+ layertype_props = add_compound_property<OCompoundProperty>(m_name + ":" + layertype_name, m_props);
+ }
+
+ std::string name = cdtype_to_name(cdata, (CustomDataType)type, n);
+ void *data = CustomData_get_layer_n(cdata, type, n);
+ write_sample_call<0>(this, layertype_props, (CustomDataType)type, name, data, num_data);
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+CustomDataReader::CustomDataReader(const std::string &name, CustomDataMask cdmask) :
+ m_name(name),
+ m_cdmask(cdmask)
+{
+}
+
+CustomDataReader::~CustomDataReader()
+{
+ for (LayerPropsMap::iterator it = m_layer_props.begin(); it != m_layer_props.end(); ++it) {
+ BasePropertyReaderPtr prop = it->second;
+ if (prop)
+ prop.reset();
+ }
+}
+
+PTCReadSampleResult CustomDataReader::read_sample(const ISampleSelector &ss, CustomData *cdata, int num_data, ICompoundProperty &parent)
+{
+ m_props = add_compound_property<ICompoundProperty>(m_name, parent);
+
+ for (int type = 0; type < CD_NUMTYPES; ++type) {
+ CustomDataMask mask = (1ull << type);
+ /* only use specified types */
+ if (!(mask & m_cdmask))
+ continue;
+
+ const char *layertype_name = CustomData_layertype_name(type);
+
+ BasePropertyReaderPtr ptr = m_props.getPtr()->asCompoundPtr()->getProperty(m_name + ":" + layertype_name);
+ if (!ptr) {
+ /* no layer of this type stored */
+ continue;
+ }
+ ICompoundProperty layertype_props(ptr->asCompoundPtr(), kWrapExisting);
+
+ for (int i = 0; i < layertype_props.getNumProperties(); ++i) {
+ const std::string &name = layertype_props.getPropertyHeader(i).getName();
+ char layer_name[MAX_CUSTOMDATA_LAYER_NAME];
+ int n;
+ void *data;
+
+ cdtype_from_name(cdata, name, type, &n, layer_name, sizeof(layer_name));
+ if (layer_name[0] == '\0')
+ data = CustomData_add_layer(cdata, type, CD_DEFAULT, NULL, num_data);
+ else
+ data = CustomData_add_layer_named(cdata, type, CD_DEFAULT, NULL, num_data, layer_name);
+
+ read_sample_call<0>(this, layertype_props, ss, (CustomDataType)type, name, data, num_data);
+ }
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_customdata.h b/source/blender/pointcache/alembic/abc_customdata.h
new file mode 100644
index 00000000000..9da3b92c584
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_customdata.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_ABC_CUSTOMDATA_H
+#define PTC_ABC_CUSTOMDATA_H
+
+#include <map>
+
+#include <Alembic/AbcGeom/IGeomParam.h>
+#include <Alembic/AbcGeom/OGeomParam.h>
+#include <Alembic/Abc/IBaseProperty.h>
+#include <Alembic/Abc/TypedPropertyTraits.h>
+
+#include "abc_reader.h"
+#include "abc_writer.h"
+
+extern "C" {
+#include "BKE_customdata.h"
+
+#include "DNA_customdata_types.h"
+}
+
+namespace PTC {
+
+using namespace Alembic;
+
+std::string abc_customdata_layer_name(CustomData *cdata, CustomDataType type, int n);
+
+struct CustomDataWriter {
+ typedef std::map<std::string, Abc::BasePropertyWriterPtr> LayerPropsMap;
+ typedef std::pair<std::string, Abc::BasePropertyWriterPtr> LayerPropsPair;
+
+ CustomDataWriter(const std::string &name, CustomDataMask cdmask);
+ ~CustomDataWriter();
+
+ void init(Abc::TimeSamplingPtr time_sampling);
+
+ void write_sample(CustomData *cdata, int num_data, Abc::OCompoundProperty &parent);
+
+ Abc::OCompoundProperty &props() { return m_props; }
+
+ template <typename PropertyT, typename ParentT>
+ PropertyT add_scalar_property(const std::string &name, ParentT &parent)
+ {
+ LayerPropsMap::iterator it = m_layer_props.find(name);
+ if (it == m_layer_props.end()) {
+ PropertyT prop = PropertyT(parent, name, m_time_sampling);
+ m_layer_props.insert(LayerPropsPair(name, prop.getPtr()));
+ return prop;
+ }
+ else {
+ return PropertyT(it->second->asScalarPtr(), Abc::kWrapExisting);
+ }
+ }
+
+ template <typename PropertyT, typename ParentT>
+ PropertyT add_array_property(const std::string &name, ParentT &parent)
+ {
+ LayerPropsMap::iterator it = m_layer_props.find(name);
+ if (it == m_layer_props.end()) {
+ PropertyT prop = PropertyT(parent, name, m_time_sampling);
+ m_layer_props.insert(LayerPropsPair(name, prop.getPtr()));
+ return prop;
+ }
+ else {
+ return PropertyT(it->second->asArrayPtr(), Abc::kWrapExisting);
+ }
+ }
+
+ template <typename PropertyT, typename ParentT>
+ PropertyT add_compound_property(const std::string &name, ParentT &parent)
+ {
+ LayerPropsMap::iterator it = m_layer_props.find(name);
+ if (it == m_layer_props.end()) {
+ PropertyT prop = PropertyT(parent, name, m_time_sampling);
+ m_layer_props.insert(LayerPropsPair(name, prop.getPtr()));
+ return prop;
+ }
+ else {
+ return PropertyT(it->second->asCompoundPtr(), Abc::kWrapExisting);
+ }
+ }
+
+ std::string cdtype_to_name(CustomData *cdata, CustomDataType type, int n);
+
+private:
+ std::string m_name;
+ CustomDataMask m_cdmask;
+
+ Abc::TimeSamplingPtr m_time_sampling;
+ Abc::OCompoundProperty m_props;
+ LayerPropsMap m_layer_props;
+};
+
+struct CustomDataReader {
+ typedef std::map<std::string, Abc::BasePropertyReaderPtr> LayerPropsMap;
+ typedef std::pair<std::string, Abc::BasePropertyReaderPtr> LayerPropsPair;
+
+ CustomDataReader(const std::string &name, CustomDataMask cdmask);
+ ~CustomDataReader();
+
+ PTCReadSampleResult read_sample(const Abc::ISampleSelector &ss, CustomData *cdata, int num_data, Abc::ICompoundProperty &parent);
+
+ Abc::ICompoundProperty &props() { return m_props; }
+
+ template <typename PropertyT, typename ParentT>
+ PropertyT add_scalar_property(const std::string &name, ParentT &parent)
+ {
+ LayerPropsMap::iterator it = m_layer_props.find(name);
+ if (it == m_layer_props.end()) {
+ PropertyT prop = PropertyT(parent, name, 0);
+ m_layer_props.insert(LayerPropsPair(name, prop.getPtr()));
+ return prop;
+ }
+ else {
+ return PropertyT(it->second->asScalarPtr(), Abc::kWrapExisting);
+ }
+ }
+
+ template <typename PropertyT, typename ParentT>
+ PropertyT add_array_property(const std::string &name, ParentT &parent)
+ {
+ LayerPropsMap::iterator it = m_layer_props.find(name);
+ if (it == m_layer_props.end()) {
+ PropertyT prop = PropertyT(parent, name, 0);
+ m_layer_props.insert(LayerPropsPair(name, prop.getPtr()));
+ return prop;
+ }
+ else {
+ return PropertyT(it->second->asArrayPtr(), Abc::kWrapExisting);
+ }
+ }
+
+ template <typename PropertyT, typename ParentT>
+ PropertyT add_compound_property(const std::string &name, ParentT &parent)
+ {
+ LayerPropsMap::iterator it = m_layer_props.find(name);
+ if (it == m_layer_props.end()) {
+ PropertyT prop = PropertyT(parent, name, 0);
+ m_layer_props.insert(LayerPropsPair(name, prop.getPtr()));
+ return prop;
+ }
+ else {
+ return PropertyT(it->second->asCompoundPtr(), Abc::kWrapExisting);
+ }
+ }
+
+ void cdtype_from_name(CustomData *cdata, const std::string &name, int type, int *n, char *layer_name, int max_layer_name);
+
+private:
+ std::string m_name;
+ CustomDataMask m_cdmask;
+
+ Abc::ICompoundProperty m_props;
+ LayerPropsMap m_layer_props;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_CLOTH_H */
diff --git a/source/blender/pointcache/alembic/abc_frame_mapper.cpp b/source/blender/pointcache/alembic/abc_frame_mapper.cpp
new file mode 100644
index 00000000000..ba5ded10b6e
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_frame_mapper.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "abc_frame_mapper.h"
+
+extern "C" {
+#include "DNA_scene_types.h"
+}
+
+namespace PTC {
+
+#ifdef WITH_ALEMBIC
+
+using namespace Abc;
+using namespace AbcCoreAbstract;
+
+FrameMapper::FrameMapper(double fps, float start_frame)
+{
+ m_frames_per_sec = fps;
+ m_sec_per_frame = (fps == 0.0 ? 0.0 : 1.0 / fps);
+ m_start_frame = start_frame;
+ m_start_time = start_frame * m_sec_per_frame;
+}
+
+chrono_t FrameMapper::frame_to_time(float frame) const
+{
+ return (double)frame * m_sec_per_frame;
+}
+
+float FrameMapper::time_to_frame(chrono_t time) const
+{
+ return (float)(time * m_frames_per_sec);
+}
+
+#endif /* WITH_ALEMBIC */
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_frame_mapper.h b/source/blender/pointcache/alembic/abc_frame_mapper.h
new file mode 100644
index 00000000000..b61baeeb4a7
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_frame_mapper.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_ABC_FRAME_MAPPER_H
+#define PTC_ABC_FRAME_MAPPER_H
+
+#ifdef WITH_ALEMBIC
+#include <Alembic/AbcCoreAbstract/Foundation.h>
+#include <Alembic/Abc/ISampleSelector.h>
+#endif
+
+struct Scene;
+
+namespace PTC {
+
+#ifdef WITH_ALEMBIC
+
+using namespace Alembic;
+using Alembic::AbcCoreAbstract::chrono_t;
+
+class FrameMapper {
+public:
+ FrameMapper(double fps, float start_frame);
+
+ double frames_per_second() const { return m_frames_per_sec; }
+ double seconds_per_frame() const { return m_sec_per_frame; }
+ double start_frame() const { return m_start_frame; }
+ double start_time() const { return m_start_time; }
+
+ chrono_t frame_to_time(float frame) const;
+ float time_to_frame(chrono_t time) const;
+
+private:
+ double m_frames_per_sec, m_sec_per_frame;
+ double m_start_frame, m_start_time;
+};
+
+#endif /* WITH_ALEMBIC */
+
+} /* namespace PTC */
+
+#endif /* PTC_UTIL_FRAME_MAPPER_H */
diff --git a/source/blender/pointcache/alembic/abc_group.cpp b/source/blender/pointcache/alembic/abc_group.cpp
new file mode 100644
index 00000000000..38cbc19694e
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_group.cpp
@@ -0,0 +1,770 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <map>
+#include <sstream>
+#include <string>
+
+#include <Alembic/Abc/IObject.h>
+#include <Alembic/Abc/OObject.h>
+
+#include "abc_mesh.h"
+#include "abc_group.h"
+#include "abc_interpolate.h"
+#include "abc_object.h"
+#include "abc_particles.h"
+#include "abc_simdebug.h"
+
+extern "C" {
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+
+#include "DNA_group_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_anim.h"
+#include "BKE_cache_library.h"
+#include "BKE_global.h"
+#include "BKE_group.h"
+#include "BKE_library.h"
+#include "BKE_strands.h"
+}
+
+#include "util_task.h"
+
+namespace PTC {
+
+using namespace Abc;
+using namespace AbcGeom;
+
+AbcGroupWriter::AbcGroupWriter(const std::string &name, Group *group) :
+ GroupWriter(group, name)
+{
+}
+
+void AbcGroupWriter::init_abc()
+{
+ if (m_abc_object)
+ return;
+
+ m_abc_object = abc_archive()->add_id_object<OObject>((ID *)m_group);
+}
+
+void AbcGroupWriter::create_refs()
+{
+ GroupObject *gob = (GroupObject *)m_group->gobject.first;
+ int i = 0;
+ for (; gob; gob = gob->next, ++i) {
+ OObject abc_object = abc_archive()->get_id_object((ID *)gob->ob);
+ if (abc_object) {
+ std::stringstream ss;
+ ss << i;
+ m_abc_object.addChildInstance(abc_object, std::string("group_object") + ss.str());
+ }
+ }
+}
+
+void AbcGroupWriter::write_sample()
+{
+ if (!m_abc_object)
+ return;
+}
+
+
+AbcGroupReader::AbcGroupReader(const std::string &name, Group *group) :
+ GroupReader(group, name)
+{
+}
+
+void AbcGroupReader::init_abc(IObject object)
+{
+ if (m_abc_object)
+ return;
+ m_abc_object = object;
+}
+
+PTCReadSampleResult AbcGroupReader::read_sample_abc(chrono_t /*time*/)
+{
+ if (!m_abc_object)
+ return PTC_READ_SAMPLE_INVALID;
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+/* ========================================================================= */
+
+AbcDupligroupWriter::AbcDupligroupWriter(const std::string &name, EvaluationContext *eval_ctx, Scene *scene, Group *group, CacheLibrary *cachelib) :
+ GroupWriter(group, name),
+ m_eval_ctx(eval_ctx),
+ m_scene(scene),
+ m_cachelib(cachelib)
+{
+}
+
+AbcDupligroupWriter::~AbcDupligroupWriter()
+{
+ for (IDWriterMap::iterator it = m_id_writers.begin(); it != m_id_writers.end(); ++it) {
+ if (it->second)
+ delete it->second;
+ }
+}
+
+void AbcDupligroupWriter::init_abc()
+{
+ if (m_abc_group)
+ return;
+
+ m_abc_group = abc_archive()->add_id_object<OObject>((ID *)m_group);
+}
+
+void AbcDupligroupWriter::write_sample_object(Object *ob, bool write_data)
+{
+ AbcWriter *ob_writer;
+
+ /* TODO(sergey): Optimize this out using RW mutex. */
+ {
+ thread_scoped_lock lock(m_init_mutex);
+ ob_writer = find_id_writer((ID *)ob);
+ if (!ob_writer) {
+ bool do_mesh = write_data && (m_cachelib->data_types & CACHE_TYPE_DERIVED_MESH);
+ bool do_hair = write_data && (m_cachelib->data_types & CACHE_TYPE_HAIR);
+
+ ob_writer = new AbcObjectWriter(ob->id.name, m_scene, ob, do_mesh, do_hair);
+ ob_writer->init(abc_archive());
+ m_id_writers.insert(IDWriterPair((ID *)ob, ob_writer));
+ }
+ }
+
+ ob_writer->write_sample();
+}
+
+void AbcDupligroupWriter::write_sample_dupli(DupliObject *dob, int index)
+{
+ OObject abc_object = abc_archive()->get_id_object((ID *)dob->ob);
+ if (!abc_object)
+ return;
+
+ std::stringstream ss;
+ ss << "DupliObject" << index;
+ std::string name = ss.str();
+
+ OObject abc_dupli = m_abc_group.getChild(name);
+ OCompoundProperty props;
+ OM44fProperty prop_matrix;
+ OBoolProperty prop_visible;
+ if (!abc_dupli) {
+ abc_dupli = OObject(m_abc_group, name, 0);
+ m_object_writers.push_back(abc_dupli.getPtr());
+ props = abc_dupli.getProperties();
+
+ abc_dupli.addChildInstance(abc_object, "object");
+
+ prop_matrix = OM44fProperty(props, "matrix", abc_archive()->frame_sampling());
+ m_property_writers.push_back(prop_matrix.getPtr());
+ prop_visible = OBoolProperty(props, "visible", abc_archive()->frame_sampling());
+ m_property_writers.push_back(prop_visible.getPtr());
+ }
+ else {
+ props = abc_dupli.getProperties();
+
+ prop_matrix = OM44fProperty(props.getProperty("matrix").getPtr()->asScalarPtr(), kWrapExisting);
+ prop_visible = OBoolProperty(props.getProperty("visible").getPtr()->asScalarPtr(), kWrapExisting);
+ }
+
+ prop_matrix.set(M44f(dob->mat));
+
+ bool show_object = (abc_archive()->use_render())? !(dob->ob->restrictflag & OB_RESTRICT_RENDER) : !(dob->ob->restrictflag & OB_RESTRICT_VIEW);
+ bool visible = show_object && (!dob->no_draw);
+ prop_visible.set(visible);
+}
+
+void AbcDupligroupWriter::write_sample()
+{
+ if (!m_abc_group)
+ return;
+
+ ListBase *duplilist = group_duplilist_ex(m_eval_ctx, m_scene, m_group, true);
+ DupliObject *dob;
+ int i;
+
+ /* use a set to ensure each object is handled only once */
+ std::set<Object*> objects;
+ for (dob = (DupliObject *)duplilist->first; dob; dob = dob->next) {
+ if (dob->ob)
+ objects.insert(dob->ob);
+ }
+
+ /* tag objects for which to store data */
+ BKE_cache_library_tag_used_objects(m_cachelib);
+
+ UtilTaskPool pool;
+ /* write actual object data: duplicator itself + all instanced objects */
+ for (std::set<Object*>::const_iterator it = objects.begin(); it != objects.end(); ++it) {
+ Object *ob = *it;
+ bool write_data = (ob->id.flag & LIB_DOIT);
+ pool.push(function_bind(&AbcDupligroupWriter::write_sample_object, this, ob, write_data));
+ }
+
+ pool.wait_work();
+
+ /* write dupli instances */
+ for (dob = (DupliObject *)duplilist->first, i = 0; dob; dob = dob->next, ++i) {
+ write_sample_dupli(dob, i);
+ }
+
+ free_object_duplilist(duplilist);
+}
+
+AbcWriter *AbcDupligroupWriter::find_id_writer(ID *id) const
+{
+ IDWriterMap::const_iterator it = m_id_writers.find(id);
+ if (it == m_id_writers.end())
+ return NULL;
+ else
+ return it->second;
+}
+
+/* ------------------------------------------------------------------------- */
+
+AbcDupliCacheWriter::AbcDupliCacheWriter(const std::string &name, Group *group, DupliCache *dupcache, int data_types, bool do_sim_debug) :
+ GroupWriter(group, name),
+ m_dupcache(dupcache),
+ m_data_types(data_types),
+ m_simdebug_writer(NULL)
+{
+ if (do_sim_debug) {
+ BKE_sim_debug_data_set_enabled(true);
+ if (_sim_debug_data)
+ m_simdebug_writer = new AbcSimDebugWriter("sim_debug", _sim_debug_data);
+ }
+}
+
+AbcDupliCacheWriter::~AbcDupliCacheWriter()
+{
+ for (IDWriterMap::iterator it = m_id_writers.begin(); it != m_id_writers.end(); ++it) {
+ if (it->second)
+ delete it->second;
+ }
+
+ if (m_simdebug_writer)
+ delete m_simdebug_writer;
+}
+
+void AbcDupliCacheWriter::init_abc()
+{
+ if (m_abc_group)
+ return;
+
+ m_abc_group = abc_archive()->add_id_object<OObject>((ID *)m_group);
+
+ if (m_simdebug_writer) {
+ m_simdebug_writer->init(abc_archive());
+ m_simdebug_writer->init_abc(abc_archive()->root());
+ }
+}
+
+void AbcDupliCacheWriter::write_sample_object_data(DupliObjectData *data)
+{
+ AbcWriter *ob_writer = find_id_writer((ID *)data->ob);
+ if (!ob_writer) {
+ bool do_mesh = (m_data_types & CACHE_TYPE_DERIVED_MESH);
+ bool do_hair = (m_data_types & CACHE_TYPE_HAIR);
+
+ ob_writer = new AbcDupliObjectWriter(data->ob->id.name, data, do_mesh, do_hair);
+ ob_writer->init(abc_archive());
+ m_id_writers.insert(IDWriterPair((ID *)data->ob, ob_writer));
+ }
+
+ ob_writer->write_sample();
+}
+
+void AbcDupliCacheWriter::write_sample_dupli(DupliObject *dob, int index)
+{
+ OObject abc_object = abc_archive()->get_id_object((ID *)dob->ob);
+ if (!abc_object)
+ return;
+
+ std::stringstream ss;
+ ss << "DupliObject" << index;
+ std::string name = ss.str();
+
+ OObject abc_dupli = m_abc_group.getChild(name);
+ OCompoundProperty props;
+ OBoolProperty prop_visible;
+ OM44fProperty prop_matrix;
+ if (!abc_dupli) {
+ abc_dupli = OObject(m_abc_group, name, 0);
+ m_object_writers.push_back(abc_dupli.getPtr());
+ props = abc_dupli.getProperties();
+
+ abc_dupli.addChildInstance(abc_object, "object");
+
+ prop_matrix = OM44fProperty(props, "matrix", abc_archive()->frame_sampling());
+ m_property_writers.push_back(prop_matrix.getPtr());
+ prop_visible = OBoolProperty(props, "visible", abc_archive()->frame_sampling());
+ m_property_writers.push_back(prop_visible.getPtr());
+ }
+ else {
+ props = abc_dupli.getProperties();
+
+ prop_matrix = OM44fProperty(props.getProperty("matrix").getPtr()->asScalarPtr(), kWrapExisting);
+ prop_visible = OBoolProperty(props.getProperty("visible").getPtr()->asScalarPtr(), kWrapExisting);
+ }
+
+ prop_matrix.set(M44f(dob->mat));
+
+ bool show_object = (abc_archive()->use_render())? !(dob->ob->restrictflag & OB_RESTRICT_RENDER) : !(dob->ob->restrictflag & OB_RESTRICT_VIEW);
+ bool visible = show_object && (!dob->no_draw);
+ prop_visible.set(visible);
+}
+
+void AbcDupliCacheWriter::write_sample()
+{
+ if (!m_abc_group)
+ return;
+
+ DupliObject *dob;
+ int i;
+
+ struct DupliCacheIterator *iter = BKE_dupli_cache_iter_new(m_dupcache);
+ for (; BKE_dupli_cache_iter_valid(iter); BKE_dupli_cache_iter_next(iter)) {
+ DupliObjectData *data = BKE_dupli_cache_iter_get(iter);
+
+ write_sample_object_data(data);
+ }
+ BKE_dupli_cache_iter_free(iter);
+
+ /* write dupli instances */
+ for (dob = (DupliObject *)m_dupcache->duplilist.first, i = 0; dob; dob = dob->next, ++i) {
+ write_sample_dupli(dob, i);
+ }
+
+ if (m_simdebug_writer) {
+ m_simdebug_writer->write_sample();
+ }
+}
+
+AbcWriter *AbcDupliCacheWriter::find_id_writer(ID *id) const
+{
+ IDWriterMap::const_iterator it = m_id_writers.find(id);
+ if (it == m_id_writers.end())
+ return NULL;
+ else
+ return it->second;
+}
+
+/* ------------------------------------------------------------------------- */
+
+AbcDupliCacheReader::AbcDupliCacheReader(const std::string &name, Group *group, DupliCache *dupli_cache,
+ bool read_strands_motion, bool read_strands_children, bool read_sim_debug) :
+ GroupReader(group, name),
+ dupli_cache(dupli_cache),
+ m_read_strands_motion(read_strands_motion),
+ m_read_strands_children(read_strands_children),
+ m_simdebug_reader(NULL)
+{
+ /* XXX this mapping allows fast lookup of existing objects in Blender data
+ * to associate with duplis. Later i may be possible to create instances of
+ * non-DNA data, but for the time being this is a requirement due to other code parts (drawing, rendering)
+ */
+ build_object_map(G.main, group);
+
+ if (read_sim_debug) {
+ BKE_sim_debug_data_set_enabled(true);
+ if (_sim_debug_data)
+ m_simdebug_reader = new AbcSimDebugReader(_sim_debug_data);
+ }
+}
+
+AbcDupliCacheReader::~AbcDupliCacheReader()
+{
+ if (m_simdebug_reader)
+ delete m_simdebug_reader;
+}
+
+void AbcDupliCacheReader::init_abc(IObject /*object*/)
+{
+}
+
+void AbcDupliCacheReader::read_dupligroup_object(IObject object, chrono_t time)
+{
+ if (GS(object.getName().c_str()) == ID_OB) {
+ /* instances are handled later, we create true object data here */
+ if (object.isInstanceDescendant())
+ return;
+
+ Object *b_ob = find_object(object.getName());
+ if (!b_ob)
+ return;
+
+ /* Always add dupli data, even if no geometry is stored.
+ * Any missing geometry data will be replaced by the original uncached data in drawing/rendering if available.
+ */
+ DupliObjectData *dupli_data = BKE_dupli_cache_add_object(dupli_cache, b_ob);
+ insert_dupli_data(object.getPtr(), dupli_data);
+
+ for (int i = 0; i < object.getNumChildren(); ++i) {
+ IObject child = object.getChild(i);
+ const MetaData &metadata = child.getMetaData();
+
+ if (IPolyMeshSchema::matches(metadata)) {
+ AbcDerivedMeshReader dm_reader("mesh", b_ob);
+ dm_reader.init(abc_archive());
+ dm_reader.init_abc(child);
+ if (dm_reader.read_sample_abc(time) != PTC_READ_SAMPLE_INVALID) {
+ BKE_dupli_object_data_set_mesh(dupli_data, dm_reader.acquire_result());
+ }
+ else {
+ dm_reader.discard_result();
+ }
+ }
+ else if (ICurvesSchema::matches(metadata)) {
+ Strands *strands;
+ StrandsChildren *children;
+ BKE_dupli_object_data_find_strands(dupli_data, child.getName().c_str(), &strands, &children);
+
+ AbcStrandsReader strands_reader(strands, children, m_read_strands_motion, m_read_strands_children);
+ strands_reader.init(abc_archive());
+ strands_reader.init_abc(child);
+ if (strands_reader.read_sample_abc(time) != PTC_READ_SAMPLE_INVALID) {
+ Strands *newstrands = strands_reader.acquire_result();
+ if (strands && strands != newstrands) {
+ /* reader can replace strands internally if topology does not match */
+ BKE_strands_free(strands);
+ }
+ BKE_dupli_object_data_add_strands(dupli_data, child.getName().c_str(), newstrands);
+
+ StrandsChildren *newchildren = strands_reader.child_reader().acquire_result();
+ if (children && children != newchildren) {
+ /* reader can replace strands internally if topology does not match */
+ BKE_strands_children_free(children);
+ }
+ BKE_dupli_object_data_add_strands_children(dupli_data, child.getName().c_str(), newchildren);
+ }
+ else {
+ strands_reader.discard_result();
+ strands_reader.child_reader().discard_result();
+ }
+ }
+ }
+ }
+}
+
+void AbcDupliCacheReader::read_dupligroup_group(IObject abc_group, chrono_t time)
+{
+ ISampleSelector ss = get_frame_sample_selector(time);
+
+ if (GS(abc_group.getName().c_str()) == ID_GR) {
+ size_t num_child = abc_group.getNumChildren();
+
+ for (size_t i = 0; i < num_child; ++i) {
+ IObject abc_dupli = abc_group.getChild(i);
+ ICompoundProperty props = abc_dupli.getProperties();
+
+ IM44fProperty prop_matrix(props, "matrix", 0);
+ M44f abc_matrix = abc_interpolate_sample_linear(prop_matrix, time);
+ float matrix[4][4];
+ memcpy(matrix, abc_matrix.getValue(), sizeof(matrix));
+
+ IBoolProperty prop_visible(props, "visible", 0);
+ bool visible = prop_visible.getValue(ss);
+
+ IObject abc_dupli_object = abc_dupli.getChild("object");
+ if (abc_dupli_object.isInstanceRoot()) {
+ DupliObjectData *dupli_data = find_dupli_data(abc_dupli_object.getPtr());
+ if (dupli_data) {
+ DupliObject *dob = BKE_dupli_cache_add_instance(dupli_cache, matrix, dupli_data);
+ dob->no_draw = !visible;
+ }
+ }
+ }
+ }
+}
+
+PTCReadSampleResult AbcDupliCacheReader::read_sample_abc(chrono_t time)
+{
+ IObject abc_top = abc_archive()->root();
+ IObject abc_group = abc_archive()->get_id_object((ID *)m_group);
+ if (!abc_group)
+ return PTC_READ_SAMPLE_INVALID;
+
+ /* first create shared object data */
+ for (size_t i = 0; i < abc_top.getNumChildren(); ++i) {
+ read_dupligroup_object(abc_top.getChild(i), time);
+ }
+
+ BKE_dupli_cache_clear_instances(dupli_cache);
+
+ /* now generate dupli instances for the group */
+ read_dupligroup_group(abc_group, time);
+
+ // XXX reader init is a mess ...
+ if (m_simdebug_reader) {
+ if (abc_top.getChildHeader("sim_debug")) {
+ m_simdebug_reader->init(abc_archive());
+ m_simdebug_reader->init_abc(abc_top.getChild("sim_debug"));
+
+ m_simdebug_reader->read_sample_abc(time);
+ }
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+DupliObjectData *AbcDupliCacheReader::find_dupli_data(ObjectReaderPtr ptr) const
+{
+ DupliMap::const_iterator it = dupli_map.find(ptr);
+ if (it == dupli_map.end())
+ return NULL;
+ else
+ return it->second;
+}
+
+void AbcDupliCacheReader::insert_dupli_data(ObjectReaderPtr ptr, DupliObjectData *data)
+{
+ dupli_map.insert(DupliPair(ptr, data));
+}
+
+void AbcDupliCacheReader::build_object_map(Main *bmain, Group *group)
+{
+ BKE_main_id_tag_idcode(bmain, ID_OB, false);
+ BKE_main_id_tag_idcode(bmain, ID_GR, false);
+ object_map.clear();
+
+ build_object_map_add_group(group);
+}
+
+Object *AbcDupliCacheReader::find_object(const std::string &name) const
+{
+ ObjectMap::const_iterator it = object_map.find(name);
+ if (it == object_map.end())
+ return NULL;
+ else
+ return it->second;
+}
+
+void AbcDupliCacheReader::build_object_map_add_group(Group *group)
+{
+ if (group->id.flag & LIB_DOIT)
+ return;
+ group->id.flag |= LIB_DOIT;
+
+ for (GroupObject *gob = (GroupObject *)group->gobject.first; gob; gob = gob->next) {
+ Object *ob = gob->ob;
+ if (ob->id.flag & LIB_DOIT)
+ continue;
+ ob->id.flag |= LIB_DOIT;
+ object_map.insert(ObjectPair(ob->id.name, ob));
+
+ if ((ob->transflag & OB_DUPLIGROUP) && ob->dup_group) {
+ build_object_map_add_group(ob->dup_group);
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+AbcDupliObjectWriter::AbcDupliObjectWriter(const std::string &name, DupliObjectData *dupdata, bool do_mesh, bool do_strands) :
+ ObjectWriter(dupdata->ob, name),
+ m_dupdata(dupdata),
+ m_do_strands(do_strands),
+ m_dm_writer(0)
+{
+ if (do_mesh) {
+ if (m_ob && m_ob->type == OB_MESH) {
+ m_dm_writer = new AbcDerivedMeshWriter("mesh", dupdata->ob, &dupdata->dm);
+ }
+ }
+}
+
+AbcStrandsWriter *AbcDupliObjectWriter::find_strands_writer(const std::string &name) const
+{
+ StrandsWriters::const_iterator it = m_strands_writers.find(name);
+ return (it != m_strands_writers.end()) ? it->second : NULL;
+}
+
+AbcStrandsWriter *AbcDupliObjectWriter::add_strands_writer(const std::string &name)
+{
+ StrandsWriters::const_iterator it = m_strands_writers.find(name);
+ if (it != m_strands_writers.end()) {
+ return it->second;
+ }
+ else {
+ AbcStrandsWriter *writer = new AbcStrandsWriter(name, m_dupdata);
+ m_strands_writers.insert(StrandsWritersPair(name, writer));
+
+ writer->init(abc_archive());
+ writer->init_abc(m_abc_object);
+
+ return writer;
+ }
+}
+
+AbcDupliObjectWriter::~AbcDupliObjectWriter()
+{
+ if (m_dm_writer)
+ delete m_dm_writer;
+ for (StrandsWriters::iterator it = m_strands_writers.begin(); it != m_strands_writers.end(); ++it) {
+ if (it->second)
+ delete it->second;
+ }
+}
+
+void AbcDupliObjectWriter::init_abc()
+{
+ if (m_abc_object)
+ return;
+
+ m_abc_object = abc_archive()->add_id_object<OObject>((ID *)m_ob);
+
+ if (m_dm_writer) {
+ /* XXX not nice */
+ m_dm_writer->init(abc_archive());
+ m_dm_writer->init_abc(m_abc_object);
+ }
+}
+
+void AbcDupliObjectWriter::write_sample()
+{
+ if (!m_abc_object)
+ return;
+
+ if (m_dm_writer) {
+ m_dm_writer->write_sample();
+ }
+
+ if (m_do_strands) {
+ int index = 0;
+ for (DupliObjectDataStrands *link = (DupliObjectDataStrands *)m_dupdata->strands.first;
+ link;
+ link = link->next, ++index) {
+ AbcStrandsWriter *writer = add_strands_writer(link->name);
+ writer->write_sample();
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+AbcDupliObjectReader::AbcDupliObjectReader(const std::string &name, Object *ob, DupliObjectData *dupli_data,
+ bool read_strands_motion, bool read_strands_children) :
+ ObjectReader(ob, name),
+ dupli_data(dupli_data),
+ m_read_strands_motion(read_strands_motion),
+ m_read_strands_children(read_strands_children)
+{
+}
+
+AbcDupliObjectReader::~AbcDupliObjectReader()
+{
+}
+
+void AbcDupliObjectReader::init(ReaderArchive *archive)
+{
+ AbcReader::init(archive);
+
+ if (abc_archive()->root().getChildHeader(m_name))
+ m_abc_object = abc_archive()->root().getChild(m_name);
+}
+
+void AbcDupliObjectReader::init_abc(IObject object)
+{
+ m_abc_object = object;
+}
+
+void AbcDupliObjectReader::read_dupligroup_object(IObject object, chrono_t time)
+{
+ if (GS(object.getName().c_str()) == ID_OB) {
+ /* instances are handled later, we create true object data here */
+ if (object.isInstanceDescendant())
+ return;
+
+ BKE_dupli_object_data_init(dupli_data, m_ob);
+
+ for (int i = 0; i < object.getNumChildren(); ++i) {
+ IObject child = object.getChild(i);
+ const MetaData &metadata = child.getMetaData();
+
+ if (IPolyMeshSchema::matches(metadata)) {
+ AbcDerivedMeshReader dm_reader("mesh", m_ob);
+ dm_reader.init(abc_archive());
+ dm_reader.init_abc(child);
+ if (dm_reader.read_sample_abc(time) != PTC_READ_SAMPLE_INVALID) {
+ BKE_dupli_object_data_set_mesh(dupli_data, dm_reader.acquire_result());
+ }
+ else {
+ dm_reader.discard_result();
+ }
+ }
+ else if (ICurvesSchema::matches(metadata)) {
+ Strands *strands;
+ StrandsChildren *children;
+ BKE_dupli_object_data_find_strands(dupli_data, child.getName().c_str(), &strands, &children);
+
+ AbcStrandsReader strands_reader(strands, children, m_read_strands_motion, m_read_strands_children);
+ strands_reader.init(abc_archive());
+ strands_reader.init_abc(child);
+ if (strands_reader.read_sample_abc(time) != PTC_READ_SAMPLE_INVALID) {
+ Strands *newstrands = strands_reader.acquire_result();
+ if (strands && strands != newstrands) {
+ /* reader can replace strands internally if topology does not match */
+ BKE_strands_free(strands);
+ }
+ BKE_dupli_object_data_add_strands(dupli_data, child.getName().c_str(), newstrands);
+
+ StrandsChildren *newchildren = strands_reader.child_reader().acquire_result();
+ if (children && children != newchildren) {
+ /* reader can replace strands internally if topology does not match */
+ BKE_strands_children_free(children);
+ }
+ BKE_dupli_object_data_add_strands_children(dupli_data, child.getName().c_str(), newchildren);
+ }
+ else {
+ strands_reader.discard_result();
+ strands_reader.child_reader().discard_result();
+ }
+ }
+ }
+ }
+}
+
+PTCReadSampleResult AbcDupliObjectReader::read_sample_abc(chrono_t time)
+{
+ if (!m_abc_object)
+ return PTC_READ_SAMPLE_INVALID;
+
+ read_dupligroup_object(m_abc_object, time);
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+DupliObjectData *AbcDupliObjectReader::find_dupli_data(ObjectReaderPtr ptr) const
+{
+ DupliMap::const_iterator it = dupli_map.find(ptr);
+ if (it == dupli_map.end())
+ return NULL;
+ else
+ return it->second;
+}
+
+void AbcDupliObjectReader::insert_dupli_data(ObjectReaderPtr ptr, DupliObjectData *data)
+{
+ dupli_map.insert(DupliPair(ptr, data));
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_group.h b/source/blender/pointcache/alembic/abc_group.h
new file mode 100644
index 00000000000..ae8c58edc45
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_group.h
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_ABC_GROUP_H
+#define PTC_ABC_GROUP_H
+
+#include "ptc_types.h"
+
+#include "abc_reader.h"
+#include "abc_schema.h"
+#include "abc_writer.h"
+
+#include "util_thread.h"
+
+struct CacheLibrary;
+struct DupliCache;
+struct DupliObject;
+struct DupliObjectData;
+struct Group;
+struct Object;
+struct Scene;
+
+namespace PTC {
+
+class AbcDerivedMeshWriter;
+class AbcStrandsWriter;
+class AbcSimDebugWriter;
+class AbcSimDebugReader;
+
+class AbcGroupWriter : public GroupWriter, public AbcWriter {
+public:
+ AbcGroupWriter(const std::string &name, Group *group);
+
+ void init_abc();
+ void create_refs();
+
+ void write_sample();
+
+private:
+ Abc::OObject m_abc_object;
+};
+
+class AbcGroupReader : public GroupReader, public AbcReader {
+public:
+ AbcGroupReader(const std::string &name, Group *group);
+
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+private:
+ Abc::IObject m_abc_object;
+};
+
+/* ========================================================================= */
+
+class AbcDupligroupWriter : public GroupWriter, public AbcWriter {
+public:
+ typedef std::vector<Abc::ObjectWriterPtr> ObjectWriterList;
+ typedef std::vector<Abc::BasePropertyWriterPtr> PropertyWriterList;
+
+ typedef std::map<ID *, AbcWriter *> IDWriterMap;
+ typedef std::pair<ID *, AbcWriter *> IDWriterPair;
+
+ AbcDupligroupWriter(const std::string &name, EvaluationContext *eval_ctx, Scene *scene, Group *group, CacheLibrary *cachelib);
+ ~AbcDupligroupWriter();
+
+ void init_abc();
+
+ void write_sample();
+ void write_sample_object(Object *ob, bool write_data);
+ void write_sample_dupli(DupliObject *dob, int index);
+
+ AbcWriter *find_id_writer(ID *id) const;
+
+private:
+ EvaluationContext *m_eval_ctx;
+ Scene *m_scene;
+ CacheLibrary *m_cachelib;
+
+ Abc::OObject m_abc_group;
+ ObjectWriterList m_object_writers;
+ PropertyWriterList m_property_writers;
+ IDWriterMap m_id_writers;
+ thread_mutex m_init_mutex;
+};
+
+class AbcDupliCacheWriter : public GroupWriter, public AbcWriter {
+public:
+ typedef std::vector<Abc::ObjectWriterPtr> ObjectWriterList;
+ typedef std::vector<Abc::BasePropertyWriterPtr> PropertyWriterList;
+
+ typedef std::map<ID *, AbcWriter *> IDWriterMap;
+ typedef std::pair<ID *, AbcWriter *> IDWriterPair;
+
+ AbcDupliCacheWriter(const std::string &name, Group *group, DupliCache *dupcache, int data_types, bool do_sim_debug = false);
+ ~AbcDupliCacheWriter();
+
+ void init_abc();
+
+ void write_sample();
+ void write_sample_object_data(DupliObjectData *data);
+ void write_sample_dupli(DupliObject *dob, int index);
+
+ AbcWriter *find_id_writer(ID *id) const;
+
+private:
+ DupliCache *m_dupcache;
+ int m_data_types;
+
+ Abc::OObject m_abc_group;
+ ObjectWriterList m_object_writers;
+ PropertyWriterList m_property_writers;
+ IDWriterMap m_id_writers;
+ AbcSimDebugWriter *m_simdebug_writer;
+};
+
+class AbcDupliCacheReader : public GroupReader, public AbcReader {
+public:
+ typedef std::map<Abc::ObjectReaderPtr, DupliObjectData*> DupliMap;
+ typedef std::pair<Abc::ObjectReaderPtr, DupliObjectData*> DupliPair;
+
+ typedef std::map<std::string, Object*> ObjectMap;
+ typedef std::pair<std::string, Object*> ObjectPair;
+
+public:
+ AbcDupliCacheReader(const std::string &name, Group *group, DupliCache *dupcache,
+ bool read_strands_motion, bool read_strands_children, bool read_sim_debug);
+ ~AbcDupliCacheReader();
+
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+protected:
+ void read_dupligroup_object(Abc::IObject object, chrono_t time);
+ void read_dupligroup_group(Abc::IObject abc_group, chrono_t time);
+
+ DupliObjectData *find_dupli_data(Abc::ObjectReaderPtr ptr) const;
+ void insert_dupli_data(Abc::ObjectReaderPtr ptr, DupliObjectData *data);
+
+ void build_object_map(Main *bmain, Group *group);
+ void build_object_map_add_group(Group *group);
+ Object *find_object(const std::string &name) const;
+
+private:
+ DupliMap dupli_map;
+ DupliCache *dupli_cache;
+
+ ObjectMap object_map;
+ bool m_read_strands_motion, m_read_strands_children;
+ AbcSimDebugReader *m_simdebug_reader;
+};
+
+
+class AbcDupliObjectWriter : public ObjectWriter, public AbcWriter {
+public:
+ typedef std::map<std::string, AbcStrandsWriter *> StrandsWriters;
+ typedef std::pair<std::string, AbcStrandsWriter *> StrandsWritersPair;
+
+ AbcDupliObjectWriter(const std::string &name, DupliObjectData *dupdata, bool do_mesh, bool do_strands);
+ ~AbcDupliObjectWriter();
+
+ void init_abc();
+
+ void write_sample();
+
+ AbcStrandsWriter *find_strands_writer(const std::string &name) const;
+ AbcStrandsWriter *add_strands_writer(const std::string &name);
+
+private:
+ DupliObjectData *m_dupdata;
+ bool m_do_strands;
+
+ Abc::OObject m_abc_object;
+ AbcDerivedMeshWriter *m_dm_writer;
+ StrandsWriters m_strands_writers;
+};
+
+class AbcDupliObjectReader : public ObjectReader, public AbcReader {
+public:
+ typedef std::map<Abc::ObjectReaderPtr, DupliObjectData*> DupliMap;
+ typedef std::pair<Abc::ObjectReaderPtr, DupliObjectData*> DupliPair;
+
+public:
+ AbcDupliObjectReader(const std::string &name, Object *ob, DupliObjectData *dupli_data,
+ bool read_strands_motion, bool read_strands_children);
+ ~AbcDupliObjectReader();
+
+ void init(ReaderArchive *archive);
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+protected:
+ void read_dupligroup_object(Abc::IObject object, chrono_t time);
+
+ DupliObjectData *find_dupli_data(Abc::ObjectReaderPtr ptr) const;
+ void insert_dupli_data(Abc::ObjectReaderPtr ptr, DupliObjectData *data);
+
+private:
+ DupliMap dupli_map;
+ DupliObjectData *dupli_data;
+ bool m_read_strands_motion, m_read_strands_children;
+
+ Abc::IObject m_abc_object;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_OBJECT_H */
diff --git a/source/blender/pointcache/alembic/abc_info.cpp b/source/blender/pointcache/alembic/abc_info.cpp
new file mode 100644
index 00000000000..35d30cb6d0c
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_info.cpp
@@ -0,0 +1,516 @@
+//-*****************************************************************************
+//
+// Copyright (c) 2009-2013,
+// Sony Pictures Imageworks, Inc. and
+// Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Sony Pictures Imageworks, nor
+// Industrial Light & Magic nor the names of their contributors may be used
+// to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+//-*****************************************************************************
+
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <Alembic/AbcGeom/All.h>
+#include <Alembic/AbcCoreAbstract/All.h>
+#include <Alembic/AbcCoreFactory/All.h>
+#include <Alembic/Util/All.h>
+#include <Alembic/Abc/TypedPropertyTraits.h>
+
+#include <sstream>
+
+#include "alembic.h"
+
+extern "C" {
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_ID.h"
+
+#include "BKE_cache_library.h"
+#include "BKE_idprop.h"
+}
+
+using namespace ::Alembic::AbcGeom;
+
+namespace PTC {
+
+static void metadata_from_idprops(MetaData &md, IDProperty *prop);
+
+void abc_metadata_from_idprops_group(MetaData &md, IDProperty *prop)
+{
+ if (!prop)
+ return;
+
+ IDProperty *child;
+ for (child = (IDProperty *)prop->data.group.first; child; child = child->next)
+ metadata_from_idprops(md, child);
+}
+
+static void metadata_from_idprops(MetaData &md, IDProperty *prop)
+{
+ /* skip default metadata entries, these are set explicitly */
+ std::string key(prop->name);
+ if (key.compare(kApplicationNameKey)==0 || key.compare(kDateWrittenKey)==0 || key.compare(kUserDescriptionKey)==0)
+ return;
+
+ switch (prop->type) {
+#if 0 /* don't support recursion yet */
+ case IDP_GROUP: {
+ metadata_from_idprops_group(md, child);
+ break;
+ }
+
+ case IDP_ARRAY: {
+ if (prop->data.pointer) {
+ IDProperty **array = (IDProperty **)prop->data.pointer;
+ for (int a = 0; a < prop->len; a++)
+ metadata_from_idprops(md, array[a]);
+ }
+ break;
+ }
+#endif
+
+ /* only string properties are used */
+ case IDP_STRING: {
+ md.set(prop->name, IDP_String(prop));
+ break;
+ }
+ }
+}
+
+void abc_metadata_to_idprops_group(const MetaData &md, IDProperty *prop)
+{
+ if (!prop)
+ return;
+
+ for (MetaData::const_iterator it = md.begin(); it != md.end(); ++it) {
+ const std::string &key = it->first;
+ const std::string &value = it->second;
+
+ /* skip default metadata entries, these are stored in CacheArchiveInfo */
+ if (key.compare(kApplicationNameKey)==0 || key.compare(kDateWrittenKey)==0 || key.compare(kUserDescriptionKey)==0)
+ continue;
+
+ IDPropertyTemplate val;
+ val.string.str = value.c_str();
+ val.string.len = value.length();
+ IDP_ReplaceInGroup(prop, IDP_New(IDP_STRING, &val, key.c_str()));
+ }
+}
+
+MetaData abc_create_archive_info(const char *app_name, const char *description, const struct tm *t, IDProperty *props)
+{
+ MetaData md;
+
+ md.set(kApplicationNameKey, app_name);
+ md.set(kUserDescriptionKey, description);
+
+ if (!t) {
+ time_t curtime = time(NULL);
+ t = localtime(&curtime);
+ }
+
+ if (t) {
+ char buf[256];
+ strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M", t);
+ md.set(kDateWrittenKey, buf);
+ }
+
+ /* store custom properties as metadata */
+ if (props && props->type == IDP_GROUP)
+ abc_metadata_from_idprops_group(md, props);
+
+ return md;
+}
+
+/* ========================================================================= */
+
+struct stringstream {
+ stringstream(void (*cb)(void *, const char *), void *userdata) :
+ cb(cb),
+ userdata(userdata)
+ {
+ }
+
+ void (*cb)(void *, const char *);
+ void *userdata;
+
+ template <typename T>
+ friend stringstream& operator << (stringstream &stream, T s);
+};
+
+template <typename T>
+stringstream& operator << (stringstream &stream, T s)
+{
+ std::stringstream ss;
+ ss << s;
+ stream.cb(stream.userdata, ss.str().c_str());
+ return stream;
+}
+
+static const std::string g_sep(";");
+static const std::string g_endl("\n");
+
+static void info_stream_properties(stringstream &ss, ICompoundProperty, std::string &);
+
+template <class PROP>
+static void info_stream_array_property(stringstream &ss, PROP iProp, const std::string &iIndent)
+{
+ std::string ptype = "ArrayProperty ";
+ size_t asize = 0;
+
+ AbcA::ArraySamplePtr samp;
+ index_t maxSamples = iProp.getNumSamples();
+ for (index_t i = 0 ; i < maxSamples; ++i) {
+ iProp.get(samp, ISampleSelector(i));
+ asize = samp->size();
+ }
+
+ std::string mdstring = "interpretation=";
+ mdstring += iProp.getMetaData().get("interpretation");
+
+ std::stringstream dtype;
+ dtype << "datatype=";
+ dtype << iProp.getDataType();
+
+ std::stringstream asizestr;
+ asizestr << ";arraysize=";
+ asizestr << asize;
+
+ mdstring += g_sep;
+
+ mdstring += dtype.str();
+
+ mdstring += asizestr.str();
+
+ ss << iIndent << " " << ptype << "name=" << iProp.getName()
+ << g_sep << mdstring << g_sep << "numsamps="
+ << iProp.getNumSamples() << g_endl;
+}
+
+template <class PROP>
+static void info_stream_scalar_property(stringstream &ss, PROP iProp, const std::string &iIndent)
+{
+ std::string ptype = "ScalarProperty ";
+ size_t asize = 0;
+
+ const AbcA::DataType &dt = iProp.getDataType();
+ const Alembic::Util ::uint8_t extent = dt.getExtent();
+ Alembic::Util::Dimensions dims(extent);
+ AbcA::ArraySamplePtr samp = AbcA::AllocateArraySample( dt, dims );
+ index_t maxSamples = iProp.getNumSamples();
+ for (index_t i = 0 ; i < maxSamples; ++i) {
+ iProp.get(const_cast<void *>(samp->getData()), ISampleSelector(i));
+ asize = samp->size();
+ }
+
+ std::string mdstring = "interpretation=";
+ mdstring += iProp.getMetaData().get("interpretation");
+
+ std::stringstream dtype;
+ dtype << "datatype=";
+ dtype << dt;
+
+ std::stringstream asizestr;
+ asizestr << ";arraysize=";
+ asizestr << asize;
+
+ mdstring += g_sep;
+
+ mdstring += dtype.str();
+
+ mdstring += asizestr.str();
+
+ ss << iIndent << " " << ptype << "name=" << iProp.getName()
+ << g_sep << mdstring << g_sep << "numsamps="
+ << iProp.getNumSamples() << g_endl;
+}
+
+static void info_stream_compound_property(stringstream &ss, ICompoundProperty iProp, std::string &ioIndent)
+{
+ std::string oldIndent = ioIndent;
+ ioIndent += " ";
+
+ std::string interp = "schema=";
+ interp += iProp.getMetaData().get("schema");
+
+ ss << ioIndent << "CompoundProperty " << "name=" << iProp.getName()
+ << g_sep << interp << g_endl;
+
+ info_stream_properties(ss, iProp, ioIndent);
+
+ ioIndent = oldIndent;
+}
+
+static void info_stream_properties(stringstream &ss, ICompoundProperty iParent, std::string &ioIndent )
+{
+ std::string oldIndent = ioIndent;
+ for (size_t i = 0 ; i < iParent.getNumProperties() ; i++) {
+ PropertyHeader header = iParent.getPropertyHeader(i);
+
+ if (header.isCompound()) {
+ info_stream_compound_property(ss, ICompoundProperty(iParent, header.getName()), ioIndent);
+ }
+ else if (header.isScalar()) {
+ info_stream_scalar_property(ss, IScalarProperty(iParent, header.getName()), ioIndent);
+ }
+ else {
+ BLI_assert(header.isArray());
+ info_stream_array_property(ss, IArrayProperty(iParent, header.getName()), ioIndent);
+ }
+ }
+
+ ioIndent = oldIndent;
+}
+
+static void info_stream_object(stringstream &ss, IObject iObj, std::string iIndent)
+{
+ // Object has a name, a full name, some meta data,
+ // and then it has a compound property full of properties.
+ std::string path = iObj.getFullName();
+
+ if (iObj.isInstanceRoot()) {
+ if (path != "/") {
+ ss << "Object " << "name=" << path
+ << " [Instance " << iObj.instanceSourcePath() << "]"
+ << g_endl;
+ }
+ }
+ else if (iObj.isInstanceDescendant()) {
+ /* skip non-root instances to avoid repetition */
+ return;
+ }
+ else {
+ if (path != "/") {
+ ss << "Object " << "name=" << path << g_endl;
+ }
+
+ // Get the properties.
+ ICompoundProperty props = iObj.getProperties();
+ info_stream_properties(ss, props, iIndent);
+
+ // now the child objects
+ for (size_t i = 0 ; i < iObj.getNumChildren() ; i++) {
+ info_stream_object(ss, IObject(iObj, iObj.getChildHeader(i).getName()), iIndent);
+ }
+ }
+}
+
+void abc_archive_info_stream(IArchive &archive, void (*stream)(void *, const char *), void *userdata)
+{
+ stringstream ss(stream, userdata);
+
+ ss << "Alembic Archive Info for "
+ << Alembic::AbcCoreAbstract::GetLibraryVersion()
+ << g_endl;
+
+ std::string appName;
+ std::string libraryVersionString;
+ Alembic::Util::uint32_t libraryVersion;
+ std::string whenWritten;
+ std::string userDescription;
+ GetArchiveInfo(archive,
+ appName,
+ libraryVersionString,
+ libraryVersion,
+ whenWritten,
+ userDescription);
+
+ if (appName != "") {
+ ss << " file written by: " << appName << g_endl;
+ ss << " using Alembic : " << libraryVersionString << g_endl;
+ ss << " written on : " << whenWritten << g_endl;
+ ss << " user description : " << userDescription << g_endl;
+ ss << g_endl;
+ }
+ else {
+// ss << argv[1] << g_endl;
+ ss << " (file doesn't have any ArchiveInfo)"
+ << g_endl;
+ ss << g_endl;
+ }
+
+ info_stream_object(ss, archive.getTop(), "");
+}
+
+/* ========================================================================= */
+
+static void info_nodes_properties(CacheArchiveInfo *info, ICompoundProperty, CacheArchiveInfoNode *parent, bool calc_bytes_size);
+
+template <class PROP>
+static void info_nodes_array_property(CacheArchiveInfo *info, PROP iProp, CacheArchiveInfoNode *parent, bool calc_bytes_size)
+{
+ CacheArchiveInfoNode *node = BKE_cache_archive_info_add_node(info, parent, eCacheArchiveInfoNode_Type_ArrayProperty, iProp.getName().c_str());
+
+ index_t num_samples = iProp.getNumSamples();
+
+ const DataType &datatype = iProp.getDataType();
+
+ node->num_samples = num_samples;
+ BLI_strncpy(node->datatype_name, PODName(datatype.getPod()), sizeof(node->datatype_name));
+ node->datatype_extent = (short)datatype.getExtent();
+
+ if (calc_bytes_size) {
+ size_t max_array_size = 0;
+ size_t tot_array_size = 0;
+ for (index_t i = 0; i < num_samples; ++i) {
+ AbcA::ArraySamplePtr samp;
+ iProp.get(samp, ISampleSelector(i));
+ size_t array_size = samp->size();
+ max_array_size = std::max(max_array_size, array_size);
+ tot_array_size += array_size;
+ }
+ node->bytes_size = datatype.getNumBytes() * tot_array_size;
+ node->array_size = max_array_size;
+
+ if (parent)
+ parent->bytes_size += node->bytes_size;
+ }
+}
+
+template <class PROP>
+static void info_nodes_scalar_property(CacheArchiveInfo *info, PROP iProp, CacheArchiveInfoNode *parent, bool calc_bytes_size)
+{
+ CacheArchiveInfoNode *node = BKE_cache_archive_info_add_node(info, parent, eCacheArchiveInfoNode_Type_ScalarProperty, iProp.getName().c_str());
+
+ index_t num_samples = iProp.getNumSamples();
+
+ const DataType &datatype = iProp.getDataType();
+
+ node->num_samples = num_samples;
+ BLI_strncpy(node->datatype_name, PODName(datatype.getPod()), sizeof(node->datatype_name));
+ node->datatype_extent = (short)datatype.getExtent();
+
+ if (calc_bytes_size) {
+ node->bytes_size = datatype.getNumBytes() * num_samples;
+
+ if (parent)
+ parent->bytes_size += node->bytes_size;
+ }
+}
+
+static void info_nodes_compound_property(CacheArchiveInfo *info, ICompoundProperty iProp, CacheArchiveInfoNode *parent, bool calc_bytes_size)
+{
+ CacheArchiveInfoNode *node = BKE_cache_archive_info_add_node(info, parent, eCacheArchiveInfoNode_Type_CompoundProperty, iProp.getName().c_str());
+
+ info_nodes_properties(info, iProp, node, calc_bytes_size);
+
+ if (calc_bytes_size && parent)
+ parent->bytes_size += node->bytes_size;
+}
+
+static void info_nodes_properties(CacheArchiveInfo *info, ICompoundProperty iParent, CacheArchiveInfoNode *parent, bool calc_bytes_size)
+{
+ for (size_t i = 0 ; i < iParent.getNumProperties() ; i++) {
+ PropertyHeader header = iParent.getPropertyHeader(i);
+
+ if (header.isCompound()) {
+ info_nodes_compound_property(info, ICompoundProperty(iParent, header.getName()), parent, calc_bytes_size);
+ }
+ else if (header.isScalar()) {
+ info_nodes_scalar_property(info, IScalarProperty(iParent, header.getName()), parent, calc_bytes_size);
+ }
+ else {
+ BLI_assert(header.isArray());
+ info_nodes_array_property(info, IArrayProperty(iParent, header.getName()), parent, calc_bytes_size);
+ }
+ }
+}
+
+static void info_nodes_object(CacheArchiveInfo *info, IObject iObj, CacheArchiveInfoNode *parent, bool calc_bytes_size)
+{
+ CacheArchiveInfoNode *node = BKE_cache_archive_info_add_node(info, parent, eCacheArchiveInfoNode_Type_Object, iObj.getName().c_str());
+
+ if (iObj.isInstanceRoot()) {
+ }
+ else if (iObj.isInstanceDescendant()) {
+ }
+ else {
+ // Get the properties.
+ ICompoundProperty props = iObj.getProperties();
+ info_nodes_properties(info, props, node, calc_bytes_size);
+
+ // now the child objects
+ for (size_t i = 0 ; i < iObj.getNumChildren() ; i++) {
+ info_nodes_object(info, IObject(iObj, iObj.getChildHeader(i).getName()), node, calc_bytes_size);
+ }
+ }
+
+ if (calc_bytes_size && parent)
+ parent->bytes_size += node->bytes_size;
+}
+
+void abc_archive_info_nodes(IArchive &archive, CacheArchiveInfo *info, IDProperty *metadata, bool calc_nodes, bool calc_bytes_size)
+{
+// ss << "Alembic Archive Info for "
+// << Alembic::AbcCoreAbstract::GetLibraryVersion()
+// << g_endl;
+
+ std::string appName;
+ std::string libraryVersionString;
+ uint32_t libraryVersion;
+ std::string whenWritten;
+ std::string userDescription;
+ GetArchiveInfo(archive, appName, libraryVersionString, libraryVersion, whenWritten, userDescription);
+
+ if (appName != "") {
+ BLI_strncpy(info->app_name, appName.c_str(), sizeof(info->app_name));
+ BLI_strncpy(info->date_written, whenWritten.c_str(), sizeof(info->date_written));
+ BLI_strncpy(info->description, userDescription.c_str(), sizeof(info->description));
+ }
+ else {
+ info->app_name[0] = '\0';
+ info->date_written[0] = '\0';
+ info->description[0] = '\0';
+ }
+
+ if (metadata)
+ abc_metadata_to_idprops_group(archive.getPtr()->getMetaData(), metadata);
+
+ if (calc_nodes)
+ info_nodes_object(info, archive.getTop(), NULL, calc_bytes_size);
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_interpolate.cpp b/source/blender/pointcache/alembic/abc_interpolate.cpp
new file mode 100644
index 00000000000..989734f3c56
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_interpolate.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "abc_interpolate.h"
+
+extern "C" {
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+}
+
+namespace PTC {
+
+using namespace Abc;
+
+
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_interpolate.h b/source/blender/pointcache/alembic/abc_interpolate.h
new file mode 100644
index 00000000000..102f4680c81
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_interpolate.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_ABC_INTERPOLATE_H
+#define PTC_ABC_INTERPOLATE_H
+
+#include <Alembic/Abc/ISampleSelector.h>
+#include <Alembic/Abc/IScalarProperty.h>
+#include <Alembic/Abc/TypedPropertyTraits.h>
+#include <Alembic/Abc/ITypedArrayProperty.h>
+#include <Alembic/Abc/ITypedScalarProperty.h>
+#include <Alembic/Abc/TypedArraySample.h>
+
+extern "C" {
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+}
+
+namespace PTC {
+
+using namespace Alembic;
+using namespace Abc;
+
+using Alembic::Util::shared_ptr;
+
+enum InterpolateSemanticDefault {
+ InterpolateSemanticDefault_None = 0,
+};
+
+enum InterpolateSemanticVector {
+ InterpolateSemanticVector_Linear = 0,
+ InterpolateSemanticVector_Slerp = 1,
+};
+
+/* linear blending for primitive types */
+template <typename T>
+BLI_INLINE T interpolate_sample(const T &val0, const T &val1, float t)
+{
+ return val0 * (1.0f-t) + val1 * t;
+}
+
+/* vector semantics */
+BLI_INLINE V3f interpolate_sample(const V3f &val0, const V3f &val1, float t, InterpolateSemanticVector semantic)
+{
+ switch (semantic) {
+ case InterpolateSemanticVector_Linear:
+ return val0 * (1.0f-t) + val1 * t;
+ case InterpolateSemanticVector_Slerp:
+ V3f result;
+ interp_v3_v3v3_slerp_safe(result.getValue(), val0.getValue(), val1.getValue(), t);
+ return result;
+ }
+ return V3f(0.0f, 0.0f, 0.0f);
+}
+
+BLI_INLINE Quatf interpolate_sample(const Quatf &val0, const Quatf &val1, float t)
+{
+ float qt0[4] = {val0.r, val0.v.x, val0.v.y, val0.v.z};
+ float qt1[4] = {val1.r, val1.v.x, val1.v.y, val1.v.z};
+ float result[4];
+ interp_qt_qtqt(result, qt0, qt1, t);
+ return Quatf(result[0], result[1], result[2], result[3]);
+}
+
+BLI_INLINE M44f interpolate_sample(const M44f &val0, const M44f &val1, float t)
+{
+ float loc[3], quat[4], size[3];
+ float loc0[3], quat0[4], size0[3];
+ float loc1[3], quat1[4], size1[3];
+ mat4_decompose(loc0, quat0, size0, (float (*)[4])val0.getValue());
+ mat4_decompose(loc1, quat1, size1, (float (*)[4])val1.getValue());
+
+ /* linear interpolation for rotation and scale */
+ interp_v3_v3v3(loc, loc0, loc1, t);
+
+ /* use simpe nlerp instead of slerp. it's faster and almost the same */
+ interp_v4_v4v4(quat, quat0, quat1, t);
+ normalize_qt(quat);
+
+ interp_v3_v3v3(size, size0, size1, t);
+
+ M44f result;
+ loc_quat_size_to_mat4((float (*)[4])result.getValue(), loc, quat, size);
+
+ return result;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* These wrapper types allow calling all the interpolate_sample variants without knowing the semantics type
+ * structs are required for this, since partial specialization does not work with functions.
+ */
+
+/* forward declaration */
+template <typename ArraySampleT, typename SemanticT>
+BLI_INLINE shared_ptr<ArraySampleT> interpolate_sample(const ArraySampleT &val0, const ArraySampleT &val1, float t, SemanticT semantic);
+
+template <typename T, typename SemanticT>
+struct InterpolateSampleCaller {
+ BLI_INLINE T call(const T &val0, const T &val1, float t, SemanticT semantic)
+ {
+ return interpolate_sample(val0, val1, t, semantic);
+ }
+};
+
+template <typename T>
+struct InterpolateSampleCaller<T, InterpolateSemanticDefault> {
+ BLI_INLINE T call(const T &val0, const T &val1, float t, InterpolateSemanticDefault)
+ {
+ return interpolate_sample(val0, val1, t);
+ }
+};
+
+/* ------------------------------------------------------------------------- */
+
+/* Scalar Properties */
+
+template <typename PropT, typename SemanticT>
+typename PropT::value_type abc_interpolate_sample_linear(const PropT &prop, chrono_t time, SemanticT semantic)
+{
+ typedef typename PropT::value_type value_type;
+
+ ISampleSelector ss0(time, ISampleSelector::kFloorIndex);
+ ISampleSelector ss1(time, ISampleSelector::kCeilIndex);
+
+ index_t index0 = ss0.getIndex(prop.getTimeSampling(), prop.getNumSamples());
+ index_t index1 = ss1.getIndex(prop.getTimeSampling(), prop.getNumSamples());
+ if (index0 == index1) {
+ /* no interpolation needed */
+ return prop.getValue(ss0);
+ }
+ else {
+ chrono_t time0 = prop.getTimeSampling()->getSampleTime(index0);
+ chrono_t time1 = prop.getTimeSampling()->getSampleTime(index1);
+
+ float t = (time1 > time0) ? (time - time0) / (time1 - time0) : 0.0f;
+ return InterpolateSampleCaller<value_type, SemanticT>::call(prop.getValue(ss0), prop.getValue(ss1), t, semantic);
+ }
+}
+
+template <typename PropT>
+typename PropT::value_type abc_interpolate_sample_linear(const PropT &prop, chrono_t time)
+{
+ return abc_interpolate_sample_linear(prop, time, InterpolateSemanticDefault_None);
+}
+
+
+/* Array Properties */
+
+template <typename ArraySampleT, typename SemanticT>
+BLI_INLINE shared_ptr<ArraySampleT> interpolate_array_sample(const ArraySampleT &val0, const ArraySampleT &val1, float t, SemanticT semantic)
+{
+ typedef ArraySampleT sample_type;
+ typedef shared_ptr<ArraySampleT> sample_ptr_type;
+ typedef typename ArraySampleT::value_type value_type;
+
+ size_t size0 = val0.size();
+ size_t size1 = val1.size();
+ size_t maxsize = size0 > size1 ? size0 : size1;
+ size_t minsize = size0 < size1 ? size0 : size1;
+
+ const value_type *data0 = val0.get();
+ const value_type *data1 = val1.get();
+ value_type *result = new value_type[maxsize];
+ value_type *data = result;
+
+ for (size_t i = 0; i < minsize; ++i) {
+ *data = InterpolateSampleCaller<value_type, SemanticT>::call(*data0, *data1, t, semantic);
+ ++data;
+ ++data0;
+ ++data1;
+ }
+
+ if (size0 > minsize) {
+ for (size_t i = minsize; i < size0; ++i) {
+ *data = *data0;
+ ++data;
+ ++data0;
+ }
+ }
+ else if (size1 > minsize) {
+ for (size_t i = minsize; i < size1; ++i) {
+ *data = *data1;
+ ++data;
+ ++data1;
+ }
+ }
+
+ return sample_ptr_type(new sample_type(result, maxsize));
+}
+
+template <typename TraitsT, typename SemanticT>
+shared_ptr<typename ITypedArrayProperty<TraitsT>::sample_type> abc_interpolate_sample_linear(const ITypedArrayProperty<TraitsT> &prop, chrono_t time, SemanticT semantic)
+{
+ ISampleSelector ss0(time, ISampleSelector::kFloorIndex);
+ ISampleSelector ss1(time, ISampleSelector::kCeilIndex);
+
+ index_t index0 = ss0.getIndex(prop.getTimeSampling(), prop.getNumSamples());
+ index_t index1 = ss1.getIndex(prop.getTimeSampling(), prop.getNumSamples());
+ if (index0 == index1) {
+ /* no interpolation needed */
+ return prop.getValue(ss0);
+ }
+ else {
+ chrono_t time0 = prop.getTimeSampling()->getSampleTime(index0);
+ chrono_t time1 = prop.getTimeSampling()->getSampleTime(index1);
+
+ float t = (time1 > time0) ? (time - time0) / (time1 - time0) : 0.0f;
+ return interpolate_array_sample(*prop.getValue(ss0), *prop.getValue(ss1), t, semantic);
+ }
+}
+
+template <typename TraitsT>
+shared_ptr<typename ITypedArrayProperty<TraitsT>::sample_type> abc_interpolate_sample_linear(const ITypedArrayProperty<TraitsT> &prop, chrono_t time)
+{
+ return abc_interpolate_sample_linear(prop, time, InterpolateSemanticDefault_None);
+}
+
+} /* namespace PTC */
+
+#endif /* PTC_ABC_INTERPOLATE_H */
diff --git a/source/blender/pointcache/alembic/abc_mesh.cpp b/source/blender/pointcache/alembic/abc_mesh.cpp
new file mode 100644
index 00000000000..57f07d47099
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_mesh.cpp
@@ -0,0 +1,630 @@
+/*
+ * Copyright 2014, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "abc_interpolate.h"
+#include "abc_mesh.h"
+
+extern "C" {
+#include "BLI_math.h"
+
+#include "DNA_object_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_mesh.h"
+
+#include "PIL_time.h"
+}
+
+#include "PTC_api.h"
+
+//#define USE_TIMING
+
+namespace PTC {
+
+using namespace Abc;
+using namespace AbcGeom;
+
+/* CD layers that are stored in generic customdata arrays created with CD_ALLOC */
+/* XXX CD_MASK_MTFACE and CD_MASK_MTEXPOLY are currently still needed as dummies for syncing
+ * particle UV and MCol layers to the mesh shader attributes ...
+ */
+static CustomDataMask CD_MASK_CACHE_EXCLUDE =
+ CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MFACE | CD_MASK_MPOLY | CD_MASK_MLOOP |
+ /*CD_MASK_MTFACE | CD_MASK_MTEXPOLY |*/
+ CD_MASK_PROP_STR |
+ CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX |
+ CD_MASK_MDISPS | CD_MASK_CREASE | CD_MASK_BWEIGHT | CD_MASK_RECAST | CD_MASK_PAINT_MASK |
+ CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE;
+
+static CustomDataMask CD_MASK_CACHE_VERT = ~(CD_MASK_CACHE_EXCLUDE | CD_MASK_NORMAL);
+static CustomDataMask CD_MASK_CACHE_EDGE = ~(CD_MASK_CACHE_EXCLUDE);
+static CustomDataMask CD_MASK_CACHE_FACE = ~(CD_MASK_CACHE_EXCLUDE);
+static CustomDataMask CD_MASK_CACHE_POLY = ~(CD_MASK_CACHE_EXCLUDE);
+static CustomDataMask CD_MASK_CACHE_LOOP = ~(CD_MASK_CACHE_EXCLUDE);
+
+struct MVertSample {
+ std::vector<V3f> co;
+ std::vector<N3f> no;
+ std::vector<int8_t> flag;
+ std::vector<int8_t> bweight;
+};
+
+struct MEdgeSample {
+ std::vector<uint32_t> verts;
+ std::vector<int16_t> flag;
+ std::vector<int8_t> crease;
+ std::vector<int8_t> bweight;
+};
+
+struct MPolySample {
+ /*std::vector<int32_t> loopstart;*/ /* loopstart is not stored explicitly */
+ std::vector<int32_t> totloop;
+ std::vector<int16_t> mat_nr;
+ std::vector<int8_t> flag;
+};
+
+struct MLoopSample {
+ /* XXX these are unsigned int in DNA, but Alembic expects signed int */
+ std::vector<int32_t> verts;
+ std::vector<int32_t> edges;
+};
+
+AbcDerivedMeshWriter::AbcDerivedMeshWriter(const std::string &name, Object *ob, DerivedMesh **dm_ptr) :
+ DerivedMeshWriter(ob, dm_ptr, name),
+ m_vert_data_writer("vertex_data", CD_MASK_CACHE_VERT),
+ m_edge_data_writer("edge_data", CD_MASK_CACHE_EDGE),
+ m_face_data_writer("face_data", CD_MASK_CACHE_FACE),
+ m_poly_data_writer("poly_data", CD_MASK_CACHE_POLY),
+ m_loop_data_writer("loop_data", CD_MASK_CACHE_LOOP)
+{
+}
+
+AbcDerivedMeshWriter::~AbcDerivedMeshWriter()
+{
+}
+
+void AbcDerivedMeshWriter::init_abc(OObject parent)
+{
+ if (m_mesh)
+ return;
+
+ m_mesh = OPolyMesh(parent, m_name, abc_archive()->frame_sampling_index());
+
+ OPolyMeshSchema &schema = m_mesh.getSchema();
+// OCompoundProperty geom_props = schema.getArbGeomParams();
+ OCompoundProperty user_props = schema.getUserProperties();
+
+ m_prop_vert_normals = ON3fArrayProperty(user_props, "vertex_normals", frame_sampling());
+ m_prop_vert_flag = OCharArrayProperty(user_props, "vertex_flag", frame_sampling());
+ m_prop_vert_bweight = OCharArrayProperty(user_props, "vertex_bweight", frame_sampling());
+
+ m_prop_edge_verts = OUInt32ArrayProperty(user_props, "edge_verts", frame_sampling());
+ m_prop_edge_flag = OInt16ArrayProperty(user_props, "edge_flag", frame_sampling());
+ m_prop_edge_crease = OCharArrayProperty(user_props, "edge_crease", frame_sampling());
+ m_prop_edge_bweight = OCharArrayProperty(user_props, "edge_bweight", frame_sampling());
+
+ m_prop_poly_mat_nr = OInt16ArrayProperty(user_props, "poly_mat_nr", frame_sampling());
+ m_prop_poly_flag = OCharArrayProperty(user_props, "poly_flag", frame_sampling());
+
+ m_prop_loop_verts = OInt32ArrayProperty(user_props, "loop_verts", frame_sampling());
+ m_prop_loop_edges = OInt32ArrayProperty(user_props, "loop_edges", frame_sampling());
+
+ m_vert_data_writer.init(frame_sampling());
+ m_edge_data_writer.init(frame_sampling());
+ m_face_data_writer.init(frame_sampling());
+ m_poly_data_writer.init(frame_sampling());
+ m_loop_data_writer.init(frame_sampling());
+}
+
+/* XXX modifiers are not allowed to generate poly normals on their own!
+ * see assert in DerivedMesh.c : dm_ensure_display_normals
+ */
+#if 0
+static void ensure_normal_data(DerivedMesh *dm)
+{
+ MVert *mverts = dm->getVertArray(dm);
+ MLoop *mloops = dm->getLoopArray(dm);
+ MPoly *mpolys = dm->getPolyArray(dm);
+ CustomData *cdata = dm->getPolyDataLayout(dm);
+ float (*polynors)[3];
+ int totvert = dm->getNumVerts(dm);
+ int totloop = dm->getNumLoops(dm);
+ int totpoly = dm->getNumPolys(dm);
+
+ if (CustomData_has_layer(cdata, CD_NORMAL))
+ polynors = (float (*)[3])CustomData_get_layer(cdata, CD_NORMAL);
+ else
+ polynors = (float (*)[3])CustomData_add_layer(cdata, CD_NORMAL, CD_CALLOC, NULL, totpoly);
+
+ BKE_mesh_calc_normals_poly(mverts, totvert, mloops, mpolys, totloop, totpoly, polynors, false);
+}
+#endif
+
+static void create_sample_verts(DerivedMesh *dm, MVertSample &sample)
+{
+ MVert *mv, *mverts = dm->getVertArray(dm);
+ int i, totvert = dm->getNumVerts(dm);
+
+ sample.co.reserve(totvert);
+ sample.no.reserve(totvert);
+ sample.flag.reserve(totvert);
+ sample.bweight.reserve(totvert);
+ for (i = 0, mv = mverts; i < totvert; ++i, ++mv) {
+ float nor[3];
+
+ sample.co.push_back(V3f(mv->co[0], mv->co[1], mv->co[2]));
+
+ normal_short_to_float_v3(nor, mv->no);
+ sample.no.push_back(N3f(nor[0], nor[1], nor[2]));
+
+ sample.flag.push_back(mv->flag);
+ sample.bweight.push_back(mv->bweight);
+ }
+}
+
+static void create_sample_edges(DerivedMesh *dm, MEdgeSample &sample)
+{
+ MEdge *me, *medges = dm->getEdgeArray(dm);
+ int i, totedge = dm->getNumEdges(dm);
+
+ sample.verts.reserve(totedge * 2);
+ sample.flag.reserve(totedge);
+ sample.crease.reserve(totedge);
+ sample.bweight.reserve(totedge);
+
+ for (i = 0, me = medges; i < totedge; ++i, ++me) {
+ sample.verts.push_back(me->v1);
+ sample.verts.push_back(me->v2);
+ sample.flag.push_back(me->flag);
+ sample.crease.push_back(me->crease);
+ sample.bweight.push_back(me->bweight);
+ }
+}
+
+static void create_sample_polys(DerivedMesh *dm, MPolySample &sample)
+{
+ MPoly *mp, *mpolys = dm->getPolyArray(dm);
+ int i, totpoly = dm->getNumPolys(dm);
+
+ sample.totloop.reserve(totpoly);
+ sample.mat_nr.reserve(totpoly);
+ sample.flag.reserve(totpoly);
+
+ for (i = 0, mp = mpolys; i < totpoly; ++i, ++mp) {
+ sample.totloop.push_back(mp->totloop);
+ sample.mat_nr.push_back(mp->mat_nr);
+ sample.flag.push_back(mp->flag);
+ }
+}
+
+static void create_sample_loops(DerivedMesh *dm, MLoopSample &sample)
+{
+ MLoop *ml, *mloops = dm->getLoopArray(dm);
+ int i, totloop = dm->getNumLoops(dm);
+
+ sample.verts.reserve(totloop);
+ sample.edges.reserve(totloop);
+
+ for (i = 0, ml = mloops; i < totloop; ++i, ++ml) {
+ sample.verts.push_back(ml->v);
+ sample.edges.push_back(ml->e);
+ }
+}
+
+static N3fArraySample create_sample_loop_normals(DerivedMesh *dm, std::vector<N3f> &data)
+{
+ CustomData *cdata = dm->getLoopDataLayout(dm);
+ float (*nor)[3], (*loopnors)[3];
+ int i, totloop = dm->getNumLoops(dm);
+
+ if (!CustomData_has_layer(cdata, CD_NORMAL))
+ return N3fArraySample();
+
+ loopnors = (float (*)[3])CustomData_get_layer(cdata, CD_NORMAL);
+
+ data.reserve(totloop);
+ for (i = 0, nor = loopnors; i < totloop; ++i, ++nor) {
+ float *vec = *nor;
+ data.push_back(N3f(vec[0], vec[1], vec[2]));
+ }
+
+ return N3fArraySample(data);
+}
+
+void AbcDerivedMeshWriter::write_sample()
+{
+ if (!m_mesh)
+ return;
+
+ DerivedMesh *output_dm = *m_dm_ptr;
+ if (!output_dm)
+ return;
+
+ /* TODO make this optional by a flag? */
+ /* XXX does not work atm, see comment above */
+ /*ensure_normal_data(output_dm);*/
+
+ OPolyMeshSchema &schema = m_mesh.getSchema();
+ OCompoundProperty user_props = schema.getUserProperties();
+
+ MVertSample vert_sample;
+ MEdgeSample edge_sample;
+ MPolySample poly_sample;
+ MLoopSample loop_sample;
+
+ std::vector<N3f> loop_normals_buffer;
+
+ // TODO decide how to handle vertex/face normals, in caching vs. export ...
+ DM_ensure_normals(output_dm);
+
+ create_sample_verts(output_dm, vert_sample);
+ create_sample_edges(output_dm, edge_sample);
+ create_sample_polys(output_dm, poly_sample);
+ create_sample_loops(output_dm, loop_sample);
+
+ N3fArraySample lnormals = create_sample_loop_normals(output_dm, loop_normals_buffer);
+
+ OPolyMeshSchema::Sample sample = OPolyMeshSchema::Sample(
+ P3fArraySample(vert_sample.co),
+ Int32ArraySample(loop_sample.verts),
+ Int32ArraySample(poly_sample.totloop),
+ OV2fGeomParam::Sample(), /* XXX define how/which UV map should be considered primary for the alembic schema */
+ ON3fGeomParam::Sample(lnormals, kFacevaryingScope)
+ );
+ schema.set(sample);
+
+ m_prop_vert_normals.set(N3fArraySample(vert_sample.no));
+ m_prop_vert_flag.set(CharArraySample(vert_sample.flag));
+ m_prop_vert_bweight.set(CharArraySample(vert_sample.bweight));
+
+ m_prop_edge_verts.set(UInt32ArraySample(edge_sample.verts));
+ m_prop_edge_flag.set(Int16ArraySample(edge_sample.flag));
+ m_prop_edge_crease.set(CharArraySample(edge_sample.crease));
+ m_prop_edge_bweight.set(CharArraySample(edge_sample.bweight));
+
+ m_prop_poly_mat_nr.set(Int16ArraySample(poly_sample.mat_nr));
+ m_prop_poly_flag.set(CharArraySample(poly_sample.flag));
+
+ m_prop_loop_verts.set(Int32ArraySample(loop_sample.verts));
+ m_prop_loop_edges.set(Int32ArraySample(loop_sample.edges));
+
+ CustomData *vdata = output_dm->getVertDataLayout(output_dm);
+ int num_vdata = output_dm->getNumVerts(output_dm);
+ m_vert_data_writer.write_sample(vdata, num_vdata, user_props);
+
+ CustomData *edata = output_dm->getEdgeDataLayout(output_dm);
+ int num_edata = output_dm->getNumEdges(output_dm);
+ m_edge_data_writer.write_sample(edata, num_edata, user_props);
+
+ CustomData *pdata = output_dm->getPolyDataLayout(output_dm);
+ int num_pdata = output_dm->getNumPolys(output_dm);
+ m_poly_data_writer.write_sample(pdata, num_pdata, user_props);
+
+ CustomData *ldata = output_dm->getLoopDataLayout(output_dm);
+ int num_ldata = output_dm->getNumLoops(output_dm);
+ m_loop_data_writer.write_sample(ldata, num_ldata, user_props);
+
+ DM_ensure_tessface(output_dm);
+ CustomData *fdata = output_dm->getTessFaceDataLayout(output_dm);
+ int num_fdata = output_dm->getNumTessFaces(output_dm);
+ m_face_data_writer.write_sample(fdata, num_fdata, user_props);
+}
+
+/* ========================================================================= */
+
+AbcDerivedMeshReader::AbcDerivedMeshReader(const std::string &name, Object *ob) :
+ DerivedMeshReader(ob, name),
+ m_vert_data_reader("vertex_data", CD_MASK_CACHE_VERT),
+ m_edge_data_reader("edge_data", CD_MASK_CACHE_EDGE),
+ m_face_data_reader("face_data", CD_MASK_CACHE_FACE),
+ m_poly_data_reader("poly_data", CD_MASK_CACHE_POLY),
+ m_loop_data_reader("loop_data", CD_MASK_CACHE_LOOP)
+{
+}
+
+AbcDerivedMeshReader::~AbcDerivedMeshReader()
+{
+}
+
+void AbcDerivedMeshReader::init_abc(IObject object)
+{
+ if (m_mesh)
+ return;
+ m_mesh = IPolyMesh(object, kWrapExisting);
+
+ IPolyMeshSchema &schema = m_mesh.getSchema();
+ ICompoundProperty geom_props = schema.getArbGeomParams();
+ ICompoundProperty user_props = schema.getUserProperties();
+
+ m_prop_vert_normals = IN3fArrayProperty(user_props, "vertex_normals", 0);
+ m_prop_vert_flag = ICharArrayProperty(user_props, "vertex_flag", 0);
+ m_prop_vert_bweight = ICharArrayProperty(user_props, "vertex_bweight", 0);
+
+ m_prop_edge_verts = IUInt32ArrayProperty(user_props, "edge_verts", 0);
+ m_prop_edge_flag = IInt16ArrayProperty(user_props, "edge_flag", 0);
+ m_prop_edge_crease = ICharArrayProperty(user_props, "edge_crease", 0);
+ m_prop_edge_bweight = ICharArrayProperty(user_props, "edge_bweight", 0);
+
+ m_prop_poly_mat_nr = IInt16ArrayProperty(user_props, "poly_mat_nr", 0);
+ m_prop_poly_flag = ICharArrayProperty(user_props, "poly_flag", 0);
+
+ m_prop_loop_verts = IInt32ArrayProperty(user_props, "loop_verts", 0);
+ m_prop_loop_edges = IInt32ArrayProperty(user_props, "loop_edges", 0);
+}
+
+static PTCReadSampleResult apply_sample_verts(DerivedMesh *dm, P3fArraySamplePtr sample_co, N3fArraySamplePtr sample_no,
+ CharArraySamplePtr sample_flag, CharArraySamplePtr sample_bweight)
+{
+ int totvert = dm->getNumVerts(dm);
+
+ if (sample_co->size() != totvert ||
+ sample_no->size() != totvert ||
+ sample_flag->size() != totvert ||
+ sample_bweight->size() != totvert)
+ {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ MVert *mv = dm->getVertArray(dm);
+ for (int i = 0; i < totvert; ++i) {
+ copy_v3_v3(mv->co, (*sample_co)[i].getValue());
+ normal_float_to_short_v3(mv->no, (*sample_no)[i].getValue());
+ mv->flag = (*sample_flag)[i];
+ mv->bweight = (*sample_bweight)[i];
+
+ ++mv;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+static PTCReadSampleResult apply_sample_edges(DerivedMesh *dm, UInt32ArraySamplePtr sample_verts, Int16ArraySamplePtr sample_flag,
+ CharArraySamplePtr sample_crease, CharArraySamplePtr sample_bweight, bool &has_edges)
+{
+ int totedge = dm->getNumEdges(dm);
+ if (sample_verts->size() != totedge * 2 ||
+ sample_flag->size() != totedge ||
+ sample_crease->size() != totedge ||
+ sample_bweight->size() != totedge)
+ {
+ has_edges = false;
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ const uint32_t *data_verts = sample_verts->get();
+ const int16_t *data_flag = sample_flag->get();
+ const int8_t *data_crease = sample_crease->get();
+ const int8_t *data_bweight = sample_bweight->get();
+
+ MEdge *me = dm->getEdgeArray(dm);
+ for (int i = 0; i < totedge; ++i) {
+ me->v1 = data_verts[0];
+ me->v2 = data_verts[1];
+ me->flag = *data_flag;
+ me->crease = *data_crease;
+ me->bweight = *data_bweight;
+
+ ++me;
+ data_verts += 2;
+ ++data_flag;
+ ++data_crease;
+ ++data_bweight;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+static PTCReadSampleResult apply_sample_polys(DerivedMesh *dm, Int32ArraySamplePtr sample_totloop, Int16ArraySamplePtr sample_mat_nr, CharArraySamplePtr sample_flag)
+{
+ int totpoly = dm->getNumPolys(dm);
+ if (sample_totloop->size() != totpoly ||
+ sample_mat_nr->size() != totpoly ||
+ sample_flag->size() != totpoly)
+ {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ const int32_t *data_totloop = sample_totloop->get();
+ const int16_t *data_mat_nr = sample_mat_nr->get();
+ const int8_t *data_flag = sample_flag->get();
+
+ int loopstart = 0;
+ MPoly *mp = dm->getPolyArray(dm);
+ for (int i = 0; i < totpoly; ++i) {
+ mp->totloop = *data_totloop;
+ mp->loopstart = loopstart;
+ mp->mat_nr = *data_mat_nr;
+ mp->flag = *data_flag;
+
+ loopstart += mp->totloop;
+
+ ++mp;
+ ++data_totloop;
+ ++data_mat_nr;
+ ++data_flag;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+static PTCReadSampleResult apply_sample_loops(DerivedMesh *dm, Int32ArraySamplePtr sample_verts, Int32ArraySamplePtr sample_edges, bool &has_edges)
+{
+ int totloop = dm->getNumLoops(dm);
+ if (sample_verts->size() != totloop)
+ return PTC_READ_SAMPLE_INVALID;
+
+ const int32_t *data_verts = sample_verts->get();
+
+ MLoop *ml = dm->getLoopArray(dm);
+ for (int i = 0; i < totloop; ++i) {
+ ml->v = *data_verts;
+
+ ++ml;
+ ++data_verts;
+ }
+
+ /* edge data is optional, if not available the edges must be recalculated */
+ if (sample_edges->size() == totloop) {
+ const int32_t *data_edges = sample_edges->get();
+
+ MLoop *ml = dm->getLoopArray(dm);
+ for (int i = 0; i < totloop; ++i) {
+ ml->e = *data_edges;
+
+ ++ml;
+ ++data_edges;
+ }
+ }
+ else {
+ has_edges = false;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+PTCReadSampleResult AbcDerivedMeshReader::read_sample_abc(chrono_t time)
+{
+#ifdef USE_TIMING
+ double start_time;
+ double time_get_sample, time_build_mesh, time_calc_edges, time_calc_normals;
+
+#define PROFILE_START \
+ start_time = PIL_check_seconds_timer();
+#define PROFILE_END(var) \
+ var = PIL_check_seconds_timer() - start_time;
+#else
+#define PROFILE_START ;
+#define PROFILE_END(var) ;
+#endif
+
+ /* discard existing result data */
+ discard_result();
+
+ if (!m_mesh)
+ return PTC_READ_SAMPLE_INVALID;
+
+ IPolyMeshSchema &schema = m_mesh.getSchema();
+ if (schema.getNumSamples() == 0)
+ return PTC_READ_SAMPLE_INVALID;
+ ICompoundProperty user_props = schema.getUserProperties();
+
+ ISampleSelector ss = get_frame_sample_selector(time);
+
+ PROFILE_START;
+ IPolyMeshSchema::Sample sample;
+ schema.get(sample, ss);
+
+ P3fArraySamplePtr vert_co = abc_interpolate_sample_linear(schema.getPositionsProperty(), time);
+ Int32ArraySamplePtr loop_verts = sample.getFaceIndices();
+ Int32ArraySamplePtr poly_totloop = sample.getFaceCounts();
+
+ N3fArraySamplePtr vnormals;
+ bool has_normals = false;
+ if (m_prop_vert_normals && m_prop_vert_normals.getNumSamples() > 0) {
+ vnormals = abc_interpolate_sample_linear(m_prop_vert_normals, time, InterpolateSemanticVector_Slerp);
+ has_normals = vnormals->valid();
+ }
+
+ UInt32ArraySamplePtr edge_verts = m_prop_edge_verts.getValue(ss);
+ Int32ArraySamplePtr loop_edges = m_prop_loop_edges.getValue(ss);
+ PROFILE_END(time_get_sample);
+
+ PROFILE_START;
+ bool has_edges = true; // XXX do we have to check for existing sample in advance?
+ int totverts = vert_co->size();
+ int totloops = loop_verts->size();
+ int totpolys = poly_totloop->size();
+ int totedges = has_edges ? edge_verts->size() >> 1 : 0;
+ m_result = CDDM_new(totverts, totedges, 0, totloops, totpolys);
+
+ apply_sample_verts(m_result, vert_co, vnormals, m_prop_vert_flag.getValue(ss), m_prop_vert_bweight.getValue(ss));
+ apply_sample_edges(m_result, edge_verts, m_prop_edge_flag.getValue(ss), m_prop_edge_crease.getValue(ss), m_prop_edge_bweight.getValue(ss), has_edges);
+ apply_sample_polys(m_result, poly_totloop, m_prop_poly_mat_nr.getValue(ss), m_prop_poly_flag.getValue(ss));
+ apply_sample_loops(m_result, loop_verts, loop_edges, has_edges);
+ PROFILE_END(time_build_mesh);
+
+ CustomData *vdata = m_result->getVertDataLayout(m_result);
+ int num_vdata = totverts;
+ m_vert_data_reader.read_sample(ss, vdata, num_vdata, user_props);
+
+ CustomData *edata = m_result->getEdgeDataLayout(m_result);
+ int num_edata = totedges;
+ m_edge_data_reader.read_sample(ss, edata, num_edata, user_props);
+
+ CustomData *pdata = m_result->getPolyDataLayout(m_result);
+ int num_pdata = totpolys;
+ m_poly_data_reader.read_sample(ss, pdata, num_pdata, user_props);
+
+ CustomData *ldata = m_result->getLoopDataLayout(m_result);
+ int num_ldata = totloops;
+ m_loop_data_reader.read_sample(ss, ldata, num_ldata, user_props);
+
+ DM_ensure_tessface(m_result);
+ CustomData *fdata = m_result->getTessFaceDataLayout(m_result);
+ int num_fdata = m_result->getNumTessFaces(m_result);
+ m_face_data_reader.read_sample(ss, fdata, num_fdata, user_props);
+
+ PROFILE_START;
+ if (!has_edges)
+ CDDM_calc_edges(m_result);
+ PROFILE_END(time_calc_edges);
+
+ PROFILE_START;
+ /* we need all normal properties defined, otherwise have to recalculate */
+ has_normals &= CustomData_has_layer(pdata, CD_NORMAL);
+ if (!has_normals) {
+ /* make sure normals are recalculated if there is no sample data */
+ m_result->dirty = (DMDirtyFlag)((int)m_result->dirty | DM_DIRTY_NORMALS);
+ }
+ DM_ensure_normals(m_result); /* only recalculates normals if no valid samples were found (has_normals == false) */
+ PROFILE_END(time_calc_normals);
+
+// BLI_assert(DM_is_valid(m_result));
+
+#ifdef USE_TIMING
+ printf("-------- Point Cache Timing --------\n");
+ printf("read sample: %f seconds\n", time_get_sample);
+ printf("build mesh: %f seconds\n", time_build_mesh);
+ printf("calculate edges: %f seconds\n", time_calc_edges);
+ printf("calculate normals: %f seconds\n", time_calc_normals);
+ printf("------------------------------------\n");
+#endif
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+/* ========================================================================= */
+
+AbcDerivedFinalRealtimeWriter::AbcDerivedFinalRealtimeWriter(const std::string &name, Object *ob) :
+ AbcDerivedMeshWriter(name, ob, &ob->derivedFinal)
+{
+}
+
+
+AbcDerivedFinalRenderWriter::AbcDerivedFinalRenderWriter(const std::string &name, Scene *scene, Object *ob, DerivedMesh **render_dm_ptr) :
+ AbcDerivedMeshWriter(name, ob, render_dm_ptr),
+ m_scene(scene)
+{
+}
+
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_mesh.h b/source/blender/pointcache/alembic/abc_mesh.h
new file mode 100644
index 00000000000..e00ccc3eef1
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_mesh.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2014, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_ABC_MESH_H
+#define PTC_ABC_MESH_H
+
+#include <Alembic/AbcGeom/IPolyMesh.h>
+#include <Alembic/AbcGeom/OPolyMesh.h>
+
+#include "ptc_types.h"
+
+#include "abc_customdata.h"
+#include "abc_reader.h"
+#include "abc_schema.h"
+#include "abc_writer.h"
+
+extern "C" {
+#include "DNA_modifier_types.h"
+
+#include "BKE_DerivedMesh.h"
+}
+
+struct Object;
+struct DerivedMesh;
+
+namespace PTC {
+
+class AbcDerivedMeshWriter : public DerivedMeshWriter, public AbcWriter {
+public:
+ AbcDerivedMeshWriter(const std::string &name, Object *ob, DerivedMesh **dm_ptr);
+ ~AbcDerivedMeshWriter();
+
+ void init_abc(Abc::OObject parent);
+
+ void write_sample();
+
+private:
+ AbcGeom::OPolyMesh m_mesh;
+
+ /* MVert attributes */
+ AbcGeom::ON3fArrayProperty m_prop_vert_normals;
+ AbcGeom::OCharArrayProperty m_prop_vert_flag;
+ AbcGeom::OCharArrayProperty m_prop_vert_bweight;
+
+ /* MEdge attributes */
+ AbcGeom::OUInt32ArrayProperty m_prop_edge_verts;
+ AbcGeom::OInt16ArrayProperty m_prop_edge_flag;
+ AbcGeom::OCharArrayProperty m_prop_edge_crease;
+ AbcGeom::OCharArrayProperty m_prop_edge_bweight;
+
+ /* MPoly attributes */
+ AbcGeom::OInt16ArrayProperty m_prop_poly_mat_nr;
+ AbcGeom::OCharArrayProperty m_prop_poly_flag;
+
+ /* MLoop attributes */
+ AbcGeom::OInt32ArrayProperty m_prop_loop_verts;
+ AbcGeom::OInt32ArrayProperty m_prop_loop_edges;
+
+ CustomDataWriter m_vert_data_writer;
+ CustomDataWriter m_edge_data_writer;
+ CustomDataWriter m_face_data_writer;
+ CustomDataWriter m_poly_data_writer;
+ CustomDataWriter m_loop_data_writer;
+};
+
+class AbcDerivedMeshReader : public DerivedMeshReader, public AbcReader {
+public:
+ AbcDerivedMeshReader(const std::string &name, Object *ob);
+ ~AbcDerivedMeshReader();
+
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+private:
+ AbcGeom::IPolyMesh m_mesh;
+
+ /* MVert attributes */
+ AbcGeom::IN3fArrayProperty m_prop_vert_normals;
+ AbcGeom::ICharArrayProperty m_prop_vert_flag;
+ AbcGeom::ICharArrayProperty m_prop_vert_bweight;
+
+ /* MEdge attributes */
+ AbcGeom::IUInt32ArrayProperty m_prop_edge_verts;
+ AbcGeom::IInt16ArrayProperty m_prop_edge_flag;
+ AbcGeom::ICharArrayProperty m_prop_edge_crease;
+ AbcGeom::ICharArrayProperty m_prop_edge_bweight;
+
+ /* MPoly attributes */
+ AbcGeom::IInt16ArrayProperty m_prop_poly_mat_nr;
+ AbcGeom::ICharArrayProperty m_prop_poly_flag;
+
+ /* MLoop attributes */
+ AbcGeom::IInt32ArrayProperty m_prop_loop_verts;
+ AbcGeom::IInt32ArrayProperty m_prop_loop_edges;
+
+ CustomDataReader m_vert_data_reader;
+ CustomDataReader m_edge_data_reader;
+ CustomDataReader m_face_data_reader;
+ CustomDataReader m_poly_data_reader;
+ CustomDataReader m_loop_data_reader;
+};
+
+
+/* -------------------------------------------------------------------------
+ * Writing derived mesh results requires different variants
+ * depending on viewport/render output and whether a cache modifier is used.
+ *
+ * Render DMs are constructed on-the-fly for each sample write, since they
+ * are not constructed immediately during scene frame updates. The writer is
+ * expected to only be called once per frame and object.
+ *
+ * If a cache modifier is used it must be have be activate at the time when
+ * the DM is built. For viewport output this means it should activate the
+ * modifier during it's whole lifetime, so that it caches meshes during the
+ * scene frame update. For render output the modifier should only be active
+ * during the render DM construction.
+ * ------------------------------------------------------------------------- */
+
+
+class AbcDerivedFinalRealtimeWriter : public AbcDerivedMeshWriter {
+public:
+ AbcDerivedFinalRealtimeWriter(const std::string &name, Object *ob);
+};
+
+
+class AbcDerivedFinalRenderWriter : public AbcDerivedMeshWriter {
+public:
+ AbcDerivedFinalRenderWriter(const std::string &name, Scene *scene, Object *ob, DerivedMesh **render_dm_ptr);
+
+private:
+ Scene *m_scene;
+};
+
+
+} /* namespace PTC */
+
+#endif /* PTC_MESH_H */
diff --git a/source/blender/pointcache/alembic/abc_object.cpp b/source/blender/pointcache/alembic/abc_object.cpp
new file mode 100644
index 00000000000..6d4fcdb5b1e
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_object.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "abc_mesh.h"
+#include "abc_object.h"
+#include "abc_particles.h"
+
+extern "C" {
+#include "BLI_math.h"
+
+#include "DNA_object_types.h"
+
+#include "BKE_object.h"
+}
+
+namespace PTC {
+
+using namespace Abc;
+using namespace AbcGeom;
+
+thread_mutex AbcObjectWriter::m_sample_write_mutex;
+
+AbcObjectWriter::AbcObjectWriter(const std::string &name, Scene *scene, Object *ob, bool do_mesh, bool do_hair) :
+ ObjectWriter(ob, name),
+ m_scene(scene),
+ m_final_dm(NULL),
+ m_dm_writer(0)
+{
+ if (do_mesh) {
+ if (m_ob->type == OB_MESH) {
+ m_dm_writer = new AbcDerivedMeshWriter("mesh", ob, &m_final_dm);
+ }
+ }
+
+ if (do_hair) {
+ for (ParticleSystem *psys = (ParticleSystem *)ob->particlesystem.first; psys; psys = psys->next) {
+ if (psys->part && psys->part->type == PART_HAIR) {
+ m_hair_writers.push_back(new AbcHairWriter(psys->name, ob, psys));
+ }
+ }
+ }
+}
+
+AbcObjectWriter::~AbcObjectWriter()
+{
+ if (m_dm_writer)
+ delete m_dm_writer;
+ for (int i = 0; i < m_hair_writers.size(); ++i)
+ if (m_hair_writers[i])
+ delete m_hair_writers[i];
+}
+
+void AbcObjectWriter::init_abc()
+{
+ if (m_abc_object)
+ return;
+
+ m_abc_object = abc_archive()->add_id_object<OObject>((ID *)m_ob);
+
+ if (m_dm_writer) {
+ /* XXX not nice */
+ m_dm_writer->init(abc_archive());
+ m_dm_writer->init_abc(m_abc_object);
+ }
+
+ for (int i = 0; i < m_hair_writers.size(); ++i) {
+ AbcHairWriter *hair_writer = m_hair_writers[i];
+ if (hair_writer) {
+ hair_writer->init(abc_archive());
+ hair_writer->init_abc(m_abc_object);
+ }
+ }
+}
+
+#if 0
+void AbcObjectWriter::create_refs()
+{
+ if ((m_ob->transflag & OB_DUPLIGROUP) && m_ob->dup_group) {
+ OObject abc_group = abc_archive()->get_id_object((ID *)m_ob->dup_group);
+ if (abc_group)
+ m_abc_object.addChildInstance(abc_group, "dup_group");
+ }
+}
+#endif
+
+void AbcObjectWriter::write_sample()
+{
+ if (!m_abc_object)
+ return;
+
+ if (m_dm_writer) {
+ if (abc_archive()->use_render()) {
+ m_final_dm = mesh_create_derived_render(m_scene, m_ob, CD_MASK_BAREMESH);
+
+ if (m_final_dm) {
+ {
+ thread_scoped_lock lock(m_sample_write_mutex);
+ m_dm_writer->write_sample();
+ }
+
+ m_final_dm->release(m_final_dm);
+ }
+ }
+ else {
+ m_final_dm = m_ob->derivedFinal;
+ if (!m_final_dm)
+ m_final_dm = mesh_get_derived_final(m_scene, m_ob, CD_MASK_BAREMESH);
+
+ if (m_final_dm) {
+ thread_scoped_lock lock(m_sample_write_mutex);
+ m_dm_writer->write_sample();
+ }
+ }
+ }
+
+ for (int i = 0; i < m_hair_writers.size(); ++i) {
+ AbcHairWriter *hair_writer = m_hair_writers[i];
+ if (hair_writer) {
+ thread_scoped_lock lock(m_sample_write_mutex);
+ hair_writer->write_sample();
+ }
+ }
+}
+
+
+AbcObjectReader::AbcObjectReader(const std::string &name, Object *ob) :
+ ObjectReader(ob, name)
+{
+}
+
+void AbcObjectReader::init_abc(IObject object)
+{
+ if (m_abc_object)
+ return;
+ m_abc_object = object;
+}
+
+PTCReadSampleResult AbcObjectReader::read_sample_abc(chrono_t /*time*/)
+{
+ if (!m_abc_object)
+ return PTC_READ_SAMPLE_INVALID;
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_object.h b/source/blender/pointcache/alembic/abc_object.h
new file mode 100644
index 00000000000..973a3312d1b
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_object.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_ABC_OBJECT_H
+#define PTC_ABC_OBJECT_H
+
+#include <vector>
+
+#include <Alembic/Abc/IObject.h>
+#include <Alembic/Abc/OObject.h>
+#include <Alembic/AbcGeom/IXform.h>
+#include <Alembic/AbcGeom/OXform.h>
+
+#include "ptc_types.h"
+
+#include "abc_reader.h"
+#include "abc_schema.h"
+#include "abc_writer.h"
+
+#include "util_thread.h"
+
+struct DerivedMesh;
+struct Object;
+struct Scene;
+
+namespace PTC {
+
+class AbcDerivedMeshWriter;
+class AbcHairWriter;
+
+class AbcObjectWriter : public ObjectWriter, public AbcWriter {
+public:
+ typedef std::vector<AbcHairWriter *> HairWriters;
+
+ AbcObjectWriter(const std::string &name, Scene *scene, Object *ob, bool do_mesh, bool do_hair);
+ ~AbcObjectWriter();
+
+ void init_abc();
+#if 0
+ void create_refs();
+#endif
+
+ void write_sample();
+
+private:
+ Scene *m_scene;
+ DerivedMesh *m_final_dm;
+
+ Abc::OObject m_abc_object;
+ AbcDerivedMeshWriter *m_dm_writer;
+ HairWriters m_hair_writers;
+ static thread_mutex m_sample_write_mutex;
+};
+
+class AbcObjectReader : public ObjectReader, public AbcReader {
+public:
+ AbcObjectReader(const std::string &name, Object *ob);
+
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+private:
+ Abc::IObject m_abc_object;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_OBJECT_H */
diff --git a/source/blender/pointcache/alembic/abc_particles.cpp b/source/blender/pointcache/alembic/abc_particles.cpp
new file mode 100644
index 00000000000..10e3ee55d3a
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_particles.cpp
@@ -0,0 +1,1284 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "abc_cloth.h"
+#include "abc_interpolate.h"
+#include "abc_mesh.h"
+#include "abc_particles.h"
+
+extern "C" {
+#include "BLI_listbase.h"
+#include "BLI_math_color.h"
+#include "BLI_math.h"
+
+#include "DNA_listBase.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_particle_types.h"
+
+#include "BKE_anim.h"
+#include "BKE_mesh_sample.h"
+#include "BKE_particle.h"
+#include "BKE_strands.h"
+}
+
+namespace PTC {
+
+using namespace Abc;
+using namespace AbcGeom;
+
+
+struct StrandsChildrenSample {
+ std::vector<int32_t> numverts;
+ std::vector<Quatf> root_rotations;
+ std::vector<V3f> root_positions;
+ std::vector<float32_t> cutoff;
+
+ std::vector<V3f> positions;
+ std::vector<float32_t> times;
+ std::vector<int32_t> parents;
+ std::vector<float32_t> parent_weights;
+ std::vector<V2f> curve_uvs;
+ std::vector<C3f> curve_vcols;
+};
+
+struct StrandsSample {
+ std::vector<int32_t> numverts;
+ std::vector<Quatf> root_rotations;
+ std::vector<uint32_t> root_orig_verts;
+ std::vector<float32_t> root_orig_weights;
+ std::vector<int32_t> root_orig_poly;
+ std::vector<uint32_t> root_orig_loops;
+
+ std::vector<V3f> positions;
+ std::vector<float32_t> times;
+ std::vector<float32_t> weights;
+
+ std::vector<V3f> motion_co;
+ std::vector<V3f> motion_vel;
+};
+
+AbcHairChildrenWriter::AbcHairChildrenWriter(const std::string &name, Object *ob, ParticleSystem *psys) :
+ ParticlesWriter(ob, psys, name)
+{
+ m_psmd = psys_get_modifier(ob, psys);
+}
+
+AbcHairChildrenWriter::~AbcHairChildrenWriter()
+{
+}
+
+void AbcHairChildrenWriter::init_abc(OObject parent)
+{
+ if (m_curves)
+ return;
+
+ /* XXX non-escaped string construction here ... */
+ m_curves = OCurves(parent, m_name, abc_archive()->frame_sampling_index());
+
+ OCurvesSchema &schema = m_curves.getSchema();
+ OCompoundProperty geom_props = schema.getArbGeomParams();
+ OCompoundProperty user_props = schema.getUserProperties();
+
+ m_prop_root_rot = OQuatfArrayProperty(user_props, "root_rotations", abc_archive()->frame_sampling());
+ m_prop_root_positions = OV3fArrayProperty(user_props, "root_positions", abc_archive()->frame_sampling());
+ m_param_cutoff = OFloatGeomParam(geom_props, "cutoff", false, kUniformScope, 1, 0);
+ m_param_times = OFloatGeomParam(geom_props, "times", false, kVertexScope, 1, 0);
+ m_prop_parents = OInt32ArrayProperty(user_props, "parents", abc_archive()->frame_sampling());
+ m_prop_parent_weights = OFloatArrayProperty(user_props, "parent_weights", abc_archive()->frame_sampling());
+ m_prop_curve_uvs = AbcGeom::OV2fArrayProperty(user_props, "curve_uvs", abc_archive()->frame_sampling());
+ m_prop_curve_vcols = AbcGeom::OC3fArrayProperty(user_props, "curve_vcols", abc_archive()->frame_sampling());
+}
+
+static int hair_children_count_totkeys(ParticleCacheKey **pathcache, int totpart)
+{
+ int p;
+ int totkeys = 0;
+
+ if (pathcache) {
+ for (p = 0; p < totpart; ++p) {
+ ParticleCacheKey *keys = pathcache[p];
+ totkeys += keys->segments + 1;
+ }
+ }
+
+ return totkeys;
+}
+
+#if 0
+static int hair_children_parent_advance(HairKey *keys, int totkeys, float time, int k)
+{
+ for (; k + 1 < totkeys; ++k) {
+ if (keys[k+1].time > time)
+ break;
+ }
+ return k;
+}
+
+static void hair_children_calc_strand(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, ChildParticle *cpa, ParticleCacheKey *keys, int maxkeys, StrandsChildrenSample &sample)
+{
+ const bool between = (psys->part->childtype == PART_CHILD_FACES);
+ ParticleData *parent[4];
+ float weight[4];
+ float hairmat[4][4][4];
+ int parent_key[4] = {0,0,0,0};
+
+ int i, k;
+
+ if (between) {
+ for (i = 0; i < 4; ++i) {
+ parent[i] = &psys->particles[cpa->pa[i]];
+ weight[i] = cpa->w[i];
+ if (parent[i])
+ psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, parent[i], hairmat[i]);
+ }
+ }
+ else {
+ parent[0] = &psys->particles[cpa->parent];
+ parent[1] = parent[2] = parent[3] = NULL;
+ weight[0] = 1.0f;
+ weight[1] = weight[2] = weight[3] = 0.0f;
+ if (parent[0])
+ psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, parent[0], hairmat[0]);
+ }
+
+ int numkeys = keys->segments + 1;
+ for (k = 0; k < numkeys; ++k) {
+ ParticleCacheKey *key = &keys[k];
+ /* XXX particle time values are too messy and confusing, recalculate */
+ float time = maxkeys > 1 ? (float)k / (float)(maxkeys-1) : 0.0f;
+
+ float parent_co[3];
+ zero_v3(parent_co);
+ for (i = 0; i < 4; ++i) {
+ if (!parent[i] || weight[i] <= 0.0f)
+ continue;
+ parent_key[i] = hair_children_parent_advance(parent[i]->hair, parent[i]->totkey, time, parent_key[i]);
+
+ float key_co[3];
+ if (parent_key[i] + 1 < parent[i]->totkey) {
+ HairKey *key0 = &parent[i]->hair[parent_key[i]];
+ HairKey *key1 = &parent[i]->hair[parent_key[i] + 1];
+ float x = (key1->time > key0->time) ? (time - key0->time) / (key1->time - key0->time) : 0.0f;
+ interp_v3_v3v3(key_co, key0->co, key1->co, x);
+ }
+ else {
+ HairKey *key0 = &parent[i]->hair[parent_key[i]];
+ copy_v3_v3(key_co, key0->co);
+ }
+
+ madd_v3_v3fl(parent_co, key_co, weight[i]);
+
+ /* Hair keys are in hair root space, pathcache keys are in world space,
+ * transform both to world space to calculate the offset
+ */
+ mul_m4_v3(hairmat[i], parent_co);
+ }
+
+ /* child position is an offset from the parent */
+ float co[3];
+ sub_v3_v3v3(co, key->co, parent_co);
+
+ sample.positions.push_back(V3f(parent_co[0], parent_co[1], parent_co[2]));
+ sample.times.push_back(time);
+ }
+}
+#endif
+
+BLI_INLINE bool particle_get_face(ParticleSystem *psys, int num_tessface, ChildParticle *cpa, int *r_num, float **r_fuv)
+{
+ ParticleSettings *part = psys->part;
+ const bool between = (part->childtype == PART_CHILD_FACES);
+
+ int num = DMCACHE_NOTFOUND;
+ float *fuv;
+ if (between) {
+ num = cpa->num;
+ fuv = cpa->fuv;
+ }
+ else if (part->from == PART_FROM_FACE) {
+ ParticleData *pa = psys->particles + cpa->pa[0];
+ num = pa->num_dmcache;
+ if (num == DMCACHE_NOTFOUND)
+ num = pa->num;
+ if (num >= num_tessface) {
+ /* happens when simplify is enabled gives invalid coords but would crash otherwise */
+ num = DMCACHE_NOTFOUND;
+ }
+ fuv = pa->fuv;
+ }
+
+ if (ELEM(num, DMCACHE_NOTFOUND, DMCACHE_ISCHILD)) {
+ return false;
+ }
+ else {
+ *r_num = num;
+ *r_fuv = fuv;
+ return true;
+ }
+}
+
+static void hair_children_get_uvs(ParticleSystem *psys, ParticleSystemModifierData *psmd, int totpart, StrandsChildrenSample &sample)
+{
+ const int num_tessface = psmd->dm->getNumTessFaces(psmd->dm);
+
+ MFace *mface = (MFace *)psmd->dm->getTessFaceArray(psmd->dm);
+ const CustomData *facedata = psmd->dm->getTessFaceDataLayout(psmd->dm);
+ int tot_layers = CustomData_number_of_layers(facedata, CD_MTFACE);
+
+ sample.curve_uvs.reserve(tot_layers * totpart);
+
+ for (int num_uv = 0; num_uv < tot_layers; ++num_uv) {
+ MTFace *mtface = (MTFace *)CustomData_get_layer_n(facedata, CD_MTFACE, num_uv);
+ if (!mtface)
+ continue;
+
+ for (int p = 0; p < totpart; ++p) {
+ ChildParticle *cpa = &psys->child[p];
+
+ int num;
+ float *fuv;
+ if (particle_get_face(psys, num_tessface, cpa, &num, &fuv)) {
+ MFace *mf = mface + num;
+ MTFace *mtf = mtface + num;
+
+ float uv[2];
+ psys_interpolate_uvs(mtf, mf->v4, fuv, uv);
+ sample.curve_uvs.push_back(V2f(uv[0], uv[1]));
+ }
+ else {
+ sample.curve_uvs.push_back(V2f(0.0f, 0.0f));
+ }
+ }
+ }
+}
+
+static void hair_children_get_vcols(ParticleSystem *psys, ParticleSystemModifierData *psmd, int totpart, StrandsChildrenSample &sample)
+{
+ const int num_tessface = psmd->dm->getNumTessFaces(psmd->dm);
+
+ MFace *mface = (MFace *)psmd->dm->getTessFaceArray(psmd->dm);
+ const CustomData *facedata = psmd->dm->getTessFaceDataLayout(psmd->dm);
+ int tot_layers = CustomData_number_of_layers(facedata, CD_MCOL);
+
+ sample.curve_vcols.reserve(tot_layers * totpart);
+
+ for (int num_vcol = 0; num_vcol < tot_layers; ++num_vcol) {
+ MCol *mcol = (MCol *)CustomData_get_layer_n(facedata, CD_MCOL, num_vcol);
+ if (!mcol)
+ continue;
+
+ for (int p = 0; p < totpart; ++p) {
+ ChildParticle *cpa = &psys->child[p];
+
+ int num;
+ float *fuv;
+ if (particle_get_face(psys, num_tessface, cpa, &num, &fuv)) {
+ MFace *mf = mface + num;
+ /* XXX another legacy thing: MCol are tessface data, but 4 values per face ... */
+ MCol *mc = mcol + 4 * num;
+
+ MCol col;
+ psys_interpolate_mcol(mc, mf->v4, fuv, &col);
+ /* XXX stupid legacy code: MCol stores values are BGR */
+ unsigned char icol[3] = {col.b, col.g, col.r};
+ C3f fcol;
+ rgb_uchar_to_float(fcol.getValue(), icol);
+ sample.curve_vcols.push_back(fcol);
+ }
+ else {
+ sample.curve_vcols.push_back(C3f(0.0f, 0.0f, 0.0f));
+ }
+ }
+ }
+}
+
+static void hair_children_create_sample(Object *ob, ParticleSystem *psys, ParticleSystemModifierData *psmd, ParticleCacheKey **pathcache, int totpart, int totkeys, int maxkeys,
+ StrandsChildrenSample &sample, bool write_constants)
+{
+ ParticleSettings *part = psys->part;
+ const bool between = (part->childtype == PART_CHILD_FACES);
+
+ int p, k;
+
+ if (write_constants) {
+ sample.numverts.reserve(totpart);
+ sample.parents.reserve(4*totpart);
+ sample.parent_weights.reserve(4*totpart);
+
+ sample.positions.reserve(totkeys);
+ sample.times.reserve(totkeys);
+ }
+
+ sample.root_rotations.reserve(totpart);
+ sample.root_positions.reserve(totpart);
+
+ for (p = 0; p < totpart; ++p) {
+ ChildParticle *cpa = &psys->child[p];
+
+ float hairmat[4][4];
+ psys_child_mat_to_object(ob, psys, psmd, cpa, hairmat);
+
+ if (pathcache) {
+ ParticleCacheKey *keys = pathcache[p];
+ int numkeys = keys->segments + 1;
+
+ if (write_constants) {
+ sample.numverts.push_back(numkeys);
+ if (between) {
+ sample.parents.push_back(cpa->pa[0]);
+ sample.parents.push_back(cpa->pa[1]);
+ sample.parents.push_back(cpa->pa[2]);
+ sample.parents.push_back(cpa->pa[3]);
+ sample.parent_weights.push_back(cpa->w[0]);
+ sample.parent_weights.push_back(cpa->w[1]);
+ sample.parent_weights.push_back(cpa->w[2]);
+ sample.parent_weights.push_back(cpa->w[3]);
+ }
+ else {
+ sample.parents.push_back(cpa->parent);
+ sample.parents.push_back(-1);
+ sample.parents.push_back(-1);
+ sample.parents.push_back(-1);
+ sample.parent_weights.push_back(1.0f);
+ sample.parent_weights.push_back(0.0f);
+ sample.parent_weights.push_back(0.0f);
+ sample.parent_weights.push_back(0.0f);
+ }
+
+ float imat[4][4];
+ mul_m4_m4m4(imat, ob->obmat, hairmat);
+ invert_m4(imat);
+
+ for (k = 0; k < numkeys; ++k) {
+ ParticleCacheKey *key = &keys[k];
+
+ /* pathcache keys are in world space, transform to hair root space */
+ float co[3];
+ mul_v3_m4v3(co, imat, key->co);
+
+ sample.positions.push_back(V3f(co[0], co[1], co[2]));
+ /* XXX particle time values are too messy and confusing, recalculate */
+ sample.times.push_back(maxkeys > 1 ? (float)k / (float)(maxkeys-1) : 0.0f);
+ }
+ }
+ }
+
+ float qt[4];
+ mat4_to_quat(qt, hairmat);
+ sample.root_rotations.push_back(Quatf(qt[0], qt[1], qt[2], qt[3]));
+ float *co = hairmat[3];
+ sample.root_positions.push_back(V3f(co[0], co[1], co[2]));
+ sample.cutoff.push_back(-1.0f);
+ }
+
+ if (write_constants) {
+ DM_ensure_tessface(psmd->dm);
+ hair_children_get_uvs(psys, psmd, totpart, sample);
+ hair_children_get_vcols(psys, psmd, totpart, sample);
+ }
+}
+
+void AbcHairChildrenWriter::write_sample()
+{
+ if (!m_curves)
+ return;
+
+ int totkeys = hair_children_count_totkeys(m_psys->childcache, m_psys->totchild);
+
+ int keysteps = abc_archive()->use_render() ? m_psys->part->ren_step : m_psys->part->draw_step;
+ int maxkeys = (1 << keysteps) + 1 + (m_psys->part->kink);
+ if (ELEM(m_psys->part->kink, PART_KINK_SPIRAL))
+ maxkeys += m_psys->part->kink_extra_steps;
+
+ OCurvesSchema &schema = m_curves.getSchema();
+
+ StrandsChildrenSample child_sample;
+ OCurvesSchema::Sample sample;
+ if (schema.getNumSamples() == 0) {
+ /* write curve sizes only first time, assuming they are constant! */
+ hair_children_create_sample(m_ob, m_psys, m_psmd, m_psys->childcache, m_psys->totchild, totkeys, maxkeys, child_sample, true);
+ sample = OCurvesSchema::Sample(child_sample.positions, child_sample.numverts);
+
+ m_prop_parents.set(Int32ArraySample(child_sample.parents));
+ m_prop_parent_weights.set(FloatArraySample(child_sample.parent_weights));
+ m_prop_curve_uvs.set(V2fArraySample(child_sample.curve_uvs));
+ m_prop_curve_vcols.set(C3fArraySample(child_sample.curve_vcols));
+
+ m_param_times.set(OFloatGeomParam::Sample(FloatArraySample(child_sample.times), kVertexScope));
+
+ schema.set(sample);
+ }
+ else {
+ hair_children_create_sample(m_ob, m_psys, m_psmd, m_psys->childcache, m_psys->totchild, totkeys, maxkeys, child_sample, false);
+ }
+
+ m_prop_root_rot.set(QuatfArraySample(child_sample.root_rotations));
+ m_prop_root_positions.set(V3fArraySample(child_sample.root_positions));
+ m_param_cutoff.set(OFloatGeomParam::Sample(FloatArraySample(child_sample.cutoff), kUniformScope));
+}
+
+
+AbcHairWriter::AbcHairWriter(const std::string &name, Object *ob, ParticleSystem *psys) :
+ ParticlesWriter(ob, psys, name),
+ m_child_writer("children", ob, psys)
+{
+ m_psmd = psys_get_modifier(ob, psys);
+}
+
+AbcHairWriter::~AbcHairWriter()
+{
+}
+
+void AbcHairWriter::init(WriterArchive *archive)
+{
+ AbcWriter::init(archive);
+ m_child_writer.init(archive);
+}
+
+void AbcHairWriter::init_abc(OObject parent)
+{
+ if (m_curves)
+ return;
+ m_curves = OCurves(parent, m_name, abc_archive()->frame_sampling_index());
+
+ OCurvesSchema &schema = m_curves.getSchema();
+ OCompoundProperty geom_props = schema.getArbGeomParams();
+
+ m_param_root_rot = OQuatfGeomParam(geom_props, "root_rotations", false, kUniformScope, 1, 0);
+ m_param_root_orig_verts = OUInt32GeomParam(geom_props, "root_orig_verts", false, kUniformScope, 1, 0);
+ m_param_root_orig_weights = OFloatGeomParam(geom_props, "root_orig_weights", false, kUniformScope, 1, 0);
+ m_param_root_orig_poly = OInt32GeomParam(geom_props, "root_orig_poly", false, kUniformScope, 1, 0);
+ m_param_root_orig_loops = OUInt32GeomParam(geom_props, "root_orig_loops", false, kUniformScope, 1, 0);
+
+ m_param_times = OFloatGeomParam(geom_props, "times", false, kVertexScope, 1, 0);
+ m_param_weights = OFloatGeomParam(geom_props, "weights", false, kVertexScope, 1, 0);
+
+ m_child_writer.init_abc(m_curves);
+}
+
+static int hair_count_totverts(ParticleSystem *psys)
+{
+ int p;
+ int totverts = 0;
+
+ for (p = 0; p < psys->totpart; ++p) {
+ ParticleData *pa = &psys->particles[p];
+ totverts += pa->totkey;
+ }
+
+ return totverts;
+}
+
+static void hair_create_sample(Object *ob, DerivedMesh *dm, ParticleSystem *psys, StrandsSample &sample, bool do_numverts)
+{
+ int totpart = psys->totpart;
+ int totverts = hair_count_totverts(psys);
+ int p, k;
+
+ if (totverts == 0)
+ return;
+
+ if (do_numverts)
+ sample.numverts.reserve(totpart);
+ sample.root_rotations.reserve(totpart);
+ sample.root_orig_verts.reserve(totpart * 3);
+ sample.root_orig_weights.reserve(totpart * 3);
+ sample.root_orig_poly.reserve(totpart);
+ sample.root_orig_loops.reserve(totpart * 3);
+ sample.positions.reserve(totverts);
+ sample.times.reserve(totverts);
+ sample.weights.reserve(totverts);
+
+ for (p = 0; p < totpart; ++p) {
+ ParticleData *pa = &psys->particles[p];
+ int numverts = pa->totkey;
+ float hairmat[4][4];
+
+ if (do_numverts)
+ sample.numverts.push_back(numverts);
+
+ psys_mat_hair_to_object(ob, dm, psys->part->from, pa, hairmat);
+ float root_qt[4];
+ mat4_to_quat(root_qt, hairmat);
+ sample.root_rotations.push_back(Quatf(root_qt[0], root_qt[1], root_qt[2], root_qt[3]));
+
+ MSurfaceSample surf;
+ BKE_mesh_sample_from_particle(&surf, psys, dm, pa);
+ sample.root_orig_verts.push_back(surf.orig_verts[0]);
+ sample.root_orig_verts.push_back(surf.orig_verts[1]);
+ sample.root_orig_verts.push_back(surf.orig_verts[2]);
+ sample.root_orig_weights.push_back(surf.orig_weights[0]);
+ sample.root_orig_weights.push_back(surf.orig_weights[1]);
+ sample.root_orig_weights.push_back(surf.orig_weights[2]);
+ sample.root_orig_poly.push_back(surf.orig_poly);
+ sample.root_orig_loops.push_back(surf.orig_loops[0]);
+ sample.root_orig_loops.push_back(surf.orig_loops[1]);
+ sample.root_orig_loops.push_back(surf.orig_loops[2]);
+
+ for (k = 0; k < numverts; ++k) {
+ HairKey *key = &pa->hair[k];
+
+ /* hair keys are in "hair space" relative to the mesh,
+ * store them in object space for compatibility and to avoid
+ * complexities of how particles work.
+ */
+ float co[3];
+ mul_v3_m4v3(co, hairmat, key->co);
+
+ sample.positions.push_back(V3f(co[0], co[1], co[2]));
+ /* XXX particle time values are too messy and confusing, recalculate */
+ sample.times.push_back(numverts > 1 ? (float)k / (float)(numverts-1) : 0.0f);
+ sample.weights.push_back(key->weight);
+ }
+ }
+}
+
+void AbcHairWriter::write_sample()
+{
+ if (!m_curves)
+ return;
+ if (!m_psmd || !m_psmd->dm)
+ return;
+
+ OCurvesSchema &schema = m_curves.getSchema();
+
+ StrandsSample hair_sample;
+ OCurvesSchema::Sample sample;
+ if (schema.getNumSamples() == 0) {
+ /* write curve sizes only first time, assuming they are constant! */
+ hair_create_sample(m_ob, m_psmd->dm, m_psys, hair_sample, true);
+ sample = OCurvesSchema::Sample(hair_sample.positions, hair_sample.numverts);
+ }
+ else {
+ hair_create_sample(m_ob, m_psmd->dm, m_psys, hair_sample, false);
+ sample = OCurvesSchema::Sample(hair_sample.positions);
+ }
+ schema.set(sample);
+
+ m_param_root_rot.set(OQuatfGeomParam::Sample(QuatfArraySample(hair_sample.root_rotations), kUniformScope));
+ m_param_root_orig_verts.set(OUInt32GeomParam::Sample(UInt32ArraySample(hair_sample.root_orig_verts), kUniformScope));
+ m_param_root_orig_weights.set(OFloatGeomParam::Sample(FloatArraySample(hair_sample.root_orig_weights), kUniformScope));
+ m_param_root_orig_poly.set(OInt32GeomParam::Sample(Int32ArraySample(hair_sample.root_orig_poly), kUniformScope));
+ m_param_root_orig_loops.set(OUInt32GeomParam::Sample(UInt32ArraySample(hair_sample.root_orig_loops), kUniformScope));
+
+ m_param_times.set(OFloatGeomParam::Sample(FloatArraySample(hair_sample.times), kVertexScope));
+ m_param_weights.set(OFloatGeomParam::Sample(FloatArraySample(hair_sample.weights), kVertexScope));
+
+ m_child_writer.write_sample();
+}
+
+
+AbcStrandsChildrenWriter::AbcStrandsChildrenWriter(const std::string &name, const std::string &abc_name, DupliObjectData *dobdata) :
+ m_name(name),
+ m_abc_name(abc_name),
+ m_dobdata(dobdata)
+{
+}
+
+StrandsChildren *AbcStrandsChildrenWriter::get_strands() const
+{
+ StrandsChildren *children;
+ BKE_dupli_object_data_find_strands(m_dobdata, m_name.c_str(), NULL, &children);
+ return children;
+}
+
+void AbcStrandsChildrenWriter::init_abc(OObject parent)
+{
+ if (m_curves)
+ return;
+ m_curves = OCurves(parent, m_abc_name, abc_archive()->frame_sampling_index());
+
+ OCurvesSchema &schema = m_curves.getSchema();
+ OCompoundProperty geom_props = schema.getArbGeomParams();
+ OCompoundProperty user_props = schema.getUserProperties();
+
+ m_prop_root_rot = OQuatfArrayProperty(user_props, "root_rotations", abc_archive()->frame_sampling());
+ m_prop_root_positions = OV3fArrayProperty(user_props, "root_positions", abc_archive()->frame_sampling());
+ m_param_cutoff = OFloatGeomParam(geom_props, "cutoff", false, kUniformScope, 1, abc_archive()->frame_sampling());
+ m_param_times = OFloatGeomParam(geom_props, "times", false, kVertexScope, 1, abc_archive()->frame_sampling());
+ m_prop_parents = OInt32ArrayProperty(user_props, "parents", abc_archive()->frame_sampling());
+ m_prop_parent_weights = OFloatArrayProperty(user_props, "parent_weights", abc_archive()->frame_sampling());
+ m_prop_curve_uvs = AbcGeom::OV2fArrayProperty(user_props, "curve_uvs", abc_archive()->frame_sampling());
+ m_prop_curve_vcols = AbcGeom::OC3fArrayProperty(user_props, "curve_vcols", abc_archive()->frame_sampling());
+}
+
+static void strands_children_get_uvs(StrandsChildren *strands, StrandsChildrenSample &sample)
+{
+ int totuv = strands->numuv * strands->totcurves;
+
+ sample.curve_uvs.reserve(totuv);
+
+ for (int i = 0; i < totuv; ++i) {
+ StrandsChildCurveUV *uv = &strands->curve_uvs[i];
+ sample.curve_uvs.push_back(V2f(uv->uv[0], uv->uv[1]));
+ }
+}
+
+static void strands_children_get_vcols(StrandsChildren *strands, StrandsChildrenSample &sample)
+{
+ int totvcol = strands->numvcol * strands->totcurves;
+
+ sample.curve_vcols.reserve(totvcol);
+
+ for (int i = 0; i < totvcol; ++i) {
+ StrandsChildCurveVCol *vcol = &strands->curve_vcols[i];
+ sample.curve_vcols.push_back(C3f(vcol->vcol[0], vcol->vcol[1], vcol->vcol[2]));
+ }
+}
+
+static void strands_children_create_sample(StrandsChildren *strands, StrandsChildrenSample &sample, bool write_constants)
+{
+ int totcurves = strands->totcurves;
+ int totverts = strands->totverts;
+
+ if (write_constants) {
+ sample.numverts.reserve(totcurves);
+ sample.parents.reserve(4*totcurves);
+ sample.parent_weights.reserve(4*totcurves);
+
+ sample.positions.reserve(totverts);
+ sample.times.reserve(totverts);
+ }
+
+ sample.root_rotations.reserve(totcurves);
+ sample.root_positions.reserve(totcurves);
+
+ StrandChildIterator it_strand;
+ for (BKE_strand_child_iter_init(&it_strand, strands); BKE_strand_child_iter_valid(&it_strand); BKE_strand_child_iter_next(&it_strand)) {
+ int numverts = it_strand.curve->numverts;
+
+ if (write_constants) {
+ sample.numverts.push_back(numverts);
+
+ sample.parents.push_back(it_strand.curve->parents[0]);
+ sample.parents.push_back(it_strand.curve->parents[1]);
+ sample.parents.push_back(it_strand.curve->parents[2]);
+ sample.parents.push_back(it_strand.curve->parents[3]);
+ sample.parent_weights.push_back(it_strand.curve->parent_weights[0]);
+ sample.parent_weights.push_back(it_strand.curve->parent_weights[1]);
+ sample.parent_weights.push_back(it_strand.curve->parent_weights[2]);
+ sample.parent_weights.push_back(it_strand.curve->parent_weights[3]);
+
+ StrandChildVertexIterator it_vert;
+ for (BKE_strand_child_vertex_iter_init(&it_vert, &it_strand); BKE_strand_child_vertex_iter_valid(&it_vert); BKE_strand_child_vertex_iter_next(&it_vert)) {
+ const float *co = it_vert.vertex->base;
+ sample.positions.push_back(V3f(co[0], co[1], co[2]));
+ sample.times.push_back(it_vert.vertex->time);
+ }
+ }
+
+ float qt[4];
+ mat4_to_quat(qt, it_strand.curve->root_matrix);
+ sample.root_rotations.push_back(Quatf(qt[0], qt[1], qt[2], qt[3]));
+ float *co = it_strand.curve->root_matrix[3];
+ sample.root_positions.push_back(V3f(co[0], co[1], co[2]));
+ sample.cutoff.push_back(it_strand.curve->cutoff);
+ }
+
+ if (write_constants) {
+ strands_children_get_uvs(strands, sample);
+ strands_children_get_vcols(strands, sample);
+ }
+}
+
+void AbcStrandsChildrenWriter::write_sample()
+{
+ if (!m_curves)
+ return;
+ StrandsChildren *strands = get_strands();
+ if (!strands)
+ return;
+
+ OCurvesSchema &schema = m_curves.getSchema();
+
+ StrandsChildrenSample strands_sample;
+ OCurvesSchema::Sample sample;
+ if (schema.getNumSamples() == 0) {
+ /* write curve sizes only first time, assuming they are constant! */
+ strands_children_create_sample(strands, strands_sample, true);
+ sample = OCurvesSchema::Sample(strands_sample.positions, strands_sample.numverts);
+
+ m_prop_parents.set(Int32ArraySample(strands_sample.parents));
+ m_prop_parent_weights.set(FloatArraySample(strands_sample.parent_weights));
+ m_prop_curve_uvs.set(V2fArraySample(strands_sample.curve_uvs));
+ m_prop_curve_vcols.set(C3fArraySample(strands_sample.curve_vcols));
+
+ m_param_times.set(OFloatGeomParam::Sample(FloatArraySample(strands_sample.times), kVertexScope));
+
+ schema.set(sample);
+ }
+ else {
+ strands_children_create_sample(strands, strands_sample, false);
+ }
+
+ m_prop_root_rot.set(QuatfArraySample(strands_sample.root_rotations));
+ m_prop_root_positions.set(V3fArraySample(strands_sample.root_positions));
+ m_param_cutoff.set(OFloatGeomParam::Sample(FloatArraySample(strands_sample.cutoff), kUniformScope));
+}
+
+
+AbcStrandsWriter::AbcStrandsWriter(const std::string &name, DupliObjectData *dobdata) :
+ m_name(name),
+ m_dobdata(dobdata),
+ m_child_writer(name, "children", dobdata)
+{
+}
+
+Strands *AbcStrandsWriter::get_strands() const
+{
+ Strands *strands;
+ BKE_dupli_object_data_find_strands(m_dobdata, m_name.c_str(), &strands, NULL);
+ return strands;
+}
+
+void AbcStrandsWriter::init(WriterArchive *archive)
+{
+ AbcWriter::init(archive);
+ m_child_writer.init(archive);
+}
+
+void AbcStrandsWriter::init_abc(OObject parent)
+{
+ if (m_curves)
+ return;
+ m_curves = OCurves(parent, m_name, abc_archive()->frame_sampling_index());
+
+ OCurvesSchema &schema = m_curves.getSchema();
+ OCompoundProperty geom_props = schema.getArbGeomParams();
+
+ m_param_root_rot = OQuatfGeomParam(geom_props, "root_rotations", false, kUniformScope, 1, abc_archive()->frame_sampling());
+ m_param_root_orig_verts = OUInt32GeomParam(geom_props, "root_orig_verts", false, kUniformScope, 1, 0);
+ m_param_root_orig_weights = OFloatGeomParam(geom_props, "root_orig_weights", false, kUniformScope, 1, 0);
+ m_param_root_orig_poly = OInt32GeomParam(geom_props, "root_orig_poly", false, kUniformScope, 1, 0);
+ m_param_root_orig_loops = OUInt32GeomParam(geom_props, "root_orig_loops", false, kUniformScope, 1, 0);
+
+ m_param_times = OFloatGeomParam(geom_props, "times", false, kVertexScope, 1, abc_archive()->frame_sampling());
+ m_param_weights = OFloatGeomParam(geom_props, "weights", false, kVertexScope, 1, abc_archive()->frame_sampling());
+
+ m_param_motion_state = OCompoundProperty(geom_props, "motion_state", abc_archive()->frame_sampling());
+ m_param_motion_co = OP3fGeomParam(m_param_motion_state, "position", false, kVertexScope, 1, abc_archive()->frame_sampling());
+ m_param_motion_vel = OV3fGeomParam(m_param_motion_state, "velocity", false, kVertexScope, 1, abc_archive()->frame_sampling());
+
+ m_child_writer.init_abc(m_curves);
+}
+
+static void strands_create_sample(Strands *strands, StrandsSample &sample, bool do_numverts)
+{
+ const bool do_state = strands->state;
+
+ int totcurves = strands->totcurves;
+ int totverts = strands->totverts;
+
+ if (totverts == 0)
+ return;
+
+ if (do_numverts)
+ sample.numverts.reserve(totcurves);
+ sample.root_rotations.reserve(totcurves);
+ sample.root_orig_verts.reserve(totcurves * 3);
+ sample.root_orig_weights.reserve(totcurves * 3);
+ sample.root_orig_poly.reserve(totcurves);
+ sample.root_orig_loops.reserve(totcurves * 3);
+
+ sample.positions.reserve(totverts);
+ sample.times.reserve(totverts);
+ sample.weights.reserve(totverts);
+ if (do_state) {
+ sample.motion_co.reserve(totverts);
+ sample.motion_vel.reserve(totverts);
+ }
+
+ StrandIterator it_strand;
+ for (BKE_strand_iter_init(&it_strand, strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ int numverts = it_strand.curve->numverts;
+
+ if (do_numverts)
+ sample.numverts.push_back(numverts);
+ float qt[4];
+ mat3_to_quat(qt, it_strand.curve->root_matrix);
+ sample.root_rotations.push_back(Quatf(qt[0], qt[1], qt[2], qt[3]));
+
+ sample.root_orig_verts.push_back(it_strand.curve->msurf.orig_verts[0]);
+ sample.root_orig_verts.push_back(it_strand.curve->msurf.orig_verts[1]);
+ sample.root_orig_verts.push_back(it_strand.curve->msurf.orig_verts[2]);
+ sample.root_orig_weights.push_back(it_strand.curve->msurf.orig_weights[0]);
+ sample.root_orig_weights.push_back(it_strand.curve->msurf.orig_weights[1]);
+ sample.root_orig_weights.push_back(it_strand.curve->msurf.orig_weights[2]);
+ sample.root_orig_poly.push_back(it_strand.curve->msurf.orig_poly);
+ sample.root_orig_loops.push_back(it_strand.curve->msurf.orig_loops[0]);
+ sample.root_orig_loops.push_back(it_strand.curve->msurf.orig_loops[1]);
+ sample.root_orig_loops.push_back(it_strand.curve->msurf.orig_loops[2]);
+
+ StrandVertexIterator it_vert;
+ for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) {
+ const float *co = it_vert.vertex->co;
+ sample.positions.push_back(V3f(co[0], co[1], co[2]));
+ sample.times.push_back(it_vert.vertex->time);
+ sample.weights.push_back(it_vert.vertex->weight);
+
+ if (do_state) {
+ float *co = it_vert.state->co;
+ float *vel = it_vert.state->vel;
+ sample.motion_co.push_back(V3f(co[0], co[1], co[2]));
+ sample.motion_vel.push_back(V3f(vel[0], vel[1], vel[2]));
+ }
+ }
+ }
+}
+
+void AbcStrandsWriter::write_sample()
+{
+ if (!m_curves)
+ return;
+ Strands *strands = get_strands();
+ if (!strands)
+ return;
+
+ OCurvesSchema &schema = m_curves.getSchema();
+
+ StrandsSample strands_sample;
+ OCurvesSchema::Sample sample;
+ if (schema.getNumSamples() == 0) {
+ /* write curve sizes only first time, assuming they are constant! */
+ strands_create_sample(strands, strands_sample, true);
+ sample = OCurvesSchema::Sample(strands_sample.positions, strands_sample.numverts);
+ }
+ else {
+ strands_create_sample(strands, strands_sample, false);
+ sample = OCurvesSchema::Sample(strands_sample.positions);
+ }
+ schema.set(sample);
+
+ m_param_root_rot.set(OQuatfGeomParam::Sample(QuatfArraySample(strands_sample.root_rotations), kUniformScope));
+ m_param_root_orig_verts.set(OUInt32GeomParam::Sample(UInt32ArraySample(strands_sample.root_orig_verts), kUniformScope));
+ m_param_root_orig_weights.set(OFloatGeomParam::Sample(FloatArraySample(strands_sample.root_orig_weights), kUniformScope));
+ m_param_root_orig_poly.set(OInt32GeomParam::Sample(Int32ArraySample(strands_sample.root_orig_poly), kUniformScope));
+ m_param_root_orig_loops.set(OUInt32GeomParam::Sample(UInt32ArraySample(strands_sample.root_orig_loops), kUniformScope));
+
+ m_param_times.set(OFloatGeomParam::Sample(FloatArraySample(strands_sample.times), kVertexScope));
+ m_param_weights.set(OFloatGeomParam::Sample(FloatArraySample(strands_sample.weights), kVertexScope));
+
+ if (strands->state) {
+ m_param_motion_co.set(OP3fGeomParam::Sample(P3fArraySample(strands_sample.motion_co), kVertexScope));
+ m_param_motion_vel.set(OV3fGeomParam::Sample(V3fArraySample(strands_sample.motion_vel), kVertexScope));
+ }
+
+ m_child_writer.write_sample();
+}
+
+#if 0
+#define PRINT_M3_FORMAT "((%.3f, %.3f, %.3f), (%.3f, %.3f, %.3f), (%.3f, %.3f, %.3f))"
+#define PRINT_M3_ARGS(m) (double)m[0][0], (double)m[0][1], (double)m[0][2], (double)m[1][0], (double)m[1][1], (double)m[1][2], (double)m[2][0], (double)m[2][1], (double)m[2][2]
+#define PRINT_M4_FORMAT "((%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f), (%.3f, %.3f, %.3f, %.3f))"
+#define PRINT_M4_ARGS(m) (double)m[0][0], (double)m[0][1], (double)m[0][2], (double)m[0][3], (double)m[1][0], (double)m[1][1], (double)m[1][2], (double)m[1][3], \
+ (double)m[2][0], (double)m[2][1], (double)m[2][2], (double)m[2][3], (double)m[3][0], (double)m[3][1], (double)m[3][2], (double)m[3][3]
+#endif
+
+AbcStrandsChildrenReader::AbcStrandsChildrenReader(StrandsChildren *strands) :
+ m_strands(strands)
+{
+}
+
+AbcStrandsChildrenReader::~AbcStrandsChildrenReader()
+{
+ discard_result();
+}
+
+void AbcStrandsChildrenReader::init_abc(IObject object)
+{
+ if (m_curves)
+ return;
+ m_curves = ICurves(object, kWrapExisting);
+
+ ICurvesSchema &schema = m_curves.getSchema();
+ ICompoundProperty geom_props = schema.getArbGeomParams();
+ ICompoundProperty user_props = schema.getUserProperties();
+
+ m_prop_root_rot = IQuatfArrayProperty(user_props, "root_rotations");
+ m_prop_root_positions = IV3fArrayProperty(user_props, "root_positions");
+ if (geom_props.getPropertyHeader("cutoff"))
+ m_param_cutoff = IFloatGeomParam(geom_props, "cutoff");
+ m_param_times = IFloatGeomParam(geom_props, "times");
+ m_prop_parents = IInt32ArrayProperty(user_props, "parents", 0);
+ m_prop_parent_weights = IFloatArrayProperty(user_props, "parent_weights", 0);
+ m_prop_curve_uvs = IV2fArrayProperty(user_props, "curve_uvs", 0);
+ m_prop_curve_vcols = IC3fArrayProperty(user_props, "curve_vcols", 0);
+}
+
+PTCReadSampleResult AbcStrandsChildrenReader::read_sample_abc(chrono_t time)
+{
+ ISampleSelector ss = get_frame_sample_selector(time);
+
+ if (!m_curves.valid()) {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ ICurvesSchema &schema = m_curves.getSchema();
+ if (schema.getNumSamples() == 0) {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ ICurvesSchema::Sample sample;
+ schema.get(sample, ss);
+
+ P3fArraySamplePtr sample_co = sample.getPositions();
+ Int32ArraySamplePtr sample_numvert = sample.getCurvesNumVertices();
+ QuatfArraySamplePtr sample_root_rotations = abc_interpolate_sample_linear(m_prop_root_rot, time);
+ V3fArraySamplePtr sample_root_positions = abc_interpolate_sample_linear(m_prop_root_positions, time);
+ IFloatGeomParam::Sample sample_cutoff;
+ if (m_param_cutoff)
+ sample_cutoff = m_param_cutoff.getExpandedValue(ss);
+ IFloatGeomParam::Sample sample_time = m_param_times.getExpandedValue(ss);
+ Int32ArraySamplePtr sample_parents = m_prop_parents.getValue(ss);
+ FloatArraySamplePtr sample_parent_weights = m_prop_parent_weights.getValue(ss);
+ V2fArraySamplePtr sample_curve_uvs = m_prop_curve_uvs.getValue(ss);
+ C3fArraySamplePtr sample_curve_vcols = m_prop_curve_vcols.getValue(ss);
+
+ if (!sample_co || !sample_numvert) {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ int totcurves = sample_numvert->size();
+ int totverts = sample_co->size();
+
+ if (!totcurves) {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ if (sample_root_rotations->size() != totcurves ||
+ sample_root_positions->size() != totcurves ||
+ sample_parents->size() != 4 * totcurves ||
+ sample_parent_weights->size() != 4 * totcurves)
+ return PTC_READ_SAMPLE_INVALID;
+
+ if (m_strands && (m_strands->totcurves != totcurves || m_strands->totverts != totverts))
+ m_strands = NULL;
+ if (!m_strands)
+ m_strands = BKE_strands_children_new(totcurves, totverts);
+
+ const int32_t *numvert = sample_numvert->get();
+ const Quatf *root_rot = sample_root_rotations->get();
+ const V3f *root_positions = sample_root_positions->get();
+ const float32_t *cutoff = sample_cutoff ? sample_cutoff.getVals()->get() : NULL;
+ const int32_t *parents = sample_parents->get();
+ const float32_t *parent_weights = sample_parent_weights->get();
+ for (int i = 0; i < sample_numvert->size(); ++i) {
+ StrandsChildCurve *scurve = &m_strands->curves[i];
+ scurve->numverts = *numvert;
+ scurve->cutoff = -1.0f;
+
+ float qt[4] = {root_rot->r, root_rot->v.x, root_rot->v.y, root_rot->v.z};
+ quat_to_mat4(scurve->root_matrix, qt);
+ copy_v3_v3(scurve->root_matrix[3], root_positions->getValue());
+
+ scurve->cutoff = cutoff ? *cutoff : -1.0f;
+
+ scurve->parents[0] = parents[0];
+ scurve->parents[1] = parents[1];
+ scurve->parents[2] = parents[2];
+ scurve->parents[3] = parents[3];
+ scurve->parent_weights[0] = parent_weights[0];
+ scurve->parent_weights[1] = parent_weights[1];
+ scurve->parent_weights[2] = parent_weights[2];
+ scurve->parent_weights[3] = parent_weights[3];
+
+ ++numvert;
+ ++root_rot;
+ ++root_positions;
+ parents += 4;
+ parent_weights += 4;
+ if (cutoff) ++cutoff;
+ }
+
+ if (sample_curve_uvs->size() > 0 && sample_curve_uvs->size() % totcurves == 0) {
+ int num_layers = sample_curve_uvs->size() / totcurves;
+
+ const V2f *uvs = sample_curve_uvs->get();
+
+ BKE_strands_children_add_uvs(m_strands, num_layers);
+
+ StrandsChildCurveUV *scurve_uv = m_strands->curve_uvs;
+ for (int j = 0; j < num_layers; ++j) {
+ for (int i = 0; i < totcurves; ++i) {
+ copy_v2_v2(scurve_uv->uv, uvs->getValue());
+
+ ++uvs;
+ ++scurve_uv;
+ }
+ }
+ }
+
+ if (sample_curve_vcols->size() > 0 && sample_curve_vcols->size() % totcurves == 0) {
+ int num_layers = sample_curve_vcols->size() / totcurves;
+
+ const C3f *vcols = sample_curve_vcols->get();
+
+ BKE_strands_children_add_vcols(m_strands, num_layers);
+
+ StrandsChildCurveVCol *scurve_vcol = m_strands->curve_vcols;
+ for (int j = 0; j < num_layers; ++j) {
+ for (int i = 0; i < totcurves; ++i) {
+ copy_v3_v3(scurve_vcol->vcol, vcols->getValue());
+
+ ++vcols;
+ ++scurve_vcol;
+ }
+ }
+ }
+
+ const V3f *co = sample_co->get();
+ const float32_t *curve_time = sample_time.getVals()->get();
+ for (int i = 0; i < sample_co->size(); ++i) {
+ StrandsChildVertex *svert = &m_strands->verts[i];
+ copy_v3_v3(svert->co, co->getValue());
+ copy_v3_v3(svert->base, svert->co);
+ svert->time = *curve_time;
+
+ ++co;
+ ++curve_time;
+ }
+
+ BKE_strands_children_ensure_normals(m_strands);
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+StrandsChildren *AbcStrandsChildrenReader::acquire_result()
+{
+ StrandsChildren *strands = m_strands;
+ m_strands = NULL;
+ return strands;
+}
+
+void AbcStrandsChildrenReader::discard_result()
+{
+ BKE_strands_children_free(m_strands);
+ m_strands = NULL;
+}
+
+
+AbcStrandsReader::AbcStrandsReader(Strands *strands, StrandsChildren *children, bool read_motion, bool read_children) :
+ m_read_motion(read_motion),
+ m_read_children(read_children),
+ m_strands(strands),
+ m_child_reader(children)
+{
+}
+
+AbcStrandsReader::~AbcStrandsReader()
+{
+ discard_result();
+}
+
+void AbcStrandsReader::init(ReaderArchive *archive)
+{
+ AbcReader::init(archive);
+ m_child_reader.init(archive);
+}
+
+void AbcStrandsReader::init_abc(IObject object)
+{
+ if (m_curves)
+ return;
+ m_curves = ICurves(object, kWrapExisting);
+
+ ICurvesSchema &schema = m_curves.getSchema();
+ ICompoundProperty geom_props = schema.getArbGeomParams();
+
+ m_param_root_rot = IQuatfGeomParam(geom_props, "root_rotations");
+ m_param_root_orig_verts = IUInt32GeomParam(geom_props, "root_orig_verts");
+ m_param_root_orig_weights = IFloatGeomParam(geom_props, "root_orig_weights");
+ m_param_root_orig_poly = IInt32GeomParam(geom_props, "root_orig_poly");
+ m_param_root_orig_loops = IUInt32GeomParam(geom_props, "root_orig_loops");
+
+ m_param_times = IFloatGeomParam(geom_props, "times");
+ m_param_weights = IFloatGeomParam(geom_props, "weights");
+
+ if (m_read_motion && geom_props.getPropertyHeader("motion_state")) {
+ m_param_motion_state = ICompoundProperty(geom_props, "motion_state");
+ m_param_motion_co = IP3fGeomParam(m_param_motion_state, "position");
+ m_param_motion_vel = IV3fGeomParam(m_param_motion_state, "velocity");
+ }
+
+ if (m_read_children && m_curves.getChildHeader("children")) {
+ IObject child = m_curves.getChild("children");
+ m_child_reader.init_abc(child);
+ }
+}
+
+PTCReadSampleResult AbcStrandsReader::read_sample_abc(chrono_t time)
+{
+ ISampleSelector ss = get_frame_sample_selector(time);
+
+ if (!m_curves.valid())
+ return PTC_READ_SAMPLE_INVALID;
+
+ ICurvesSchema &schema = m_curves.getSchema();
+ if (schema.getNumSamples() == 0)
+ return PTC_READ_SAMPLE_INVALID;
+
+ ICurvesSchema::Sample sample, sample_base;
+ schema.get(sample, ss);
+ schema.get(sample_base, ISampleSelector((index_t)0));
+
+ P3fArraySamplePtr sample_co = sample.getPositions();
+ P3fArraySamplePtr sample_co_base = sample_base.getPositions();
+ Int32ArraySamplePtr sample_numvert = sample.getCurvesNumVertices();
+ IQuatfGeomParam::Sample sample_root_rotations = m_param_root_rot.getExpandedValue(ss);
+ IUInt32GeomParam::Sample sample_root_orig_verts = m_param_root_orig_verts.getExpandedValue(ss);
+ IFloatGeomParam::Sample sample_root_orig_weights = m_param_root_orig_weights.getExpandedValue(ss);
+ IInt32GeomParam::Sample sample_root_orig_poly = m_param_root_orig_poly.getExpandedValue(ss);
+ IUInt32GeomParam::Sample sample_root_orig_loops = m_param_root_orig_loops.getExpandedValue(ss);
+ IQuatfGeomParam::Sample sample_root_rotations_base = m_param_root_rot.getExpandedValue(ISampleSelector((index_t)0));
+ IFloatGeomParam::Sample sample_time = m_param_times.getExpandedValue(ss);
+ IFloatGeomParam::Sample sample_weight = m_param_weights.getExpandedValue(ss);
+
+ if (!sample_co || !sample_numvert || !sample_co_base || sample_co_base->size() != sample_co->size())
+ return PTC_READ_SAMPLE_INVALID;
+
+ if (m_strands && (m_strands->totcurves != sample_numvert->size() || m_strands->totverts != sample_co->size()))
+ m_strands = NULL;
+ if (!m_strands)
+ m_strands = BKE_strands_new(sample_numvert->size(), sample_co->size());
+
+ const int32_t *numvert = sample_numvert->get();
+ const Quatf *root_rot = sample_root_rotations.getVals()->get();
+ const uint32_t *orig_verts = sample_root_orig_verts.getVals()->get();
+ const float32_t *orig_weights = sample_root_orig_weights.getVals()->get();
+ const int32_t *orig_poly = sample_root_orig_poly.getVals()->get();
+ const uint32_t *orig_loops = sample_root_orig_loops.getVals()->get();
+ for (int i = 0; i < sample_numvert->size(); ++i) {
+ StrandsCurve *scurve = &m_strands->curves[i];
+ scurve->numverts = *numvert;
+ float qt[4] = {root_rot->r, root_rot->v.x, root_rot->v.y, root_rot->v.z};
+ quat_to_mat3(scurve->root_matrix, qt);
+ scurve->msurf.orig_verts[0] = orig_verts[0];
+ scurve->msurf.orig_verts[1] = orig_verts[1];
+ scurve->msurf.orig_verts[2] = orig_verts[2];
+ scurve->msurf.orig_weights[0] = orig_weights[0];
+ scurve->msurf.orig_weights[1] = orig_weights[1];
+ scurve->msurf.orig_weights[2] = orig_weights[2];
+ scurve->msurf.orig_poly = *orig_poly;
+ scurve->msurf.orig_loops[0] = orig_loops[0];
+ scurve->msurf.orig_loops[1] = orig_loops[1];
+ scurve->msurf.orig_loops[2] = orig_loops[2];
+
+ ++numvert;
+ ++root_rot;
+ orig_verts += 3;
+ orig_weights += 3;
+ orig_poly += 1;
+ orig_loops += 3;
+ }
+
+ const V3f *co = sample_co->get();
+ const float32_t *curve_time = sample_time.getVals()->get();
+ const float32_t *weight = sample_weight.getVals()->get();
+ for (int i = 0; i < sample_co->size(); ++i) {
+ StrandsVertex *svert = &m_strands->verts[i];
+ copy_v3_v3(svert->co, co->getValue());
+ svert->time = *curve_time;
+ svert->weight = *weight;
+
+ ++co;
+ ++curve_time;
+ ++weight;
+ }
+
+ /* Correction for base coordinates: these are in object space of frame 1,
+ * but we want the relative shape. Offset them to the current root location.
+ */
+ const Quatf *root_rot_base = sample_root_rotations_base.getVals()->get();
+ const V3f *co_base = sample_co_base->get();
+ StrandIterator it_strand;
+ for (BKE_strand_iter_init(&it_strand, m_strands); BKE_strand_iter_valid(&it_strand); BKE_strand_iter_next(&it_strand)) {
+ if (it_strand.curve->numverts <= 0)
+ continue;
+
+ float hairmat_base[4][4];
+ float qt[4] = {root_rot_base->r, root_rot_base->v.x, root_rot_base->v.y, root_rot_base->v.z};
+ quat_to_mat4(hairmat_base, qt);
+ copy_v3_v3(hairmat_base[3], co_base[0].getValue());
+
+ float hairmat[4][4];
+ copy_m4_m3(hairmat, it_strand.curve->root_matrix);
+ copy_v3_v3(hairmat[3], it_strand.verts[0].co);
+
+ float mat[4][4];
+ invert_m4_m4(mat, hairmat_base);
+ mul_m4_m4m4(mat, hairmat, mat);
+
+ StrandVertexIterator it_vert;
+ for (BKE_strand_vertex_iter_init(&it_vert, &it_strand); BKE_strand_vertex_iter_valid(&it_vert); BKE_strand_vertex_iter_next(&it_vert)) {
+// mul_v3_m4v3(it_vert.vertex->base, mat, co_base->getValue());
+ copy_v3_v3(it_vert.vertex->base, it_vert.vertex->co);
+
+ ++co_base;
+ }
+
+ ++root_rot_base;
+ }
+
+ if (m_read_motion &&
+ m_param_motion_co && m_param_motion_co.getNumSamples() > 0 &&
+ m_param_motion_vel && m_param_motion_vel.getNumSamples() > 0)
+ {
+ IP3fGeomParam::Sample sample_motion_co = m_param_motion_co.getExpandedValue(ss);
+ IV3fGeomParam::Sample sample_motion_vel = m_param_motion_vel.getExpandedValue(ss);
+
+ const V3f *co = sample_motion_co.getVals()->get();
+ const V3f *vel = sample_motion_vel.getVals()->get();
+ if (co && vel) {
+ BKE_strands_add_motion_state(m_strands);
+
+ for (int i = 0; i < m_strands->totverts; ++i) {
+ StrandsMotionState *ms = &m_strands->state[i];
+ copy_v3_v3(ms->co, co->getValue());
+ copy_v3_v3(ms->vel, vel->getValue());
+
+ ++co;
+ ++vel;
+ }
+ }
+ }
+
+ BKE_strands_ensure_normals(m_strands);
+
+ if (m_read_children) {
+ m_child_reader.read_sample_abc(time);
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+Strands *AbcStrandsReader::acquire_result()
+{
+ Strands *strands = m_strands;
+ m_strands = NULL;
+ return strands;
+}
+
+void AbcStrandsReader::discard_result()
+{
+ BKE_strands_free(m_strands);
+ m_strands = NULL;
+}
+
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_particles.h b/source/blender/pointcache/alembic/abc_particles.h
new file mode 100644
index 00000000000..26d1bce78bd
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_particles.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_ABC_PARTICLES_H
+#define PTC_ABC_PARTICLES_H
+
+#include <Alembic/AbcGeom/IPoints.h>
+#include <Alembic/AbcGeom/OPoints.h>
+#include <Alembic/AbcGeom/ICurves.h>
+#include <Alembic/AbcGeom/OCurves.h>
+
+#include "ptc_types.h"
+
+#include "PTC_api.h"
+
+#include "abc_reader.h"
+#include "abc_schema.h"
+#include "abc_writer.h"
+#include "abc_cloth.h"
+
+struct ListBase;
+struct Object;
+struct ParticleSystem;
+struct ParticleCacheKey;
+struct Strands;
+struct StrandsChildren;
+
+namespace PTC {
+
+class AbcDerivedMeshWriter;
+class AbcDerivedMeshReader;
+
+
+class AbcHairChildrenWriter : public ParticlesWriter, public AbcWriter {
+public:
+ AbcHairChildrenWriter(const std::string &name, Object *ob, ParticleSystem *psys);
+ ~AbcHairChildrenWriter();
+
+ void init_abc(Abc::OObject parent);
+
+ void write_sample();
+
+private:
+ ParticleSystemModifierData *m_psmd;
+
+ AbcGeom::OCurves m_curves;
+ AbcGeom::OQuatfArrayProperty m_prop_root_rot;
+ AbcGeom::OV3fArrayProperty m_prop_root_positions;
+ AbcGeom::OFloatGeomParam m_param_cutoff;
+ AbcGeom::OFloatGeomParam m_param_times;
+ AbcGeom::OInt32ArrayProperty m_prop_parents;
+ AbcGeom::OFloatArrayProperty m_prop_parent_weights;
+ AbcGeom::OV2fArrayProperty m_prop_curve_uvs;
+ AbcGeom::OC3fArrayProperty m_prop_curve_vcols;
+};
+
+
+class AbcHairWriter : public ParticlesWriter, public AbcWriter {
+public:
+ AbcHairWriter(const std::string &name, Object *ob, ParticleSystem *psys);
+ ~AbcHairWriter();
+
+ void init(WriterArchive *archive);
+ void init_abc(Abc::OObject parent);
+
+ void write_sample();
+
+private:
+ ParticleSystemModifierData *m_psmd;
+
+ AbcGeom::OCurves m_curves;
+ AbcGeom::OQuatfGeomParam m_param_root_rot;
+ AbcGeom::OUInt32GeomParam m_param_root_orig_verts;
+ AbcGeom::OFloatGeomParam m_param_root_orig_weights;
+ AbcGeom::OInt32GeomParam m_param_root_orig_poly;
+ AbcGeom::OUInt32GeomParam m_param_root_orig_loops;
+ AbcGeom::OFloatGeomParam m_param_times;
+ AbcGeom::OFloatGeomParam m_param_weights;
+
+ AbcHairChildrenWriter m_child_writer;
+};
+
+
+class AbcStrandsChildrenWriter : public AbcWriter {
+public:
+ AbcStrandsChildrenWriter(const std::string &name, const std::string &abc_name, DupliObjectData *dobdata);
+
+ StrandsChildren *get_strands() const;
+
+ void init_abc(Abc::OObject parent);
+
+ void write_sample();
+
+private:
+ std::string m_name;
+ std::string m_abc_name;
+ DupliObjectData *m_dobdata;
+
+ AbcGeom::OCurves m_curves;
+ AbcGeom::OQuatfArrayProperty m_prop_root_rot;
+ AbcGeom::OV3fArrayProperty m_prop_root_positions;
+ AbcGeom::OFloatGeomParam m_param_cutoff;
+ AbcGeom::OFloatGeomParam m_param_times;
+ AbcGeom::OInt32ArrayProperty m_prop_parents;
+ AbcGeom::OFloatArrayProperty m_prop_parent_weights;
+ AbcGeom::OV2fArrayProperty m_prop_curve_uvs;
+ AbcGeom::OC3fArrayProperty m_prop_curve_vcols;
+};
+
+
+class AbcStrandsWriter : public AbcWriter {
+public:
+ AbcStrandsWriter(const std::string &name, DupliObjectData *dobdata);
+
+ Strands *get_strands() const;
+
+ void init(WriterArchive *archive);
+ void init_abc(Abc::OObject parent);
+
+ void write_sample();
+
+private:
+ std::string m_name;
+ DupliObjectData *m_dobdata;
+
+ AbcGeom::OCurves m_curves;
+ AbcGeom::OQuatfGeomParam m_param_root_rot;
+ AbcGeom::OUInt32GeomParam m_param_root_orig_verts;
+ AbcGeom::OFloatGeomParam m_param_root_orig_weights;
+ AbcGeom::OInt32GeomParam m_param_root_orig_poly;
+ AbcGeom::OUInt32GeomParam m_param_root_orig_loops;
+ AbcGeom::OFloatGeomParam m_param_times;
+ AbcGeom::OFloatGeomParam m_param_weights;
+ AbcGeom::OCompoundProperty m_param_motion_state;
+ AbcGeom::OP3fGeomParam m_param_motion_co;
+ AbcGeom::OV3fGeomParam m_param_motion_vel;
+
+ AbcStrandsChildrenWriter m_child_writer;
+};
+
+
+class AbcStrandsChildrenReader : public AbcReader {
+public:
+ AbcStrandsChildrenReader(StrandsChildren *strands);
+ ~AbcStrandsChildrenReader();
+
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+ StrandsChildren *get_result() { return m_strands; }
+ StrandsChildren *acquire_result();
+ void discard_result();
+
+private:
+ StrandsChildren *m_strands;
+
+ AbcGeom::ICurves m_curves;
+ AbcGeom::IQuatfArrayProperty m_prop_root_rot;
+ AbcGeom::IV3fArrayProperty m_prop_root_positions;
+ AbcGeom::IFloatGeomParam m_param_cutoff;
+ AbcGeom::IFloatGeomParam m_param_times;
+ AbcGeom::IInt32ArrayProperty m_prop_parents;
+ AbcGeom::IFloatArrayProperty m_prop_parent_weights;
+ AbcGeom::IV2fArrayProperty m_prop_curve_uvs;
+ AbcGeom::IC3fArrayProperty m_prop_curve_vcols;
+};
+
+
+class AbcStrandsReader : public AbcReader {
+public:
+ AbcStrandsReader(Strands *strands, StrandsChildren *children, bool read_motion, bool read_children);
+ ~AbcStrandsReader();
+
+ void init(ReaderArchive *archive);
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+ Strands *acquire_result();
+ void discard_result();
+
+ AbcStrandsChildrenReader &child_reader() { return m_child_reader; }
+
+private:
+ bool m_read_motion, m_read_children;
+ Strands *m_strands;
+
+ AbcGeom::ICurves m_curves;
+ AbcGeom::IQuatfGeomParam m_param_root_rot;
+ AbcGeom::IUInt32GeomParam m_param_root_orig_verts;
+ AbcGeom::IFloatGeomParam m_param_root_orig_weights;
+ AbcGeom::IInt32GeomParam m_param_root_orig_poly;
+ AbcGeom::IUInt32GeomParam m_param_root_orig_loops;
+ AbcGeom::IFloatGeomParam m_param_times;
+ AbcGeom::IFloatGeomParam m_param_weights;
+ AbcGeom::ICompoundProperty m_param_motion_state;
+ AbcGeom::IP3fGeomParam m_param_motion_co;
+ AbcGeom::IV3fGeomParam m_param_motion_vel;
+
+ AbcStrandsChildrenReader m_child_reader;
+};
+
+
+} /* namespace PTC */
+
+#endif /* PTC_PARTICLES_H */
diff --git a/source/blender/pointcache/alembic/abc_reader.cpp b/source/blender/pointcache/alembic/abc_reader.cpp
new file mode 100644
index 00000000000..17f574af7ac
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_reader.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+//#include <Alembic/AbcCoreHDF5/ReadWrite.h>
+#include <Alembic/AbcCoreOgawa/ReadWrite.h>
+#include <Alembic/Abc/ArchiveInfo.h>
+#include <Alembic/Abc/IArchive.h>
+#include <Alembic/Abc/IObject.h>
+
+#include "alembic.h"
+#include "abc_reader.h"
+
+#include "util_error_handler.h"
+
+extern "C" {
+#include "DNA_ID.h"
+}
+
+namespace PTC {
+
+using namespace Abc;
+
+AbcReaderArchive *AbcReaderArchive::open(double fps, float start_frame, const std::string &filename, ErrorHandler *error_handler)
+{
+ IArchive abc_archive;
+ PTC_SAFE_CALL_BEGIN
+// abc_archive = IArchive(AbcCoreHDF5::ReadArchive(), filename, Abc::ErrorHandler::kThrowPolicy);
+ abc_archive = IArchive(AbcCoreOgawa::ReadArchive(), filename, Abc::ErrorHandler::kThrowPolicy);
+ PTC_SAFE_CALL_END_HANDLER(error_handler)
+
+ if (abc_archive)
+ return new AbcReaderArchive(fps, start_frame, error_handler, abc_archive);
+ else
+ return NULL;
+}
+
+AbcReaderArchive::AbcReaderArchive(double fps, float start_frame, ErrorHandler *error_handler, IArchive abc_archive) :
+ FrameMapper(fps, start_frame),
+ m_error_handler(error_handler),
+ m_use_render(false),
+ m_abc_archive(abc_archive)
+{
+ if (m_abc_archive.getTop().getChildHeader("root"))
+ m_abc_root = IObject(m_abc_archive.getTop(), "root");
+ else
+ m_abc_root = IObject();
+
+ if (m_abc_archive.getTop().getChildHeader("root_render"))
+ m_abc_root_render = IObject(m_abc_archive.getTop(), "root_render");
+ else
+ m_abc_root_render = IObject();
+}
+
+AbcReaderArchive::~AbcReaderArchive()
+{
+}
+
+PTCArchiveResolution AbcReaderArchive::get_resolutions()
+{
+ int res = 0;
+ if (m_abc_root)
+ res |= PTC_RESOLUTION_PREVIEW;
+ if (m_abc_root_render)
+ res |= PTC_RESOLUTION_RENDER;
+ return (PTCArchiveResolution)res;
+}
+
+Abc::IObject AbcReaderArchive::root()
+{
+ if (m_use_render)
+ return m_abc_root_render ? m_abc_root_render : m_abc_root;
+ else
+ return m_abc_root ? m_abc_root : m_abc_root_render;
+}
+
+IObject AbcReaderArchive::get_id_object(ID *id)
+{
+ if (!m_abc_archive)
+ return IObject();
+
+ IObject root = this->root();
+ return root.getChild(id->name);
+}
+
+bool AbcReaderArchive::has_id_object(ID *id)
+{
+ if (!m_abc_archive)
+ return false;
+
+ IObject root = this->root();
+ return root.getChild(id->name).valid();
+}
+
+bool AbcReaderArchive::get_frame_range(int &start_frame, int &end_frame)
+{
+ if (m_abc_archive) {
+ double start_time, end_time;
+ GetArchiveStartAndEndTime(m_abc_archive, start_time, end_time);
+ start_frame = (int)time_to_frame(start_time);
+ end_frame = (int)time_to_frame(end_time);
+ return true;
+ }
+ else {
+ start_frame = end_frame = 1;
+ return false;
+ }
+}
+
+void AbcReaderArchive::get_info_stream(void (*stream)(void *, const char *), void *userdata)
+{
+ if (m_abc_archive)
+ abc_archive_info_stream(m_abc_archive, stream, userdata);
+}
+
+void AbcReaderArchive::get_info(CacheArchiveInfo *info, IDProperty *metadata)
+{
+ if (m_abc_archive)
+ abc_archive_info_nodes(m_abc_archive, info, metadata, false, false);
+}
+
+void AbcReaderArchive::get_info_nodes(CacheArchiveInfo *info, bool calc_bytes_size)
+{
+ if (m_abc_archive)
+ abc_archive_info_nodes(m_abc_archive, info, NULL, true, calc_bytes_size);
+}
+
+ISampleSelector AbcReaderArchive::get_frame_sample_selector(float frame)
+{
+ return ISampleSelector(frame_to_time(frame), ISampleSelector::kFloorIndex);
+}
+
+ISampleSelector AbcReaderArchive::get_frame_sample_selector(chrono_t time)
+{
+ return ISampleSelector(time, ISampleSelector::kFloorIndex);
+}
+
+
+bool AbcReader::get_frame_range(int &start_frame, int &end_frame)
+{
+ return m_abc_archive->get_frame_range(start_frame, end_frame);
+}
+
+PTCReadSampleResult AbcReader::test_sample(float frame)
+{
+ if (m_abc_archive) {
+ int start_frame, end_frame;
+ m_abc_archive->get_frame_range(start_frame, end_frame);
+
+ if (frame < start_frame)
+ return PTC_READ_SAMPLE_EARLY;
+ else if (frame > end_frame)
+ return PTC_READ_SAMPLE_LATE;
+ else {
+ /* TODO could also be EXACT, but INTERPOLATED is more general
+ * do we need to support this?
+ * checking individual time samplings is also possible, but more involved.
+ */
+ return PTC_READ_SAMPLE_INTERPOLATED;
+ }
+ }
+ else {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+}
+
+PTCReadSampleResult AbcReader::read_sample(float frame)
+{
+
+ try {
+ return read_sample_abc(m_abc_archive->frame_to_time(frame));
+ }
+ catch (Alembic::Util::Exception e) {
+ handle_alembic_exception(ErrorHandler::get_default_handler(), PTC_ERROR_CRITICAL, e);
+ return PTC_READ_SAMPLE_INVALID;
+ }
+ return PTC_READ_SAMPLE_INVALID;
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_reader.h b/source/blender/pointcache/alembic/abc_reader.h
new file mode 100644
index 00000000000..86cfdd91d51
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_reader.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_ABC_READER_H
+#define PTC_ABC_READER_H
+
+#include <string>
+
+#include <Alembic/Abc/IArchive.h>
+#include <Alembic/Abc/IObject.h>
+#include <Alembic/Abc/ISampleSelector.h>
+
+#include "reader.h"
+
+#include "abc_frame_mapper.h"
+
+#include "util_error_handler.h"
+#include "util_types.h"
+
+namespace PTC {
+
+using namespace Alembic;
+
+using Abc::chrono_t;
+
+class AbcReaderArchive : public ReaderArchive, public FrameMapper {
+public:
+ virtual ~AbcReaderArchive();
+
+ static AbcReaderArchive *open(double fps, float start_frame, const std::string &filename, ErrorHandler *error_handler);
+
+ PTCArchiveResolution get_resolutions();
+ bool use_render() const { return m_use_render; }
+ void use_render(bool enable) { m_use_render = enable; }
+
+ Abc::IArchive abc_archive() const { return m_abc_archive; }
+ Abc::IObject root();
+
+ Abc::IObject get_id_object(ID *id);
+ bool has_id_object(ID *id);
+
+ bool get_frame_range(int &start_frame, int &end_frame);
+ Abc::ISampleSelector get_frame_sample_selector(float frame);
+ Abc::ISampleSelector get_frame_sample_selector(chrono_t time);
+
+ void get_info_stream(void (*stream)(void *, const char *), void *userdata);
+ void get_info(CacheArchiveInfo *info, IDProperty *metadata);
+ void get_info_nodes(CacheArchiveInfo *info, bool calc_bytes_size);
+
+protected:
+ AbcReaderArchive(double fps, float start_frame, ErrorHandler *error_handler, Abc::IArchive abc_archive);
+
+protected:
+ ErrorHandler *m_error_handler;
+ bool m_use_render;
+
+ Abc::IArchive m_abc_archive;
+ Abc::IObject m_abc_root;
+ Abc::IObject m_abc_root_render;
+};
+
+class AbcReader : public Reader {
+public:
+ AbcReader() :
+ m_abc_archive(0)
+ {}
+
+ void init(ReaderArchive *archive)
+ {
+ BLI_assert(dynamic_cast<AbcReaderArchive*>(archive));
+ m_abc_archive = static_cast<AbcReaderArchive*>(archive);
+ }
+
+ virtual void init_abc(Abc::IObject /*object*/) {}
+
+ AbcReaderArchive *abc_archive() const { return m_abc_archive; }
+
+ bool get_frame_range(int &start_frame, int &end_frame);
+
+ Abc::ISampleSelector get_frame_sample_selector(float frame) { return m_abc_archive->get_frame_sample_selector(frame); }
+ Abc::ISampleSelector get_frame_sample_selector(chrono_t time) { return m_abc_archive->get_frame_sample_selector(time); }
+
+ PTCReadSampleResult test_sample(float frame);
+ PTCReadSampleResult read_sample(float frame);
+ virtual PTCReadSampleResult read_sample_abc(chrono_t time) = 0;
+
+private:
+ AbcReaderArchive *m_abc_archive;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_READER_H */
diff --git a/source/blender/pointcache/alembic/abc_schema.h b/source/blender/pointcache/alembic/abc_schema.h
new file mode 100644
index 00000000000..d8d70ddac05
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_schema.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_ABC_SCHEMA_H
+#define PTC_ABC_SCHEMA_H
+
+#include <Alembic/AbcGeom/SchemaInfoDeclarations.h>
+#include <Alembic/AbcGeom/IGeomBase.h>
+#include <Alembic/AbcGeom/OGeomBase.h>
+
+namespace PTC {
+
+#if 0
+#define PTC_SCHEMA_INFO ALEMBIC_ABCGEOM_DECLARE_SCHEMA_INFO
+
+using namespace Alembic::AbcGeom;
+#endif
+
+
+#if 0
+/* XXX We define an extended schema class to implement the wrapper constructor.
+ * This was removed in Alembic 1.1 for some reason ...
+ */
+template <class SCHEMA>
+class OSchemaObject : public Abc::OSchemaObject<SCHEMA>
+{
+public:
+ //! The default constructor creates an empty OSchemaObject function set.
+ //! ...
+ OSchemaObject() : Abc::OSchemaObject<SCHEMA>() {}
+
+ //! The primary constructor creates an OSchemaObject as a child of the
+ //! first argument, which is any Abc or AbcCoreAbstract (or other)
+ //! object which can be intrusively cast to an ObjectWriterPtr.
+ template <class OBJECT_PTR>
+ OSchemaObject(OBJECT_PTR iParentObject,
+ const std::string &iName,
+
+ const Argument &iArg0 = Argument(),
+ const Argument &iArg1 = Argument(),
+ const Argument &iArg2 = Argument())
+ : Abc::OSchemaObject<SCHEMA>(iParentObject, iName, iArg0, iArg1, iArg2)
+ {}
+
+ //! Wrap an existing schema object.
+ //! ...
+ template <class OBJECT_PTR>
+ OSchemaObject(OBJECT_PTR iThisObject,
+ WrapExistingFlag iFlag,
+ const Argument &iArg0 = Argument(),
+ const Argument &iArg1 = Argument(),
+ const Argument &iArg2 = Argument() );
+};
+
+//-*****************************************************************************
+template<class SCHEMA>
+template<class OBJECT_PTR>
+inline OSchemaObject<SCHEMA>::OSchemaObject(
+ OBJECT_PTR iObject,
+ WrapExistingFlag iFlag,
+ const Argument &iArg0,
+ const Argument &iArg1,
+ const Argument &iArg2 )
+ : OObject(iObject,
+ iFlag,
+ GetErrorHandlerPolicy(iObject,
+ iArg0, iArg1, iArg2))
+{
+ ALEMBIC_ABC_SAFE_CALL_BEGIN("OSchemaObject::OSchemaObject( wrap )");
+
+ const AbcA::ObjectHeader &oheader = this->getHeader();
+
+ Abc::OSchemaObject<SCHEMA>::m_schema = SCHEMA(
+ this->getProperties().getProperty(SCHEMA::getDefaultSchemaName()).getPtr()->asCompoundPtr(),
+ iFlag,
+ this->getErrorHandlerPolicy(),
+ GetSchemaInterpMatching(iArg0, iArg1, iArg2));
+
+ /* XXX TODO gives compiler error atm */
+// ABCA_ASSERT(matches(oheader, GetSchemaInterpMatching(iArg0, iArg1, iArg2)),
+// "Incorrect match of schema: "
+// << oheader.getMetaData().get( "schemaObjTitle" )
+// << " to expected: "
+// << getSchemaObjTitle());
+
+ ALEMBIC_ABC_SAFE_CALL_END_RESET();
+}
+#endif
+
+} /* namespace PTC */
+
+#endif /* PTC_SCHEMA_H */
diff --git a/source/blender/pointcache/alembic/abc_simdebug.cpp b/source/blender/pointcache/alembic/abc_simdebug.cpp
new file mode 100644
index 00000000000..191b2856dc9
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_simdebug.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2014, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "abc_simdebug.h"
+
+extern "C" {
+#include "BLI_ghash.h"
+#include "BLI_math.h"
+}
+
+#include "PTC_api.h"
+
+namespace PTC {
+
+using namespace Abc;
+
+struct SimDebugSample {
+ std::vector<uint32_t> category_hash;
+ std::vector<uint32_t> hash;
+
+ std::vector<int32_t> type;
+ std::vector<C3f> color;
+ std::vector<V3f> v1;
+ std::vector<V3f> v2;
+};
+
+AbcSimDebugWriter::AbcSimDebugWriter(const std::string &name, SimDebugData *data) :
+ m_name(name),
+ m_data(data)
+{
+}
+
+AbcSimDebugWriter::~AbcSimDebugWriter()
+{
+}
+
+void AbcSimDebugWriter::init_abc(OObject parent)
+{
+ if (m_object)
+ return;
+
+ m_object = OObject(parent, m_name, abc_archive()->frame_sampling_index());
+ OCompoundProperty props = m_object.getProperties();
+
+ m_prop_category_hash = OUInt32ArrayProperty(props, "category_hash", abc_archive()->frame_sampling_index());
+ m_prop_hash = OUInt32ArrayProperty(props, "hash", abc_archive()->frame_sampling_index());
+ m_prop_type = OInt32ArrayProperty(props, "type", abc_archive()->frame_sampling_index());
+ m_prop_color = OC3fArrayProperty(props, "color", abc_archive()->frame_sampling_index());
+ m_prop_v1 = OV3fArrayProperty(props, "v1", abc_archive()->frame_sampling_index());
+ m_prop_v2 = OV3fArrayProperty(props, "v2", abc_archive()->frame_sampling_index());
+}
+
+static void create_sample(SimDebugData *data, SimDebugSample &sample)
+{
+ int numelem = BLI_ghash_size(data->gh);
+ GHashIterator iter;
+
+ sample.category_hash.reserve(numelem);
+ sample.hash.reserve(numelem);
+ sample.type.reserve(numelem);
+ sample.color.reserve(numelem);
+ sample.v1.reserve(numelem);
+ sample.v2.reserve(numelem);
+
+ for (BLI_ghashIterator_init(&iter, data->gh); !BLI_ghashIterator_done(&iter); BLI_ghashIterator_step(&iter)) {
+ SimDebugElement *elem = (SimDebugElement *)BLI_ghashIterator_getValue(&iter);
+
+ sample.category_hash.push_back(elem->category_hash);
+ sample.hash.push_back(elem->hash);
+ sample.type.push_back(elem->type);
+ sample.color.push_back(C3f(elem->color[0], elem->color[1], elem->color[2]));
+ sample.v1.push_back(V3f(elem->v1[0], elem->v1[1], elem->v1[2]));
+ sample.v2.push_back(V3f(elem->v2[0], elem->v2[1], elem->v2[2]));
+ }
+}
+
+void AbcSimDebugWriter::write_sample()
+{
+ if (!m_object)
+ return;
+
+ SimDebugSample sample;
+
+ create_sample(m_data, sample);
+
+ m_prop_category_hash.set(UInt32ArraySample(sample.category_hash));
+ m_prop_hash.set(UInt32ArraySample(sample.hash));
+ m_prop_type.set(Int32ArraySample(sample.type));
+ m_prop_color.set(C3fArraySample(sample.color));
+ m_prop_v1.set(V3fArraySample(sample.v1));
+ m_prop_v2.set(V3fArraySample(sample.v2));
+}
+
+/* ========================================================================= */
+
+AbcSimDebugReader::AbcSimDebugReader(SimDebugData *data) :
+ m_data(data)
+{
+}
+
+AbcSimDebugReader::~AbcSimDebugReader()
+{
+}
+
+void AbcSimDebugReader::init_abc(IObject object)
+{
+ if (m_object)
+ return;
+ m_object = IObject(object, kWrapExisting);
+ ICompoundProperty props = m_object.getProperties();
+
+ m_prop_category_hash = IUInt32ArrayProperty(props, "category_hash");
+ m_prop_hash = IUInt32ArrayProperty(props, "hash");
+ m_prop_type = IInt32ArrayProperty(props, "type");
+ m_prop_color = IC3fArrayProperty(props, "color");
+ m_prop_v1 = IV3fArrayProperty(props, "v1");
+ m_prop_v2 = IV3fArrayProperty(props, "v2");
+}
+
+static PTCReadSampleResult apply_sample(SimDebugData *data,
+ UInt32ArraySamplePtr sample_category_hash, UInt32ArraySamplePtr sample_hash,
+ Int32ArraySamplePtr sample_type, C3fArraySamplePtr sample_color,
+ V3fArraySamplePtr sample_v1, V3fArraySamplePtr sample_v2)
+{
+ int numelem = sample_hash->size();
+
+ if (sample_category_hash->size() != numelem ||
+ sample_type->size() != numelem ||
+ sample_color->size() != numelem ||
+ sample_v1->size() != numelem ||
+ sample_v2->size() != numelem)
+ {
+ return PTC_READ_SAMPLE_INVALID;
+ }
+
+ const uint32_t *data_category_hash = sample_category_hash->get();
+ const uint32_t *data_hash = sample_hash->get();
+ const int32_t *data_type = sample_type->get();
+ const C3f *data_color = sample_color->get();
+ const V3f *data_v1 = sample_v1->get();
+ const V3f *data_v2 = sample_v2->get();
+
+ for (int i = 0; i < numelem; ++i) {
+ BKE_sim_debug_data_add_element_ex(data, *data_type, data_v1->getValue(), data_v2->getValue(), data_color->x, data_color->y, data_color->z, *data_category_hash, *data_hash);
+
+ ++data_category_hash;
+ ++data_hash;
+ ++data_type;
+ ++data_color;
+ ++data_v1;
+ ++data_v2;
+ }
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+PTCReadSampleResult AbcSimDebugReader::read_sample_abc(chrono_t time)
+{
+ if (!m_object)
+ return PTC_READ_SAMPLE_INVALID;
+
+ ISampleSelector ss = get_frame_sample_selector(time);
+
+ apply_sample(m_data, m_prop_category_hash.getValue(ss), m_prop_hash.getValue(ss), m_prop_type.getValue(ss),
+ m_prop_color.getValue(ss), m_prop_v1.getValue(ss), m_prop_v2.getValue(ss));
+
+ return PTC_READ_SAMPLE_EXACT;
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_simdebug.h b/source/blender/pointcache/alembic/abc_simdebug.h
new file mode 100644
index 00000000000..b549a31cf34
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_simdebug.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_ABC_SIMDEBUG_H
+#define PTC_ABC_SIMDEBUG_H
+
+#include <Alembic/Abc/IObject.h>
+#include <Alembic/Abc/OObject.h>
+#include <Alembic/AbcGeom/Foundation.h>
+
+#include "ptc_types.h"
+
+#include "abc_reader.h"
+#include "abc_writer.h"
+
+extern "C" {
+#include "BKE_effect.h"
+}
+
+namespace PTC {
+
+class AbcSimDebugWriter : public AbcWriter {
+public:
+ AbcSimDebugWriter(const std::string &name, SimDebugData *data);
+ ~AbcSimDebugWriter();
+
+ void init_abc(Abc::OObject parent);
+
+ void write_sample();
+
+private:
+ std::string m_name;
+ SimDebugData *m_data;
+
+ Abc::OObject m_object;
+ AbcGeom::OUInt32ArrayProperty m_prop_category_hash;
+ AbcGeom::OUInt32ArrayProperty m_prop_hash;
+ AbcGeom::OInt32ArrayProperty m_prop_type;
+ AbcGeom::OC3fArrayProperty m_prop_color;
+ AbcGeom::OV3fArrayProperty m_prop_v1;
+ AbcGeom::OV3fArrayProperty m_prop_v2;
+};
+
+class AbcSimDebugReader : public AbcReader {
+public:
+ AbcSimDebugReader(SimDebugData *data);
+ ~AbcSimDebugReader();
+
+ void init_abc(Abc::IObject object);
+
+ PTCReadSampleResult read_sample_abc(chrono_t time);
+
+private:
+ SimDebugData *m_data;
+
+ Abc::IObject m_object;
+ AbcGeom::IUInt32ArrayProperty m_prop_category_hash;
+ AbcGeom::IUInt32ArrayProperty m_prop_hash;
+ AbcGeom::IInt32ArrayProperty m_prop_type;
+ AbcGeom::IC3fArrayProperty m_prop_color;
+ AbcGeom::IV3fArrayProperty m_prop_v1;
+ AbcGeom::IV3fArrayProperty m_prop_v2;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_SIMDEBUG_H */
diff --git a/source/blender/pointcache/alembic/abc_split.cpp b/source/blender/pointcache/alembic/abc_split.cpp
new file mode 100644
index 00000000000..7a522a3f3c0
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_split.cpp
@@ -0,0 +1,236 @@
+//-*****************************************************************************
+//
+// Copyright (c) 2009-2013,
+// Sony Pictures Imageworks, Inc. and
+// Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Sony Pictures Imageworks, nor
+// Industrial Light & Magic nor the names of their contributors may be used
+// to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+//-*****************************************************************************
+
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <map>
+
+#include <Alembic/AbcGeom/All.h>
+#include <Alembic/AbcCoreAbstract/All.h>
+#include <Alembic/AbcCoreFactory/All.h>
+#include <Alembic/Util/All.h>
+#include <Alembic/Abc/TypedPropertyTraits.h>
+
+#include "alembic.h"
+
+extern "C" {
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_cache_library.h"
+}
+
+using namespace ::Alembic::AbcGeom;
+
+namespace PTC {
+
+static void slice_properties(ICompoundProperty iParent, OCompoundProperty out_parent, TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter);
+
+static void slice_array_property(IArrayProperty iProp, OCompoundProperty out_parent, TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter)
+{
+ OArrayProperty out(out_parent, iProp.getName(), iProp.getDataType(), iProp.getMetaData(), time_sampling);
+
+ ArrayPropertyReaderPtr reader = iProp.getPtr();
+ ArrayPropertyWriterPtr writer = out.getPtr();
+
+ size_t num_samples = iProp.getNumSamples();
+ if (num_samples == 0)
+ return;
+
+// index_t istart = reader->getFloorIndex(start).first;
+// index_t iend = reader->getFloorIndex(end).first;
+
+ char *buf = NULL;
+
+ for (index_t index = 0; index < iProp.getNumSamples(); ++index) {
+ chrono_t time = time_sampling->getSampleTime(index);
+ if (filter.use_time(time) || writer->getNumSamples() == 0) {
+ ArraySamplePtr sample_ptr;
+ reader->getSample(index, sample_ptr);
+
+ writer->setSample(*sample_ptr);
+ }
+ else {
+ writer->setFromPreviousSample();
+ }
+ }
+
+ if (buf)
+ delete[] buf;
+}
+
+static void slice_scalar_property(IScalarProperty iProp, OCompoundProperty out_parent, TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter)
+{
+ OScalarProperty out(out_parent, iProp.getName(), iProp.getDataType(), iProp.getMetaData(), time_sampling);
+
+ ScalarPropertyReaderPtr reader = iProp.getPtr();
+ ScalarPropertyWriterPtr writer = out.getPtr();
+ size_t num_bytes = reader->getDataType().getNumBytes();
+
+ size_t num_samples = iProp.getNumSamples();
+ if (num_samples == 0)
+ return;
+
+// index_t istart = reader->getFloorIndex(start).first;
+// index_t iend = reader->getFloorIndex(end).first;
+
+ char *buf = new char[num_bytes];
+
+#if 0
+ if (istart > ostart) {
+ /* fill the gap between start indices with the first sample,
+ * so that output sample times match input sample times as close as possible.
+ */
+ for (index_t index = istart)
+ }
+#endif
+
+ for (index_t index = 0; index < iProp.getNumSamples(); ++index) {
+ chrono_t time = time_sampling->getSampleTime(index);
+ if (filter.use_time(time) || writer->getNumSamples() == 0) {
+ reader->getSample(index, (void*)buf);
+
+ writer->setSample((void*)buf);
+ }
+ else {
+ writer->setFromPreviousSample();
+ }
+ }
+
+ delete[] buf;
+}
+
+static void slice_compound_property(ICompoundProperty iProp, OCompoundProperty out_parent, TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter)
+{
+ OCompoundProperty out(out_parent, iProp.getName(), iProp.getMetaData());
+
+ slice_properties(iProp, out, time_sampling, filter);
+}
+
+static void slice_properties(ICompoundProperty iParent, OCompoundProperty out_parent, TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter)
+{
+ for (size_t i = 0 ; i < iParent.getNumProperties() ; i++) {
+ PropertyHeader header = iParent.getPropertyHeader(i);
+
+ if (header.isCompound()) {
+ slice_compound_property(ICompoundProperty(iParent, header.getName()), out_parent, time_sampling, filter);
+ }
+ else if (header.isScalar()) {
+ slice_scalar_property(IScalarProperty(iParent, header.getName()), out_parent, time_sampling, filter);
+ }
+ else {
+ BLI_assert(header.isArray());
+ slice_array_property(IArrayProperty(iParent, header.getName()), out_parent, time_sampling, filter);
+ }
+ }
+}
+
+typedef std::map<ObjectReaderPtr, ObjectWriterPtr> ObjectMap;
+typedef std::pair<ObjectReaderPtr, ObjectWriterPtr> ObjectPair;
+
+static void slice_object(IObject iObj, OObject out, TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter, ObjectMap &object_map)
+{
+ // Get the properties.
+ slice_properties(iObj.getProperties(), out.getProperties(), time_sampling, filter);
+
+ // now the child objects
+ for (size_t i = 0 ; i < iObj.getNumChildren() ; i++) {
+ const ObjectHeader &child_header = iObj.getChildHeader(i);
+ IObject child = IObject(iObj, child_header.getName());
+
+ /* Note: child instances are added later, once all actual objects have been copied */
+ if (!child.isInstanceRoot()) {
+ /* XXX reuse if the output object already exists.
+ * This should not happen, but currently some root objects are created
+ * in advance when opening writer archives. In the future these will not be needed
+ * and this check will become unnecessary.
+ */
+ OObject out_child = out.getChild(child_header.getName());
+ if (!out_child)
+ out_child = OObject(out, child_header.getName(), child_header.getMetaData());
+ object_map[child.getPtr()] = out_child.getPtr();
+
+ slice_object(child, out_child, time_sampling, filter, object_map);
+ }
+ }
+}
+
+static void slice_object_instances(IObject iObj, OObject out, const ObjectMap &object_map)
+{
+ // now the child objects
+ for (size_t i = 0 ; i < iObj.getNumChildren() ; i++) {
+ const ObjectHeader &child_header = iObj.getChildHeader(i);
+ IObject child = IObject(iObj, child_header.getName());
+
+ if (child.isInstanceRoot()) {
+ ObjectMap::const_iterator it = object_map.find(child.getPtr());
+ BLI_assert(it != object_map.end());
+ OObject out_target(it->second, kWrapExisting);
+
+ out.addChildInstance(out_target, child_header.getName());
+ }
+ else {
+ OObject out_child(out.getChild(child_header.getName()).getPtr(), kWrapExisting);
+ slice_object_instances(child, out_child, object_map);
+ }
+ }
+}
+
+void abc_archive_slice(IArchive in, OArchive out, TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter)
+{
+ ObjectMap object_map;
+
+ slice_object(in.getTop(), out.getTop(), time_sampling, filter, object_map);
+ slice_object_instances(in.getTop(), out.getTop(), object_map);
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_writer.cpp b/source/blender/pointcache/alembic/abc_writer.cpp
new file mode 100644
index 00000000000..a294858f2e5
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_writer.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+//#include <Alembic/AbcCoreHDF5/ReadWrite.h>
+#include <Alembic/AbcCoreOgawa/ReadWrite.h>
+#include <Alembic/Abc/OObject.h>
+#include <Alembic/Abc/ArchiveInfo.h>
+
+#include "alembic.h"
+#include "abc_writer.h"
+
+#include "util_error_handler.h"
+
+extern "C" {
+#include <ctime>
+
+#include "BLI_fileops.h"
+#include "BLI_path_util.h"
+}
+
+namespace PTC {
+
+using namespace Abc;
+
+/* make sure the file's directory exists */
+static void ensure_directory(const char *filename)
+{
+ char dir[FILE_MAXDIR];
+ BLI_split_dir_part(filename, dir, sizeof(dir));
+ BLI_dir_create_recursive(dir);
+}
+
+AbcWriterArchive *AbcWriterArchive::open(double fps, float start_frame, const std::string &filename, PTCArchiveResolution resolutions,
+ const char *app_name, const char *description, const struct tm *time, IDProperty *metadata, ErrorHandler *error_handler)
+{
+ ensure_directory(filename.c_str());
+
+ MetaData md = abc_create_archive_info(app_name, description, time, metadata);
+
+ OArchive abc_archive;
+ PTC_SAFE_CALL_BEGIN
+// abc_archive = OArchive(AbcCoreHDF5::WriteArchive(), filename, md, Abc::ErrorHandler::kThrowPolicy);
+ abc_archive = OArchive(AbcCoreOgawa::WriteArchive(), filename, md, Abc::ErrorHandler::kThrowPolicy);
+ PTC_SAFE_CALL_END_HANDLER(error_handler)
+
+ if (abc_archive)
+ return new AbcWriterArchive(fps, start_frame, resolutions, error_handler, abc_archive);
+ else
+ return NULL;
+}
+
+AbcWriterArchive::AbcWriterArchive(double fps, float start_frame, PTCArchiveResolution resolutions, ErrorHandler *error_handler, OArchive abc_archive) :
+ FrameMapper(fps, start_frame),
+ m_error_handler(error_handler),
+ m_use_render(false),
+ m_abc_archive(abc_archive)
+{
+ if (m_abc_archive) {
+ chrono_t cycle_time = this->seconds_per_frame();
+ chrono_t start_time = this->start_time();
+ m_frame_sampling = m_abc_archive.addTimeSampling(TimeSampling(cycle_time, start_time));
+
+ if (resolutions & PTC_RESOLUTION_PREVIEW)
+ m_abc_root = OObject(m_abc_archive.getTop(), "root");
+ if (resolutions & PTC_RESOLUTION_RENDER)
+ m_abc_root_render = OObject(m_abc_archive.getTop(), "root_render");
+ }
+}
+
+AbcWriterArchive::~AbcWriterArchive()
+{
+}
+
+OObject AbcWriterArchive::get_id_object(ID *id)
+{
+ if (!m_abc_archive)
+ return OObject();
+
+ ObjectWriterPtr root_ptr = root().getPtr();
+
+ ObjectWriterPtr child = root_ptr->getChild(id->name);
+ if (child)
+ return OObject(child, kWrapExisting);
+ else {
+ const ObjectHeader *child_header = root_ptr->getChildHeader(id->name);
+ if (child_header)
+ return OObject(root_ptr->createChild(*child_header), kWrapExisting);
+ else {
+ return OObject();
+ }
+ }
+}
+
+OObject AbcWriterArchive::root()
+{
+ if (m_use_render)
+ return m_abc_root_render ? m_abc_root_render : OObject();
+ else
+ return m_abc_root ? m_abc_root : OObject();
+}
+
+bool AbcWriterArchive::has_id_object(ID *id)
+{
+ if (!m_abc_archive)
+ return false;
+
+ ObjectWriterPtr root_ptr = root().getPtr();
+
+ return root_ptr->getChildHeader(id->name) != NULL;
+}
+
+TimeSamplingPtr AbcWriterArchive::frame_sampling()
+{
+ return m_abc_archive.getTimeSampling(m_frame_sampling);
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/alembic/abc_writer.h b/source/blender/pointcache/alembic/abc_writer.h
new file mode 100644
index 00000000000..60a25ff1e0a
--- /dev/null
+++ b/source/blender/pointcache/alembic/abc_writer.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_ABC_WRITER_H
+#define PTC_ABC_WRITER_H
+
+#include <string>
+
+#include <Alembic/Abc/OArchive.h>
+#include <Alembic/Abc/OObject.h>
+
+#include "writer.h"
+
+#include "abc_frame_mapper.h"
+
+#include "util_error_handler.h"
+#include "util_types.h"
+
+extern "C" {
+#include "BLI_utildefines.h"
+
+#include "DNA_ID.h"
+}
+
+struct tm;
+
+namespace PTC {
+
+using namespace Alembic;
+
+class AbcWriterArchive : public WriterArchive, public FrameMapper {
+public:
+ virtual ~AbcWriterArchive();
+
+ static AbcWriterArchive *open(double fps, float start_frame, const std::string &filename, PTCArchiveResolution resolutions,
+ const char *app_name, const char *description, const struct tm *time, IDProperty *metadata, ErrorHandler *error_handler);
+
+ bool use_render() const { return m_use_render; }
+ void use_render(bool enable) { m_use_render = enable; }
+
+ Abc::OArchive abc_archive() const { return m_abc_archive; }
+ Abc::OObject root();
+
+ Abc::OObject get_id_object(ID *id);
+ bool has_id_object(ID *id);
+
+ template <class OObjectT>
+ OObjectT add_id_object(ID *id);
+
+ uint32_t frame_sampling_index() const { return m_frame_sampling; }
+ Abc::TimeSamplingPtr frame_sampling();
+
+protected:
+ AbcWriterArchive(double fps, float start_frame, PTCArchiveResolution resolutions, ErrorHandler *error_handler, Abc::OArchive abc_archive);
+
+protected:
+ ErrorHandler *m_error_handler;
+ uint32_t m_frame_sampling;
+ bool m_use_render;
+
+ Abc::OArchive m_abc_archive;
+ Abc::OObject m_abc_root;
+ Abc::OObject m_abc_root_render;
+};
+
+class AbcWriter : public Writer {
+public:
+ Abc::TimeSamplingPtr frame_sampling() { return m_abc_archive->frame_sampling(); }
+
+ void init(WriterArchive *archive)
+ {
+ BLI_assert(dynamic_cast<AbcWriterArchive*>(archive));
+ m_abc_archive = static_cast<AbcWriterArchive*>(archive);
+
+ init_abc();
+ }
+
+ /* one of these should be implemented by subclasses */
+ virtual void init_abc() {}
+ virtual void init_abc(Abc::OObject /*parent*/) {}
+
+ AbcWriterArchive *abc_archive() const { return m_abc_archive; }
+
+private:
+ AbcWriterArchive *m_abc_archive;
+};
+
+/* ------------------------------------------------------------------------- */
+
+template <class OObjectT>
+OObjectT AbcWriterArchive::add_id_object(ID *id)
+{
+ using namespace Abc;
+
+ if (!m_abc_archive)
+ return OObjectT();
+
+ ObjectWriterPtr root_ptr = this->root().getPtr();
+
+ ObjectWriterPtr child = root_ptr->getChild(id->name);
+ if (child)
+ return OObjectT(child, kWrapExisting);
+ else {
+ const ObjectHeader *child_header = root_ptr->getChildHeader(id->name);
+ if (child_header)
+ return OObjectT(root_ptr->createChild(*child_header), kWrapExisting);
+ else {
+ return OObjectT(root_ptr, id->name, frame_sampling_index());
+ }
+ }
+}
+
+} /* namespace PTC */
+
+#endif /* PTC_WRITER_H */
diff --git a/source/blender/pointcache/alembic/alembic.cpp b/source/blender/pointcache/alembic/alembic.cpp
new file mode 100644
index 00000000000..d0985167908
--- /dev/null
+++ b/source/blender/pointcache/alembic/alembic.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "PTC_api.h"
+
+#include "ptc_types.h"
+
+#include "abc_reader.h"
+#include "abc_writer.h"
+#include "abc_cloth.h"
+#include "abc_group.h"
+#include "abc_mesh.h"
+#include "abc_object.h"
+#include "abc_particles.h"
+
+#include "alembic.h"
+
+namespace PTC {
+
+struct ArchiveSlicesFilter : public AbcArchiveFrameFilter {
+ std::vector<Abc::chrono_t> start;
+ std::vector<Abc::chrono_t> end;
+
+ bool use_time(Abc::chrono_t time) const
+ {
+ assert(start.size() == end.size());
+ for (size_t i = 0; i < start.size(); ++i) {
+ if (time >= start[i] && time <= end[i])
+ return true;
+ }
+ return false;
+ }
+};
+
+class AbcFactory : public Factory {
+ const std::string &get_default_extension()
+ {
+ static std::string ext = "abc";
+ return ext;
+ }
+
+ WriterArchive *open_writer_archive(double fps, float start_frame, const std::string &name, PTCArchiveResolution resolutions,
+ const char *app_name, const char *description, const struct tm *time, struct IDProperty *metadata, ErrorHandler *error_handler)
+ {
+ return AbcWriterArchive::open(fps, start_frame, name, resolutions, app_name, description, time, metadata, error_handler);
+ }
+
+ ReaderArchive *open_reader_archive(double fps, float start_frame, const std::string &name, ErrorHandler *error_handler)
+ {
+ return AbcReaderArchive::open(fps, start_frame, name, error_handler);
+ }
+
+ void slice(ReaderArchive *in, WriterArchive *out, struct ListBase *slices)
+ {
+ BLI_assert(dynamic_cast<AbcReaderArchive*>(in));
+ BLI_assert(dynamic_cast<AbcWriterArchive*>(out));
+ AbcReaderArchive *abc_in = static_cast<AbcReaderArchive*>(in);
+ AbcWriterArchive *abc_out = static_cast<AbcWriterArchive*>(out);
+
+ ArchiveSlicesFilter abc_filter;
+ for (CacheSlice *slice = (CacheSlice *)slices->first; slice; slice = slice->next) {
+ abc_filter.start.push_back(abc_in->frame_to_time(slice->start));
+ abc_filter.end.push_back(abc_in->frame_to_time(slice->end));
+ }
+
+ abc_archive_slice(abc_in->abc_archive(), abc_out->abc_archive(), abc_out->frame_sampling(), abc_filter);
+ }
+
+ Writer *create_writer_object(const std::string &name, Scene *scene, Object *ob)
+ {
+ return new AbcObjectWriter(name, scene, ob, true, true);
+ }
+
+ Reader *create_reader_object(const std::string &name, Object *ob)
+ {
+ return new AbcObjectReader(name, ob);
+ }
+
+ Writer *create_writer_group(const std::string &name, Group *group)
+ {
+ return new AbcGroupWriter(name, group);
+ }
+
+ Reader *create_reader_group(const std::string &name, Group *group)
+ {
+ return new AbcGroupReader(name, group);
+ }
+
+
+ /* Cloth */
+ Writer *create_writer_cloth(const std::string &name, Object *ob, ClothModifierData *clmd)
+ {
+ return new AbcClothWriter(name, ob, clmd);
+ }
+
+ Reader *create_reader_cloth(const std::string &name, Object *ob, ClothModifierData *clmd)
+ {
+ return new AbcClothReader(name, ob, clmd);
+ }
+
+ /* Modifier Stack */
+ Writer *create_writer_derived_mesh(const std::string &name, Object *ob, DerivedMesh **dm_ptr)
+ {
+ return new AbcDerivedMeshWriter(name, ob, dm_ptr);
+ }
+
+ Reader *create_reader_derived_mesh(const std::string &name, Object *ob)
+ {
+ return new AbcDerivedMeshReader(name, ob);
+ }
+
+ Writer *create_writer_derived_final_realtime(const std::string &name, Object *ob)
+ {
+ return new AbcDerivedFinalRealtimeWriter(name, ob);
+ }
+
+ Writer *create_writer_derived_final_render(const std::string &name, Scene *scene, Object *ob, DerivedMesh **render_dm_ptr)
+ {
+ return new AbcDerivedFinalRenderWriter(name, scene, ob, render_dm_ptr);
+ }
+
+
+ Writer *create_writer_dupligroup(const std::string &name, EvaluationContext *eval_ctx, Scene *scene, Group *group, CacheLibrary *cachelib)
+ {
+ return new AbcDupligroupWriter(name, eval_ctx, scene, group, cachelib);
+ }
+
+ Writer *create_writer_duplicache(const std::string &name, Group *group, DupliCache *dupcache, int datatypes, bool do_sim_debug)
+ {
+ return new AbcDupliCacheWriter(name, group, dupcache, datatypes, do_sim_debug);
+ }
+
+ Reader *create_reader_duplicache(const std::string &name, Group *group, DupliCache *dupcache,
+ bool read_strands_motion, bool read_strands_children, bool read_sim_debug)
+ {
+ return new AbcDupliCacheReader(name, group, dupcache, read_strands_motion, read_strands_children, read_sim_debug);
+ }
+
+ Reader *create_reader_duplicache_object(const std::string &name, Object *ob, DupliObjectData *data,
+ bool read_strands_motion, bool read_strands_children)
+ {
+ return new AbcDupliObjectReader(name, ob, data, read_strands_motion, read_strands_children);
+ }
+};
+
+}
+
+void PTC_alembic_init()
+{
+ static PTC::AbcFactory abc_factory;
+
+ PTC::Factory::alembic = &abc_factory;
+}
diff --git a/source/blender/pointcache/alembic/alembic.h b/source/blender/pointcache/alembic/alembic.h
new file mode 100644
index 00000000000..fc387edaa88
--- /dev/null
+++ b/source/blender/pointcache/alembic/alembic.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_ALEMBIC_H
+#define PTC_ALEMBIC_H
+
+#include <string>
+
+#include <Alembic/Abc/IArchive.h>
+
+struct CacheArchiveInfo;
+struct IDProperty;
+
+namespace PTC {
+
+using namespace Alembic;
+
+void abc_metadata_from_idprops_group(Abc::MetaData &md, IDProperty *prop);
+void abc_metadata_to_idprops_group(const Abc::MetaData &md, IDProperty *prop);
+Abc::MetaData abc_create_archive_info(const char *app_name, const char *description, const struct tm *t, struct IDProperty *props);
+
+void abc_archive_info_stream(Alembic::Abc::IArchive &archive, void (*stream)(void *, const char *), void *userdata);
+void abc_archive_info_nodes(Alembic::Abc::IArchive &archive, CacheArchiveInfo *info, IDProperty *metadata, bool calc_nodes, bool calc_bytes_size);
+
+struct AbcArchiveFrameFilter {
+ virtual bool use_time(Abc::chrono_t time) const = 0;
+};
+
+void abc_archive_slice(Abc::IArchive in, Abc::OArchive out, Abc::TimeSamplingPtr time_sampling, const AbcArchiveFrameFilter &filter);
+
+} /* namespace PTC */
+
+#endif /* PTC_CLOTH_H */
diff --git a/source/blender/pointcache/intern/ptc_types.cpp b/source/blender/pointcache/intern/ptc_types.cpp
new file mode 100644
index 00000000000..f2df356ca5b
--- /dev/null
+++ b/source/blender/pointcache/intern/ptc_types.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "ptc_types.h"
+
+extern "C" {
+#include "BKE_DerivedMesh.h"
+}
+
+namespace PTC {
+
+Factory *Factory::alembic = NULL;
+
+DerivedMesh *DerivedMeshReader::acquire_result()
+{
+ DerivedMesh *dm = m_result;
+ m_result = NULL;
+ return dm;
+}
+
+void DerivedMeshReader::discard_result()
+{
+ if (m_result) {
+ m_result->release(m_result);
+ m_result = NULL;
+ }
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/intern/ptc_types.h b/source/blender/pointcache/intern/ptc_types.h
new file mode 100644
index 00000000000..091afa1f7b5
--- /dev/null
+++ b/source/blender/pointcache/intern/ptc_types.h
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_TYPES_H
+#define PTC_TYPES_H
+
+#include "reader.h"
+#include "writer.h"
+
+extern "C" {
+#include "DNA_group_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_object_force.h"
+#include "DNA_particle_types.h"
+}
+
+struct CacheLibrary;
+struct IDProperty;
+
+namespace PTC {
+
+class ClothWriter {
+public:
+ ClothWriter(Object *ob, ClothModifierData *clmd, const std::string &name) :
+ m_ob(ob),
+ m_clmd(clmd),
+ m_name(name)
+ {}
+
+ ~ClothWriter()
+ {}
+
+protected:
+ Object *m_ob;
+ ClothModifierData *m_clmd;
+ std::string m_name;
+};
+
+class ClothReader {
+public:
+ ClothReader(Object *ob, ClothModifierData *clmd, const std::string &name) :
+ m_ob(ob),
+ m_clmd(clmd),
+ m_name(name)
+ {}
+
+ ~ClothReader()
+ {}
+
+protected:
+ Object *m_ob;
+ ClothModifierData *m_clmd;
+ std::string m_name;
+};
+
+
+class DerivedMeshWriter {
+public:
+ /** \note Targeted DerivedMesh at \a dm_ptr must be available only on \fn write_sample calls */
+ DerivedMeshWriter(Object *ob, DerivedMesh **dm_ptr, const std::string &name) :
+ m_ob(ob),
+ m_dm_ptr(dm_ptr),
+ m_name(name)
+ {}
+
+ ~DerivedMeshWriter()
+ {}
+
+protected:
+ Object *m_ob;
+ DerivedMesh **m_dm_ptr;
+ std::string m_name;
+};
+
+class DerivedMeshReader {
+public:
+ DerivedMeshReader(Object *ob, const std::string &name) :
+ m_ob(ob),
+ m_result(0),
+ m_name(name)
+ {}
+
+ ~DerivedMeshReader()
+ {
+ discard_result();
+ }
+
+ DerivedMesh *acquire_result();
+ void discard_result();
+
+protected:
+ Object *m_ob;
+ DerivedMesh *m_result;
+ std::string m_name;
+};
+
+class GroupWriter {
+public:
+ GroupWriter(Group *group, const std::string &name) :
+ m_group(group),
+ m_name(name)
+ {}
+
+protected:
+ Group *m_group;
+ std::string m_name;
+};
+
+class GroupReader {
+public:
+ GroupReader(Group *group, const std::string &name) :
+ m_group(group),
+ m_name(name)
+ {}
+
+protected:
+ Group *m_group;
+ std::string m_name;
+};
+
+class ObjectWriter {
+public:
+ ObjectWriter(Object *ob, const std::string &name) :
+ m_ob(ob),
+ m_name(name)
+ {}
+
+protected:
+ Object *m_ob;
+ std::string m_name;
+};
+
+class ObjectReader {
+public:
+ ObjectReader(Object *ob, const std::string &name) :
+ m_ob(ob),
+ m_name(name)
+ {}
+
+protected:
+ Object *m_ob;
+ std::string m_name;
+};
+
+class ParticlesWriter {
+public:
+ ParticlesWriter(Object *ob, ParticleSystem *psys, const std::string &name) :
+ m_ob(ob),
+ m_psys(psys),
+ m_name(name)
+ {}
+
+ ~ParticlesWriter()
+ {}
+
+protected:
+ Object *m_ob;
+ ParticleSystem *m_psys;
+ std::string m_name;
+};
+
+class ParticlesReader {
+public:
+ ParticlesReader(Object *ob, ParticleSystem *psys, const std::string &name) :
+ m_ob(ob),
+ m_psys(psys),
+ m_name(name),
+ m_totpoint(0)
+ {}
+
+ ~ParticlesReader()
+ {}
+
+ int totpoint() const { return m_totpoint; }
+
+protected:
+ Object *m_ob;
+ ParticleSystem *m_psys;
+ std::string m_name;
+
+ int m_totpoint;
+};
+
+struct Factory {
+ virtual const std::string &get_default_extension() = 0;
+ virtual WriterArchive *open_writer_archive(double fps, float start_frame, const std::string &name, PTCArchiveResolution resolutions,
+ const char *app_name, const char *description, const struct tm *time, struct IDProperty *metadata, ErrorHandler *error_handler) = 0;
+ virtual ReaderArchive *open_reader_archive(double fps, float start_frame, const std::string &name, ErrorHandler *error_handler) = 0;
+
+ virtual void slice(ReaderArchive *in, WriterArchive *out, struct ListBase *slices) = 0;
+
+ virtual Writer *create_writer_object(const std::string &name, Scene *scene, Object *ob) = 0;
+ virtual Reader *create_reader_object(const std::string &name, Object *ob) = 0;
+
+ virtual Writer *create_writer_group(const std::string &name, Group *group) = 0;
+ virtual Reader *create_reader_group(const std::string &name, Group *group) = 0;
+
+ /* Cloth */
+ virtual Writer *create_writer_cloth(const std::string &name, Object *ob, ClothModifierData *clmd) = 0;
+ virtual Reader *create_reader_cloth(const std::string &name, Object *ob, ClothModifierData *clmd) = 0;
+
+ /* Modifier Stack */
+ virtual Writer *create_writer_derived_mesh(const std::string &name, Object *ob, DerivedMesh **dm_ptr) = 0;
+ virtual Reader *create_reader_derived_mesh(const std::string &name, Object *ob) = 0;
+
+ virtual Writer *create_writer_derived_final_realtime(const std::string &name, Object *ob) = 0;
+ virtual Writer *create_writer_derived_final_render(const std::string &name, Scene *scene, Object *ob, DerivedMesh **render_dm_ptr) = 0;
+
+ virtual Writer *create_writer_dupligroup(const std::string &name, EvaluationContext *eval_ctx, Scene *scene, Group *group, CacheLibrary *cachelib) = 0;
+ virtual Writer *create_writer_duplicache(const std::string &name, Group *group, DupliCache *dupcache, int datatypes, bool do_sim_debug) = 0;
+ virtual Reader *create_reader_duplicache(const std::string &name, Group *group, DupliCache *dupcache,
+ bool read_strands_motion, bool read_strands_children, bool read_sim_debug) = 0;
+ virtual Reader *create_reader_duplicache_object(const std::string &name, Object *ob, DupliObjectData *data,
+ bool read_strands_motion, bool read_strands_children) = 0;
+
+ static Factory *alembic;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_EXPORT_H */
diff --git a/source/blender/pointcache/intern/reader.cpp b/source/blender/pointcache/intern/reader.cpp
new file mode 100644
index 00000000000..56b0f54e982
--- /dev/null
+++ b/source/blender/pointcache/intern/reader.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "reader.h"
+#include "util_error_handler.h"
+
+extern "C" {
+#include "DNA_scene_types.h"
+}
+
+namespace PTC {
+
+Reader::Reader() :
+ m_error_handler(0)
+{
+}
+
+Reader::Reader(ErrorHandler *error_handler) :
+ m_error_handler(error_handler)
+{
+}
+
+Reader::~Reader()
+{
+ if (m_error_handler)
+ delete m_error_handler;
+}
+
+void Reader::set_error_handler(ErrorHandler *handler)
+{
+ if (m_error_handler)
+ delete m_error_handler;
+
+ m_error_handler = handler;
+}
+
+bool Reader::valid() const
+{
+ return m_error_handler ? m_error_handler->max_error_level() >= PTC_ERROR_CRITICAL : true;
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/intern/reader.h b/source/blender/pointcache/intern/reader.h
new file mode 100644
index 00000000000..39d0ab20197
--- /dev/null
+++ b/source/blender/pointcache/intern/reader.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_READER_H
+#define PTC_READER_H
+
+#include <string>
+
+#include "util_error_handler.h"
+#include "util_types.h"
+#include "PTC_api.h"
+
+struct ID;
+struct IDProperty;
+struct CacheArchiveInfo;
+
+namespace PTC {
+
+class ReaderArchive {
+public:
+ virtual ~ReaderArchive() {}
+
+ virtual PTCArchiveResolution get_resolutions() = 0;
+ virtual void use_render(bool enable) = 0;
+
+ virtual bool get_frame_range(int &start_frame, int &end_frame) = 0;
+ virtual void get_info_stream(void (*stream)(void *, const char *), void *userdata) = 0;
+ virtual void get_info(CacheArchiveInfo *info, IDProperty *metadata) = 0;
+ virtual void get_info_nodes(CacheArchiveInfo *info, bool calc_bytes_size) = 0;
+};
+
+class Reader {
+public:
+ Reader();
+ Reader(ErrorHandler *error_handler);
+ virtual ~Reader();
+
+ virtual void init(ReaderArchive *archive) = 0;
+
+ void set_error_handler(ErrorHandler *handler);
+ ErrorHandler *get_error_handler() const { return m_error_handler; }
+ bool valid() const;
+
+ virtual bool get_frame_range(int &start_frame, int &end_frame) = 0;
+ virtual PTCReadSampleResult test_sample(float frame) = 0;
+ virtual PTCReadSampleResult read_sample(float frame) = 0;
+
+protected:
+ ErrorHandler *m_error_handler;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_READER_H */
diff --git a/source/blender/pointcache/intern/writer.cpp b/source/blender/pointcache/intern/writer.cpp
new file mode 100644
index 00000000000..97a6b9ad405
--- /dev/null
+++ b/source/blender/pointcache/intern/writer.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "writer.h"
+
+extern "C" {
+#include "DNA_scene_types.h"
+}
+
+namespace PTC {
+
+Writer::Writer() :
+ m_error_handler(0)
+{
+}
+
+Writer::Writer(ErrorHandler *handler) :
+ m_error_handler(handler)
+{
+}
+
+Writer::~Writer()
+{
+ if (m_error_handler)
+ delete m_error_handler;
+}
+
+void Writer::set_error_handler(ErrorHandler *handler)
+{
+ if (m_error_handler)
+ delete m_error_handler;
+
+ m_error_handler = handler;
+}
+
+bool Writer::valid() const
+{
+ return m_error_handler ? m_error_handler->max_error_level() >= PTC_ERROR_CRITICAL : true;
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/intern/writer.h b/source/blender/pointcache/intern/writer.h
new file mode 100644
index 00000000000..008c391bffc
--- /dev/null
+++ b/source/blender/pointcache/intern/writer.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_WRITER_H
+#define PTC_WRITER_H
+
+#include <string>
+
+#include "util_error_handler.h"
+
+struct ID;
+
+namespace PTC {
+
+class WriterArchive {
+public:
+ virtual ~WriterArchive() {}
+
+ virtual void use_render(bool enable) = 0;
+};
+
+class Writer {
+public:
+ Writer();
+ Writer(ErrorHandler *handler);
+ virtual ~Writer();
+
+ void set_error_handler(ErrorHandler *handler);
+ bool valid() const;
+
+ virtual void init(WriterArchive *archive) = 0;
+
+ /* create references to other objects */
+ virtual void create_refs() {}
+
+ virtual void write_sample() = 0;
+
+protected:
+ ErrorHandler *m_error_handler;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_WRITER_H */
diff --git a/source/blender/pointcache/util/util_error_handler.cpp b/source/blender/pointcache/util/util_error_handler.cpp
new file mode 100644
index 00000000000..e1a326e1713
--- /dev/null
+++ b/source/blender/pointcache/util/util_error_handler.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <iostream>
+
+#include "util_error_handler.h"
+
+extern "C" {
+#include "BKE_modifier.h"
+}
+
+namespace PTC {
+
+ErrorHandler *ErrorHandler::m_default_handler = new StdErrorHandler(PTC_ERROR_INFO);
+
+ErrorHandler::ErrorHandler() :
+ m_max_level(PTC_ERROR_NONE)
+{
+}
+
+ErrorHandler::~ErrorHandler()
+{
+}
+
+void ErrorHandler::set_error_level(PTCErrorLevel level)
+{
+ if (level > m_max_level)
+ m_max_level = level;
+}
+
+void ErrorHandler::set_default_handler(ErrorHandler *handler)
+{
+ if (m_default_handler)
+ delete m_default_handler;
+
+ if (handler)
+ m_default_handler = handler;
+ else
+ m_default_handler = new StdErrorHandler(PTC_ERROR_INFO);
+}
+
+void ErrorHandler::clear_default_handler()
+{
+ if (m_default_handler)
+ delete m_default_handler;
+
+ m_default_handler = new StdErrorHandler(PTC_ERROR_INFO);
+}
+
+
+StdErrorHandler::StdErrorHandler(PTCErrorLevel level) :
+ m_verbosity(level)
+{
+}
+
+void StdErrorHandler::handle(PTCErrorLevel level, const char *message)
+{
+ /* ignore levels below the verbosity setting */
+ if (level >= m_verbosity) {
+ std::cerr << message << std::endl;
+ }
+}
+
+
+CallbackErrorHandler::CallbackErrorHandler(PTCErrorCallback cb, void *userdata) :
+ m_callback(cb),
+ m_userdata(userdata)
+{
+}
+
+void CallbackErrorHandler::handle(PTCErrorLevel level, const char *message)
+{
+ m_callback(m_userdata, level, message);
+}
+
+
+ModifierErrorHandler::ModifierErrorHandler(ModifierData *md) :
+ m_modifier(md)
+{
+}
+
+void ModifierErrorHandler::handle(PTCErrorLevel UNUSED(level), const char *message)
+{
+ modifier_setError(m_modifier, "%s", message);
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/util/util_error_handler.h b/source/blender/pointcache/util/util_error_handler.h
new file mode 100644
index 00000000000..61baa03a357
--- /dev/null
+++ b/source/blender/pointcache/util/util_error_handler.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_UTIL_ERROR_HANDLER_H
+#define PTC_UTIL_ERROR_HANDLER_H
+
+#include <stdio.h>
+
+#ifdef WITH_ALEMBIC
+#include <Alembic/Abc/ErrorHandler.h>
+#endif
+
+extern "C" {
+#include "BLI_utildefines.h"
+#include "BLI_string.h"
+}
+
+#include "util_types.h"
+
+struct ModifierData;
+struct ReportList;
+
+namespace PTC {
+
+class ErrorHandler
+{
+public:
+ ErrorHandler();
+ virtual ~ErrorHandler();
+
+ virtual void handle(PTCErrorLevel level, const char *message) = 0;
+ void set_error_level(PTCErrorLevel level);
+ PTCErrorLevel max_error_level() const { return m_max_level; }
+
+ static ErrorHandler *get_default_handler() { return m_default_handler; }
+ static void set_default_handler(ErrorHandler *handler);
+ static void clear_default_handler();
+
+private:
+ PTCErrorLevel m_max_level;
+
+ static ErrorHandler *m_default_handler;
+};
+
+
+class StdErrorHandler : public ErrorHandler
+{
+public:
+ StdErrorHandler(PTCErrorLevel level);
+
+ void handle(PTCErrorLevel level, const char *message);
+
+ PTCErrorLevel get_verbosity() const { return m_verbosity; }
+ void set_verbosity(PTCErrorLevel level) { m_verbosity = level; }
+
+private:
+ PTCErrorLevel m_verbosity;
+};
+
+
+/* Use Blender reports system to log Alembic errors */
+class CallbackErrorHandler : public ErrorHandler
+{
+public:
+ CallbackErrorHandler(PTCErrorCallback cb, void *userdata);
+
+ void handle(PTCErrorLevel level, const char *message);
+
+private:
+ PTCErrorCallback m_callback;
+ void *m_userdata;
+};
+
+
+class ModifierErrorHandler : public ErrorHandler
+{
+public:
+ ModifierErrorHandler(ModifierData *md);
+
+ void handle(PTCErrorLevel level, const char *message);
+
+private:
+ ModifierData *m_modifier;
+};
+
+/* -------------------------------- */
+
+#ifdef WITH_ALEMBIC
+
+/* XXX With current Alembic version 1.5 we only get a combined error message.
+ * This function try to extract some more information and return a nicer message format.
+ */
+BLI_INLINE void split_alembic_error_message(const char *msg, const char **origin, const char **base_msg)
+{
+ const char delim[] = {'\n', '\0'};
+ char *sep, *suffix;
+
+ BLI_str_partition(msg, delim, &sep, &suffix);
+ if (suffix) {
+ *origin = msg;
+ BLI_str_partition(suffix, delim, &sep, &suffix);
+ if (suffix) {
+ *base_msg = suffix;
+ }
+ else {
+ *base_msg = msg;
+ }
+ }
+ else {
+ *origin = *base_msg = msg;
+ }
+}
+
+/* wrapper templates so the exception macro can be used with references as well as pointers */
+
+template <typename T>
+void handle_alembic_exception(T &handler, PTCErrorLevel level, const Alembic::Util::Exception &e)
+{
+ const char *origin, *msg;
+ split_alembic_error_message(e.what(), &origin, &msg);
+
+ handler.set_error_level(level);
+ handler.handle(level, msg);
+}
+
+template <typename T>
+void handle_alembic_exception(T *handler, PTCErrorLevel level, const Alembic::Util::Exception &e)
+{
+ static StdErrorHandler default_handler(PTC_ERROR_WARNING);
+ if (!handler)
+ handler = &default_handler;
+
+ const char *origin, *msg;
+ split_alembic_error_message(e.what(), &origin, &msg);
+
+ handler->set_error_level(level);
+ handler->handle(level, msg);
+}
+
+#endif
+
+/* -------------------------------- */
+
+/* macros for convenient exception handling */
+
+#define PTC_SAFE_CALL_BEGIN \
+ try {
+
+#ifdef WITH_ALEMBIC
+#define PTC_SAFE_CALL_END_HANDLER(handler) \
+ } \
+ catch (Alembic::Util::Exception e) { \
+ handle_alembic_exception((handler), PTC_ERROR_CRITICAL, e); \
+ }
+#else
+#define PTC_SAFE_CALL_END_HANDLER(handler) \
+ }
+#endif
+
+#ifdef WITH_ALEMBIC
+#define PTC_SAFE_CALL_END_HANDLER_LEVEL(handler, level) \
+ } \
+ catch (Alembic::Util::Exception e) { \
+ handle_alembic_exception((handler), (level), e); \
+ }
+#else
+#define PTC_SAFE_CALL_END_HANDLER_LEVEL(handler, level) \
+ }
+#endif
+
+#ifdef WITH_ALEMBIC
+#define PTC_SAFE_CALL_END \
+ } \
+ catch (Alembic::Util::Exception e) { \
+ handle_alembic_exception(ErrorHandler::get_default_handler(), PTC_ERROR_CRITICAL, e); \
+ }
+#else
+#define PTC_SAFE_CALL_END \
+ }
+#endif
+
+/* -------------------------------- */
+
+} /* namespace PTC */
+
+#endif /* PTC_UTIL_ERROR_HANDLER_H */
diff --git a/source/blender/pointcache/util/util_function.h b/source/blender/pointcache/util/util_function.h
new file mode 100644
index 00000000000..97f0278237d
--- /dev/null
+++ b/source/blender/pointcache/util/util_function.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_UTIL_FUNCTION
+#define PTC_UTIL_FUNCTION
+
+#if __cplusplus > 199711L
+
+#include <functional>
+
+namespace PTC {
+
+using std::function;
+using namespace std::placeholders;
+#define function_bind std::bind
+
+} /* namespace PTC */
+
+#else
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
+namespace PTC {
+
+using boost::function;
+#define function_bind boost::bind
+
+} /* namespace PTC */
+
+#endif
+
+#endif /* PTC_UTIL_FUNCTION */
diff --git a/source/blender/pointcache/util/util_task.cpp b/source/blender/pointcache/util/util_task.cpp
new file mode 100644
index 00000000000..feecdd54b97
--- /dev/null
+++ b/source/blender/pointcache/util/util_task.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "util_task.h"
+
+#include "BLI_task.h"
+#include "BLI_threads.h"
+
+namespace PTC {
+
+class UtilTask {
+public:
+ explicit UtilTask(const UtilTaskFunction& run) : run_(run) {}
+ void run()
+ {
+ run_();
+ }
+protected:
+ UtilTaskFunction run_;
+};
+
+static void task_function(TaskPool * /*pool*/,
+ void *taskdata,
+ int /*threadid*/)
+{
+ UtilTask *task = reinterpret_cast<UtilTask*>(taskdata);
+ task->run();
+ delete task;
+}
+
+UtilTaskPool::UtilTaskPool()
+{
+ scheduler_ = BLI_task_scheduler_get();
+ pool_ = BLI_task_pool_create(scheduler_, NULL);
+}
+
+UtilTaskPool::~UtilTaskPool()
+{
+ BLI_task_pool_free(pool_);
+}
+
+void UtilTaskPool::push(const UtilTaskFunction& run, bool front)
+{
+ UtilTask *task = new UtilTask(run);
+ BLI_task_pool_push(pool_,
+ task_function,
+ task,
+ false,
+ front? TASK_PRIORITY_HIGH: TASK_PRIORITY_LOW);
+}
+
+void UtilTaskPool::wait_work()
+{
+ BLI_task_pool_work_and_wait(pool_);
+}
+
+void UtilTaskPool::cancel()
+{
+ BLI_task_pool_cancel(pool_);
+}
+
+void UtilTaskPool::stop()
+{
+ BLI_task_pool_stop(pool_);
+}
+
+bool UtilTaskPool::cancelled()
+{
+ return BLI_task_pool_canceled(pool_);
+}
+
+} /* namespace PTC */
diff --git a/source/blender/pointcache/util/util_task.h b/source/blender/pointcache/util/util_task.h
new file mode 100644
index 00000000000..be2f1cca84f
--- /dev/null
+++ b/source/blender/pointcache/util/util_task.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_UTIL_TASK_H
+#define PTC_UTIL_TASK_H
+
+#include "util_function.h"
+
+struct TaskScheduler;
+struct TaskPool;
+
+namespace PTC {
+
+typedef function<void(void)> UtilTaskFunction;
+
+class UtilTaskPool {
+public:
+ UtilTaskPool();
+ ~UtilTaskPool();
+
+ void push(const UtilTaskFunction& run, bool front = false);
+
+ void wait_work();
+ void cancel();
+ void stop();
+
+ bool cancelled();
+
+protected:
+ struct TaskScheduler *scheduler_;
+ struct TaskPool *pool_;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_UTIL_TASK_H */
diff --git a/source/blender/pointcache/util/util_thread.h b/source/blender/pointcache/util/util_thread.h
new file mode 100644
index 00000000000..6b41a35ee3f
--- /dev/null
+++ b/source/blender/pointcache/util/util_thread.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_UTIL_THREAD_H
+#define PTC_UTIL_THREAD_H
+
+#include "BLI_threads.h"
+
+namespace PTC {
+
+class thread_mutex {
+public:
+ thread_mutex()
+ {
+ BLI_mutex_init(&mutex_);
+ }
+
+ ~thread_mutex()
+ {
+ BLI_mutex_end(&mutex_);
+ }
+
+ void lock()
+ {
+ BLI_mutex_lock(&mutex_);
+ }
+
+ bool trylock()
+ {
+ return BLI_mutex_trylock(&mutex_);
+ }
+
+ void unlock()
+ {
+ BLI_mutex_unlock(&mutex_);
+ }
+
+protected:
+ ThreadMutex mutex_;
+};
+
+class thread_scoped_lock {
+public:
+ explicit thread_scoped_lock(thread_mutex& mutex)
+ : mutex_(mutex)
+ {
+ mutex_.lock();
+ }
+
+ ~thread_scoped_lock() {
+ mutex_.unlock();
+ }
+protected:
+ thread_mutex& mutex_;
+};
+
+} /* namespace PTC */
+
+#endif /* PTC_UTIL_THREAD_H */
diff --git a/source/blender/pointcache/util/util_types.h b/source/blender/pointcache/util/util_types.h
new file mode 100644
index 00000000000..0ee027c7700
--- /dev/null
+++ b/source/blender/pointcache/util/util_types.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef PTC_UTIL_TYPES_H
+#define PTC_UTIL_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum PTCArchiveResolution {
+ PTC_RESOLUTION_NONE = 0,
+ PTC_RESOLUTION_PREVIEW = (1 << 0),
+ PTC_RESOLUTION_RENDER = (1 << 1),
+} PTCArchiveResolution;
+
+typedef enum PTCErrorLevel {
+ PTC_ERROR_NONE = 0,
+ PTC_ERROR_INFO = 1,
+ PTC_ERROR_WARNING = 2,
+ PTC_ERROR_CRITICAL = 3,
+} PTCErrorLevel;
+
+typedef void (*PTCErrorCallback)(void *userdata, PTCErrorLevel level, const char *message);
+
+typedef enum PTCReadSampleResult {
+ PTC_READ_SAMPLE_INVALID = 0, /* no valid result can be retrieved */
+ PTC_READ_SAMPLE_EARLY, /* request time before first sample */
+ PTC_READ_SAMPLE_LATE, /* request time after last sample */
+ PTC_READ_SAMPLE_EXACT, /* found sample for requested frame */
+ PTC_READ_SAMPLE_INTERPOLATED /* no exact sample, but found enclosing samples for interpolation */
+} PTCReadSampleResult;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PTC_UTIL_TYPES_H */
diff --git a/source/blender/python/SConscript b/source/blender/python/SConscript
index 5f032f25cf3..ce367ec9516 100644
--- a/source/blender/python/SConscript
+++ b/source/blender/python/SConscript
@@ -199,7 +199,10 @@ if env['WITH_BF_OIIO']:
if env['WITH_BF_PLAYER']:
defs.append('WITH_PLAYER')
-
+
+if env['WITH_BF_OPENVDB']:
+ defs.append('WITH_OPENVDB')
+ incs += ' #/intern/openvdb'
if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'win64-mingw', 'linuxcross', 'win64-vc'):
incs += ' ' + env['BF_PTHREADS_INC']
diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c
index 3adf37f78f0..1d5f0d7ac37 100644
--- a/source/blender/python/bmesh/bmesh_py_types_customdata.c
+++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c
@@ -987,8 +987,8 @@ PyObject *BPy_BMLayerItem_GetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer)
ret = BPy_BMDeformVert_CreatePyObject(value);
break;
}
- case CD_PROP_FLT:
case CD_PAINT_MASK:
+ case CD_PROP_FLT:
{
ret = PyFloat_FromDouble(*(float *)value);
break;
@@ -1065,8 +1065,8 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj
ret = BPy_BMDeformVert_AssignPyObject(value, py_value);
break;
}
- case CD_PROP_FLT:
case CD_PAINT_MASK:
+ case CD_PROP_FLT:
{
float tmp_val = PyFloat_AsDouble(py_value);
if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) {
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index ed04152182e..1a3e92cab8a 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -53,6 +53,7 @@ set(SRC
bpy_app_handlers.c
bpy_app_ocio.c
bpy_app_oiio.c
+ bpy_app_openvdb.c
bpy_app_sdl.c
bpy_app_translations.c
bpy_driver.c
@@ -82,6 +83,7 @@ set(SRC
bpy_app_handlers.h
bpy_app_ocio.h
bpy_app_oiio.h
+ bpy_app_openvdb.h
bpy_app_sdl.h
bpy_app_translations.h
bpy_driver.h
@@ -264,6 +266,14 @@ if(WITH_OPENCOLORIO)
add_definitions(-DWITH_OCIO)
endif()
+if(WITH_OPENVDB)
+ add_definitions(-DWITH_OPENVDB)
+ add_definitions(-DOPENVDB_USE_BLOSC)
+ list(APPEND INC
+ ../../../../intern/openvdb
+ )
+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 1cf0c44fd87..c3219087caa 100644
--- a/source/blender/python/intern/bpy_app.c
+++ b/source/blender/python/intern/bpy_app.c
@@ -36,6 +36,7 @@
#include "bpy_app_ffmpeg.h"
#include "bpy_app_ocio.h"
#include "bpy_app_oiio.h"
+#include "bpy_app_openvdb.h"
#include "bpy_app_sdl.h"
#include "bpy_app_build_options.h"
@@ -98,6 +99,7 @@ static PyStructSequence_Field app_info_fields[] = {
{(char *)"ffmpeg", (char *)"FFmpeg library information backend"},
{(char *)"ocio", (char *)"OpenColorIO library information backend"},
{(char *)"oiio", (char *)"OpenImageIO library information backend"},
+ {(char *)"openvdb", (char *)"OpenVDB library information backend"},
{(char *)"sdl", (char *)"SDL library information backend"},
{(char *)"build_options", (char *)"A set containing most important enabled optional build features"},
{(char *)"handlers", (char *)"Application handler callbacks"},
@@ -175,6 +177,7 @@ static PyObject *make_app_info(void)
SetObjItem(BPY_app_ffmpeg_struct());
SetObjItem(BPY_app_ocio_struct());
SetObjItem(BPY_app_oiio_struct());
+ SetObjItem(BPY_app_openvdb_struct());
SetObjItem(BPY_app_sdl_struct());
SetObjItem(BPY_app_build_options_struct());
SetObjItem(BPY_app_handlers_struct());
diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c
index 975d76ca42d..692ebf552c7 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 *)"opencolorio", NULL},
{(char *)"player", NULL},
{(char *)"openmp", NULL},
+ {(char *)"openvdb", NULL},
{NULL}
};
@@ -303,6 +304,12 @@ static PyObject *make_builtopts_info(void)
SetObjIncref(Py_False);
#endif
+#ifdef WITH_OPENVDB
+ SetObjIncref(Py_True);
+#else
+ SetObjIncref(Py_False);
+#endif
+
#undef SetObjIncref
return builtopts_info;
diff --git a/source/blender/python/intern/bpy_app_openvdb.c b/source/blender/python/intern/bpy_app_openvdb.c
new file mode 100644
index 00000000000..4fef74f7a2c
--- /dev/null
+++ b/source/blender/python/intern/bpy_app_openvdb.c
@@ -0,0 +1,117 @@
+/*
+ * ***** 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) 2015 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_openvdb.c
+ * \ingroup pythonintern
+ */
+
+#include <Python.h>
+#include "BLI_utildefines.h"
+
+#include "bpy_app_openvdb.h"
+
+#ifdef WITH_OPENVDB
+# include "openvdb_capi.h"
+#endif
+
+static PyTypeObject BlenderAppOVDBType;
+
+static PyStructSequence_Field app_openvdb_info_fields[] = {
+ {(char *)"supported", (char *)("Boolean, True when Blender is built with OpenVDB support")},
+ {(char *)("version"), (char *)("The OpenVDB version as a tuple of 3 numbers")},
+ {(char *)("version_string"), (char *)("The OpenVDB version formatted as a string")},
+ {NULL}
+};
+
+static PyStructSequence_Desc app_openvdb_info_desc = {
+ (char *)"bpy.app.openvdb", /* name */
+ (char *)"This module contains information about OpenColorIO blender is linked against", /* doc */
+ app_openvdb_info_fields, /* fields */
+ ARRAY_SIZE(app_openvdb_info_fields) - 1
+};
+
+static PyObject *make_openvdb_info(void)
+{
+ PyObject *openvdb_info;
+ int pos = 0;
+
+#ifdef WITH_OPENVDB
+ int curversion;
+#endif
+
+ openvdb_info = PyStructSequence_New(&BlenderAppOVDBType);
+ if (openvdb_info == NULL) {
+ return NULL;
+ }
+
+#ifndef WITH_OPENVDB
+#define SetStrItem(str) \
+ PyStructSequence_SET_ITEM(openvdb_info, pos++, PyUnicode_FromString(str))
+#endif
+
+#define SetObjItem(obj) \
+ PyStructSequence_SET_ITEM(openvdb_info, pos++, obj)
+
+#ifdef WITH_OPENVDB
+ curversion = OpenVDB_getVersionHex();
+ SetObjItem(PyBool_FromLong(1));
+ SetObjItem(Py_BuildValue("(iii)",
+ curversion >> 24, (curversion >> 16) % 256, (curversion >> 8) % 256));
+ SetObjItem(PyUnicode_FromFormat("%2d, %2d, %2d",
+ curversion >> 24, (curversion >> 16) % 256, (curversion >> 8) % 256));
+#else
+ SetObjItem(PyBool_FromLong(0));
+ SetObjItem(Py_BuildValue("(iii)", 0, 0, 0));
+ SetStrItem("Unknown");
+#endif
+
+ if (PyErr_Occurred()) {
+ Py_CLEAR(openvdb_info);
+ return NULL;
+ }
+
+#undef SetStrItem
+#undef SetObjItem
+
+ return openvdb_info;
+}
+
+PyObject *BPY_app_openvdb_struct(void)
+{
+ PyObject *ret;
+
+ PyStructSequence_InitType(&BlenderAppOVDBType, &app_openvdb_info_desc);
+
+ ret = make_openvdb_info();
+
+ /* prevent user from creating new instances */
+ BlenderAppOVDBType.tp_init = NULL;
+ BlenderAppOVDBType.tp_new = NULL;
+ BlenderAppOVDBType.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_openvdb.h b/source/blender/python/intern/bpy_app_openvdb.h
new file mode 100644
index 00000000000..12fa54ea7a3
--- /dev/null
+++ b/source/blender/python/intern/bpy_app_openvdb.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) 2015 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_openvdb.h
+ * \ingroup pythonintern
+ */
+
+#ifndef __BPY_APP_OPENVDB_H__
+#define __BPY_APP_OPENVDB_H__
+
+PyObject *BPY_app_openvdb_struct(void);
+
+#endif /* __BPY_APP_OPENVDB_H__ */
+
diff --git a/source/blender/render/extern/include/RE_render_ext.h b/source/blender/render/extern/include/RE_render_ext.h
index 6adbb6d0e62..9d8a1a66771 100644
--- a/source/blender/render/extern/include/RE_render_ext.h
+++ b/source/blender/render/extern/include/RE_render_ext.h
@@ -59,6 +59,12 @@ void RE_free_sample_material(struct Material *mat);
void RE_sample_material_color(struct Material *mat, float color[3], float *alpha, const float volume_co[3], const float surface_co[3],
int face_index, short hit_quad, struct DerivedMesh *orcoDm, struct Object *ob);
+/* pointdensity.c */
+
+struct PointDensity;
+
+void RE_sample_point_density(struct Scene *scene, struct PointDensity *pd, int resolution, float *values);
+
void RE_init_texture_rng(void);
void RE_exit_texture_rng(void);
#endif /* __RE_RENDER_EXT_H__ */
diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c
index 31efdb95ac1..4fa942eac99 100644
--- a/source/blender/render/intern/source/pipeline.c
+++ b/source/blender/render/intern/source/pipeline.c
@@ -2812,6 +2812,8 @@ static bool check_valid_camera_multiview(Scene *scene, Object *camera, ReportLis
static int check_valid_camera(Scene *scene, Object *camera_override, ReportList *reports)
{
+ const char *err_msg = "No camera found in scene \"%s\"";
+
if (camera_override == NULL && scene->camera == NULL)
scene->camera = BKE_scene_camera_find(scene);
@@ -2823,14 +2825,17 @@ static int check_valid_camera(Scene *scene, Object *camera_override, ReportList
Sequence *seq = scene->ed->seqbase.first;
while (seq) {
- if (seq->type == SEQ_TYPE_SCENE && seq->scene) {
+ if ((seq->type == SEQ_TYPE_SCENE) &&
+ ((seq->flag & SEQ_SCENE_STRIPS) == 0) &&
+ (seq->scene != NULL))
+ {
if (!seq->scene_camera) {
if (!seq->scene->camera && !BKE_scene_camera_find(seq->scene)) {
/* camera could be unneeded due to composite nodes */
Object *override = (seq->scene == scene) ? camera_override : NULL;
if (!check_valid_compositing_camera(seq->scene, override)) {
- BKE_reportf(reports, RPT_ERROR, "No camera found in scene \"%s\"", seq->scene->id.name+2);
+ BKE_reportf(reports, RPT_ERROR, err_msg, seq->scene->id.name + 2);
return false;
}
}
@@ -2844,7 +2849,7 @@ static int check_valid_camera(Scene *scene, Object *camera_override, ReportList
}
}
else if (!check_valid_compositing_camera(scene, camera_override)) {
- BKE_report(reports, RPT_ERROR, "No camera found in scene");
+ BKE_reportf(reports, RPT_ERROR, err_msg, scene->id.name + 2);
return false;
}
diff --git a/source/blender/render/intern/source/pointdensity.c b/source/blender/render/intern/source/pointdensity.c
index 3a1f1fbb744..5cd8a739125 100644
--- a/source/blender/render/intern/source/pointdensity.c
+++ b/source/blender/render/intern/source/pointdensity.c
@@ -45,6 +45,7 @@
#include "BKE_DerivedMesh.h"
#include "BKE_lattice.h"
#include "BKE_main.h"
+#include "BKE_object.h"
#include "BKE_particle.h"
#include "BKE_scene.h"
#include "BKE_texture.h"
@@ -58,6 +59,8 @@
#include "texture.h"
#include "pointdensity.h"
+#include "RE_render_ext.h"
+
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* defined in pipeline.c, is hardcopy of active dynamic allocated Render */
/* only to be used here in this file, it's for speed */
@@ -83,6 +86,10 @@ static int point_data_used(PointDensity *pd)
{
pd_bitflag |= POINT_DATA_LIFE;
}
+ if ((pd->color_source == TEX_PD_COLOR_PARTTEX))
+ {
+ pd_bitflag |= POINT_DATA_COLOR;
+ }
}
return pd_bitflag;
@@ -104,6 +111,10 @@ static void alloc_point_data(PointDensity *pd, int total_particles, int point_da
/* store 1 channel of lifetime data */
data_size += 1;
}
+ if (point_data_used & POINT_DATA_COLOR) {
+ /* store 3 channels of color data */
+ data_size += 3;
+ }
if (data_size) {
pd->point_data = MEM_mallocN(sizeof(float) * data_size * total_particles,
@@ -111,6 +122,19 @@ static void alloc_point_data(PointDensity *pd, int total_particles, int point_da
}
}
+/* offset of age and color data in the common point data array */
+static void point_data_get_offset(int total_particles, int point_data_used, int *offset_life, int *offset_color)
+{
+ *offset_life = *offset_color = 0;
+ if (point_data_used & POINT_DATA_VEL) {
+ *offset_life += total_particles * 3;
+ *offset_color += total_particles * 3;
+ }
+ if (point_data_used & POINT_DATA_LIFE) {
+ *offset_color += total_particles;
+ }
+}
+
static void pointdensity_cache_psys(Scene *scene,
PointDensity *pd,
Object *ob,
@@ -126,7 +150,7 @@ static void pointdensity_cache_psys(Scene *scene,
ParticleData *pa = NULL;
float cfra = BKE_scene_frame_get(scene);
int i /*, childexists*/ /* UNUSED */;
- int total_particles, offset = 0;
+ int total_particles, offset_life = 0, offset_color = 0;
int data_used = point_data_used(pd);
float partco[3];
@@ -148,6 +172,7 @@ static void pointdensity_cache_psys(Scene *scene,
sim.scene = scene;
sim.ob = ob;
sim.psys = psys;
+ sim.psmd = psys_get_modifier(ob, psys);
/* in case ob->imat isn't up-to-date */
invert_m4_m4(ob->imat, ob->obmat);
@@ -158,9 +183,7 @@ static void pointdensity_cache_psys(Scene *scene,
pd->point_tree = BLI_bvhtree_new(total_particles, 0.0, 4, 6);
alloc_point_data(pd, total_particles, data_used);
pd->totpoints = total_particles;
- if (data_used & POINT_DATA_VEL) {
- offset = pd->totpoints * 3;
- }
+ point_data_get_offset(total_particles, data_used, &offset_life, &offset_color);
#if 0 /* UNUSED */
if (psys->totchild > 0 && !(psys->part->draw & PART_DRAW_PARENT))
@@ -223,7 +246,12 @@ static void pointdensity_cache_psys(Scene *scene,
pd->point_data[i * 3 + 2] = state.vel[2];
}
if (data_used & POINT_DATA_LIFE) {
- pd->point_data[offset + i] = state.time;
+ pd->point_data[offset_life + i] = state.time;
+ }
+ if (data_used & POINT_DATA_COLOR) {
+ ParticleTexture ptex;
+ psys_get_texture(&sim, pa, &ptex, PAMAP_COLOR, cfra);
+ copy_v3_v3(&pd->point_data[offset_color + i * 3], ptex.color);
}
}
@@ -385,8 +413,9 @@ typedef struct PointDensityRangeData {
short falloff_type;
short noise_influence;
float *age;
+ float *col;
int point_data_used;
- int offset;
+ int offset_life, offset_color;
struct CurveMapping *density_curve;
float velscale;
} PointDensityRangeData;
@@ -403,7 +432,10 @@ static void accum_density(void *userdata, int index, float squared_dist)
pdr->vec[2] += pdr->point_data[index * 3 + 2]; // * density;
}
if (pdr->point_data_used & POINT_DATA_LIFE) {
- *pdr->age += pdr->point_data[pdr->offset + index]; // * density;
+ *pdr->age += pdr->point_data[pdr->offset_life + index]; // * density;
+ }
+ if (pdr->point_data_used & POINT_DATA_COLOR) {
+ add_v3_v3(pdr->col, &pdr->point_data[pdr->offset_color + index]); // * density;
}
if (pdr->falloff_type == TEX_PD_FALLOFF_STD)
@@ -418,7 +450,7 @@ static void accum_density(void *userdata, int index, float squared_dist)
density = sqrtf(dist);
else if (pdr->falloff_type == TEX_PD_FALLOFF_PARTICLE_AGE) {
if (pdr->point_data_used & POINT_DATA_LIFE)
- density = dist * MIN2(pdr->point_data[pdr->offset + index], 1.0f);
+ density = dist * MIN2(pdr->point_data[pdr->offset_life + index], 1.0f);
else
density = dist;
}
@@ -439,7 +471,7 @@ static void accum_density(void *userdata, int index, float squared_dist)
static void init_pointdensityrangedata(PointDensity *pd, PointDensityRangeData *pdr,
- float *density, float *vec, float *age, struct CurveMapping *density_curve, float velscale)
+ float *density, float *vec, float *age, float *col, struct CurveMapping *density_curve, float velscale)
{
pdr->squared_radius = pd->radius * pd->radius;
pdr->density = density;
@@ -447,23 +479,26 @@ static void init_pointdensityrangedata(PointDensity *pd, PointDensityRangeData *
pdr->falloff_type = pd->falloff_type;
pdr->vec = vec;
pdr->age = age;
+ pdr->col = col;
pdr->softness = pd->falloff_softness;
pdr->noise_influence = pd->noise_influence;
pdr->point_data_used = point_data_used(pd);
- pdr->offset = (pdr->point_data_used & POINT_DATA_VEL) ? pd->totpoints * 3 : 0;
+ point_data_get_offset(pd->totpoints, pdr->point_data_used, &pdr->offset_life, &pdr->offset_color);
pdr->density_curve = density_curve;
pdr->velscale = velscale;
}
-int pointdensitytex(Tex *tex, const float texvec[3], TexResult *texres)
+static int pointdensity(PointDensity *pd,
+ const float texvec[3],
+ TexResult *texres,
+ float *r_age,
+ float r_vec[3])
{
int retval = TEX_INT;
- PointDensity *pd = tex->pd;
PointDensityRangeData pdr;
float density = 0.0f, age = 0.0f, time = 0.0f;
- float vec[3] = {0.0f, 0.0f, 0.0f}, co[3];
- float col[4];
+ float vec[3] = {0.0f, 0.0f, 0.0f}, color[3] = {0.0f, 0.0f, 0.0f}, co[3];
float turb, noise_fac;
int num = 0;
@@ -472,7 +507,7 @@ int pointdensitytex(Tex *tex, const float texvec[3], TexResult *texres)
if ((!pd) || (!pd->point_tree))
return 0;
- init_pointdensityrangedata(pd, &pdr, &density, vec, &age,
+ init_pointdensityrangedata(pd, &pdr, &density, vec, &age, color,
(pd->flag & TEX_PD_FALLOFF_CURVE ? pd->falloff_curve : NULL),
pd->falloff_speed_scale * 0.001f);
noise_fac = pd->noise_fac * 0.5f; /* better default */
@@ -525,11 +560,24 @@ int pointdensitytex(Tex *tex, const float texvec[3], TexResult *texres)
}
texres->tin = density;
- BRICONT;
+ texres->tr = color[0];
+ texres->tg = color[1];
+ texres->tb = color[2];
+ if (r_age != NULL) {
+ *r_age = age;
+ }
+ if (r_vec != NULL) {
+ copy_v3_v3(r_vec, vec);
+ }
- if (pd->color_source == TEX_PD_COLOR_CONSTANT)
- return retval;
+ return retval;
+}
+static int pointdensity_color(PointDensity *pd, TexResult *texres, float age, const float vec[3])
+{
+ int retval = 0;
+ float col[4];
+
retval |= TEX_RGB;
switch (pd->color_source) {
@@ -559,8 +607,12 @@ int pointdensitytex(Tex *tex, const float texvec[3], TexResult *texres)
}
case TEX_PD_COLOR_PARTVEL:
texres->talpha = true;
- mul_v3_fl(vec, pd->speed_scale);
- copy_v3_v3(&texres->tr, vec);
+ mul_v3_v3fl(&texres->tr, vec, pd->speed_scale);
+ texres->ta = texres->tin;
+ break;
+ case TEX_PD_COLOR_PARTTEX:
+ /* texres already has the particle color */
+ texres->talpha = true;
texres->ta = texres->tin;
break;
case TEX_PD_COLOR_CONSTANT:
@@ -568,6 +620,20 @@ int pointdensitytex(Tex *tex, const float texvec[3], TexResult *texres)
texres->tr = texres->tg = texres->tb = texres->ta = 1.0f;
break;
}
+
+ return retval;
+}
+
+int pointdensitytex(Tex *tex, const float texvec[3], TexResult *texres)
+{
+ PointDensity *pd = tex->pd;
+ float age = 0.0f;
+ float vec[3] = {0.0f, 0.0f, 0.0f};
+ int retval = pointdensity(pd, texvec, texres, &age, vec);
+
+ BRICONT;
+
+ retval |= pointdensity_color(pd, texres, age, vec);
BRICONTRGB;
return retval;
@@ -578,3 +644,104 @@ int pointdensitytex(Tex *tex, const float texvec[3], TexResult *texres)
}
#endif
}
+
+static void sample_dummy_point_density(int resolution, float *values)
+{
+ memset(values, 0, sizeof(float) * 4 * resolution * resolution * resolution);
+}
+
+static void particle_system_minmax(Object *object,
+ ParticleSystem *psys,
+ float radius,
+ float min[3], float max[3])
+{
+ ParticleSettings *part = psys->part;
+ float imat[4][4];
+ float size[3] = {radius, radius, radius};
+ PARTICLE_P;
+ INIT_MINMAX(min, max);
+ if (part->type == PART_HAIR) {
+ /* TOOD(sergey): Not supported currently. */
+ return;
+ }
+ invert_m4_m4(imat, object->obmat);
+ LOOP_PARTICLES {
+ float co_object[3], co_min[3], co_max[3];
+ mul_v3_m4v3(co_object, imat, pa->state.co);
+ sub_v3_v3v3(co_min, co_object, size);
+ add_v3_v3v3(co_max, co_object, size);
+ minmax_v3v3_v3(min, max, co_min);
+ minmax_v3v3_v3(min, max, co_max);
+ }
+}
+
+void RE_sample_point_density(Scene *scene, PointDensity *pd,
+ int resolution, float *values)
+{
+ const size_t resolution2 = resolution * resolution;
+ Object *object = pd->object;
+ size_t x, y, z;
+ float min[3], max[3], dim[3], mat[4][4];
+
+ if (object == NULL) {
+ sample_dummy_point_density(resolution, values);
+ return;
+ }
+
+ if (pd->source == TEX_PD_PSYS) {
+ ParticleSystem *psys;
+ if (pd->psys == 0) {
+ sample_dummy_point_density(resolution, values);
+ return;
+ }
+ psys = BLI_findlink(&object->particlesystem, pd->psys - 1);
+ if (psys == NULL) {
+ sample_dummy_point_density(resolution, values);
+ return;
+ }
+ particle_system_minmax(object, psys, pd->radius, min, max);
+ }
+ else {
+ float radius[3] = {pd->radius, pd->radius, pd->radius};
+ float *loc, *size;
+ BKE_object_obdata_texspace_get(pd->object, NULL, &loc, &size, NULL);
+ sub_v3_v3v3(min, loc, size);
+ add_v3_v3v3(max, loc, size);
+ /* Adjust texture space to include density points on the boundaries. */
+ sub_v3_v3(min, radius);
+ add_v3_v3(max, radius);
+ }
+
+ sub_v3_v3v3(dim, max, min);
+ if (dim[0] <= 0.0f || dim[1] <= 0.0f || dim[2] <= 0.0f) {
+ sample_dummy_point_density(resolution, values);
+ return;
+ }
+
+ /* Same matricies/resolution as dupli_render_particle_set(). */
+ unit_m4(mat);
+ cache_pointdensity_ex(scene, pd, mat, mat, 1, 1);
+
+ for (z = 0; z < resolution; ++z) {
+ for (y = 0; y < resolution; ++y) {
+ for (x = 0; x < resolution; ++x) {
+ size_t index = z * resolution2 + y * resolution + x;
+ float texvec[3];
+ float age, vec[3];
+ TexResult texres;
+
+ copy_v3_v3(texvec, min);
+ texvec[0] += dim[0] * (float)x / (float)resolution;
+ texvec[1] += dim[1] * (float)y / (float)resolution;
+ texvec[2] += dim[2] * (float)z / (float)resolution;
+
+ pointdensity(pd, texvec, &texres, &age, vec);
+ pointdensity_color(pd, &texres, age, vec);
+
+ copy_v3_v3(&values[index*4 + 0], &texres.tr);
+ values[index*4 + 3] = texres.tin;
+ }
+ }
+ }
+ free_pointdensity(pd);
+}
diff --git a/source/blender/windowmanager/3d_widgets/arrow_widget.c b/source/blender/windowmanager/3d_widgets/arrow_widget.c
new file mode 100644
index 00000000000..6cd427a019a
--- /dev/null
+++ b/source/blender/windowmanager/3d_widgets/arrow_widget.c
@@ -0,0 +1,249 @@
+int _WIDGET_nverts_arrow = 81;
+int _WIDGET_ntris_arrow = 76;
+
+float _WIDGET_verts_arrow[][3] = {
+ {0.023005, 0.023005, 0.000524},
+ {0.030057, 0.012450, 0.000524},
+ {0.032533, 0.000000, 0.000524},
+ {0.030057, -0.012450, 0.000524},
+ {0.023005, -0.023005, 0.000524},
+ {0.012450, -0.030057, 0.000524},
+ {-0.000000, -0.032533, 0.000524},
+ {-0.012450, -0.030057, 0.000524},
+ {-0.023005, -0.023005, 0.000524},
+ {-0.030057, -0.012450, 0.000524},
+ {-0.032533, -0.000000, 0.000524},
+ {-0.030057, 0.012450, 0.000524},
+ {-0.023005, 0.023005, 0.000524},
+ {-0.012450, 0.030057, 0.000524},
+ {-0.000000, 0.032533, 0.000524},
+ {0.012450, 0.030057, 0.000524},
+ {-0.000000, 0.131111, 0.666568},
+ {-0.050174, 0.121131, 0.666568},
+ {-0.092709, 0.092709, 0.666568},
+ {-0.121131, 0.050174, 0.666568},
+ {-0.131111, -0.000000, 0.666568},
+ {-0.121131, -0.050174, 0.666568},
+ {-0.092709, -0.092709, 0.666568},
+ {-0.050174, -0.121131, 0.666568},
+ {-0.000000, -0.131111, 0.666568},
+ {0.050174, -0.121131, 0.666568},
+ {0.092709, -0.092709, 0.666568},
+ {0.121131, -0.050174, 0.666568},
+ {0.131111, 0.000000, 0.666568},
+ {0.121131, 0.050174, 0.666568},
+ {0.092709, 0.092709, 0.666568},
+ {0.050174, 0.121131, 0.666568},
+ {-0.000000, 0.131111, 0.666568},
+ {-0.050174, 0.121131, 0.666568},
+ {-0.092709, 0.092709, 0.666568},
+ {-0.121131, 0.050174, 0.666568},
+ {-0.131111, -0.000000, 0.666568},
+ {-0.121131, -0.050174, 0.666568},
+ {-0.092709, -0.092709, 0.666568},
+ {-0.050174, -0.121131, 0.666568},
+ {-0.000000, -0.131111, 0.666568},
+ {0.050174, -0.121131, 0.666568},
+ {0.092709, -0.092709, 0.666568},
+ {0.121131, -0.050174, 0.666568},
+ {0.131111, 0.000000, 0.666568},
+ {0.121131, 0.050174, 0.666568},
+ {0.092709, 0.092709, 0.666568},
+ {0.050174, 0.121131, 0.666568},
+ {-0.000000, -0.000000, 0.999876},
+ {0.023005, 0.023005, 0.000524},
+ {0.030057, 0.012450, 0.000524},
+ {0.032533, 0.000000, 0.000524},
+ {0.030057, -0.012450, 0.000524},
+ {0.023005, -0.023005, 0.000524},
+ {0.012450, -0.030057, 0.000524},
+ {-0.000000, -0.032533, 0.000524},
+ {-0.012450, -0.030057, 0.000524},
+ {-0.023005, -0.023005, 0.000524},
+ {-0.030057, -0.012450, 0.000524},
+ {-0.032533, -0.000000, 0.000524},
+ {-0.030057, 0.012450, 0.000524},
+ {-0.023005, 0.023005, 0.000524},
+ {-0.012450, 0.030057, 0.000524},
+ {-0.000000, 0.032533, 0.000524},
+ {0.012450, 0.030057, 0.000524},
+ {0.023005, 0.023005, 0.673867},
+ {0.030057, 0.012450, 0.673867},
+ {0.032533, 0.000000, 0.673867},
+ {0.030057, -0.012450, 0.673867},
+ {0.023005, -0.023005, 0.673867},
+ {0.012450, -0.030057, 0.673867},
+ {-0.000000, -0.032533, 0.673867},
+ {-0.012450, -0.030057, 0.673867},
+ {-0.023005, -0.023005, 0.673867},
+ {-0.030057, -0.012450, 0.673867},
+ {-0.032533, -0.000000, 0.673867},
+ {-0.030057, 0.012450, 0.673867},
+ {-0.023005, 0.023005, 0.673867},
+ {-0.012450, 0.030057, 0.673867},
+ {-0.000000, 0.032533, 0.673867},
+ {0.012450, 0.030057, 0.673867},
+};
+
+float _WIDGET_normals_arrow[][3] = {
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.000000, -1.000000},
+ {0.000000, 0.930570, 0.366039},
+ {-0.356120, 0.859737, 0.366039},
+ {-0.658010, 0.658010, 0.366039},
+ {-0.859737, 0.356120, 0.366039},
+ {-0.930570, 0.000000, 0.366039},
+ {-0.859737, -0.356120, 0.366039},
+ {-0.658010, -0.658010, 0.366039},
+ {-0.356120, -0.859737, 0.366039},
+ {0.000000, -0.930570, 0.366039},
+ {0.356120, -0.859737, 0.366039},
+ {0.658010, -0.658010, 0.366039},
+ {0.859737, -0.356120, 0.366039},
+ {0.930570, 0.000000, 0.366039},
+ {0.859737, 0.356120, 0.366039},
+ {0.658010, 0.658010, 0.366039},
+ {0.356120, 0.859737, 0.366039},
+ {0.000000, 0.000000, 1.000000},
+ {0.707083, 0.707083, 0.000000},
+ {0.923856, 0.382672, 0.000000},
+ {1.000000, 0.000000, 0.000000},
+ {0.923856, -0.382672, 0.000000},
+ {0.707083, -0.707083, 0.000000},
+ {0.382672, -0.923856, 0.000000},
+ {0.000000, -1.000000, 0.000000},
+ {-0.382672, -0.923856, 0.000000},
+ {-0.707083, -0.707083, 0.000000},
+ {-0.923856, -0.382672, 0.000000},
+ {-1.000000, 0.000000, 0.000000},
+ {-0.923856, 0.382672, 0.000000},
+ {-0.707083, 0.707083, 0.000000},
+ {-0.382672, 0.923856, 0.000000},
+ {0.000000, 1.000000, 0.000000},
+ {0.382672, 0.923856, 0.000000},
+ {0.707083, 0.707083, 0.000000},
+ {0.923856, 0.382672, 0.000000},
+ {1.000000, 0.000000, 0.000000},
+ {0.923856, -0.382672, 0.000000},
+ {0.707083, -0.707083, 0.000000},
+ {0.382672, -0.923856, 0.000000},
+ {0.000000, -0.999969, 0.000000},
+ {-0.382672, -0.923856, 0.000000},
+ {-0.707083, -0.707083, 0.000000},
+ {-0.923856, -0.382672, 0.000000},
+ {-1.000000, 0.000000, 0.000000},
+ {-0.923856, 0.382672, 0.000000},
+ {-0.707083, 0.707083, 0.000000},
+ {-0.382672, 0.923856, 0.000000},
+ {0.000000, 1.000000, 0.000000},
+ {0.382672, 0.923856, 0.000000},
+};
+
+unsigned short _WIDGET_indices_arrow[] = {
+ 13, 1, 9,
+ 49, 65, 66,
+ 62, 78, 79,
+ 51, 67, 68,
+ 53, 69, 70,
+ 55, 71, 72,
+ 57, 73, 74,
+ 59, 75, 76,
+ 64, 80, 65,
+ 61, 77, 78,
+ 50, 66, 67,
+ 52, 68, 69,
+ 54, 70, 71,
+ 56, 72, 73,
+ 30, 29, 28,
+ 39, 40, 48,
+ 37, 38, 48,
+ 35, 36, 48,
+ 46, 47, 48,
+ 33, 34, 48,
+ 44, 45, 48,
+ 42, 43, 48,
+ 40, 41, 48,
+ 38, 39, 48,
+ 36, 37, 48,
+ 47, 32, 48,
+ 34, 35, 48,
+ 45, 46, 48,
+ 32, 33, 48,
+ 43, 44, 48,
+ 41, 42, 48,
+ 60, 76, 77,
+ 63, 79, 80,
+ 58, 74, 75,
+ 11, 12, 13,
+ 9, 10, 11,
+ 7, 8, 9,
+ 7, 9, 6,
+ 5, 6, 4,
+ 1, 2, 3,
+ 1, 14, 0,
+ 63, 62, 79,
+ 9, 11, 13,
+ 50, 49, 66,
+ 14, 15, 0,
+ 6, 9, 1,
+ 1, 13, 14,
+ 6, 3, 4,
+ 3, 6, 1,
+ 52, 51, 68,
+ 54, 53, 70,
+ 56, 55, 72,
+ 58, 57, 74,
+ 60, 59, 76,
+ 49, 64, 65,
+ 62, 61, 78,
+ 51, 50, 67,
+ 53, 52, 69,
+ 55, 54, 71,
+ 57, 56, 73,
+ 17, 20, 18,
+ 61, 60, 77,
+ 21, 20, 22,
+ 23, 20, 24,
+ 26, 25, 24,
+ 16, 31, 30,
+ 64, 63, 80,
+ 28, 27, 26,
+ 17, 26, 20,
+ 20, 26, 24,
+ 28, 26, 16,
+ 16, 30, 28,
+ 20, 23, 22,
+ 16, 26, 17,
+ 20, 19, 18,
+ 59, 58, 75,
+};
diff --git a/source/blender/windowmanager/3d_widgets/dial_widget.c b/source/blender/windowmanager/3d_widgets/dial_widget.c
new file mode 100644
index 00000000000..002bd029dac
--- /dev/null
+++ b/source/blender/windowmanager/3d_widgets/dial_widget.c
@@ -0,0 +1,779 @@
+int _WIDGET_nverts_dial = 192;
+int _WIDGET_ntris_dial = 384;
+
+float _WIDGET_verts_dial[][3] = {
+ {1.034000, 0.000000, 0.000000},
+ {1.017000, 0.000000, 0.029445},
+ {0.983000, 0.000000, 0.029445},
+ {0.966000, 0.000000, 0.000000},
+ {0.983000, 0.000000, -0.029445},
+ {1.017000, 0.000000, -0.029445},
+ {1.014132, 0.201723, 0.000000},
+ {0.997459, 0.198407, 0.029445},
+ {0.964112, 0.191774, 0.029445},
+ {0.947439, 0.188457, 0.000000},
+ {0.964112, 0.191774, -0.029445},
+ {0.997459, 0.198407, -0.029445},
+ {0.955292, 0.395695, 0.000000},
+ {0.939586, 0.389189, 0.029445},
+ {0.908174, 0.376178, 0.029445},
+ {0.892468, 0.369672, 0.000000},
+ {0.908174, 0.376178, -0.029445},
+ {0.939586, 0.389189, -0.029445},
+ {0.859740, 0.574460, 0.000000},
+ {0.845605, 0.565015, 0.029445},
+ {0.817335, 0.546126, 0.029445},
+ {0.803200, 0.536681, 0.000000},
+ {0.817335, 0.546126, -0.029445},
+ {0.845605, 0.565015, -0.029445},
+ {0.731148, 0.731148, 0.000000},
+ {0.719128, 0.719128, 0.029445},
+ {0.695086, 0.695086, 0.029445},
+ {0.683065, 0.683065, 0.000000},
+ {0.695086, 0.695086, -0.029445},
+ {0.719128, 0.719128, -0.029445},
+ {0.574460, 0.859740, 0.000000},
+ {0.565015, 0.845605, 0.029445},
+ {0.546125, 0.817335, 0.029445},
+ {0.536681, 0.803200, 0.000000},
+ {0.546125, 0.817335, -0.029445},
+ {0.565015, 0.845605, -0.029445},
+ {0.395695, 0.955291, 0.000000},
+ {0.389189, 0.939585, 0.029445},
+ {0.376178, 0.908173, 0.029445},
+ {0.369672, 0.892467, 0.000000},
+ {0.376178, 0.908173, -0.029445},
+ {0.389189, 0.939585, -0.029445},
+ {0.201724, 1.014132, 0.000000},
+ {0.198407, 0.997459, 0.029445},
+ {0.191774, 0.964112, 0.029445},
+ {0.188457, 0.947439, 0.000000},
+ {0.191774, 0.964112, -0.029445},
+ {0.198407, 0.997459, -0.029445},
+ {0.000000, 1.034000, 0.000000},
+ {0.000000, 1.017000, 0.029445},
+ {0.000000, 0.983000, 0.029445},
+ {0.000000, 0.966000, 0.000000},
+ {0.000000, 0.983000, -0.029445},
+ {0.000000, 1.017000, -0.029445},
+ {-0.201723, 1.014132, 0.000000},
+ {-0.198407, 0.997459, 0.029445},
+ {-0.191774, 0.964112, 0.029445},
+ {-0.188457, 0.947439, 0.000000},
+ {-0.191774, 0.964112, -0.029445},
+ {-0.198407, 0.997459, -0.029445},
+ {-0.395695, 0.955291, 0.000000},
+ {-0.389189, 0.939585, 0.029445},
+ {-0.376178, 0.908174, 0.029445},
+ {-0.369672, 0.892468, 0.000000},
+ {-0.376178, 0.908174, -0.029445},
+ {-0.389189, 0.939585, -0.029445},
+ {-0.574459, 0.859740, 0.000000},
+ {-0.565015, 0.845605, 0.029445},
+ {-0.546125, 0.817335, 0.029445},
+ {-0.536681, 0.803200, 0.000000},
+ {-0.546125, 0.817335, -0.029445},
+ {-0.565015, 0.845605, -0.029445},
+ {-0.731149, 0.731148, 0.000000},
+ {-0.719128, 0.719127, 0.029445},
+ {-0.695086, 0.695086, 0.029445},
+ {-0.683065, 0.683065, 0.000000},
+ {-0.695086, 0.695086, -0.029445},
+ {-0.719128, 0.719127, -0.029445},
+ {-0.859740, 0.574460, 0.000000},
+ {-0.845604, 0.565015, 0.029445},
+ {-0.817335, 0.546126, 0.029445},
+ {-0.803200, 0.536681, 0.000000},
+ {-0.817335, 0.546126, -0.029445},
+ {-0.845604, 0.565015, -0.029445},
+ {-0.955291, 0.395695, 0.000000},
+ {-0.939585, 0.389189, 0.029445},
+ {-0.908173, 0.376178, 0.029445},
+ {-0.892468, 0.369672, 0.000000},
+ {-0.908173, 0.376178, -0.029445},
+ {-0.939585, 0.389189, -0.029445},
+ {-1.014132, 0.201723, 0.000000},
+ {-0.997459, 0.198407, 0.029445},
+ {-0.964112, 0.191774, 0.029445},
+ {-0.947439, 0.188457, 0.000000},
+ {-0.964112, 0.191774, -0.029445},
+ {-0.997459, 0.198407, -0.029445},
+ {-1.034000, 0.000000, 0.000000},
+ {-1.017000, 0.000000, 0.029445},
+ {-0.983000, 0.000000, 0.029445},
+ {-0.966000, 0.000000, 0.000000},
+ {-0.983000, 0.000000, -0.029445},
+ {-1.017000, 0.000000, -0.029445},
+ {-1.014132, -0.201723, 0.000000},
+ {-0.997459, -0.198407, 0.029445},
+ {-0.964112, -0.191774, 0.029445},
+ {-0.947439, -0.188457, 0.000000},
+ {-0.964112, -0.191774, -0.029445},
+ {-0.997459, -0.198407, -0.029445},
+ {-0.955292, -0.395694, 0.000000},
+ {-0.939586, -0.389189, 0.029445},
+ {-0.908174, -0.376177, 0.029445},
+ {-0.892468, -0.369672, 0.000000},
+ {-0.908174, -0.376177, -0.029445},
+ {-0.939586, -0.389189, -0.029445},
+ {-0.859740, -0.574460, 0.000000},
+ {-0.845604, -0.565015, 0.029445},
+ {-0.817335, -0.546126, 0.029445},
+ {-0.803200, -0.536681, 0.000000},
+ {-0.817335, -0.546126, -0.029445},
+ {-0.845604, -0.565015, -0.029445},
+ {-0.731149, -0.731148, 0.000000},
+ {-0.719128, -0.719127, 0.029445},
+ {-0.695086, -0.695086, 0.029445},
+ {-0.683065, -0.683065, 0.000000},
+ {-0.695086, -0.695086, -0.029445},
+ {-0.719128, -0.719127, -0.029445},
+ {-0.574460, -0.859739, 0.000000},
+ {-0.565015, -0.845604, 0.029445},
+ {-0.546126, -0.817334, 0.029445},
+ {-0.536681, -0.803199, 0.000000},
+ {-0.546126, -0.817334, -0.029445},
+ {-0.565015, -0.845604, -0.029445},
+ {-0.395695, -0.955291, 0.000000},
+ {-0.389189, -0.939585, 0.029445},
+ {-0.376178, -0.908174, 0.029445},
+ {-0.369672, -0.892468, 0.000000},
+ {-0.376178, -0.908174, -0.029445},
+ {-0.389189, -0.939585, -0.029445},
+ {-0.201724, -1.014132, 0.000000},
+ {-0.198407, -0.997459, 0.029445},
+ {-0.191774, -0.964112, 0.029445},
+ {-0.188458, -0.947438, 0.000000},
+ {-0.191774, -0.964112, -0.029445},
+ {-0.198407, -0.997459, -0.029445},
+ {0.000000, -1.034000, 0.000000},
+ {0.000000, -1.017000, 0.029445},
+ {0.000000, -0.983000, 0.029445},
+ {0.000000, -0.966000, 0.000000},
+ {0.000000, -0.983000, -0.029445},
+ {0.000000, -1.017000, -0.029445},
+ {0.201723, -1.014132, 0.000000},
+ {0.198407, -0.997459, 0.029445},
+ {0.191773, -0.964112, 0.029445},
+ {0.188457, -0.947439, 0.000000},
+ {0.191773, -0.964112, -0.029445},
+ {0.198407, -0.997459, -0.029445},
+ {0.395695, -0.955291, 0.000000},
+ {0.389189, -0.939585, 0.029445},
+ {0.376178, -0.908173, 0.029445},
+ {0.369672, -0.892467, 0.000000},
+ {0.376178, -0.908173, -0.029445},
+ {0.389189, -0.939585, -0.029445},
+ {0.574460, -0.859740, 0.000000},
+ {0.565015, -0.845605, 0.029445},
+ {0.546125, -0.817335, 0.029445},
+ {0.536681, -0.803200, 0.000000},
+ {0.546125, -0.817335, -0.029445},
+ {0.565015, -0.845605, -0.029445},
+ {0.731148, -0.731149, 0.000000},
+ {0.719127, -0.719128, 0.029445},
+ {0.695086, -0.695086, 0.029445},
+ {0.683065, -0.683066, 0.000000},
+ {0.695086, -0.695086, -0.029445},
+ {0.719127, -0.719128, -0.029445},
+ {0.859740, -0.574460, 0.000000},
+ {0.845605, -0.565015, 0.029445},
+ {0.817335, -0.546126, 0.029445},
+ {0.803200, -0.536681, 0.000000},
+ {0.817335, -0.546126, -0.029445},
+ {0.845605, -0.565015, -0.029445},
+ {0.955291, -0.395695, 0.000000},
+ {0.939585, -0.389189, 0.029445},
+ {0.908173, -0.376178, 0.029445},
+ {0.892467, -0.369673, 0.000000},
+ {0.908173, -0.376178, -0.029445},
+ {0.939585, -0.389189, -0.029445},
+ {1.014132, -0.201723, 0.000000},
+ {0.997459, -0.198407, 0.029445},
+ {0.964112, -0.191774, 0.029445},
+ {0.947439, -0.188457, 0.000000},
+ {0.964112, -0.191774, -0.029445},
+ {0.997459, -0.198407, -0.029445},
+};
+
+float _WIDGET_normals_dial[][3] = {
+ {1.000000, 0.000000, 0.000000},
+ {0.522691, 0.000000, 0.852504},
+ {-0.475845, 0.000000, 0.879513},
+ {-1.000000, 0.000000, 0.000000},
+ {-0.475845, 0.000000, -0.879513},
+ {0.522691, 0.000000, -0.852504},
+ {0.980773, 0.195074, 0.000000},
+ {0.512650, 0.101962, 0.852504},
+ {-0.466689, -0.092807, 0.879513},
+ {-0.980773, -0.195074, 0.000000},
+ {-0.466689, -0.092807, -0.879513},
+ {0.512650, 0.101962, -0.852504},
+ {0.923856, 0.382672, 0.000000},
+ {0.482894, 0.200018, 0.852504},
+ {-0.439619, -0.182073, 0.879513},
+ {-0.923856, -0.382672, 0.000000},
+ {-0.439619, -0.182073, -0.879513},
+ {0.482894, 0.200018, -0.852504},
+ {0.831446, 0.555559, 0.000000},
+ {0.434614, 0.290384, 0.852504},
+ {-0.395642, -0.264351, 0.879513},
+ {-0.831446, -0.555559, 0.000000},
+ {-0.395642, -0.264351, -0.879513},
+ {0.434614, 0.290384, -0.852504},
+ {0.707083, 0.707083, 0.000000},
+ {0.369610, 0.369610, 0.852504},
+ {-0.336467, -0.336467, 0.879513},
+ {-0.707083, -0.707083, 0.000000},
+ {-0.336467, -0.336467, -0.879513},
+ {0.369610, 0.369610, -0.852504},
+ {0.555559, 0.831446, 0.000000},
+ {0.290384, 0.434614, 0.852504},
+ {-0.264351, -0.395642, 0.879513},
+ {-0.555559, -0.831446, 0.000000},
+ {-0.264351, -0.395642, -0.879513},
+ {0.290384, 0.434614, -0.852504},
+ {0.382672, 0.923856, 0.000000},
+ {0.200018, 0.482894, 0.852504},
+ {-0.182073, -0.439619, 0.879513},
+ {-0.382672, -0.923856, 0.000000},
+ {-0.182073, -0.439619, -0.879513},
+ {0.200018, 0.482894, -0.852504},
+ {0.195074, 0.980773, 0.000000},
+ {0.101962, 0.512650, 0.852504},
+ {-0.092807, -0.466689, 0.879513},
+ {-0.195074, -0.980773, 0.000000},
+ {-0.092807, -0.466689, -0.879513},
+ {0.101962, 0.512650, -0.852504},
+ {0.000000, 1.000000, 0.000000},
+ {0.000000, 0.522691, 0.852504},
+ {0.000000, -0.475845, 0.879513},
+ {0.000000, -1.000000, 0.000000},
+ {0.000000, -0.475845, -0.879513},
+ {0.000000, 0.522691, -0.852504},
+ {-0.195074, 0.980773, 0.000000},
+ {-0.101962, 0.512650, 0.852504},
+ {0.092807, -0.466689, 0.879513},
+ {0.195074, -0.980773, 0.000000},
+ {0.092807, -0.466689, -0.879513},
+ {-0.101962, 0.512650, -0.852504},
+ {-0.382672, 0.923856, 0.000000},
+ {-0.200018, 0.482894, 0.852504},
+ {0.182073, -0.439619, 0.879513},
+ {0.382672, -0.923856, 0.000000},
+ {0.182073, -0.439619, -0.879513},
+ {-0.200018, 0.482894, -0.852504},
+ {-0.555559, 0.831446, 0.000000},
+ {-0.290384, 0.434614, 0.852504},
+ {0.264351, -0.395642, 0.879513},
+ {0.555559, -0.831446, 0.000000},
+ {0.264351, -0.395642, -0.879513},
+ {-0.290384, 0.434614, -0.852504},
+ {-0.707083, 0.707083, 0.000000},
+ {-0.369610, 0.369610, 0.852504},
+ {0.336467, -0.336467, 0.879513},
+ {0.707083, -0.707083, 0.000000},
+ {0.336467, -0.336467, -0.879513},
+ {-0.369610, 0.369610, -0.852504},
+ {-0.831446, 0.555559, 0.000000},
+ {-0.434614, 0.290384, 0.852504},
+ {0.395642, -0.264351, 0.879513},
+ {0.831446, -0.555559, 0.000000},
+ {0.395642, -0.264351, -0.879513},
+ {-0.434614, 0.290384, -0.852504},
+ {-0.923856, 0.382672, 0.000000},
+ {-0.482894, 0.200018, 0.852504},
+ {0.439619, -0.182073, 0.879513},
+ {0.923856, -0.382672, 0.000000},
+ {0.439619, -0.182073, -0.879513},
+ {-0.482894, 0.200018, -0.852504},
+ {-0.980773, 0.195074, 0.000000},
+ {-0.512650, 0.101962, 0.852504},
+ {0.466689, -0.092807, 0.879513},
+ {0.980773, -0.195074, 0.000000},
+ {0.466689, -0.092807, -0.879513},
+ {-0.512650, 0.101962, -0.852504},
+ {-1.000000, 0.000000, 0.000000},
+ {-0.522691, 0.000000, 0.852504},
+ {0.475845, 0.000000, 0.879513},
+ {1.000000, 0.000000, 0.000000},
+ {0.475845, 0.000000, -0.879513},
+ {-0.522691, 0.000000, -0.852504},
+ {-0.980773, -0.195074, 0.000000},
+ {-0.512650, -0.101962, 0.852504},
+ {0.466689, 0.092807, 0.879513},
+ {0.980773, 0.195074, 0.000000},
+ {0.466689, 0.092807, -0.879513},
+ {-0.512650, -0.101962, -0.852504},
+ {-0.923856, -0.382672, 0.000000},
+ {-0.482894, -0.200018, 0.852504},
+ {0.439619, 0.182073, 0.879513},
+ {0.923856, 0.382672, 0.000000},
+ {0.439619, 0.182073, -0.879513},
+ {-0.482894, -0.200018, -0.852504},
+ {-0.831446, -0.555559, 0.000000},
+ {-0.434614, -0.290384, 0.852504},
+ {0.395642, 0.264351, 0.879513},
+ {0.831446, 0.555559, 0.000000},
+ {0.395642, 0.264351, -0.879513},
+ {-0.434614, -0.290384, -0.852504},
+ {-0.707083, -0.707083, 0.000000},
+ {-0.369610, -0.369610, 0.852504},
+ {0.336467, 0.336467, 0.879513},
+ {0.707083, 0.707083, 0.000000},
+ {0.336467, 0.336467, -0.879513},
+ {-0.369610, -0.369610, -0.852504},
+ {-0.555559, -0.831446, 0.000000},
+ {-0.290384, -0.434614, 0.852504},
+ {0.264351, 0.395642, 0.879513},
+ {0.555559, 0.831446, 0.000000},
+ {0.264351, 0.395642, -0.879513},
+ {-0.290384, -0.434614, -0.852504},
+ {-0.382672, -0.923856, 0.000000},
+ {-0.200018, -0.482894, 0.852504},
+ {0.182073, 0.439619, 0.879513},
+ {0.382672, 0.923856, 0.000000},
+ {0.182073, 0.439619, -0.879513},
+ {-0.200018, -0.482894, -0.852504},
+ {-0.195074, -0.980773, 0.000000},
+ {-0.101962, -0.512650, 0.852504},
+ {0.092807, 0.466689, 0.879513},
+ {0.195074, 0.980773, 0.000000},
+ {0.092807, 0.466689, -0.879513},
+ {-0.101962, -0.512650, -0.852504},
+ {0.000000, -1.000000, 0.000000},
+ {0.000000, -0.522691, 0.852504},
+ {0.000000, 0.475845, 0.879513},
+ {0.000000, 1.000000, 0.000000},
+ {0.000000, 0.475845, -0.879513},
+ {0.000000, -0.522691, -0.852504},
+ {0.195074, -0.980773, 0.000000},
+ {0.101962, -0.512650, 0.852504},
+ {-0.092807, 0.466689, 0.879513},
+ {-0.195074, 0.980773, 0.000000},
+ {-0.092807, 0.466689, -0.879513},
+ {0.101962, -0.512650, -0.852504},
+ {0.382672, -0.923856, 0.000000},
+ {0.200018, -0.482894, 0.852504},
+ {-0.182073, 0.439619, 0.879513},
+ {-0.382672, 0.923856, 0.000000},
+ {-0.182073, 0.439619, -0.879513},
+ {0.200018, -0.482894, -0.852504},
+ {0.555559, -0.831446, 0.000000},
+ {0.290384, -0.434614, 0.852504},
+ {-0.264351, 0.395642, 0.879513},
+ {-0.555559, 0.831446, 0.000000},
+ {-0.264351, 0.395642, -0.879513},
+ {0.290384, -0.434614, -0.852504},
+ {0.707083, -0.707083, 0.000000},
+ {0.369610, -0.369610, 0.852504},
+ {-0.336467, 0.336467, 0.879513},
+ {-0.707083, 0.707083, 0.000000},
+ {-0.336467, 0.336467, -0.879513},
+ {0.369610, -0.369610, -0.852504},
+ {0.831446, -0.555559, 0.000000},
+ {0.434614, -0.290384, 0.852504},
+ {-0.395642, 0.264351, 0.879513},
+ {-0.831446, 0.555559, 0.000000},
+ {-0.395642, 0.264351, -0.879513},
+ {0.434614, -0.290384, -0.852504},
+ {0.923856, -0.382672, 0.000000},
+ {0.482894, -0.200018, 0.852504},
+ {-0.439619, 0.182073, 0.879513},
+ {-0.923856, 0.382672, 0.000000},
+ {-0.439619, 0.182073, -0.879513},
+ {0.482894, -0.200018, -0.852504},
+ {0.980773, -0.195074, 0.000000},
+ {0.512650, -0.101962, 0.852504},
+ {-0.466689, 0.092807, 0.879513},
+ {-0.980773, 0.195074, 0.000000},
+ {-0.466689, 0.092807, -0.879513},
+ {0.512650, -0.101962, -0.852504},
+};
+
+unsigned short _WIDGET_indices_dial[] = {
+ 6, 7, 1,
+ 7, 8, 2,
+ 8, 9, 3,
+ 9, 10, 4,
+ 10, 11, 5,
+ 5, 11, 6,
+ 12, 13, 7,
+ 13, 14, 8,
+ 14, 15, 9,
+ 15, 16, 10,
+ 16, 17, 11,
+ 11, 17, 12,
+ 18, 19, 13,
+ 13, 19, 20,
+ 20, 21, 15,
+ 15, 21, 22,
+ 22, 23, 17,
+ 17, 23, 18,
+ 24, 25, 19,
+ 19, 25, 26,
+ 26, 27, 21,
+ 21, 27, 28,
+ 28, 29, 23,
+ 23, 29, 24,
+ 30, 31, 25,
+ 25, 31, 32,
+ 26, 32, 33,
+ 27, 33, 34,
+ 34, 35, 29,
+ 29, 35, 30,
+ 36, 37, 31,
+ 31, 37, 38,
+ 38, 39, 33,
+ 39, 40, 34,
+ 40, 41, 35,
+ 35, 41, 36,
+ 36, 42, 43,
+ 43, 44, 38,
+ 44, 45, 39,
+ 45, 46, 40,
+ 46, 47, 41,
+ 47, 42, 36,
+ 48, 49, 43,
+ 49, 50, 44,
+ 50, 51, 45,
+ 51, 52, 46,
+ 52, 53, 47,
+ 47, 53, 48,
+ 54, 55, 49,
+ 49, 55, 56,
+ 50, 56, 57,
+ 57, 58, 52,
+ 58, 59, 53,
+ 53, 59, 54,
+ 60, 61, 55,
+ 55, 61, 62,
+ 56, 62, 63,
+ 63, 64, 58,
+ 64, 65, 59,
+ 59, 65, 60,
+ 66, 67, 61,
+ 61, 67, 68,
+ 68, 69, 63,
+ 69, 70, 64,
+ 70, 71, 65,
+ 71, 66, 60,
+ 72, 73, 67,
+ 73, 74, 68,
+ 68, 74, 75,
+ 75, 76, 70,
+ 76, 77, 71,
+ 71, 77, 72,
+ 78, 79, 73,
+ 79, 80, 74,
+ 74, 80, 81,
+ 81, 82, 76,
+ 82, 83, 77,
+ 83, 78, 72,
+ 78, 84, 85,
+ 85, 86, 80,
+ 80, 86, 87,
+ 87, 88, 82,
+ 82, 88, 89,
+ 89, 84, 78,
+ 90, 91, 85,
+ 91, 92, 86,
+ 86, 92, 93,
+ 93, 94, 88,
+ 88, 94, 95,
+ 95, 90, 84,
+ 96, 97, 91,
+ 97, 98, 92,
+ 98, 99, 93,
+ 99, 100, 94,
+ 100, 101, 95,
+ 101, 96, 90,
+ 102, 103, 97,
+ 103, 104, 98,
+ 104, 105, 99,
+ 99, 105, 106,
+ 106, 107, 101,
+ 101, 107, 102,
+ 108, 109, 103,
+ 103, 109, 110,
+ 110, 111, 105,
+ 105, 111, 112,
+ 112, 113, 107,
+ 107, 113, 108,
+ 114, 115, 109,
+ 115, 116, 110,
+ 116, 117, 111,
+ 111, 117, 118,
+ 112, 118, 119,
+ 113, 119, 114,
+ 114, 120, 121,
+ 121, 122, 116,
+ 122, 123, 117,
+ 117, 123, 124,
+ 124, 125, 119,
+ 125, 120, 114,
+ 126, 127, 121,
+ 121, 127, 128,
+ 128, 129, 123,
+ 123, 129, 130,
+ 130, 131, 125,
+ 125, 131, 126,
+ 132, 133, 127,
+ 133, 134, 128,
+ 128, 134, 135,
+ 135, 136, 130,
+ 136, 137, 131,
+ 131, 137, 132,
+ 132, 138, 139,
+ 133, 139, 140,
+ 134, 140, 141,
+ 141, 142, 136,
+ 142, 143, 137,
+ 143, 138, 132,
+ 138, 144, 145,
+ 139, 145, 146,
+ 146, 147, 141,
+ 141, 147, 148,
+ 148, 149, 143,
+ 149, 144, 138,
+ 144, 150, 151,
+ 151, 152, 146,
+ 146, 152, 153,
+ 153, 154, 148,
+ 154, 155, 149,
+ 155, 150, 144,
+ 156, 157, 151,
+ 151, 157, 158,
+ 158, 159, 153,
+ 159, 160, 154,
+ 160, 161, 155,
+ 155, 161, 156,
+ 156, 162, 163,
+ 163, 164, 158,
+ 158, 164, 165,
+ 165, 166, 160,
+ 160, 166, 167,
+ 167, 162, 156,
+ 162, 168, 169,
+ 169, 170, 164,
+ 164, 170, 171,
+ 165, 171, 172,
+ 166, 172, 173,
+ 173, 168, 162,
+ 174, 175, 169,
+ 175, 176, 170,
+ 170, 176, 177,
+ 177, 178, 172,
+ 172, 178, 179,
+ 173, 179, 174,
+ 174, 180, 181,
+ 181, 182, 176,
+ 176, 182, 183,
+ 183, 184, 178,
+ 178, 184, 185,
+ 179, 185, 180,
+ 186, 187, 181,
+ 187, 188, 182,
+ 188, 189, 183,
+ 183, 189, 190,
+ 190, 191, 185,
+ 191, 186, 180,
+ 0, 1, 187,
+ 1, 2, 188,
+ 2, 3, 189,
+ 3, 4, 190,
+ 190, 4, 5,
+ 191, 5, 0,
+ 0, 6, 1,
+ 1, 7, 2,
+ 2, 8, 3,
+ 3, 9, 4,
+ 4, 10, 5,
+ 0, 5, 6,
+ 6, 12, 7,
+ 7, 13, 8,
+ 8, 14, 9,
+ 9, 15, 10,
+ 10, 16, 11,
+ 6, 11, 12,
+ 12, 18, 13,
+ 14, 13, 20,
+ 14, 20, 15,
+ 16, 15, 22,
+ 16, 22, 17,
+ 12, 17, 18,
+ 18, 24, 19,
+ 20, 19, 26,
+ 20, 26, 21,
+ 22, 21, 28,
+ 22, 28, 23,
+ 18, 23, 24,
+ 24, 30, 25,
+ 26, 25, 32,
+ 27, 26, 33,
+ 28, 27, 34,
+ 28, 34, 29,
+ 24, 29, 30,
+ 30, 36, 31,
+ 32, 31, 38,
+ 32, 38, 33,
+ 33, 39, 34,
+ 34, 40, 35,
+ 30, 35, 36,
+ 37, 36, 43,
+ 37, 43, 38,
+ 38, 44, 39,
+ 39, 45, 40,
+ 40, 46, 41,
+ 41, 47, 36,
+ 42, 48, 43,
+ 43, 49, 44,
+ 44, 50, 45,
+ 45, 51, 46,
+ 46, 52, 47,
+ 42, 47, 48,
+ 48, 54, 49,
+ 50, 49, 56,
+ 51, 50, 57,
+ 51, 57, 52,
+ 52, 58, 53,
+ 48, 53, 54,
+ 54, 60, 55,
+ 56, 55, 62,
+ 57, 56, 63,
+ 57, 63, 58,
+ 58, 64, 59,
+ 54, 59, 60,
+ 60, 66, 61,
+ 62, 61, 68,
+ 62, 68, 63,
+ 63, 69, 64,
+ 64, 70, 65,
+ 65, 71, 60,
+ 66, 72, 67,
+ 67, 73, 68,
+ 69, 68, 75,
+ 69, 75, 70,
+ 70, 76, 71,
+ 66, 71, 72,
+ 72, 78, 73,
+ 73, 79, 74,
+ 75, 74, 81,
+ 75, 81, 76,
+ 76, 82, 77,
+ 77, 83, 72,
+ 79, 78, 85,
+ 79, 85, 80,
+ 81, 80, 87,
+ 81, 87, 82,
+ 83, 82, 89,
+ 83, 89, 78,
+ 84, 90, 85,
+ 85, 91, 86,
+ 87, 86, 93,
+ 87, 93, 88,
+ 89, 88, 95,
+ 89, 95, 84,
+ 90, 96, 91,
+ 91, 97, 92,
+ 92, 98, 93,
+ 93, 99, 94,
+ 94, 100, 95,
+ 95, 101, 90,
+ 96, 102, 97,
+ 97, 103, 98,
+ 98, 104, 99,
+ 100, 99, 106,
+ 100, 106, 101,
+ 96, 101, 102,
+ 102, 108, 103,
+ 104, 103, 110,
+ 104, 110, 105,
+ 106, 105, 112,
+ 106, 112, 107,
+ 102, 107, 108,
+ 108, 114, 109,
+ 109, 115, 110,
+ 110, 116, 111,
+ 112, 111, 118,
+ 113, 112, 119,
+ 108, 113, 114,
+ 115, 114, 121,
+ 115, 121, 116,
+ 116, 122, 117,
+ 118, 117, 124,
+ 118, 124, 119,
+ 119, 125, 114,
+ 120, 126, 121,
+ 122, 121, 128,
+ 122, 128, 123,
+ 124, 123, 130,
+ 124, 130, 125,
+ 120, 125, 126,
+ 126, 132, 127,
+ 127, 133, 128,
+ 129, 128, 135,
+ 129, 135, 130,
+ 130, 136, 131,
+ 126, 131, 132,
+ 133, 132, 139,
+ 134, 133, 140,
+ 135, 134, 141,
+ 135, 141, 136,
+ 136, 142, 137,
+ 137, 143, 132,
+ 139, 138, 145,
+ 140, 139, 146,
+ 140, 146, 141,
+ 142, 141, 148,
+ 142, 148, 143,
+ 143, 149, 138,
+ 145, 144, 151,
+ 145, 151, 146,
+ 147, 146, 153,
+ 147, 153, 148,
+ 148, 154, 149,
+ 149, 155, 144,
+ 150, 156, 151,
+ 152, 151, 158,
+ 152, 158, 153,
+ 153, 159, 154,
+ 154, 160, 155,
+ 150, 155, 156,
+ 157, 156, 163,
+ 157, 163, 158,
+ 159, 158, 165,
+ 159, 165, 160,
+ 161, 160, 167,
+ 161, 167, 156,
+ 163, 162, 169,
+ 163, 169, 164,
+ 165, 164, 171,
+ 166, 165, 172,
+ 167, 166, 173,
+ 167, 173, 162,
+ 168, 174, 169,
+ 169, 175, 170,
+ 171, 170, 177,
+ 171, 177, 172,
+ 173, 172, 179,
+ 168, 173, 174,
+ 175, 174, 181,
+ 175, 181, 176,
+ 177, 176, 183,
+ 177, 183, 178,
+ 179, 178, 185,
+ 174, 179, 180,
+ 180, 186, 181,
+ 181, 187, 182,
+ 182, 188, 183,
+ 184, 183, 190,
+ 184, 190, 185,
+ 185, 191, 180,
+ 186, 0, 187,
+ 187, 1, 188,
+ 188, 2, 189,
+ 189, 3, 190,
+ 191, 190, 5,
+ 186, 191, 0,
+};
diff --git a/source/blender/windowmanager/3d_widgets/ui_widget_library.h b/source/blender/windowmanager/3d_widgets/ui_widget_library.h
new file mode 100644
index 00000000000..6bf7ed6b659
--- /dev/null
+++ b/source/blender/windowmanager/3d_widgets/ui_widget_library.h
@@ -0,0 +1,16 @@
+
+/* arrow widget */
+extern int _WIDGET_nverts_arrow;
+extern int _WIDGET_ntris_arrow;
+
+extern float _WIDGET_verts_arrow[][3];
+extern float _WIDGET_normals_arrow[][3];
+extern unsigned short _WIDGET_indices_arrow[];
+
+/* dial widget */
+extern int _WIDGET_nverts_dial;
+extern int _WIDGET_ntris_dial;
+
+extern float _WIDGET_verts_dial[][3];
+extern float _WIDGET_normals_dial[][3];
+extern unsigned short _WIDGET_indices_dial[];
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 3f5c86857b7..d5e360de3d1 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -65,6 +65,10 @@ set(SRC
intern/wm_subwindow.c
intern/wm_window.c
intern/wm_stereo.c
+ intern/wm_widgets.c
+ intern/wm_generic_widgets.c
+ 3d_widgets/arrow_widget.c
+ 3d_widgets/dial_widget.c
WM_api.h
WM_keymap.h
@@ -77,6 +81,7 @@ set(SRC
wm_files.h
wm_subwindow.h
wm_window.h
+ 3d_widgets/ui_widget_library.h
)
if(WITH_AUDASPACE)
diff --git a/source/blender/windowmanager/SConscript b/source/blender/windowmanager/SConscript
index 912d11762e1..9c3acc2beff 100644
--- a/source/blender/windowmanager/SConscript
+++ b/source/blender/windowmanager/SConscript
@@ -29,6 +29,7 @@ Import ('env')
import os
sources = env.Glob('intern/*.c')
+sources += env.Glob('3d_widgets/*.c')
incs = [
'.',
@@ -36,6 +37,7 @@ incs = [
'#/intern/ghost',
'#/intern/guardedalloc',
'#/intern/memutil',
+ '#/intern/audaspace/intern',
'#/source/gameengine/BlenderRoutines',
env['BF_GLEW_INC'],
'#/intern/glew-mx',
@@ -52,6 +54,7 @@ incs = [
'../nodes',
'../python',
'../render/extern/include',
+ '3d_widgets',
env['BF_ZLIB_INC'],
]
incs = ' '.join(incs)
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index 9e27190de02..298940a7b94 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -39,6 +39,7 @@
/* dna-savable wmStructs here */
#include "DNA_windowmanager_types.h"
+#include "DNA_listBase.h"
#include "WM_keymap.h"
#include "BLI_compiler_attrs.h"
@@ -55,6 +56,11 @@ struct wmGesture;
struct wmJob;
struct wmOperatorType;
struct wmOperator;
+struct wmWidget;
+struct wmWidgetGroup;
+struct wmWidgetMap;
+struct wmWidgetGroupType;
+struct wmWidgetMapType;
struct rcti;
struct PointerRNA;
struct PropertyRNA;
@@ -65,6 +71,8 @@ struct ImBuf;
struct ImageFormatData;
struct ARegion;
struct wmNDOFMotionData;
+struct Main;
+struct Object;
typedef struct wmJob wmJob;
@@ -304,11 +312,11 @@ bool WM_operator_last_properties_store(struct wmOperator *op);
/* flags for WM_operator_properties_filesel */
#define WM_FILESEL_RELPATH (1 << 0)
-#define WM_FILESEL_DIRECTORY (1 << 1)
-#define WM_FILESEL_FILENAME (1 << 2)
-#define WM_FILESEL_FILEPATH (1 << 3)
-#define WM_FILESEL_FILES (1 << 4)
-
+#define WM_FILESEL_DIRECTORY (1 << 1)
+#define WM_FILESEL_FILENAME (1 << 2)
+#define WM_FILESEL_FILEPATH (1 << 3)
+#define WM_FILESEL_FILES (1 << 4)
+#define WM_FILESEL_IMAGE_COLLAPSE (1 << 5)
/* operator as a python command (resultuing string must be freed) */
char *WM_operator_pystring_ex(struct bContext *C, struct wmOperator *op,
@@ -413,6 +421,8 @@ enum {
WM_JOB_TYPE_OBJECT_SIM_FLUID,
WM_JOB_TYPE_OBJECT_BAKE_TEXTURE,
WM_JOB_TYPE_OBJECT_BAKE,
+ WM_JOB_TYPE_PTCACHE_EXPORT,
+ WM_JOB_TYPE_CACHELIBRARY_BAKE,
WM_JOB_TYPE_FILESEL_THUMBNAIL,
WM_JOB_TYPE_CLIP_BUILD_PROXY,
WM_JOB_TYPE_CLIP_TRACK_MARKERS,
@@ -420,6 +430,7 @@ enum {
WM_JOB_TYPE_CLIP_PREFETCH,
WM_JOB_TYPE_SEQ_BUILD_PROXY,
WM_JOB_TYPE_SEQ_BUILD_PREVIEW,
+ WM_JOB_TYPE_SMOKE_EXPORT,
/* add as needed, screencast, seq proxy build
* if having hard coded values is a problem */
};
@@ -483,6 +494,90 @@ void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4
float WM_event_tablet_data(const struct wmEvent *event, int *pen_flip, float tilt[2]);
bool WM_event_is_tablet(const struct wmEvent *event);
+/* widget API */
+struct wmWidget *WM_widget_new(void (*draw)(struct wmWidget *, const struct bContext *),
+ void (*render_3d_intersection)(const struct bContext *, struct wmWidget *, int),
+ int (*intersect)(struct bContext *C, const struct wmEvent *event, struct wmWidget *customdata),
+ int (*handler)(struct bContext *, const struct wmEvent *, struct wmWidget *));
+
+void WM_widget_property(struct wmWidget *, int slot, struct PointerRNA *ptr, const char *propname);
+struct PointerRNA *WM_widget_operator(struct wmWidget *, const char *opname);
+void WM_widgets_update(const struct bContext *C, struct wmWidgetMap *wmap);
+void WM_widgets_draw(const struct bContext *C, struct wmWidgetMap *wmap, bool in_scene);
+void WM_event_add_area_widgetmap_handlers(struct ARegion *ar);
+void WM_modal_handler_attach_widgetgroup(struct bContext *C, struct wmEventHandler *handler, struct wmWidgetGroupType *wgrouptype, struct wmOperator *op);
+
+void WM_widget_set_origin(struct wmWidget *widget, float origin[3]);
+void WM_widget_set_3d_scale(struct wmWidget *widget, bool scale);
+void WM_widget_set_draw_on_hover_only(struct wmWidget *widget, bool draw);
+void WM_widget_set_scene_depth(struct wmWidget *widget, bool scene);
+void WM_widget_set_scale(struct wmWidget *widget, float scale);
+
+struct wmWidgetMapType *WM_widgetmaptype_find(const char *idname, int spaceid, int regionid, bool is_3d, bool create);
+struct wmWidgetGroupType *WM_widgetgrouptype_new(int (*poll)(const struct bContext *, struct wmWidgetGroupType *),
+ void (*draw)(const struct bContext *, struct wmWidgetGroup *),
+ struct Main *bmain, const char *mapidname, short spaceid, short regionid, bool is_3d);
+void WM_widgetgrouptype_unregister(struct bContext *C, struct Main *bmain, struct wmWidgetGroupType *wgroup);
+
+/* creates a widgetmap with all registered widgets for that type */
+struct wmWidgetMap *WM_widgetmap_from_type(const char *idname, int spaceid, int regionid, bool is_3d);
+void WM_widgetmap_delete(struct wmWidgetMap *wmap);
+bool WM_widgetmap_cursor_set(struct wmWidgetMap *wmap, struct wmWindow *win);
+
+void WM_widgetmaptypes_free(void);
+
+/* wm_generic_widgets.c */
+
+enum {
+ WIDGET_ARROW_STYLE_NORMAL = 1,
+ WIDGET_ARROW_STYLE_NO_AXIS = (1 << 1),
+ WIDGET_ARROW_STYLE_CROSS = (1 << 2),
+ WIDGET_ARROW_STYLE_INVERTED = (1 << 3), /* inverted offset during interaction - if set it also sets constrained below */
+ WIDGET_ARROW_STYLE_CONSTRAINED = (1 << 4), /* clamp arrow interaction to property width */
+};
+
+enum {
+ WIDGET_DIAL_STYLE_RING = 0,
+ WIDGET_DIAL_STYLE_RING_CLIPPED = 1,
+};
+
+enum {
+ WIDGET_RECT_TRANSFORM_STYLE_TRANSLATE = 1, /* widget translates */
+ WIDGET_RECT_TRANSFORM_STYLE_ROTATE = (1 << 1), /* widget rotates */
+ WIDGET_RECT_TRANSFORM_STYLE_SCALE = (1 << 2), /* widget scales */
+ WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM = (1 << 3), /* widget scales uniformly */
+};
+
+/* slots for properties */
+enum {
+ ARROW_SLOT_OFFSET_WORLD_SPACE = 0
+};
+
+enum {
+ RECT_TRANSFORM_SLOT_OFFSET = 0,
+ RECT_TRANSFORM_SLOT_SCALE = 1
+};
+
+enum {
+ FACEMAP_SLOT_FACEMAP = 0,
+};
+
+struct wmWidget *WIDGET_arrow_new(struct wmWidgetGroup *wgroup, int style);
+void WIDGET_arrow_set_color(struct wmWidget *widget, float color[4]);
+void WIDGET_arrow_set_direction(struct wmWidget *widget, float direction[3]);
+void WIDGET_arrow_set_up_vector(struct wmWidget *widget, float direction[3]);
+void WIDGET_arrow_set_scale(struct wmWidget *widget, float scale);
+
+struct wmWidget *WIDGET_dial_new(int style);
+void WIDGET_dial_set_color(struct wmWidget *widget, float color[4]);
+void WIDGET_dial_set_direction(struct wmWidget *widget, float direction[3]);
+
+struct wmWidget *WIDGET_rect_transform_new(struct wmWidgetGroup *wgroup, int style, float width, float height);
+void WIDGET_rect_transform_set_offset(struct wmWidget *widget, float offset[2]);
+
+struct wmWidget *WIDGET_facemap_new(struct wmWidgetGroup *wgroup, int style, struct Object *ob, int facemap);
+void WIDGET_facemap_set_color(struct wmWidget *widget, float color[4]);
+
#ifdef WITH_INPUT_IME
bool WM_event_is_ime_switch(const struct wmEvent *event);
#endif
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 109ccc27d79..3a984fdc4d2 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -377,6 +377,7 @@ typedef struct wmNotifier {
#define NS_EDITMODE_ARMATURE (8<<8)
#define NS_MODE_POSE (9<<8)
#define NS_MODE_PARTICLE (10<<8)
+#define NS_MODE_HAIR (11<<8)
/* subtype 3d view editing */
#define NS_VIEW3D_GPU (16<<8)
@@ -669,6 +670,59 @@ typedef struct wmDropBox {
} wmDropBox;
+
+/* WidgetGroups store and manage groups of widgets.
+ * They are responsible for drawing necessary widgets and updating their state and position.
+ * Also they */
+typedef struct wmWidget wmWidget;
+typedef struct wmWidgetGroup wmWidgetGroup;
+typedef struct wmWidgetMapType wmWidgetMapType;
+
+/* factory class for a widgetgroup type, gets called every time a new area is spawned */
+typedef struct wmWidgetGroupType {
+ struct wmWidgetGroupType *next, *prev;
+
+ char idname[64];
+
+ /* poll if widgetmap should be active */
+ int (*poll)(const struct bContext *C, struct wmWidgetGroupType *wgrouptype) ATTR_WARN_UNUSED_RESULT;
+
+ /* update widgets, called right before drawing */
+ void (*draw)(const struct bContext *C, struct wmWidgetGroup *wgroup);
+
+ /* rna for properties */
+ struct StructRNA *srna;
+
+ /* RNA integration */
+ ExtensionRNA ext;
+
+ /* general flag */
+ int flag;
+
+ /* if type is spawned from operator this is set here */
+ void *op;
+
+ /* same as widgetmaps, so registering/unregistering goes to the correct region */
+ short spaceid, regionid;
+ char mapidname[64];
+ bool is_3d;
+} wmWidgetGroupType;
+
+typedef struct wmWidgetMap {
+ struct wmWidgetMap *next, *prev;
+
+ struct wmWidgetMapType *type;
+ ListBase widgetgroups;
+
+ /* highlighted widget for this map. We redraw the widgetmap when this changes */
+ struct wmWidget *highlighted_widget;
+ /* active widget for this map. User has clicked currently this widget and it gets all input */
+ struct wmWidget *active_widget;
+
+ /* active group is overriding all other widgets while active */
+ struct wmWidgetGroup *activegroup;
+} wmWidgetMap;
+
/* *************** migrated stuff, clean later? ************** */
typedef struct RecentFile {
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index c8ff6dac754..b43425ef6cb 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -459,7 +459,7 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm)
BLI_freelistN(&wm->paintcursors);
WM_drag_free_list(&wm->drags);
-
+
wm_reports_free(wm);
if (C && CTX_wm_manager(C) == wm) CTX_wm_manager_set(C, NULL);
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 15b155c8649..8b4fdfb3021 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -1455,6 +1455,40 @@ static void wm_handler_op_context(bContext *C, wmEventHandler *handler, const wm
}
}
+static void wm_handler_widgetmap_context(bContext *C, wmEventHandler *handler)
+{
+ bScreen *screen = CTX_wm_screen(C);
+
+ if (screen) {
+ if (handler->op_area == NULL) {
+ /* do nothing in this context */
+ }
+ else {
+ ScrArea *sa;
+
+ for (sa = screen->areabase.first; sa; sa = sa->next)
+ if (sa == handler->op_area)
+ break;
+ if (sa == NULL) {
+ /* when changing screen layouts with running modal handlers (like render display), this
+ * is not an error to print */
+ if (handler->widgetmap == NULL)
+ printf("internal error: modal widgetmap handler has invalid area\n");
+ }
+ else {
+ ARegion *ar;
+ CTX_wm_area_set(C, sa);
+ for (ar = sa->regionbase.first; ar; ar = ar->next)
+ if (ar == handler->op_region)
+ break;
+ /* XXX no warning print here, after full-area and back regions are remade */
+ if (ar)
+ CTX_wm_region_set(C, ar);
+ }
+ }
+ }
+}
+
/* called on exit or remove area, only here call cancel callback */
void WM_event_remove_handlers(bContext *C, ListBase *handlers)
{
@@ -1478,7 +1512,7 @@ void WM_event_remove_handlers(bContext *C, ListBase *handlers)
if (handler->op->type->flag & OPTYPE_UNDO)
wm->op_undo_depth--;
-
+
CTX_wm_area_set(C, area);
CTX_wm_region_set(C, region);
}
@@ -1665,8 +1699,8 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
/* warning, after this call all context data and 'event' may be freed. see check below */
retval = ot->modal(C, op, event);
+
OPERATOR_RETVAL_CHECK(retval);
-
/* when this is _not_ the case the modal modifier may have loaded
* a new blend file (demo mode does this), so we have to assume
* the event, operator etc have all been freed. - campbell */
@@ -2060,7 +2094,72 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
}
}
}
+ else if (handler->widgetmap) {
+ struct wmWidgetMap *wmap = handler->widgetmap;
+ unsigned char part;
+ short event_processed = 0;
+ wmWidget *widget = wm_widgetmap_get_active_widget(wmap);
+ ScrArea *area = CTX_wm_area(C);
+ ARegion *region = CTX_wm_region(C);
+
+ wm_handler_widgetmap_context(C, handler);
+ wm_region_mouse_co(C, event);
+
+ switch (event->type) {
+ case MOUSEMOVE:
+ if (widget) {
+ widget->handler(C, event, widget);
+ event_processed = EVT_WIDGET_UPDATE;
+ action |= WM_HANDLER_BREAK;
+ }
+ else if (wm_widgetmap_is_3d(wmap)) {
+ widget = wm_widget_find_highlighted_3D(wmap, C, event, &part);
+ wm_widgetmap_set_highlighted_widget(wmap, C, widget, part);
+ }
+ else {
+ widget = wm_widget_find_highlighted(wmap, C, event, &part);
+ wm_widgetmap_set_highlighted_widget(wmap, C, widget, part);
+ }
+ break;
+
+ case LEFTMOUSE:
+ {
+ if (widget) {
+ if (event->val == KM_RELEASE) {
+ wm_widgetmap_set_active_widget(wmap, C, event, NULL, false);
+ event_processed = EVT_WIDGET_RELEASED;
+ action |= WM_HANDLER_BREAK;
+ }
+ else {
+ action |= WM_HANDLER_BREAK;
+ }
+ }
+ else if (event->val == KM_PRESS) {
+ widget = wm_widgetmap_get_highlighted_widget(wmap);
+
+ if (widget) {
+ wm_widgetmap_set_active_widget(wmap, C, event, widget, handler->op == NULL);
+ action |= WM_HANDLER_BREAK;
+ }
+ }
+ break;
+ }
+ }
+
+ /* restore the area */
+ CTX_wm_area_set(C, area);
+ CTX_wm_region_set(C, region);
+
+ if (handler->op) {
+ /* if event was processed by an active widget pass the modified event to the operator */
+ if (event_processed) {
+ event->type = event_processed;
+ }
+ action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
+ }
+ }
else {
+ /* handle the widget first, before passing the event down */
/* modal, swallows all */
action |= wm_handler_operator_call(C, handlers, handler, event, NULL);
}
@@ -2613,7 +2712,7 @@ wmEventHandler *WM_event_add_modal_handler(bContext *C, wmOperator *op)
{
wmEventHandler *handler = MEM_callocN(sizeof(wmEventHandler), "event modal handler");
wmWindow *win = CTX_wm_window(C);
-
+
/* operator was part of macro */
if (op->opm) {
/* give the mother macro to the handler */
@@ -3578,6 +3677,7 @@ void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4])
angle = WM_event_ndof_to_axis_angle(ndof, axis);
axis_angle_to_quat(q, axis, angle);
}
+/** \} */
/* if this is a tablet event, return tablet pressure and set *pen_flip
* to 1 if the eraser tool is being used, 0 otherwise */
diff --git a/source/blender/windowmanager/intern/wm_generic_widgets.c b/source/blender/windowmanager/intern/wm_generic_widgets.c
new file mode 100644
index 00000000000..247bd6a601b
--- /dev/null
+++ b/source/blender/windowmanager/intern/wm_generic_widgets.c
@@ -0,0 +1,1109 @@
+/*
+ * ***** 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/interface/interface_generic_widgets.c
+ * \ingroup edinterface
+ */
+
+#include "RNA_types.h"
+#include "RNA_access.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_object_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_windowmanager_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_widget_types.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_math_matrix.h"
+#include "BLI_math.h"
+#include "BLI_rect.h"
+
+#include "BKE_context.h"
+#include "BKE_depsgraph.h"
+#include "BKE_object.h"
+
+#include "ED_view3d.h"
+#include "ED_screen.h"
+
+#include "WM_types.h"
+#include "WM_api.h"
+
+#include "GL/glew.h"
+#include "GPU_select.h"
+
+#include "BIF_glutil.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "UI_interface.h"
+
+#include "3d_widgets/ui_widget_library.h"
+
+#include "wm.h"
+#include "WM_types.h"
+
+
+/******************************************************
+ * GENERIC WIDGET LIBRARY *
+ ******************************************************/
+
+typedef struct WidgetDrawInfo {
+ int nverts;
+ int ntris;
+ float (*verts)[3];
+ float (*normals)[3];
+ unsigned short *indices;
+ bool init;
+} WidgetDrawInfo;
+
+
+WidgetDrawInfo arraw_head_draw_info = {0};
+WidgetDrawInfo dial_draw_info = {0};
+
+static void widget_draw_intern(WidgetDrawInfo *info, bool select)
+{
+ GLuint buf[3];
+
+ bool use_lighting = !select && ((U.tw_flag & V3D_SHADED_WIDGETS) != 0);
+
+ if (use_lighting)
+ glGenBuffers(3, buf);
+ else
+ glGenBuffers(2, buf);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glBindBuffer(GL_ARRAY_BUFFER, buf[0]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * info->nverts, info->verts, GL_STATIC_DRAW);
+ glVertexPointer(3, GL_FLOAT, 0, NULL);
+
+ if (use_lighting) {
+ glEnableClientState(GL_NORMAL_ARRAY);
+ glBindBuffer(GL_ARRAY_BUFFER, buf[2]);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * info->nverts, info->normals, GL_STATIC_DRAW);
+ glNormalPointer(GL_FLOAT, 0, NULL);
+ glShadeModel(GL_SMOOTH);
+ }
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf[1]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * (3 * info->ntris), info->indices, GL_STATIC_DRAW);
+
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_DEPTH_TEST);
+
+ glDrawElements(GL_TRIANGLES, info->ntris * 3, GL_UNSIGNED_SHORT, NULL);
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ if (use_lighting) {
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glShadeModel(GL_FLAT);
+ glDeleteBuffers(3, buf);
+ }
+ else {
+ glDeleteBuffers(2, buf);
+ }
+}
+
+/********* Arrow widget ************/
+
+#define ARROW_UP_VECTOR_SET 1
+
+typedef struct ArrowWidget {
+ wmWidget widget;
+ int style;
+ int flag;
+ float direction[3];
+ float up[3];
+ float color[4];
+ float offset;
+ /* property range and minimum for constrained arrows */
+ float range, min;
+} ArrowWidget;
+
+typedef struct ArrowInteraction {
+ float orig_origin[3];
+ float orig_mouse[2];
+ float orig_offset;
+ float orig_scale;
+
+ /* direction vector, projected in screen space */
+ float proj_direction[2];
+} ArrowInteraction;
+
+
+static void widget_arrow_get_final_pos(struct wmWidget *widget, float pos[3])
+{
+ ArrowWidget *arrow = (ArrowWidget *)widget;
+
+ mul_v3_v3fl(pos, arrow->direction, arrow->offset);
+ add_v3_v3(pos, arrow->widget.origin);
+}
+
+static void arrow_draw_geom(ArrowWidget *arrow, bool select)
+{
+ if (arrow->style & WIDGET_ARROW_STYLE_CROSS) {
+ glPushAttrib(GL_ENABLE_BIT);
+ glDisable(GL_LIGHTING);
+ glBegin(GL_LINES);
+ glVertex2f(-1.0, 0.f);
+ glVertex2f(1.0, 0.f);
+ glVertex2f(0.f, -1.0);
+ glVertex2f(0.f, 1.0);
+ glEnd();
+
+ glPopAttrib();
+ }
+ else {
+ widget_draw_intern(&arraw_head_draw_info, select);
+ }
+}
+
+static void arrow_draw_intern(ArrowWidget *arrow, bool select, bool highlight)
+{
+ float rot[3][3];
+ float mat[4][4];
+ float up[3] = {0.0f, 0.0f, 1.0f};
+ float final_pos[3];
+
+ widget_arrow_get_final_pos(&arrow->widget, final_pos);
+
+ if (arrow->flag & ARROW_UP_VECTOR_SET) {
+ copy_v3_v3(rot[2], arrow->direction);
+ copy_v3_v3(rot[1], arrow->up);
+ cross_v3_v3v3(rot[0], arrow->up, arrow->direction);
+ }
+ else {
+ rotation_between_vecs_to_mat3(rot, up, arrow->direction);
+ }
+ copy_m4_m3(mat, rot);
+ copy_v3_v3(mat[3], final_pos);
+ mul_mat3_m4_fl(mat, arrow->widget.scale);
+
+ glPushMatrix();
+ glMultMatrixf(&mat[0][0]);
+
+ if (highlight && !(arrow->widget.flag & WM_WIDGET_DRAW_HOVER))
+ glColor4f(1.0, 1.0, 0.0, 1.0);
+ else
+ glColor4fv(arrow->color);
+
+ arrow_draw_geom(arrow, select);
+
+ glPopMatrix();
+
+ if (arrow->widget.interaction_data) {
+ ArrowInteraction *data = arrow->widget.interaction_data;
+
+ copy_m4_m3(mat, rot);
+ copy_v3_v3(mat[3], data->orig_origin);
+ mul_mat3_m4_fl(mat, data->orig_scale);
+
+ glPushMatrix();
+ glMultMatrixf(&mat[0][0]);
+
+ glEnable(GL_BLEND);
+ glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
+ arrow_draw_geom(arrow, select);
+
+ glDisable(GL_BLEND);
+
+ glPopMatrix();
+ }
+}
+
+static void widget_arrow_render_3d_intersect(const struct bContext *UNUSED(C), struct wmWidget *widget, int selectionbase)
+{
+ GPU_select_load_id(selectionbase);
+ arrow_draw_intern((ArrowWidget *)widget, true, false);
+}
+
+static void widget_arrow_draw(struct wmWidget *widget, const struct bContext *UNUSED(C))
+{
+ arrow_draw_intern((ArrowWidget *)widget, false, (widget->flag & WM_WIDGET_HIGHLIGHT) != 0);
+}
+
+#define ARROW_RANGE 1.5f
+
+static int widget_arrow_handler(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget)
+{
+ ArrowWidget *arrow = (ArrowWidget *)widget;
+ ArrowInteraction *data = widget->interaction_data;
+ ARegion *ar = CTX_wm_region(C);
+ RegionView3D *rv3d = ar->regiondata;
+
+ float orig_origin[4];
+ float viewvec[3], tangent[3], plane[3];
+ float offset[4];
+ float m_diff[2];
+ float dir_2d[2], dir2d_final[2];
+ float fac, zfac;
+ float facdir = 1.0f;
+ bool use_vertical = false;
+
+ copy_v3_v3(orig_origin, data->orig_origin);
+ orig_origin[3] = 1.0f;
+ add_v3_v3v3(offset, orig_origin, arrow->direction);
+ offset[3] = 1.0f;
+
+ /* calculate view vector */
+ if (rv3d->is_persp) {
+ sub_v3_v3v3(viewvec, orig_origin, rv3d->viewinv[3]);
+ }
+ else {
+ copy_v3_v3(viewvec, rv3d->viewinv[2]);
+ }
+ normalize_v3(viewvec);
+
+ zfac = ED_view3d_calc_zfac(rv3d, orig_origin, NULL);
+
+ /* first determine if view vector is really close to the direction. If it is, we use vertical movement to determine offset,
+ * just like transform system does */
+ if (RAD2DEG(acos(dot_v3v3(viewvec, arrow->direction))) > 5.0f) {
+ /* multiply to projection space */
+ mul_m4_v4(rv3d->persmat, orig_origin);
+ mul_v4_fl(orig_origin, 1.0f/orig_origin[3]);
+ mul_m4_v4(rv3d->persmat, offset);
+ mul_v4_fl(offset, 1.0f/offset[3]);
+
+ sub_v2_v2v2(dir_2d, offset, orig_origin);
+ dir_2d[0] *= ar->winx;
+ dir_2d[1] *= ar->winy;
+ normalize_v2(dir_2d);
+ }
+ else {
+ dir_2d[0] = 0.0f;
+ dir_2d[1] = 1.0f;
+ use_vertical = true;
+ }
+
+ /* find mouse difference */
+ m_diff[0] = event->mval[0] - data->orig_mouse[0];
+ m_diff[1] = event->mval[1] - data->orig_mouse[1];
+
+ /* project the displacement on the screen space arrow direction */
+ project_v2_v2v2(dir2d_final, m_diff, dir_2d);
+
+ ED_view3d_win_to_delta(ar, dir2d_final, offset, zfac);
+
+ add_v3_v3v3(orig_origin, offset, data->orig_origin);
+
+ /* calculate view vector for the new position */
+ if (rv3d->is_persp) {
+ sub_v3_v3v3(viewvec, orig_origin, rv3d->viewinv[3]);
+ }
+ else {
+ copy_v3_v3(viewvec, rv3d->viewinv[2]);
+ }
+
+ normalize_v3(viewvec);
+ if (!use_vertical) {
+ /* now find a plane parallel to the view vector so we can intersect with the arrow direction */
+ cross_v3_v3v3(tangent, viewvec, offset);
+ cross_v3_v3v3(plane, tangent, viewvec);
+ fac = dot_v3v3(plane, offset) / dot_v3v3(arrow->direction, plane);
+
+ facdir = (fac < 0.0) ? -1.0 : 1.0;
+ mul_v3_v3fl(offset, arrow->direction, fac);
+ }
+ else {
+ facdir = (m_diff[1] < 0.0) ? -1.0 : 1.0;
+ }
+
+ /* set the property for the operator and call its modal function */
+ if (widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]) {
+ float value;
+
+ value = data->orig_offset + facdir * len_v3(offset);
+ if (arrow->style & WIDGET_ARROW_STYLE_CONSTRAINED) {
+ if (arrow->style & WIDGET_ARROW_STYLE_INVERTED)
+ value = arrow->min + arrow->range - (value * arrow->range / ARROW_RANGE);
+ else
+ value = arrow->min + (value * arrow->range / ARROW_RANGE);
+ }
+
+ RNA_property_float_set(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE], value);
+ RNA_property_update(C, &widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]);
+
+ /* accounts for clamping properly */
+ if (arrow->style & WIDGET_ARROW_STYLE_CONSTRAINED) {
+ if (arrow->style & WIDGET_ARROW_STYLE_INVERTED)
+ arrow->offset = ARROW_RANGE * (arrow->min + arrow->range - (RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]))) / arrow->range;
+ else
+ arrow->offset = ARROW_RANGE * ((RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]) - arrow->min) / arrow->range);
+ }
+ else
+ arrow->offset = RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]);
+ }
+ else {
+ arrow->offset = facdir * len_v3(offset);
+ }
+
+ /* tag the region for redraw */
+ ED_region_tag_redraw(ar);
+
+ return OPERATOR_PASS_THROUGH;
+}
+
+
+static int widget_arrow_invoke(struct bContext *UNUSED(C), const struct wmEvent *event, struct wmWidget *widget)
+{
+ ArrowWidget *arrow = (ArrowWidget *) widget;
+ ArrowInteraction *data = MEM_callocN(sizeof (ArrowInteraction), "arrow_interaction");
+
+ data->orig_offset = arrow->offset;
+
+ data->orig_mouse[0] = event->mval[0];
+ data->orig_mouse[1] = event->mval[1];
+
+ data->orig_scale = widget->scale;
+
+ widget_arrow_get_final_pos(widget, data->orig_origin);
+
+ widget->interaction_data = data;
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void widget_arrow_bind_to_prop(struct wmWidget *widget, int UNUSED(slot))
+{
+ ArrowWidget *arrow = (ArrowWidget *) widget;
+
+ if (widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]) {
+ if (arrow->style & WIDGET_ARROW_STYLE_CONSTRAINED) {
+ float min, max, step, precision;
+
+ RNA_property_float_ui_range(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE], &min, &max, &step, &precision);
+ arrow->range = max - min;
+ arrow->min = min;
+ if (arrow->style & WIDGET_ARROW_STYLE_INVERTED)
+ arrow->offset = ARROW_RANGE * (max - (RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]))) / arrow->range;
+ else
+ arrow->offset = ARROW_RANGE * ((RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]) - arrow->min) / arrow->range);
+ }
+ else {
+ /* we'd need to check the property type here but for now assume always float */
+ arrow->offset = RNA_property_float_get(&widget->ptr[ARROW_SLOT_OFFSET_WORLD_SPACE], widget->props[ARROW_SLOT_OFFSET_WORLD_SPACE]);
+ }
+ }
+ else
+ arrow->offset = 0.0f;
+}
+
+wmWidget *WIDGET_arrow_new(wmWidgetGroup *wgroup, int style)
+{
+ float dir_default[3] = {0.0f, 0.0f, 1.0f};
+ ArrowWidget *arrow;
+
+ if (!arraw_head_draw_info.init) {
+ arraw_head_draw_info.nverts = _WIDGET_nverts_arrow,
+ arraw_head_draw_info.ntris = _WIDGET_ntris_arrow,
+ arraw_head_draw_info.verts = _WIDGET_verts_arrow,
+ arraw_head_draw_info.normals = _WIDGET_normals_arrow,
+ arraw_head_draw_info.indices = _WIDGET_indices_arrow,
+ arraw_head_draw_info.init = true;
+ }
+
+ /* inverted only makes sense in a constrained arrow */
+ if (style & WIDGET_ARROW_STYLE_INVERTED)
+ style |= WIDGET_ARROW_STYLE_CONSTRAINED;
+
+ arrow = MEM_callocN(sizeof(ArrowWidget), "arrowwidget");
+
+
+ arrow->widget.draw = widget_arrow_draw;
+ arrow->widget.get_final_position = widget_arrow_get_final_pos;
+ arrow->widget.intersect = NULL;
+ arrow->widget.handler = widget_arrow_handler;
+ arrow->widget.invoke = widget_arrow_invoke;
+ arrow->widget.render_3d_intersection = widget_arrow_render_3d_intersect;
+ arrow->widget.bind_to_prop = widget_arrow_bind_to_prop;
+ arrow->widget.flag |= WM_WIDGET_SCALE_3D;
+ arrow->style = style;
+ copy_v3_v3(arrow->direction, dir_default);
+
+ wm_widget_register(wgroup, &arrow->widget);
+
+ return (wmWidget *)arrow;
+}
+
+void WIDGET_arrow_set_color(struct wmWidget *widget, float color[4])
+{
+ ArrowWidget *arrow = (ArrowWidget *)widget;
+
+ copy_v4_v4(arrow->color, color);
+}
+
+void WIDGET_arrow_set_direction(struct wmWidget *widget, float direction[3])
+{
+ ArrowWidget *arrow = (ArrowWidget *)widget;
+
+ copy_v3_v3(arrow->direction, direction);
+ normalize_v3(arrow->direction);
+}
+
+void WIDGET_arrow_set_up_vector(struct wmWidget *widget, float direction[3])
+{
+ ArrowWidget *arrow = (ArrowWidget *)widget;
+
+ if (direction) {
+ copy_v3_v3(arrow->up, direction);
+ normalize_v3(arrow->up);
+ arrow->flag |= ARROW_UP_VECTOR_SET;
+ }
+ else {
+ arrow->flag &= ~ARROW_UP_VECTOR_SET;
+ }
+}
+
+
+/********* Dial widget ************/
+
+typedef struct DialWidget {
+ wmWidget widget;
+ int style;
+ float direction[3];
+ float color[4];
+} DialWidget;
+
+static void dial_draw_intern(DialWidget *dial, bool select, bool highlight, float scale)
+{
+ float rot[3][3];
+ float mat[4][4];
+ float up[3] = {0.0f, 0.0f, 1.0f};
+
+ rotation_between_vecs_to_mat3(rot, up, dial->direction);
+ copy_m4_m3(mat, rot);
+ copy_v3_v3(mat[3], dial->widget.origin);
+ mul_mat3_m4_fl(mat, scale);
+
+ glPushMatrix();
+ glMultMatrixf(&mat[0][0]);
+
+ if (highlight)
+ glColor4f(1.0, 1.0, 0.0, 1.0);
+ else
+ glColor4fv(dial->color);
+
+ widget_draw_intern(&dial_draw_info, select);
+
+ glPopMatrix();
+
+}
+
+static void widget_dial_render_3d_intersect(const struct bContext *C, struct wmWidget *widget, int selectionbase)
+{
+ DialWidget *dial = (DialWidget *)widget;
+ ARegion *ar = CTX_wm_region(C);
+ RegionView3D *rv3d = ar->regiondata;
+
+ /* enable clipping if needed */
+ if (dial->style == WIDGET_DIAL_STYLE_RING_CLIPPED) {
+ double plane[4];
+ copy_v3db_v3fl(plane, rv3d->viewinv[2]);
+ plane[3] = -dot_v3v3(rv3d->viewinv[2], widget->origin);
+ glClipPlane(GL_CLIP_PLANE0, plane);
+ glEnable(GL_CLIP_PLANE0);
+ }
+
+ GPU_select_load_id(selectionbase);
+ dial_draw_intern(dial, true, false, dial->widget.scale);
+
+ if (dial->style == WIDGET_DIAL_STYLE_RING_CLIPPED) {
+ glDisable(GL_CLIP_PLANE0);
+ }
+}
+
+static void widget_dial_draw(struct wmWidget *widget, const struct bContext *C)
+{
+ DialWidget *dial = (DialWidget *)widget;
+ ARegion *ar = CTX_wm_region(C);
+ RegionView3D *rv3d = ar->regiondata;
+
+ /* enable clipping if needed */
+ if (dial->style == WIDGET_DIAL_STYLE_RING_CLIPPED) {
+ double plane[4];
+ copy_v3db_v3fl(plane, rv3d->viewinv[2]);
+ plane[3] = -dot_v3v3(rv3d->viewinv[2], widget->origin);
+ glClipPlane(GL_CLIP_PLANE0, plane);
+ glEnable(GL_CLIP_PLANE0);
+ }
+
+ dial_draw_intern(dial, false, (widget->flag & WM_WIDGET_HIGHLIGHT) != 0, widget->scale);
+
+ if (dial->style == WIDGET_DIAL_STYLE_RING_CLIPPED) {
+ glDisable(GL_CLIP_PLANE0);
+ }
+}
+
+wmWidget *WIDGET_dial_new(int style)
+{
+ float dir_default[3] = {0.0f, 0.0f, 1.0f};
+ DialWidget *dial;
+
+ if (!dial_draw_info.init) {
+ dial_draw_info.nverts = _WIDGET_nverts_dial,
+ dial_draw_info.ntris = _WIDGET_ntris_dial,
+ dial_draw_info.verts = _WIDGET_verts_dial,
+ dial_draw_info.normals = _WIDGET_normals_dial,
+ dial_draw_info.indices = _WIDGET_indices_dial,
+ dial_draw_info.init = true;
+ }
+
+ dial = MEM_callocN(sizeof(ArrowWidget), "arrowwidget");
+
+ dial->widget.draw = widget_dial_draw;
+ dial->widget.intersect = NULL;
+ dial->widget.render_3d_intersection = widget_dial_render_3d_intersect;
+
+ dial->style = style;
+ copy_v3_v3(dial->direction, dir_default);
+
+ return (wmWidget *)dial;
+}
+
+void WIDGET_dial_set_color(struct wmWidget *widget, float color[4])
+{
+ DialWidget *arrow = (DialWidget *)widget;
+
+ copy_v4_v4(arrow->color, color);
+}
+
+void WIDGET_dial_set_direction(struct wmWidget *widget, float direction[3])
+{
+ DialWidget *dial = (DialWidget *)widget;
+
+ copy_v3_v3(dial->direction, direction);
+ normalize_v3(dial->direction);
+}
+
+/********* Cage widget ************/
+
+enum {
+ WIDGET_RECT_TRANSFORM_INTERSECT_TRANSLATE = 1,
+ WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_LEFT = 2,
+ WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_RIGHT = 3,
+ WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_UP = 4,
+ WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_DOWN = 5
+};
+
+#define WIDGET_RECT_MIN_WIDTH 15.0f
+#define WIDGET_RESIZER_WIDTH 20.0f
+
+typedef struct RectTransformWidget {
+ wmWidget widget;
+ float offset[2]; /* position of widget */
+ float w, h; /* dimensions of widget */
+ float rotation; /* rotation of the rectangle */
+ float scale[2]; /* scaling for the widget for non-destructive editing. */
+ int style;
+} RectTransformWidget;
+
+static void rect_transform_draw_corners(rctf *r, float offsetx, float offsety)
+{
+ glBegin(GL_LINES);
+ glVertex2f(r->xmin, r->ymin + offsety);
+ glVertex2f(r->xmin, r->ymin);
+ glVertex2f(r->xmin, r->ymin);
+ glVertex2f(r->xmin + offsetx, r->ymin);
+
+ glVertex2f(r->xmax, r->ymin + offsety);
+ glVertex2f(r->xmax, r->ymin);
+ glVertex2f(r->xmax, r->ymin);
+ glVertex2f(r->xmax - offsetx, r->ymin);
+
+ glVertex2f(r->xmax, r->ymax - offsety);
+ glVertex2f(r->xmax, r->ymax);
+ glVertex2f(r->xmax, r->ymax);
+ glVertex2f(r->xmax - offsetx, r->ymax);
+
+ glVertex2f(r->xmin, r->ymax - offsety);
+ glVertex2f(r->xmin, r->ymax);
+ glVertex2f(r->xmin, r->ymax);
+ glVertex2f(r->xmin + offsetx, r->ymax);
+ glEnd();
+}
+
+static void rect_transform_draw_interaction(int highlighted, float half_w, float half_h, float w, float h)
+{
+ float verts[4][2];
+#if 0
+ unsigned short elems[4] = {0, 1, 3, 2};
+#endif
+
+ switch (highlighted) {
+ case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_LEFT:
+ verts[0][0] = -half_w + w;
+ verts[0][1] = -half_h;
+ verts[1][0] = -half_w;
+ verts[1][1] = -half_h;
+ verts[2][0] = -half_w;
+ verts[2][1] = half_h;
+ verts[3][0] = -half_w + w;
+ verts[3][1] = half_h;
+ break;
+
+ case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_RIGHT:
+ verts[0][0] = half_w - w;
+ verts[0][1] = -half_h;
+ verts[1][0] = half_w;
+ verts[1][1] = -half_h;
+ verts[2][0] = half_w;
+ verts[2][1] = half_h;
+ verts[3][0] = half_w - w;
+ verts[3][1] = half_h;
+ break;
+
+ case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_DOWN:
+ verts[0][0] = -half_w;
+ verts[0][1] = -half_h + h;
+ verts[1][0] = -half_w;
+ verts[1][1] = -half_h;
+ verts[2][0] = half_w;
+ verts[2][1] = -half_h;
+ verts[3][0] = half_w;
+ verts[3][1] = -half_h + h;
+ break;
+
+ case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_UP:
+ verts[0][0] = -half_w;
+ verts[0][1] = half_h - h;
+ verts[1][0] = -half_w;
+ verts[1][1] = half_h;
+ verts[2][0] = half_w;
+ verts[2][1] = half_h;
+ verts[3][0] = half_w;
+ verts[3][1] = half_h - h;
+ break;
+
+ default:
+ return;
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 0, verts);
+ glLineWidth(3.0);
+ glColor3f(0.0, 0.0, 0.0);
+ glDrawArrays(GL_LINE_STRIP, 0, 3);
+ glLineWidth(1.0);
+ glColor3f(1.0, 1.0, 1.0);
+ glDrawArrays(GL_LINE_STRIP, 0, 3);
+}
+
+static void widget_rect_transform_draw(struct wmWidget *widget, const struct bContext *UNUSED(C))
+{
+ RectTransformWidget *cage = (RectTransformWidget *)widget;
+ rctf r;
+ float w = cage->w;
+ float h = cage->h;
+ float half_w = w / 2.0f;
+ float half_h = h / 2.0f;
+ float aspx = 1.0f, aspy = 1.0f;
+
+ r.xmin = -half_w;
+ r.ymin = -half_h;
+ r.xmax = half_w;
+ r.ymax = half_h;
+
+ glPushMatrix();
+ glTranslatef(widget->origin[0] + cage->offset[0], widget->origin[1] + cage->offset[1], 0.0f);
+ if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM)
+ glScalef(cage->scale[0], cage->scale[0], 1.0);
+ else
+ glScalef(cage->scale[0], cage->scale[1], 1.0);
+
+ if (w > h)
+ aspx = h / w;
+ else
+ aspy = w / h;
+ w = min_ff(aspx * w / WIDGET_RESIZER_WIDTH, WIDGET_RESIZER_WIDTH / cage->scale[0]);
+ h = min_ff(aspy * h / WIDGET_RESIZER_WIDTH, WIDGET_RESIZER_WIDTH /
+ ((cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) ? cage->scale[0] : cage->scale[1]));
+
+ /* corner widgets */
+ glColor3f(0.0, 0.0, 0.0);
+ glLineWidth(3.0);
+
+ rect_transform_draw_corners(&r, w, h);
+
+ /* corner widgets */
+ glColor3f(1.0, 1.0, 1.0);
+ glLineWidth(1.0);
+ rect_transform_draw_corners(&r, w, h);
+
+ rect_transform_draw_interaction(widget->highlighted_part, half_w, half_h, w, h);
+ glPopMatrix();
+}
+
+static int widget_rect_tranfrorm_get_cursor(wmWidget *widget)
+{
+ switch (widget->highlighted_part) {
+ case WIDGET_RECT_TRANSFORM_INTERSECT_TRANSLATE:
+ return BC_HANDCURSOR;
+ case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_LEFT:
+ case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_RIGHT:
+ return CURSOR_X_MOVE;
+ case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_DOWN:
+ case WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_UP:
+ return CURSOR_Y_MOVE;
+ default:
+ return CURSOR_STD;
+ }
+}
+
+static int widget_rect_tranfrorm_intersect(struct bContext *UNUSED(C), const struct wmEvent *event, struct wmWidget *widget)
+{
+ RectTransformWidget *cage = (RectTransformWidget *)widget;
+ float mouse[2] = {event->mval[0], event->mval[1]};
+ float point_local[2];
+ float w = cage->w;
+ float h = cage->h;
+ float half_w = w / 2.0f;
+ float half_h = h / 2.0f;
+ //float matrot[2][2];
+ bool isect;
+ rctf r;
+ float aspx = 1.0f, aspy = 1.0f;
+
+ /* rotate mouse in relation to the center and relocate it */
+ sub_v2_v2v2(point_local, mouse, widget->origin);
+ point_local[0] -= cage->offset[0];
+ point_local[1] -= cage->offset[1];
+ //rotate_m2(matrot, -cage->transform.rotation);
+
+ if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM)
+ mul_v2_fl(point_local, 1.0f/cage->scale[0]);
+ else {
+ point_local[0] /= cage->scale[0];
+ point_local[1] /= cage->scale[0];
+ }
+
+ if (cage->w > cage->h)
+ aspx = h / w;
+ else
+ aspy = w / h;
+ w = min_ff(aspx * w / WIDGET_RESIZER_WIDTH, WIDGET_RESIZER_WIDTH / cage->scale[0]);
+ h = min_ff(aspy * h / WIDGET_RESIZER_WIDTH, WIDGET_RESIZER_WIDTH /
+ ((cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) ? cage->scale[0] : cage->scale[1]));
+
+ r.xmin = -half_w + w;
+ r.ymin = -half_h + h;
+ r.xmax = half_w - w;
+ r.ymax = half_h - h;
+
+ isect = BLI_rctf_isect_pt_v(&r, point_local);
+
+ if (isect)
+ return WIDGET_RECT_TRANSFORM_INTERSECT_TRANSLATE;
+
+ /* if widget does not have a scale intersection, don't do it */
+ if (cage->style & (WIDGET_RECT_TRANSFORM_STYLE_SCALE | WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM)) {
+ r.xmin = -half_w;
+ r.ymin = -half_h;
+ r.xmax = -half_w + w;
+ r.ymax = half_h;
+
+ isect = BLI_rctf_isect_pt_v(&r, point_local);
+
+ if (isect)
+ return WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_LEFT;
+
+ r.xmin = half_w - w;
+ r.ymin = -half_h;
+ r.xmax = half_w;
+ r.ymax = half_h;
+
+ isect = BLI_rctf_isect_pt_v(&r, point_local);
+
+ if (isect)
+ return WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_RIGHT;
+
+ r.xmin = -half_w;
+ r.ymin = -half_h;
+ r.xmax = half_w;
+ r.ymax = -half_h + h;
+
+ isect = BLI_rctf_isect_pt_v(&r, point_local);
+
+ if (isect)
+ return WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_DOWN;
+
+ r.xmin = -half_w;
+ r.ymin = half_h - h;
+ r.xmax = half_w;
+ r.ymax = half_h;
+
+ isect = BLI_rctf_isect_pt_v(&r, point_local);
+
+ if (isect)
+ return WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_UP;
+ }
+
+ return 0;
+}
+
+typedef struct RectTransformInteraction {
+ float orig_mouse[2];
+ float orig_offset[2];
+ float orig_scale[2];
+} RectTransformInteraction;
+
+static bool widget_rect_transform_get_property(struct wmWidget *widget, int slot, float *value)
+{
+ PropertyType type = RNA_property_type(widget->props[slot]);
+
+ if (type != PROP_FLOAT) {
+ fprintf(stderr, "Rect Transform widget can only be bound to float properties");
+ return false;
+ }
+ else {
+ if (slot == RECT_TRANSFORM_SLOT_OFFSET) {
+ if (RNA_property_array_length(&widget->ptr[slot], widget->props[slot]) != 2) {
+ fprintf(stderr, "Rect Transform widget offset not only be bound to array float property");
+ return false;
+ }
+
+ RNA_property_float_get_array(&widget->ptr[slot], widget->props[slot], value);
+ }
+ else if (slot == RECT_TRANSFORM_SLOT_SCALE) {
+ RectTransformWidget *cage = (RectTransformWidget *)widget;
+ if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM)
+ *value = RNA_property_float_get(&widget->ptr[slot], widget->props[slot]);
+ else {
+ if (RNA_property_array_length(&widget->ptr[slot], widget->props[slot]) != 2) {
+ fprintf(stderr, "Rect Transform widget scale not only be bound to array float property");
+ return false;
+ }
+ RNA_property_float_get_array(&widget->ptr[slot], widget->props[slot], value);
+ }
+ }
+ }
+
+ return true;
+}
+
+static int widget_rect_transform_invoke(struct bContext *UNUSED(C), const struct wmEvent *event, struct wmWidget *widget)
+{
+ RectTransformWidget *cage = (RectTransformWidget *) widget;
+ RectTransformInteraction *data = MEM_callocN(sizeof (RectTransformInteraction), "cage_interaction");
+
+ copy_v2_v2(data->orig_offset, cage->offset);
+ copy_v2_v2(data->orig_scale, cage->scale);
+
+ data->orig_mouse[0] = event->mval[0];
+ data->orig_mouse[1] = event->mval[1];
+
+ widget->interaction_data = data;
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int widget_rect_transform_handler(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget)
+{
+ RectTransformWidget *cage = (RectTransformWidget *) widget;
+ RectTransformInteraction *data = widget->interaction_data;
+ ARegion *ar = CTX_wm_region(C);
+ float valuex, valuey;
+ /* needed here as well in case clamping occurs */
+ float orig_ofx = cage->offset[0], orig_ofy = cage->offset[1];
+
+ valuex = (event->mval[0] - data->orig_mouse[0]);
+ valuey = (event->mval[1] - data->orig_mouse[1]);
+
+ if (widget->highlighted_part == WIDGET_RECT_TRANSFORM_INTERSECT_TRANSLATE) {
+ cage->offset[0] = data->orig_offset[0] + valuex;
+ cage->offset[1] = data->orig_offset[1] + valuey;
+ }
+ else if (widget->highlighted_part == WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_LEFT) {
+ cage->offset[0] = data->orig_offset[0] + valuex / 2.0;
+ cage->scale[0] = (cage->w * data->orig_scale[0] - valuex) / cage->w;
+ }
+ else if (widget->highlighted_part == WIDGET_RECT_TRANSFORM_INTERSECT_SCALEX_RIGHT) {
+ cage->offset[0] = data->orig_offset[0] + valuex / 2.0;
+ cage->scale[0] = (cage->w * data->orig_scale[0] + valuex) / cage->w;
+ }
+ else if (widget->highlighted_part == WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_DOWN) {
+ cage->offset[1] = data->orig_offset[1] + valuey / 2.0;
+
+ if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) {
+ cage->scale[0] = (cage->h * data->orig_scale[0] - valuey) / cage->h;
+ }
+ else {
+ cage->scale[1] = (cage->h * data->orig_scale[1] - valuey) / cage->h;
+ }
+ }
+ else if (widget->highlighted_part == WIDGET_RECT_TRANSFORM_INTERSECT_SCALEY_UP) {
+ cage->offset[1] = data->orig_offset[1] + valuey / 2.0;
+
+ if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) {
+ cage->scale[0] = (cage->h * data->orig_scale[0] + valuey) / cage->h;
+ }
+ else {
+ cage->scale[1] = (cage->h * data->orig_scale[1] + valuey) / cage->h;
+ }
+ }
+
+ /* clamping - make sure widget is at least 5 pixels wide */
+ if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM) {
+ if (cage->scale[0] < WIDGET_RECT_MIN_WIDTH / cage->h ||
+ cage->scale[0] < WIDGET_RECT_MIN_WIDTH / cage->w)
+ {
+ cage->scale[0] = max_ff(WIDGET_RECT_MIN_WIDTH / cage->h, WIDGET_RECT_MIN_WIDTH / cage->w);
+ cage->offset[0] = orig_ofx;
+ cage->offset[1] = orig_ofy;
+ }
+ }
+ else {
+ if (cage->scale[0] < WIDGET_RECT_MIN_WIDTH / cage->w) {
+ cage->scale[0] = WIDGET_RECT_MIN_WIDTH / cage->w;
+ cage->offset[0] = orig_ofx;
+ }
+ if (cage->scale[1] < WIDGET_RECT_MIN_WIDTH / cage->h) {
+ cage->scale[1] = WIDGET_RECT_MIN_WIDTH / cage->h;
+ cage->offset[1] = orig_ofy;
+ }
+ }
+
+ if (widget->props[RECT_TRANSFORM_SLOT_OFFSET]) {
+ RNA_property_float_set_array(&widget->ptr[RECT_TRANSFORM_SLOT_OFFSET], widget->props[RECT_TRANSFORM_SLOT_OFFSET], cage->offset);
+ RNA_property_update(C, &widget->ptr[RECT_TRANSFORM_SLOT_OFFSET], widget->props[RECT_TRANSFORM_SLOT_OFFSET]);
+ }
+
+ if (widget->props[RECT_TRANSFORM_SLOT_SCALE]) {
+ if (cage->style & WIDGET_RECT_TRANSFORM_STYLE_SCALE_UNIFORM)
+ RNA_property_float_set(&widget->ptr[RECT_TRANSFORM_SLOT_SCALE], widget->props[RECT_TRANSFORM_SLOT_SCALE], cage->scale[0]);
+ else
+ RNA_property_float_set_array(&widget->ptr[RECT_TRANSFORM_SLOT_SCALE], widget->props[RECT_TRANSFORM_SLOT_SCALE], cage->scale);
+ RNA_property_update(C, &widget->ptr[RECT_TRANSFORM_SLOT_SCALE], widget->props[RECT_TRANSFORM_SLOT_SCALE]);
+ }
+
+ /* tag the region for redraw */
+ ED_region_tag_redraw(ar);
+
+ return OPERATOR_PASS_THROUGH;
+}
+
+static void widget_rect_transform_bind_to_prop(struct wmWidget *widget, int slot)
+{
+ RectTransformWidget *cage = (RectTransformWidget *) widget;
+
+ if (slot == RECT_TRANSFORM_SLOT_OFFSET)
+ widget_rect_transform_get_property(widget, RECT_TRANSFORM_SLOT_OFFSET, cage->offset);
+ if (slot == RECT_TRANSFORM_SLOT_SCALE)
+ widget_rect_transform_get_property(widget, RECT_TRANSFORM_SLOT_SCALE, cage->scale);
+}
+
+struct wmWidget *WIDGET_rect_transform_new(struct wmWidgetGroup *wgroup, int style, float width, float height)
+{
+ RectTransformWidget *cage = MEM_callocN(sizeof(RectTransformWidget), "CageWidget");
+
+ cage->widget.draw = widget_rect_transform_draw;
+ cage->widget.invoke = widget_rect_transform_invoke;
+ cage->widget.bind_to_prop = widget_rect_transform_bind_to_prop;
+ cage->widget.handler = widget_rect_transform_handler;
+ cage->widget.intersect = widget_rect_tranfrorm_intersect;
+ cage->widget.get_cursor = widget_rect_tranfrorm_get_cursor;
+ cage->widget.max_prop = 2;
+ cage->scale[0] = cage->scale[1] = 1.0f;
+ cage->style = style;
+ cage->w = width;
+ cage->h = height;
+
+ wm_widget_register(wgroup, &cage->widget);
+
+ return (wmWidget *)cage;
+}
+
+void WIDGET_rect_transform_set_offset(struct wmWidget *widget, float offset[2])
+{
+ RectTransformWidget *cage = (RectTransformWidget *)widget;
+
+ copy_v2_v2(cage->offset, offset);
+}
+
+/********* Facemap widget ************/
+
+typedef struct FacemapWidget {
+ wmWidget widget;
+ Object *ob;
+ int facemap;
+ int style;
+ float color[4];
+} FacemapWidget;
+
+
+static void widget_facemap_draw(struct wmWidget *widget, const struct bContext *C)
+{
+ FacemapWidget *fmap_widget = (FacemapWidget *)widget;
+ glPushMatrix();
+ glMultMatrixf(&fmap_widget->ob->obmat[0][0]);
+ ED_draw_object_facemap(CTX_data_scene(C), fmap_widget->ob, fmap_widget->facemap);
+ glPopMatrix();
+}
+
+static void widget_facemap_render_3d_intersect(const struct bContext *C, struct wmWidget *widget, int selectionbase)
+{
+ GPU_select_load_id(selectionbase);
+ widget_facemap_draw(widget, C);
+}
+
+
+void WIDGET_facemap_set_color(struct wmWidget *widget, float color[4])
+{
+ FacemapWidget *fmap_widget = (FacemapWidget *)widget;
+ copy_v4_v4(fmap_widget->color, color);
+}
+
+struct wmWidget *WIDGET_facemap_new(struct wmWidgetGroup *wgroup, int style, struct Object *ob, int facemap)
+{
+ FacemapWidget *fmap_widget = MEM_callocN(sizeof(RectTransformWidget), "CageWidget");
+
+ fmap_widget->widget.draw = widget_facemap_draw;
+// fmap_widget->widget.invoke = NULL;
+// fmap_widget->widget.bind_to_prop = NULL;
+// fmap_widget->widget.handler = NULL;
+ fmap_widget->widget.render_3d_intersection = widget_facemap_render_3d_intersect;
+ fmap_widget->ob = ob;
+ fmap_widget->facemap = facemap;
+ fmap_widget->style = style;
+
+ wm_widget_register(wgroup, &fmap_widget->widget);
+
+ return (wmWidget *)fmap_widget;
+}
+
+
+void fix_linking_widget_lib(void)
+{
+ (void) 0;
+}
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index dc36cfbaf57..5e29dc2e166 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -168,6 +168,8 @@ void WM_init(bContext *C, int argc, const char **argv)
/* Enforce loading the UI for the initial homefile */
G.fileflags &= ~G_FILE_NO_UI;
+ ED_spacedropwidgets_init();
+
/* get the default database, plus a wm */
wm_homefile_read(C, NULL, G.factory_startup, NULL);
@@ -495,6 +497,9 @@ void WM_exit_ext(bContext *C, const bool do_python)
ED_clipboard_posebuf_free();
BKE_node_clipboard_clear();
+ /* widgetmaps after freeing blender, so no deleted data get accessed during cleaning up of areas */
+ WM_widgetmaptypes_free();
+
BLF_exit();
#ifdef WITH_INTERNATIONAL
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 66dd851fc91..0e49ed75f35 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -1271,6 +1271,11 @@ void WM_operator_properties_filesel(wmOperatorType *ot, int filter, short type,
if (flag & WM_FILESEL_RELPATH)
RNA_def_boolean(ot->srna, "relative_path", true, "Relative Path", "Select the file relative to the blend file");
+ if (flag & WM_FILESEL_IMAGE_COLLAPSE) {
+ prop = RNA_def_boolean(ot->srna, "collapse_images", true, "Collapse Images", "Colapse Images with the same base name");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ }
+
if ((filter & FILE_TYPE_IMAGE) || (filter & FILE_TYPE_MOVIE)) {
prop = RNA_def_boolean(ot->srna, "show_multiview", 0, "Enable Multi-View", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
@@ -4604,6 +4609,36 @@ static void WM_OT_radial_control(wmOperatorType *ot)
RNA_def_boolean(ot->srna, "secondary_tex", false, "Secondary Texture", "Tweak brush secondary/mask texture");
}
+/* ************************** widget property tweak *************** */
+
+static int widget_tweak_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int widget_tweak_modal(bContext *UNUSED(C), wmOperator *UNUSED(op), const wmEvent *event)
+{
+ if (event->type == EVT_WIDGET_RELEASED)
+ return OPERATOR_FINISHED;
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void WM_OT_widget_tweak(wmOperatorType *ot)
+{
+ ot->name = "Widget Tweak";
+ ot->idname = "WM_OT_widget_tweak";
+ ot->description = "Tweak property attached to a widget";
+
+ ot->invoke = widget_tweak_invoke;
+ ot->modal = widget_tweak_modal;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_BLOCKING | OPTYPE_UNDO;
+}
+
+
+
/* ************************** timer for testing ***************** */
/* uses no type defines, fully local testing function anyway... ;) */
@@ -5005,6 +5040,7 @@ void wm_operatortype_init(void)
WM_operatortype_append(WM_OT_call_menu);
WM_operatortype_append(WM_OT_call_menu_pie);
WM_operatortype_append(WM_OT_radial_control);
+ WM_operatortype_append(WM_OT_widget_tweak);
WM_operatortype_append(WM_OT_stereo3d_set);
#if defined(WIN32)
WM_operatortype_append(WM_OT_console_toggle);
@@ -5397,3 +5433,11 @@ EnumPropertyItem *RNA_mask_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA
return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->mask.first : NULL, true);
}
+EnumPropertyItem *RNA_cachelibrary_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->cache_library.first : NULL, false);
+}
+EnumPropertyItem *RNA_cachelibrary_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->cache_library.first : NULL, true);
+}
diff --git a/source/blender/windowmanager/intern/wm_widgets.c b/source/blender/windowmanager/intern/wm_widgets.c
new file mode 100644
index 00000000000..ad915311015
--- /dev/null
+++ b/source/blender/windowmanager/intern/wm_widgets.c
@@ -0,0 +1,909 @@
+/*
+ * ***** 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) 2007 Blender Foundation but based
+ * on ghostwinlay.c (C) 2001-2002 by NaN Holding BV
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation, 2008
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/intern/wm_widgets.c
+ * \ingroup wm
+ *
+ * Window management, widget API.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "DNA_screen_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_windowmanager_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_userdef_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_idprop.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+#include "BKE_screen.h"
+
+#include "ED_view3d.h"
+#include "ED_screen.h"
+#include "ED_util.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+#include "wm.h"
+#include "wm_window.h"
+#include "wm_event_system.h"
+#include "wm_event_types.h"
+#include "wm_draw.h"
+
+#include "GL/glew.h"
+#include "GPU_select.h"
+
+#include "RNA_access.h"
+#include "BPY_extern.h"
+
+/**
+ * This is a container for all widget types that can be instantiated in a region.
+ * (similar to dropboxes).
+ *
+ * \note There is only ever one of these for every (area, region) combination.
+ */
+typedef struct wmWidgetMapType {
+ struct wmWidgetMapType *next, *prev;
+ char idname[64];
+ short spaceid, regionid;
+ /**
+ * Check if widgetmap does 3D drawing
+ * (uses a different kind of interaction),
+ * - 3d: use glSelect buffer.
+ * - 2d: use simple cursor position intersection test. */
+ bool is_3d;
+ /* types of widgetgroups for this widgetmap type */
+ ListBase widgetgrouptypes;
+} wmWidgetMapType;
+
+
+/* store all widgetboxmaps here. Anyone who wants to register a widget for a certain
+ * area type can query the widgetbox to do so */
+static ListBase widgetmaptypes = {NULL, NULL};
+
+
+struct wmWidgetGroupType *WM_widgetgrouptype_new(
+ int (*poll)(const struct bContext *C, struct wmWidgetGroupType *),
+ void (*draw)(const struct bContext *, struct wmWidgetGroup *),
+ struct Main *bmain, const char *mapidname, short spaceid, short regionid, bool is_3d
+ )
+{
+ bScreen *sc;
+ struct wmWidgetMapType *wmaptype = WM_widgetmaptype_find(mapidname, spaceid, regionid, is_3d, false);
+ wmWidgetGroupType *wgrouptype;
+
+ if (!wmaptype) {
+ fprintf(stderr, "widgetgrouptype creation: widgetmap type does not exist");
+ return NULL;
+ }
+
+ wgrouptype = MEM_callocN(sizeof(wmWidgetGroupType), "widgetgroup");
+
+ wgrouptype->poll = poll;
+ wgrouptype->draw = draw;
+ wgrouptype->spaceid = spaceid;
+ wgrouptype->regionid = regionid;
+ wgrouptype->is_3d = is_3d;
+ BLI_strncpy(wgrouptype->mapidname, mapidname, 64);
+
+ /* add the type for future created areas of the same type */
+ BLI_addtail(&wmaptype->widgetgrouptypes, wgrouptype);
+
+ /* now create a widget for all existing areas. (main is missing when we create new areas so not needed) */
+ if (bmain) {
+ for (sc = bmain->screen.first; sc; sc = sc->id.next) {
+ ScrArea *sa;
+ for (sa = sc->areabase.first; sa; sa = sa->next) {
+ SpaceLink *sl;
+
+ for (sl = sa->spacedata.first; sl; sl = sl->next) {
+ ARegion *ar;
+ ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
+
+ for (ar = lb->first; ar; ar = ar->next) {
+ wmWidgetMap *wmap;
+ for (wmap = ar->widgetmaps.first; wmap; wmap = wmap->next) {
+ if (wmap->type == wmaptype) {
+ wmWidgetGroup *wgroup = MEM_callocN(sizeof(wmWidgetGroup), "widgetgroup");
+ wgroup->type = wgrouptype;
+
+ /* just add here, drawing will occur on next update */
+ BLI_addtail(&wmap->widgetgroups, wgroup);
+ wm_widgetmap_set_highlighted_widget(wmap, NULL, NULL, 0);
+ ED_region_tag_redraw(ar);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return wgrouptype;
+}
+
+wmWidget *WM_widget_new(void (*draw)(struct wmWidget *customdata, const struct bContext *C),
+ void (*render_3d_intersection)(const struct bContext *C, struct wmWidget *customdata, int selectionbase),
+ int (*intersect)(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget),
+ int (*handler)(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget))
+{
+ wmWidget *widget;
+
+ widget = MEM_callocN(sizeof(wmWidget), "widget");
+
+ widget->draw = draw;
+ widget->handler = handler;
+ widget->intersect = intersect;
+ widget->render_3d_intersection = render_3d_intersection;
+
+ return widget;
+}
+
+void WM_widget_property(struct wmWidget *widget, int slot, struct PointerRNA *ptr, const char *propname)
+{
+ if (slot < 0 || slot >= widget->max_prop) {
+ fprintf(stderr, "invalid index %d when binding property for widget type %s\n", slot, widget->idname);
+ return;
+ }
+
+ /* if widget evokes an operator we cannot use it for property manipulation */
+ widget->opname = NULL;
+ widget->ptr[slot] = *ptr;
+ widget->props[slot] = RNA_struct_find_property(ptr, propname);
+
+ if (widget->bind_to_prop)
+ widget->bind_to_prop(widget, slot);
+}
+
+PointerRNA *WM_widget_operator(struct wmWidget *widget, const char *opname)
+{
+ wmOperatorType *ot = WM_operatortype_find(opname, 0);
+
+ if (ot) {
+ widget->opname = opname;
+
+ WM_operator_properties_create_ptr(&widget->opptr, ot);
+
+ return &widget->opptr;
+ }
+ else {
+ fprintf(stderr, "Error binding operator to widget: operator %s not found!\n", opname);
+ }
+
+ return NULL;
+}
+
+
+static void wm_widget_delete(ListBase *widgetlist, wmWidget *widget)
+{
+ if (widget->opptr.data) {
+ WM_operator_properties_free(&widget->opptr);
+ }
+
+ MEM_freeN(widget->props);
+ MEM_freeN(widget->ptr);
+
+ BLI_freelinkN(widgetlist, widget);
+}
+
+
+static void widget_calculate_scale(wmWidget *widget, const bContext *C)
+{
+ float scale = 1.0f;
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ if (rv3d && !(U.tw_flag & V3D_3D_WIDGETS) && (widget->flag & WM_WIDGET_SCALE_3D)) {
+ if (widget->get_final_position) {
+ float position[3];
+
+ widget->get_final_position(widget, position);
+ scale = ED_view3d_pixel_size(rv3d, position) * U.tw_size;
+ }
+ else {
+ scale = ED_view3d_pixel_size(rv3d, widget->origin) * U.tw_size;
+ }
+ }
+
+ widget->scale = scale * widget->user_scale;
+}
+
+static bool widgets_compare(wmWidget *widget, wmWidget *widget2)
+{
+ int i;
+
+ if (widget->max_prop != widget2->max_prop)
+ return false;
+
+ for (i = 0; i < widget->max_prop; i++) {
+ if (widget->props[i] != widget2->props[i] || widget->ptr[i].data != widget2->ptr[i].data)
+ return false;
+ }
+
+ return true;
+}
+
+void WM_widgets_update(const bContext *C, wmWidgetMap *wmap)
+{
+ wmWidget *widget;
+
+ if (!wmap)
+ return;
+
+ widget = wmap->active_widget;
+
+ if (widget) {
+ widget_calculate_scale(widget, C);
+ }
+ else if (wmap->widgetgroups.first) {
+ wmWidgetGroup *wgroup;
+
+ for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup->next) {
+ if (!wgroup->type->poll || wgroup->type->poll(C, wgroup->type))
+ {
+ wmWidget *highlighted = NULL;
+
+ /* first delete and recreate the widgets */
+ for (widget = wgroup->widgets.first; widget;) {
+ wmWidget *widget_next = widget->next;
+
+ /* do not delete the highlighted widget, instead keep it to compare with the new one */
+ if (widget->flag & WM_WIDGET_HIGHLIGHT) {
+ highlighted = widget;
+ BLI_remlink(&wgroup->widgets, widget);
+ widget->next = widget->prev = NULL;
+ }
+ else {
+ wm_widget_delete(&wgroup->widgets, widget);
+ }
+ widget = widget_next;
+ }
+
+ if (wgroup->type->draw) {
+ wgroup->type->draw(C, wgroup);
+ }
+
+ if (highlighted) {
+ for (widget = wgroup->widgets.first; widget; widget = widget->next) {
+ if (widgets_compare(widget, highlighted))
+ {
+ widget->flag |= WM_WIDGET_HIGHLIGHT;
+ wmap->highlighted_widget = widget;
+ widget->highlighted_part = highlighted->highlighted_part;
+ wm_widget_delete(&wgroup->widgets, highlighted);
+ highlighted = NULL;
+ break;
+ }
+ }
+ }
+
+ /* if we don't find a highlighted widget, delete the old one here */
+ if (highlighted) {
+ MEM_freeN(highlighted);
+ highlighted = NULL;
+ wmap->highlighted_widget = NULL;
+ }
+
+ for (widget = wgroup->widgets.first; widget; widget = widget->next) {
+ widget_calculate_scale(widget, C);
+ }
+ }
+ }
+ }
+}
+
+void WM_widgets_draw(const bContext *C, wmWidgetMap *wmap, bool in_scene)
+{
+ wmWidget *widget;
+ bool use_lighting;
+
+ if (!wmap)
+ return;
+
+ use_lighting = (U.tw_flag & V3D_SHADED_WIDGETS) != 0;
+
+ if (use_lighting) {
+ float lightpos[4] = {0.0, 0.0, 1.0, 0.0};
+ float diffuse[4] = {1.0, 1.0, 1.0, 0.0};
+
+ glPushAttrib(GL_LIGHTING_BIT | GL_ENABLE_BIT);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ glEnable(GL_COLOR_MATERIAL);
+ glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
+ glPushMatrix();
+ glLoadIdentity();
+ glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
+ glPopMatrix();
+ }
+
+ widget = wmap->active_widget;
+
+ if (widget && in_scene == ((widget->flag & WM_WIDGET_SCENE_DEPTH)!= 0)) {
+ /* notice that we don't update the widgetgroup, widget is now on its own, it should have all
+ * relevant data to update itself */
+ widget->draw(widget, C);
+ }
+ else if (wmap->widgetgroups.first) {
+ wmWidgetGroup *wgroup;
+
+ for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup->next) {
+ if (!wgroup->type->poll || wgroup->type->poll(C, wgroup->type))
+ {
+ for (widget = wgroup->widgets.first; widget; widget = widget->next) {
+ if ((!(widget->flag & WM_WIDGET_DRAW_HOVER) || (widget->flag & WM_WIDGET_HIGHLIGHT)) &&
+ ((widget->flag & WM_WIDGET_SCENE_DEPTH) != 0) == in_scene)
+ {
+ widget->draw(widget, C);
+ }
+ }
+ }
+ }
+ }
+
+ if (use_lighting)
+ glPopAttrib();
+}
+
+void WM_event_add_area_widgetmap_handlers(ARegion *ar)
+{
+ wmWidgetMap *wmap;
+ wmEventHandler *handler;
+
+ for (wmap = ar->widgetmaps.first; wmap; wmap = wmap->next) {
+ handler = MEM_callocN(sizeof(wmEventHandler), "widget handler");
+
+ handler->widgetmap = wmap;
+ BLI_addtail(&ar->handlers, handler);
+ }
+}
+
+void WM_modal_handler_attach_widgetgroup(bContext *C, wmEventHandler *handler, wmWidgetGroupType *wgrouptype, wmOperator *op)
+{
+ /* maybe overly careful, but widgetgrouptype could come from a failed creation */
+ if (!wgrouptype) {
+ return;
+ }
+
+ /* now instantiate the widgetmap */
+ wgrouptype->op = op;
+
+ if (handler->op_region && handler->op_region->widgetmaps.first) {
+ wmWidgetMap *wmap;
+ for (wmap = handler->op_region->widgetmaps.first; wmap; wmap = wmap->next) {
+ wmWidgetMapType *wmaptype = wmap->type;
+
+ if (wmaptype->spaceid == wgrouptype->spaceid && wmaptype->regionid == wgrouptype->regionid) {
+ handler->widgetmap = wmap;
+ }
+ }
+ }
+
+ WM_event_add_mousemove(C);
+}
+
+bool wm_widget_register(struct wmWidgetGroup *wgroup, wmWidget *widget)
+{
+ widget->user_scale = 1.0f;
+
+ /* create at least one property for interaction */
+ if (widget->max_prop == 0) {
+ widget->max_prop = 1;
+ }
+
+ widget->props = MEM_callocN(sizeof(PropertyRNA *) * widget->max_prop, "widget->props");
+ widget->ptr = MEM_callocN(sizeof(PointerRNA) * widget->max_prop, "widget->ptr");
+
+ BLI_addtail(&wgroup->widgets, widget);
+ return true;
+}
+
+void WM_widget_set_origin(struct wmWidget *widget, float origin[3])
+{
+ copy_v3_v3(widget->origin, origin);
+}
+
+void WM_widget_set_3d_scale(struct wmWidget *widget, bool scale)
+{
+ if (scale) {
+ widget->flag |= WM_WIDGET_SCALE_3D;
+ }
+ else {
+ widget->flag &= ~WM_WIDGET_SCALE_3D;
+ }
+}
+
+
+void WM_widget_set_draw_on_hover_only(struct wmWidget *widget, bool draw)
+{
+ if (draw) {
+ widget->flag |= WM_WIDGET_DRAW_HOVER;
+ }
+ else {
+ widget->flag &= ~WM_WIDGET_DRAW_HOVER;
+ }
+}
+
+void WM_widget_set_scene_depth(struct wmWidget *widget, bool scene)
+{
+ if (scene) {
+ widget->flag |= WM_WIDGET_SCENE_DEPTH;
+ }
+ else {
+ widget->flag &= ~WM_WIDGET_SCENE_DEPTH;
+ }
+}
+
+
+void WM_widget_set_scale(struct wmWidget *widget, float scale)
+{
+ widget->user_scale = scale;
+}
+
+
+wmWidgetMapType *WM_widgetmaptype_find(const char *idname, int spaceid, int regionid, bool is_3d, bool create)
+{
+ wmWidgetMapType *wmaptype;
+
+ for (wmaptype = widgetmaptypes.first; wmaptype; wmaptype = wmaptype->next) {
+ if (wmaptype->spaceid == spaceid && wmaptype->regionid == regionid && wmaptype->is_3d == is_3d
+ && strcmp(wmaptype->idname, idname) == 0) {
+ return wmaptype;
+ }
+ }
+
+ if (!create) return NULL;
+
+ wmaptype = MEM_callocN(sizeof(wmWidgetMapType), "widgettype list");
+ wmaptype->spaceid = spaceid;
+ wmaptype->regionid = regionid;
+ wmaptype->is_3d = is_3d;
+ BLI_strncpy(wmaptype->idname, idname, 64);
+ BLI_addhead(&widgetmaptypes, wmaptype);
+
+ return wmaptype;
+}
+
+void WM_widgetmaptypes_free(void)
+{
+ wmWidgetMapType *wmaptype;
+
+ for (wmaptype = widgetmaptypes.first; wmaptype; wmaptype = wmaptype->next) {
+ BLI_freelistN(&wmaptype->widgetgrouptypes);
+ }
+ BLI_freelistN(&widgetmaptypes);
+
+ fix_linking_widget_lib();
+}
+
+bool wm_widgetmap_is_3d(struct wmWidgetMap *wmap)
+{
+ return wmap->type->is_3d;
+}
+
+static void widget_find_active_3D_loop(bContext *C, ListBase *visible_widgets)
+{
+ int selectionbase = 0;
+ LinkData *link;
+ wmWidget *widget;
+
+ for (link = visible_widgets->first; link; link = link->next) {
+ widget = link->data;
+ /* pass the selection id shifted by 8 bits. Last 8 bits are used for selected widget part id */
+ widget->render_3d_intersection(C, widget, selectionbase << 8);
+
+ selectionbase++;
+ }
+}
+
+static int wm_widget_find_highlighted_3D_intern (ListBase *visible_widgets, bContext *C, const struct wmEvent *event, float hotspot)
+{
+ ScrArea *sa = CTX_wm_area(C);
+ ARegion *ar = CTX_wm_region(C);
+ View3D *v3d = sa->spacedata.first;
+ RegionView3D *rv3d = ar->regiondata;
+ rctf rect, selrect;
+ GLuint buffer[64]; // max 4 items per select, so large enuf
+ short hits;
+ const bool do_passes = GPU_select_query_check_active();
+
+ extern void view3d_winmatrix_set(ARegion *ar, View3D *v3d, rctf *rect);
+
+ rect.xmin = event->mval[0] - hotspot;
+ rect.xmax = event->mval[0] + hotspot;
+ rect.ymin = event->mval[1] - hotspot;
+ rect.ymax = event->mval[1] + hotspot;
+
+ selrect = rect;
+
+ view3d_winmatrix_set(ar, v3d, &rect);
+ mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat);
+
+ if (do_passes)
+ GPU_select_begin(buffer, 64, &selrect, GPU_SELECT_NEAREST_FIRST_PASS, 0);
+ else
+ GPU_select_begin(buffer, 64, &selrect, GPU_SELECT_ALL, 0);
+ /* do the drawing */
+ widget_find_active_3D_loop(C, visible_widgets);
+
+ hits = GPU_select_end();
+
+ if (do_passes) {
+ GPU_select_begin(buffer, 64, &selrect, GPU_SELECT_NEAREST_SECOND_PASS, hits);
+ widget_find_active_3D_loop(C, visible_widgets);
+ GPU_select_end();
+ }
+
+ view3d_winmatrix_set(ar, v3d, NULL);
+ mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat);
+
+ if (hits == 1) {
+ return buffer[3];
+
+ }
+ /* find the widget the value belongs to */
+ else if (hits > 1) {
+ GLuint val, dep, mindep = 0, minval = -1;
+ int a;
+
+ /* we compare the hits in buffer, but value centers highest */
+ /* we also store the rotation hits separate (because of arcs) and return hits on other widgets if there are */
+
+ for (a = 0; a < hits; a++) {
+ dep = buffer[4 * a + 1];
+ val = buffer[4 * a + 3];
+
+ if (minval == -1 || dep < mindep) {
+ mindep = dep;
+ minval = val;
+ }
+ }
+
+ return minval;
+ }
+ return -1;
+}
+
+static void wm_prepare_visible_widgets_3D(struct wmWidgetMap *wmap, ListBase *visible_widgets, bContext *C)
+{
+ wmWidget *widget;
+ wmWidgetGroup *wgroup;
+
+ for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup->next) {
+ if (!wgroup->type->poll || wgroup->type->poll(C, wgroup->type)) {
+ for (widget = wgroup->widgets.first; widget; widget = widget->next) {
+ if (widget->render_3d_intersection) {
+ BLI_addhead(visible_widgets, BLI_genericNodeN(widget));
+ }
+ }
+ }
+ }
+}
+
+wmWidget *wm_widget_find_highlighted_3D(struct wmWidgetMap *wmap, bContext *C, const struct wmEvent *event, unsigned char *part)
+{
+ int ret;
+ wmWidget *result = NULL;
+
+ ListBase visible_widgets = {0};
+
+ wm_prepare_visible_widgets_3D(wmap, &visible_widgets, C);
+
+ *part = 0;
+ /* set up view matrices */
+ view3d_operator_needs_opengl(C);
+
+ ret = wm_widget_find_highlighted_3D_intern(&visible_widgets, C, event, 0.5f * (float)U.tw_hotspot);
+
+ if (ret != -1) {
+ LinkData *link;
+ int retsec;
+ retsec = wm_widget_find_highlighted_3D_intern(&visible_widgets, C, event, 0.2f * (float)U.tw_hotspot);
+
+ if (retsec != -1)
+ ret = retsec;
+
+ link = BLI_findlink(&visible_widgets, ret >> 8);
+ *part = ret & 255;
+ result = link->data;
+ }
+
+ BLI_freelistN(&visible_widgets);
+
+ return result;
+}
+
+wmWidget *wm_widget_find_highlighted(struct wmWidgetMap *wmap, bContext *C, const struct wmEvent *event, unsigned char *part)
+{
+ wmWidget *widget;
+ wmWidgetGroup *wgroup;
+
+ for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup->next) {
+ if (!wgroup->type->poll || wgroup->type->poll(C, wgroup->type)) {
+ for (widget = wgroup->widgets.first; widget; widget = widget->next) {
+ if (widget->intersect) {
+ if ((*part = widget->intersect(C, event, widget)))
+ return widget;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+bool WM_widgetmap_cursor_set(wmWidgetMap *wmap, wmWindow *win)
+{
+ for (; wmap; wmap = wmap->next) {
+ wmWidget *widget = wmap->highlighted_widget;
+ if (widget && widget->get_cursor) {
+ WM_cursor_set(win, widget->get_cursor(widget));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void wm_widgetmap_set_highlighted_widget(struct wmWidgetMap *wmap, struct bContext *C, struct wmWidget *widget, unsigned char part)
+{
+ if ((widget != wmap->highlighted_widget) || (widget && part != widget->highlighted_part)) {
+ if (wmap->highlighted_widget) {
+ wmap->highlighted_widget->flag &= ~WM_WIDGET_HIGHLIGHT;
+ wmap->highlighted_widget->highlighted_part = 0;
+ }
+
+ wmap->highlighted_widget = widget;
+
+ if (widget) {
+ widget->flag |= WM_WIDGET_HIGHLIGHT;
+ widget->highlighted_part = part;
+
+ if (C && widget->get_cursor) {
+ wmWindow *win = CTX_wm_window(C);
+ WM_cursor_set(win, widget->get_cursor(widget));
+ }
+ }
+ else if (C) {
+ wmWindow *win = CTX_wm_window(C);
+ WM_cursor_set(win, CURSOR_STD);
+ }
+
+ /* tag the region for redraw */
+ if (C) {
+ ARegion *ar = CTX_wm_region(C);
+ ED_region_tag_redraw(ar);
+ }
+ }
+}
+
+struct wmWidget *wm_widgetmap_get_highlighted_widget(struct wmWidgetMap *wmap)
+{
+ return wmap->highlighted_widget;
+}
+
+void wm_widgetmap_set_active_widget(struct wmWidgetMap *wmap, struct bContext *C, struct wmEvent *event, struct wmWidget *widget, bool call_op)
+{
+ if (widget) {
+ if (call_op) {
+ wmOperatorType *ot;
+ const char *opname = (widget->opname) ? widget->opname : "WM_OT_widget_tweak";
+
+ ot = WM_operatortype_find(opname, 0);
+
+ if (ot) {
+ /* first activate the widget itself */
+ if (widget->invoke && widget->handler) {
+ widget->flag |= WM_WIDGET_ACTIVE;
+ widget->invoke(C, event, widget);
+ wmap->active_widget = widget;
+ }
+
+ /* if operator runs modal, we will need to activate the current widgetmap on the operator handler, so it can
+ * process events first, then pass them on to the operator */
+ if (WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &widget->opptr) == OPERATOR_RUNNING_MODAL) {
+ /* check if operator added a a modal event handler */
+ wmEventHandler *handler = CTX_wm_window(C)->modalhandlers.first;
+
+ if (handler && handler->op && handler->op->type == ot) {
+ handler->widgetmap = wmap;
+ }
+ }
+
+ /* we failed to hook the widget to the operator handler or operator was cancelled, return */
+ if (!wmap->active_widget) {
+ widget->flag &= ~WM_WIDGET_ACTIVE;
+ /* first activate the widget itself */
+ if (widget->interaction_data) {
+ MEM_freeN(widget->interaction_data);
+ widget->interaction_data = NULL;
+ }
+ }
+ return;
+ }
+ else {
+ printf("Widget error: operator not found");
+ wmap->active_widget = NULL;
+ return;
+ }
+ }
+ else {
+ if (widget->invoke && widget->handler) {
+ widget->flag |= WM_WIDGET_ACTIVE;
+ widget->invoke(C, event, widget);
+ wmap->active_widget = widget;
+ }
+ }
+ }
+ else {
+ widget = wmap->active_widget;
+
+ /* deactivate, widget but first take care of some stuff */
+ if (widget) {
+ widget->flag &= ~WM_WIDGET_ACTIVE;
+ /* first activate the widget itself */
+ if (widget->interaction_data) {
+ MEM_freeN(widget->interaction_data);
+ widget->interaction_data = NULL;
+ }
+ }
+ wmap->active_widget = NULL;
+
+ if (C) {
+ ARegion *ar = CTX_wm_region(C);
+ ED_region_tag_redraw(ar);
+ WM_event_add_mousemove(C);
+ }
+ }
+}
+
+struct wmWidget *wm_widgetmap_get_active_widget(struct wmWidgetMap *wmap)
+{
+ return wmap->active_widget;
+}
+
+
+struct wmWidgetMap *WM_widgetmap_from_type(const char *idname, int spaceid, int regionid, bool is_3d)
+{
+ wmWidgetMapType *wmaptype = WM_widgetmaptype_find(idname, spaceid, regionid, is_3d, true);
+ wmWidgetGroupType *wgrouptype = wmaptype->widgetgrouptypes.first;
+ wmWidgetMap *wmap;
+
+ wmap = MEM_callocN(sizeof(wmWidgetMap), "WidgetMap");
+ wmap->type = wmaptype;
+
+ /* create all widgetgroups for this widgetmap. We may create an empty one too in anticipation of widgets from operators etc */
+ for (; wgrouptype; wgrouptype = wgrouptype->next) {
+ wmWidgetGroup *wgroup = MEM_callocN(sizeof(wmWidgetGroup), "widgetgroup");
+ wgroup->type = wgrouptype;
+ BLI_addtail(&wmap->widgetgroups, wgroup);
+ }
+
+ return wmap;
+}
+
+void WM_widgetmap_delete(struct wmWidgetMap *wmap)
+{
+ wmWidgetGroup *wgroup;
+
+ if (!wmap)
+ return;
+
+ for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup->next) {
+ wmWidget *widget;
+
+ for (widget = wgroup->widgets.first; widget;) {
+ wmWidget *widget_next = widget->next;
+ wm_widget_delete(&wgroup->widgets, widget);
+ widget = widget_next;
+ }
+ }
+ BLI_freelistN(&wmap->widgetgroups);
+
+ MEM_freeN(wmap);
+}
+
+static void wm_widgetgroup_free(bContext *C, wmWidgetMap *wmap, wmWidgetGroup *wgroup)
+{
+ wmWidget *widget;
+
+ for (widget = wgroup->widgets.first; widget;) {
+ wmWidget *widget_next = widget->next;
+ if (widget->flag & WM_WIDGET_HIGHLIGHT) {
+ wm_widgetmap_set_highlighted_widget(wmap, C, NULL, 0);
+ }
+ if (widget->flag & WM_WIDGET_ACTIVE) {
+ wm_widgetmap_set_active_widget(wmap, C, NULL, NULL, false);
+ }
+ wm_widget_delete(&wgroup->widgets, widget);
+ widget = widget_next;
+ }
+
+#ifdef WITH_PYTHON
+ if (wgroup->py_instance) {
+ /* do this first in case there are any __del__ functions or
+ * similar that use properties */
+ BPY_DECREF_RNA_INVALIDATE(wgroup->py_instance);
+ }
+#endif
+
+ if (wgroup->reports && (wgroup->reports->flag & RPT_FREE)) {
+ BKE_reports_clear(wgroup->reports);
+ MEM_freeN(wgroup->reports);
+ }
+
+ BLI_remlink(&wmap->widgetgroups, wgroup);
+ MEM_freeN(wgroup);
+}
+
+void WM_widgetgrouptype_unregister(bContext *C, Main *bmain, wmWidgetGroupType *wgrouptype)
+{
+ bScreen *sc;
+ wmWidgetMapType *wmaptype;
+
+ for (sc = bmain->screen.first; sc; sc = sc->id.next) {
+ ScrArea *sa;
+ for (sa = sc->areabase.first; sa; sa = sa->next) {
+ SpaceLink *sl;
+
+ for (sl = sa->spacedata.first; sl; sl = sl->next) {
+ ARegion *ar;
+ ListBase *lb = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
+
+ for (ar = lb->first; ar; ar = ar->next) {
+ wmWidgetMap *wmap;
+ for (wmap = ar->widgetmaps.first; wmap; wmap = wmap->next) {
+ wmWidgetGroup *wgroup, *wgroup_tmp;
+ for (wgroup = wmap->widgetgroups.first; wgroup; wgroup = wgroup_tmp) {
+ wgroup_tmp = wgroup->next;
+ if (wgroup->type == wgrouptype) {
+ wm_widgetgroup_free(C, wmap, wgroup);
+ ED_region_tag_redraw(ar);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ wmaptype = WM_widgetmaptype_find(wgrouptype->mapidname, wgrouptype->spaceid, wgrouptype->regionid, wgrouptype->is_3d, false);
+ BLI_remlink(&wmaptype->widgetgrouptypes, wgrouptype);
+ wgrouptype->prev = wgrouptype->next = NULL;
+ MEM_freeN(wgrouptype);
+}
+
diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h
index 2f06ddab1e8..a4dad8d48ee 100644
--- a/source/blender/windowmanager/wm.h
+++ b/source/blender/windowmanager/wm.h
@@ -33,6 +33,12 @@
struct wmWindow;
struct ReportList;
+struct wmEvent;
+struct wmWidgetMap;
+struct wmOperatorType;
+struct PointerRNA;
+struct PropertyRNA;
+struct wmOperator;
typedef struct wmPaintCursor {
struct wmPaintCursor *next, *prev;
@@ -43,6 +49,77 @@ typedef struct wmPaintCursor {
void (*draw)(bContext *C, int, int, void *customdata);
} wmPaintCursor;
+/* widgets are set per screen/area/region by registering them on widgetmaps */
+typedef struct wmWidget {
+ struct wmWidget *next, *prev;
+
+ char idname[64];
+
+ /* draw widget */
+ void (*draw)(struct wmWidget *widget, const struct bContext *C);
+ /* determine if the mouse intersects with the widget. The calculation should be done in the callback itself */
+ int (*intersect)(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget);
+
+ /* determines 3d intersection by rendering the widget in a selection routine. */
+ void (*render_3d_intersection)(const struct bContext *C, struct wmWidget *widget, int selectionbase);
+
+ /* handler used by the widget. Usually handles interaction tied to a widget type */
+ int (*handler)(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget);
+
+ /* widget-specific handler to update widget attributes when a property is bound */
+ void (*bind_to_prop)(struct wmWidget *widget, int slot);
+
+ /* returns the final position which may be different from the origin, depending on the widget.
+ * used in calculations of scale */
+ void (*get_final_position)(struct wmWidget *widget, float vec[3]);
+
+ /* activate a widget state when the user clicks on it */
+ int (*invoke)(struct bContext *C, const struct wmEvent *event, struct wmWidget *widget);
+
+ int (*get_cursor)(struct wmWidget *widget);
+
+ int flag; /* flags set by drawing and interaction, such as highlighting */
+
+ unsigned char highlighted_part;
+
+ /* center of widget in space, 2d or 3d */
+ float origin[3];
+
+ /* runtime property, set the scale while drawing on the viewport */
+ float scale;
+
+ /* user defined scale, in addition to the original one */
+ float user_scale;
+
+ /* data used during interaction */
+ void *interaction_data;
+
+ /* name of operator to spawn when activating the widget */
+ const char *opname;
+
+ /* operator properties if widget spawns and controls an operator, or owner pointer if widget spawns and controls a property */
+ struct PointerRNA opptr;
+
+ /* maximum number of properties attached to the widget */
+ int max_prop;
+
+ /* arrays of properties attached to various widget parameters. As the widget is interacted with, those properties get updated */
+ struct PointerRNA *ptr;
+ struct PropertyRNA **props;
+} wmWidget;
+
+/* wmWidget->flag */
+enum widgetflags {
+ /* states */
+ WM_WIDGET_HIGHLIGHT = (1 << 0),
+ WM_WIDGET_ACTIVE = (1 << 1),
+
+ WM_WIDGET_DRAW_HOVER = (1 << 2),
+
+ WM_WIDGET_SCALE_3D = (1 << 3),
+ WM_WIDGET_SCENE_DEPTH = (1 << 4) /* widget is depth culled with scene objects*/
+};
+
extern void wm_close_and_free(bContext *C, wmWindowManager *);
extern void wm_close_and_free_all(bContext *C, ListBase *);
@@ -88,6 +165,11 @@ void wm_stereo3d_set_cancel(bContext *C, wmOperator *op);
void wm_open_init_load_ui(wmOperator *op, bool use_prefs);
void wm_open_init_use_scripts(wmOperator *op, bool use_prefs);
+/* wm_widgets.c */
+bool wm_widgetmap_is_3d(struct wmWidgetMap *wmap);
+bool wm_widget_register(struct wmWidgetGroup *wgroup, struct wmWidget *widget);
+
+
/* hack to store circle select size - campbell, must replace with nice operator memory */
#define GESTURE_MEMORY
@@ -95,5 +177,7 @@ void wm_open_init_use_scripts(wmOperator *op, bool use_prefs);
extern int circle_select_size;
#endif
+void fix_linking_widget_lib(void);
+
#endif /* __WM_H__ */
diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h
index efc01b1f8a8..9127d3ec6f0 100644
--- a/source/blender/windowmanager/wm_event_system.h
+++ b/source/blender/windowmanager/wm_event_system.h
@@ -39,6 +39,8 @@
struct ScrArea;
struct ARegion;
+struct wmWidgetMap;
+struct wmWidget;
/* wmKeyMap is in DNA_windowmanager.h, it's savable */
@@ -68,6 +70,8 @@ typedef struct wmEventHandler {
/* drop box handler */
ListBase *dropboxes;
+ /* widget handler */
+ struct wmWidgetMap *widgetmap;
} wmEventHandler;
@@ -96,5 +100,14 @@ void wm_dropbox_free(void);
void wm_drags_check_ops(bContext *C, wmEvent *event);
void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect);
+/* wm_widgets.c */
+struct wmWidget *wm_widget_find_highlighted_3D(struct wmWidgetMap *wmap, struct bContext *C, const struct wmEvent *event, unsigned char *part);
+wmWidget *wm_widget_find_highlighted(struct wmWidgetMap *wmap, bContext *C, const struct wmEvent *event, unsigned char *part);
+void wm_widgetmap_set_highlighted_widget(struct wmWidgetMap *wmap, struct bContext *C, struct wmWidget *widget, unsigned char part);
+struct wmWidget *wm_widgetmap_get_highlighted_widget(struct wmWidgetMap *wmap);
+
+void wm_widgetmap_set_active_widget(struct wmWidgetMap *wmap, struct bContext *C, struct wmEvent *event, struct wmWidget *widget, bool call_op);
+struct wmWidget *wm_widgetmap_get_active_widget(struct wmWidgetMap *wmap);
+
#endif /* __WM_EVENT_SYSTEM_H__ */
diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h
index ecc29de0e7d..ff4c1874b59 100644
--- a/source/blender/windowmanager/wm_event_types.h
+++ b/source/blender/windowmanager/wm_event_types.h
@@ -330,6 +330,8 @@ enum {
EVT_DROP = 0x5023,
EVT_BUT_CANCEL = 0x5024,
+ EVT_WIDGET_UPDATE = 0x5024,
+ EVT_WIDGET_RELEASED = 0x5025,
/* ********** End of Blender internal events. ********** */
};
diff --git a/source/blenderplayer/CMakeLists.txt b/source/blenderplayer/CMakeLists.txt
index 1136524da04..4be578e7cd8 100644
--- a/source/blenderplayer/CMakeLists.txt
+++ b/source/blenderplayer/CMakeLists.txt
@@ -150,6 +150,8 @@ endif()
bf_dna
ge_videotex
bf_blenfont
+ bf_pointcache_alembic
+ bf_pointcache
bf_intern_audaspace
blenkernel_blc
bf_bmesh
@@ -210,6 +212,10 @@ endif()
list(APPEND BLENDER_SORTED_LIBS bf_intern_locale)
endif()
+ if(WITH_OPENVDB)
+ list(APPEND BLENDER_SORTED_LIBS bf_intern_openvdb)
+ 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 9c4a331f5d1..54de8242ae4 100644
--- a/source/blenderplayer/bad_level_call_stubs/stubs.c
+++ b/source/blenderplayer/bad_level_call_stubs/stubs.c
@@ -82,6 +82,7 @@ struct HookModifierData;
struct NodeBlurData;
struct Nurb;
struct Object;
+struct ParticleSystem;
struct PBVHNode;
struct PyObject;
struct Render;
@@ -111,6 +112,7 @@ struct bConstraint;
struct bConstraintOb;
struct bConstraintTarget;
struct bContextDataResult;
+struct bFaceMap;
struct bNode;
struct bNodeType;
struct bNodeSocket;
@@ -128,6 +130,7 @@ struct wmOperator;
struct wmOperatorType;
struct wmWindow;
struct wmWindowManager;
+struct wmWidgetMap;
/* -------------------------------------------------------------------- */
@@ -144,6 +147,7 @@ struct wmWindowManager;
#include "../../intern/elbeem/extern/elbeem.h"
#include "../blender/blenkernel/BKE_modifier.h"
#include "../blender/blenkernel/BKE_paint.h"
+#include "../blender/blenkernel/BKE_object_deform.h"
#include "../blender/collada/collada.h"
#include "../blender/compositor/COM_compositor.h"
#include "../blender/editors/include/ED_armature.h"
@@ -308,6 +312,13 @@ 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
+<<<<<<< HEAD
+void WM_widgetmap_delete(struct wmWidgetMap *wmap) RET_NONE
+struct wmWidgetMapType *WM_widgetmaptype_find(const char *idname, int spaceid, int regionid, bool is_3d, bool create) RET_NULL
+struct wmWidgetGroupType *WM_widgetgrouptype_new(int (*poll)(const struct bContext *, struct wmWidgetGroupType *),
+ void (*draw)(const struct bContext *, struct wmWidgetGroup *),
+ struct Main *bmain, const char *mapidname, short spaceid, short regionid, bool is_3d) RET_NULL
+void WM_widgetgrouptype_unregister(struct bContext *C, struct Main *bmain, struct wmWidgetGroupType *wgroup) RET_NONE
void WM_ndof_deadzone_set(float deadzone) RET_NONE
void WM_uilisttype_init(void) RET_NONE
@@ -381,6 +392,8 @@ void ED_fsmenu_entry_set_name(struct FSMenuEntry *fsentry, const char *name) RET
struct PTCacheEdit *PE_get_current(struct Scene *scene, struct Object *ob) RET_NULL
void PE_current_changed(struct Scene *scene, struct Object *ob) RET_NONE
+bool PE_shapekey_load(struct Object *ob, struct ParticleSystem *psys) RET_ZERO
+bool PE_shapekey_apply(struct Object *ob, struct ParticleSystem *psys) RET_ZERO
/* rna keymap */
struct wmKeyMap *WM_keymap_active(struct wmWindowManager *wm, struct wmKeyMap *keymap) RET_NULL
@@ -495,6 +508,8 @@ void ED_object_constraint_tag_update(struct Object *ob, struct bConstraint *con)
void ED_vgroup_vert_add(struct Object *ob, struct bDeformGroup *dg, int vertnum, float weight, int assignmode) RET_NONE
void ED_vgroup_vert_remove(struct Object *ob, struct bDeformGroup *dg, int vertnum) RET_NONE
float ED_vgroup_vert_weight(struct Object *ob, struct bDeformGroup *dg, int vertnum) RET_ZERO
+void ED_fmap_face_add(struct Object *ob, struct bFaceMap *fmap, int facenum) RET_NONE
+void ED_fmap_face_remove(struct Object *ob, struct bFaceMap *fmap, int facenum) RET_NONE
int ED_mesh_mirror_topo_table(struct Object *ob, char mode) RET_ZERO
int ED_mesh_mirror_spatial_table(struct Object *ob, struct BMEditMesh *em, const float co[3], char mode) RET_ZERO
@@ -598,6 +613,8 @@ 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
+struct uiLayout *uiTemplateCacheLibraryItem(struct uiLayout *layout, struct bContext *C, struct CacheLibrary *cachelib,
+ struct Object *ob, int type, int index, int enabled) RET_NULL
/* 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
@@ -626,6 +643,7 @@ void RE_engine_update_memory_stats(struct RenderEngine *engine, float mem_used,
struct RenderEngine *RE_engine_create(struct RenderEngineType *type) RET_NULL
void RE_engine_frame_set(struct RenderEngine *engine, int frame, float subframe) RET_NONE
void RE_FreePersistentData(void) RET_NONE
+void RE_sample_point_density(struct Scene *scene, struct PointDensity *pd, int resolution, float *values) RET_NONE;
/* python */
struct wmOperatorType *WM_operatortype_find(const char *idname, bool quiet) RET_NULL
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index c6674e416d9..5acce1a1f35 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -37,6 +37,7 @@ blender_include_dirs(
../blender/imbuf
../blender/render/extern/include
../blender/makesdna
+ ../blender/pointcache
../blender/gpu
../blender/windowmanager
)
diff --git a/source/creator/creator.c b/source/creator/creator.c
index a67dbd5a5c9..663a0f60be0 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -97,6 +97,8 @@
#include "IMB_imbuf.h" /* for IMB_init */
+#include "PTC_api.h"
+
#ifdef WITH_PYTHON
#include "BPY_extern.h"
#endif
@@ -1762,6 +1764,9 @@ int main(
BKE_brush_system_init();
RE_init_texture_rng();
+ /* Initialize ffmpeg if built in, also needed for bg mode if videos are
+ * rendered via ffmpeg */
+ BKE_sound_init_once();
BLI_callback_global_init();
@@ -1806,6 +1811,7 @@ int main(
RE_engines_init();
init_nodesystem();
psys_init_rng();
+ PTC_alembic_init();
/* end second init */
@@ -1821,10 +1827,6 @@ int main(
/* background render uses this font too */
BKE_vfont_builtin_register(datatoc_bfont_pfb, datatoc_bfont_pfb_size);
- /* Initialize ffmpeg if built in, also needed for bg mode if videos are
- * rendered via ffmpeg */
- BKE_sound_init_once();
-
init_def_material();
if (G.background == 0) {
diff --git a/source/gameengine/Converter/BL_ShapeDeformer.cpp b/source/gameengine/Converter/BL_ShapeDeformer.cpp
index 5e31dabfab1..1dc07b8573d 100644
--- a/source/gameengine/Converter/BL_ShapeDeformer.cpp
+++ b/source/gameengine/Converter/BL_ShapeDeformer.cpp
@@ -191,7 +191,7 @@ bool BL_ShapeDeformer::Update(void)
/* store verts locally */
VerifyStorage();
- per_keyblock_weights = BKE_keyblock_get_per_block_weights(blendobj, m_key, &cache);
+ per_keyblock_weights = BKE_keyblock_get_per_block_object_weights(blendobj, m_key, &cache);
BKE_key_evaluate_relative(0, m_bmesh->totvert, m_bmesh->totvert, (char *)(float *)m_transverts,
m_key, NULL, per_keyblock_weights, 0); /* last arg is ignored */
BKE_keyblock_free_per_block_weights(m_key, per_keyblock_weights, &cache);
diff --git a/source/gameengine/GamePlayer/ghost/CMakeLists.txt b/source/gameengine/GamePlayer/ghost/CMakeLists.txt
index a1bc7e88840..dfa115e085c 100644
--- a/source/gameengine/GamePlayer/ghost/CMakeLists.txt
+++ b/source/gameengine/GamePlayer/ghost/CMakeLists.txt
@@ -46,6 +46,7 @@ set(INC
../../../blender/imbuf
../../../blender/makesdna
../../../blender/makesrna
+ ../../../blender/pointcache
../../../../intern/container
../../../../intern/ghost
../../../../intern/glew-mx
diff --git a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp
index faa29e15b65..6d97447dff0 100644
--- a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp
+++ b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp
@@ -80,6 +80,8 @@ extern "C"
#include "IMB_imbuf.h"
#include "IMB_moviecache.h"
+
+#include "PTC_api.h"
#ifdef __APPLE__
int GHOST_HACK_getFirstFile(char buf[]);
@@ -452,6 +454,7 @@ int main(int argc, char** argv)
RNA_init();
init_nodesystem();
+ PTC_alembic_init();
initglobals();
diff --git a/source/gameengine/GamePlayer/ghost/SConscript b/source/gameengine/GamePlayer/ghost/SConscript
index 610abcd0357..27adc000413 100644
--- a/source/gameengine/GamePlayer/ghost/SConscript
+++ b/source/gameengine/GamePlayer/ghost/SConscript
@@ -57,6 +57,7 @@ incs = [
'#source/blender/include',
'#source/blender/makesdna',
'#source/blender/makesrna',
+ '#source/blender/pointcache',
'#source/gameengine/Rasterizer',
'#source/gameengine/GameLogic',
'#source/gameengine/Expressions',