Basic Alembic support
authorKévin Dietrich <kevin.dietrich@mailoo.org>
Sat, 6 Aug 2016 04:20:37 +0000 (06:20 +0200)
committerKévin Dietrich <kevin.dietrich@mailoo.org>
Sat, 6 Aug 2016 08:58:13 +0000 (10:58 +0200)
All in all, this patch adds an Alembic importer, an Alembic exporter,
and a new CacheFile data block which, for now, wraps around an Alembic
archive. This data block is made available through a new modifier ("Mesh
Sequence Cache") as well as a new constraint ("Transform Cache") to
somewhat properly support respectively geometric and transformation data
streaming from alembic caches.

A more in-depth documentation is to be found on the wiki, as well as a
 guide to compile alembic: https://wiki.blender.org/index.php/
User:Kevindietrich/AlembicBasicIo.

Many thanks to everyone involved in this little project, and huge shout
out to "cgstrive" for the thorough testings with Maya, 3ds Max, Houdini
and Realflow as well as @fjuhec, @jensverwiebe and @jasperge for the
custom builds and compile fixes.

Reviewers: sergey, campbellbarton, mont29

Reviewed By: sergey, campbellbarton, mont29

Differential Revision: https://developer.blender.org/D2060

126 files changed:
CMakeLists.txt
build_files/build_environment/install_deps.sh
build_files/cmake/Modules/FindAlembic.cmake [new file with mode: 0644]
build_files/cmake/Modules/FindHDF5.cmake [new file with mode: 0644]
build_files/cmake/config/blender_full.cmake
build_files/cmake/config/blender_lite.cmake
build_files/cmake/config/bpy_module.cmake
build_files/cmake/macros.cmake
release/scripts/modules/sys_info.py
release/scripts/startup/bl_ui/properties_constraint.py
release/scripts/startup/bl_ui/properties_data_modifier.py
release/scripts/startup/bl_ui/space_info.py
source/blender/CMakeLists.txt
source/blender/alembic/ABC_alembic.h [new file with mode: 0644]
source/blender/alembic/CMakeLists.txt [new file with mode: 0644]
source/blender/alembic/intern/abc_camera.cc [new file with mode: 0644]
source/blender/alembic/intern/abc_camera.h [new file with mode: 0644]
source/blender/alembic/intern/abc_curves.cc [new file with mode: 0644]
source/blender/alembic/intern/abc_curves.h [new file with mode: 0644]
source/blender/alembic/intern/abc_customdata.cc [new file with mode: 0644]
source/blender/alembic/intern/abc_customdata.h [new file with mode: 0644]
source/blender/alembic/intern/abc_exporter.cc [new file with mode: 0644]
source/blender/alembic/intern/abc_exporter.h [new file with mode: 0644]
source/blender/alembic/intern/abc_hair.cc [new file with mode: 0644]
source/blender/alembic/intern/abc_hair.h [new file with mode: 0644]
source/blender/alembic/intern/abc_mesh.cc [new file with mode: 0644]
source/blender/alembic/intern/abc_mesh.h [new file with mode: 0644]
source/blender/alembic/intern/abc_nurbs.cc [new file with mode: 0644]
source/blender/alembic/intern/abc_nurbs.h [new file with mode: 0644]
source/blender/alembic/intern/abc_object.cc [new file with mode: 0644]
source/blender/alembic/intern/abc_object.h [new file with mode: 0644]
source/blender/alembic/intern/abc_points.cc [new file with mode: 0644]
source/blender/alembic/intern/abc_points.h [new file with mode: 0644]
source/blender/alembic/intern/abc_transform.cc [new file with mode: 0644]
source/blender/alembic/intern/abc_transform.h [new file with mode: 0644]
source/blender/alembic/intern/abc_util.cc [new file with mode: 0644]
source/blender/alembic/intern/abc_util.h [new file with mode: 0644]
source/blender/alembic/intern/alembic_capi.cc [new file with mode: 0644]
source/blender/blenkernel/BKE_cachefile.h [new file with mode: 0644]
source/blender/blenkernel/BKE_context.h
source/blender/blenkernel/BKE_library.h
source/blender/blenkernel/BKE_main.h
source/blender/blenkernel/CMakeLists.txt
source/blender/blenkernel/intern/anim_sys.c
source/blender/blenkernel/intern/bpath.c
source/blender/blenkernel/intern/cachefile.c [new file with mode: 0644]
source/blender/blenkernel/intern/cdderivedmesh.c
source/blender/blenkernel/intern/constraint.c
source/blender/blenkernel/intern/context.c
source/blender/blenkernel/intern/depsgraph.c
source/blender/blenkernel/intern/idcode.c
source/blender/blenkernel/intern/library.c
source/blender/blenkernel/intern/library_query.c
source/blender/blenkernel/intern/library_remap.c
source/blender/blenkernel/intern/scene.c
source/blender/blenloader/CMakeLists.txt
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/blentranslation/BLT_translation.h
source/blender/depsgraph/DEG_depsgraph_build.h
source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
source/blender/depsgraph/intern/builder/deg_builder_nodes.h
source/blender/depsgraph/intern/builder/deg_builder_relations.cc
source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc
source/blender/depsgraph/intern/depsgraph_build.cc
source/blender/depsgraph/intern/depsgraph_types.h
source/blender/depsgraph/intern/nodes/deg_node_component.cc
source/blender/depsgraph/intern/nodes/deg_node_component.h
source/blender/editors/animation/anim_channels_defines.c
source/blender/editors/animation/anim_channels_edit.c
source/blender/editors/animation/anim_filter.c
source/blender/editors/animation/keyframes_draw.c
source/blender/editors/include/ED_anim_api.h
source/blender/editors/include/ED_keyframes_draw.h
source/blender/editors/include/UI_interface.h
source/blender/editors/interface/interface_icons.c
source/blender/editors/interface/interface_templates.c
source/blender/editors/io/CMakeLists.txt
source/blender/editors/io/io_alembic.c [new file with mode: 0644]
source/blender/editors/io/io_alembic.h [new file with mode: 0644]
source/blender/editors/io/io_cache.c [new file with mode: 0644]
source/blender/editors/io/io_cache.h [new file with mode: 0644]
source/blender/editors/io/io_ops.c
source/blender/editors/object/object_constraint.c
source/blender/editors/space_file/filelist.c
source/blender/editors/space_file/filesel.c
source/blender/editors/space_nla/nla_buttons.c
source/blender/editors/space_nla/nla_channels.c
source/blender/editors/space_outliner/outliner_draw.c
source/blender/editors/space_outliner/outliner_intern.h
source/blender/editors/space_outliner/outliner_tree.c
source/blender/editors/space_time/space_time.c
source/blender/makesdna/DNA_ID.h
source/blender/makesdna/DNA_action_types.h
source/blender/makesdna/DNA_cachefile_types.h [new file with mode: 0644]
source/blender/makesdna/DNA_constraint_types.h
source/blender/makesdna/DNA_modifier_types.h
source/blender/makesdna/DNA_space_types.h
source/blender/makesdna/intern/makesdna.c
source/blender/makesrna/RNA_access.h
source/blender/makesrna/RNA_enum_types.h
source/blender/makesrna/intern/CMakeLists.txt
source/blender/makesrna/intern/makesrna.c
source/blender/makesrna/intern/rna_ID.c
source/blender/makesrna/intern/rna_cachefile.c [new file with mode: 0644]
source/blender/makesrna/intern/rna_constraint.c
source/blender/makesrna/intern/rna_internal.h
source/blender/makesrna/intern/rna_main.c
source/blender/makesrna/intern/rna_main_api.c
source/blender/makesrna/intern/rna_modifier.c
source/blender/makesrna/intern/rna_scene_api.c
source/blender/makesrna/intern/rna_space.c
source/blender/makesrna/intern/rna_ui_api.c
source/blender/modifiers/CMakeLists.txt
source/blender/modifiers/MOD_modifiertypes.h
source/blender/modifiers/intern/MOD_meshsequencecache.c [new file with mode: 0644]
source/blender/modifiers/intern/MOD_util.c
source/blender/python/intern/CMakeLists.txt
source/blender/python/intern/bpy_app.c
source/blender/python/intern/bpy_app_alembic.c [new file with mode: 0644]
source/blender/python/intern/bpy_app_alembic.h [new file with mode: 0644]
source/blender/python/intern/bpy_app_build_options.c
source/blender/windowmanager/WM_api.h
source/blender/windowmanager/intern/wm_operator_props.c
source/blenderplayer/CMakeLists.txt
source/blenderplayer/bad_level_call_stubs/stubs.c

index 1dfa838a5a20df507ddd5240c1b2da34d630c546..d29162830910bf8dea5ace66d4ff519e727171f1 100644 (file)
@@ -323,6 +323,10 @@ option(WITH_CODEC_AVI           "Enable Blenders own AVI file support (raw/jpeg)
 option(WITH_CODEC_FFMPEG        "Enable FFMPeg Support (http://ffmpeg.org)" ${_init_CODEC_FFMPEG})
 option(WITH_CODEC_SNDFILE       "Enable libsndfile Support (http://www.mega-nerd.com/libsndfile)" OFF)
 
+# Alembic support
+option(WITH_ALEMBIC             "Enable Alembic Support" OFF)
+option(WITH_ALEMBIC_HDF5        "Enable Legacy Alembic Support (not officially supported)" OFF)
+
 if(APPLE)
        option(WITH_CODEC_QUICKTIME     "Enable Quicktime Support" ON)
 endif()
@@ -720,6 +724,11 @@ if(WITH_OPENIMAGEIO)
        set(WITH_IMAGE_TIFF ON)
 endif()
 
+# auto enable alembic linking dependencies
+if(WITH_ALEMBIC)
+       set(WITH_IMAGE_OPENEXR ON)
+endif()
+
 # don't store paths to libs for portable distribution
 if(WITH_INSTALL_PORTABLE)
        set(CMAKE_SKIP_BUILD_RPATH TRUE)
@@ -1091,6 +1100,21 @@ if(UNIX AND NOT APPLE)
                endif()
        endif()
 
+       if(WITH_ALEMBIC)
+               set(ALEMBIC_ROOT_DIR ${LIBDIR}/alembic)
+               find_package_wrapper(Alembic)
+
+               if(WITH_ALEMBIC_HDF5)
+                       set(HDF5_ROOT_DIR ${LIBDIR}/hdf5)
+                       find_package_wrapper(HDF5)
+               endif()
+
+               if(NOT ALEMBIC_FOUND OR (WITH_ALEMBIC_HDF5 AND NOT HDF5_FOUND))
+                       set(WITH_ALEMBIC OFF)
+                       set(WITH_ALEMBIC_HDF5 OFF)
+               endif()
+       endif()
+
        if(WITH_BOOST)
                # uses in build instructions to override include and library variables
                if(NOT BOOST_CUSTOM)
@@ -1660,6 +1684,21 @@ elseif(WIN32)
                        set(OPENVDB_LIBPATH ${LIBDIR}/openvdb/lib)
                endif()
 
+               if(WITH_ALEMBIC)
+                       set(ALEMBIC_ROOT_DIR ${LIBDIR}/alembic)
+                       find_package(Alembic)
+
+                       if(WITH_ALEMBIC_HDF5)
+                               set(HDF5_ROOT_DIR ${LIBDIR}/hdf5)
+                               find_package(HDF5)
+                       endif()
+
+                       if(NOT ALEMBIC_FOUND OR (WITH_ALEMBIC_HDF5 AND NOT HDF5_FOUND))
+                               set(WITH_ALEMBIC OFF)
+                               set(WITH_ALEMBIC_HDF5 OFF)
+                       endif()
+               endif()
+
                if(WITH_MOD_CLOTH_ELTOPO)
                        set(LAPACK ${LIBDIR}/lapack)
                        # set(LAPACK_INCLUDE_DIR ${LAPACK}/include)
@@ -1959,6 +1998,21 @@ elseif(WIN32)
                        set(OPENVDB_DEFINITIONS)
                endif()
 
+               if(WITH_ALEMBIC)
+                       set(ALEMBIC_ROOT_DIR ${LIBDIR}/alembic)
+                       find_package_wrapper(Alembic)
+
+                       if(WITH_ALEMBIC_HDF5)
+                               set(HDF5_ROOT_DIR ${LIBDIR}/hdf5)
+                               find_package_wrapper(HDF5)
+                       endif()
+
+                       if(NOT ALEMBIC_FOUND OR (WITH_ALEMBIC_HDF5 AND NOT HDF5_FOUND))
+                               set(WITH_ALEMBIC OFF)
+                               set(WITH_ALEMBIC_HDF5 OFF)
+                       endif()
+               endif()
+
                set(PLATFORM_LINKFLAGS "-Xlinker --stack=2097152")
 
                ## DISABLE - causes linking errors 
@@ -2043,6 +2097,20 @@ elseif(APPLE)
                endif()
        endif()
 
+       if(WITH_ALEMBIC)
+               set(ALEMBIC_ROOT_DIR ${LIBDIR}/alembic)
+               find_package(Alembic)
+               if(WITH_ALEMBIC_HDF5)
+                       set(HDF5_ROOT_DIR ${LIBDIR}/hdf5)
+                       find_package(HDF5)
+               endif()
+
+               if(NOT ALEMBIC_FOUND OR (WITH_ALEMBIC_HDF5 AND NOT HDF5_FOUND))
+                       set(WITH_ALEMBIC OFF)
+                       set(WITH_ALEMBIC_HDF5 OFF)
+               endif()
+       endif()
+
        if(WITH_OPENSUBDIV)
                set(OPENSUBDIV ${LIBDIR}/opensubdiv)
                set(OPENSUBDIV_LIBPATH ${OPENSUBDIV}/lib)
@@ -3215,6 +3283,7 @@ if(FIRST_RUN)
        info_cfg_option(WITH_FREESTYLE)
        info_cfg_option(WITH_OPENCOLORIO)
        info_cfg_option(WITH_OPENVDB)
+       info_cfg_option(WITH_ALEMBIC)
 
        info_cfg_text("Compiler Options:")
        info_cfg_option(WITH_BUILDINFO)
index 068ac6656238a3c9edbbe3346cfe8a6a83416979..7230fc4710544a213e2913333f856d961b6a8d1b 100755 (executable)
@@ -29,13 +29,13 @@ getopt \
 ver-ocio:,ver-oiio:,ver-llvm:,ver-osl:,ver-osd:,ver-openvdb:,\
 force-all,force-python,force-numpy,force-boost,\
 force-ocio,force-openexr,force-oiio,force-llvm,force-osl,force-osd,force-openvdb,\
-force-ffmpeg,force-opencollada,\
+force-ffmpeg,force-opencollada,force-alembic,\
 build-all,build-python,build-numpy,build-boost,\
 build-ocio,build-openexr,build-oiio,build-llvm,build-osl,build-osd,build-openvdb,\
-build-ffmpeg,build-opencollada,\
+build-ffmpeg,build-opencollada,build-alembic,\
 skip-python,skip-numpy,skip-boost,\
 skip-ocio,skip-openexr,skip-oiio,skip-llvm,skip-osl,skip-osd,skip-openvdb,\
-skip-ffmpeg,skip-opencollada \
+skip-ffmpeg,skip-opencollada,skip-alembic \
 -- "$@" \
 )
 
@@ -167,6 +167,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
     --build-openvdb
         Force the build of OpenVDB.
 
+    --build-alembic
+        Force the build of Alembic.
+
     --build-opencollada
         Force the build of OpenCOLLADA.
 
@@ -216,6 +219,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
     --force-openvdb
         Force the rebuild of OpenVDB.
 
+    --force-alembic
+        Force the rebuild of Alembic.
+
     --force-opencollada
         Force the rebuild of OpenCOLLADA.
 
@@ -258,6 +264,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS:
     --skip-openvdb
         Unconditionally skip OpenVDB installation/building.
 
+    --skip-alembic
+        Unconditionally skip Alembic installation/building.
+
     --skip-opencollada
         Unconditionally skip OpenCOLLADA installation/building.
 
@@ -343,6 +352,13 @@ OPENVDB_FORCE_BUILD=false
 OPENVDB_FORCE_REBUILD=false
 OPENVDB_SKIP=false
 
+# Alembic needs to be compiled for now
+ALEMBIC_VERSION="1.6.0"
+ALEMBIC_VERSION_MIN=$ALEMBIC_VERSION
+ALEMBIC_FORCE_BUILD=false
+ALEMBIC_FORCE_REBUILD=false
+ALEMBIC_SKIP=false
+
 # Version??
 OPENCOLLADA_VERSION="1.3"
 OPENCOLLADA_FORCE_BUILD=false
@@ -525,6 +541,7 @@ while true; do
       OPENVDB_FORCE_BUILD=true
       OPENCOLLADA_FORCE_BUILD=true
       FFMPEG_FORCE_BUILD=true
+      ALEMBIC_FORCE_BUILD=true
       shift; continue
     ;;
     --build-python)
@@ -567,6 +584,9 @@ while true; do
     --build-ffmpeg)
       FFMPEG_FORCE_BUILD=true; shift; continue
     ;;
+    --build-alembic)
+      ALEMBIC_FORCE_BUILD=true; shift; continue
+    ;;
     --force-all)
       PYTHON_FORCE_REBUILD=true
       NUMPY_FORCE_REBUILD=true
@@ -580,6 +600,7 @@ while true; do
       OPENVDB_FORCE_REBUILD=true
       OPENCOLLADA_FORCE_REBUILD=true
       FFMPEG_FORCE_REBUILD=true
+      ALEMBIC_FORCE_REBUILD=true
       shift; continue
     ;;
     --force-python)
@@ -620,6 +641,9 @@ while true; do
     --force-ffmpeg)
       FFMPEG_FORCE_REBUILD=true; shift; continue
     ;;
+    --force-alembic)
+      ALEMBIC_FORCE_REBUILD=true; shift; continue
+    ;;
     --skip-python)
       PYTHON_SKIP=true; shift; continue
     ;;
@@ -656,6 +680,9 @@ while true; do
     --skip-ffmpeg)
       FFMPEG_SKIP=true; shift; continue
     ;;
+    --skip-alembic)
+      ALEMBIC_SKIP=true; shift; continue
+    ;;
     --)
       # no more arguments to parse
       break
@@ -683,7 +710,7 @@ NUMPY_SOURCE=( "http://sourceforge.net/projects/numpy/files/NumPy/$NUMPY_VERSION
 
 _boost_version_nodots=`echo "$BOOST_VERSION" | sed -r 's/\./_/g'`
 BOOST_SOURCE=( "http://sourceforge.net/projects/boost/files/boost/$BOOST_VERSION/boost_$_boost_version_nodots.tar.bz2/download" )
-BOOST_BUILD_MODULES="--with-system --with-filesystem --with-thread --with-regex --with-locale --with-date_time --with-wave --with-iostreams"
+BOOST_BUILD_MODULES="--with-system --with-filesystem --with-thread --with-regex --with-locale --with-date_time --with-wave --with-iostreams --with-python --with-program_options"
 
 OCIO_SOURCE=( "https://github.com/imageworks/OpenColorIO/tarball/v$OCIO_VERSION" )
 
@@ -727,6 +754,12 @@ OPENVDB_SOURCE=( "https://github.com/dreamworksanimation/openvdb/archive/v${OPEN
 #~ OPENVDB_SOURCE_REPO_UID="404659fffa659da075d1c9416e4fc939139a84ee"
 #~ OPENVDB_SOURCE_REPO_BRANCH="dev"
 
+ALEMBIC_USE_REPO=false
+ALEMBIC_SOURCE=( "https://github.com/alembic/alembic/archive/${ALEMBIC_VERSION}.tar.gz" )
+# ALEMBIC_SOURCE_REPO=( "https://github.com/alembic/alembic.git" )
+# ALEMBIC_SOURCE_REPO_UID="e6c90d4faa32c4550adeaaf3f556dad4b73a92bb"
+# ALEMBIC_SOURCE_REPO_BRANCH="master"
+
 OPENCOLLADA_SOURCE=( "https://github.com/KhronosGroup/OpenCOLLADA.git" )
 OPENCOLLADA_REPO_UID="3335ac164e68b2512a40914b14c74db260e6ff7d"
 OPENCOLLADA_REPO_BRANCH="master"
@@ -767,7 +800,8 @@ You may also want to build them yourself (optional ones are [between brackets]):
     * [OpenShadingLanguage $OSL_VERSION_MIN] (from $OSL_SOURCE_REPO, branch $OSL_SOURCE_REPO_BRANCH, commit $OSL_SOURCE_REPO_UID).
     * [OpenSubDiv $OSD_VERSION_MIN] (from $OSD_SOURCE_REPO, branch $OSD_SOURCE_REPO_BRANCH, commit $OSD_SOURCE_REPO_UID).
     * [OpenVDB $OPENVDB_VERSION_MIN] (from $OPENVDB_SOURCE), [Blosc $OPENVDB_BLOSC_VERSION] (from $OPENVDB_BLOSC_SOURCE).
-    * [OpenCollada] (from $OPENCOLLADA_SOURCE, branch $OPENCOLLADA_REPO_BRANCH, commit $OPENCOLLADA_REPO_UID).\""
+    * [OpenCollada] (from $OPENCOLLADA_SOURCE, branch $OPENCOLLADA_REPO_BRANCH, commit $OPENCOLLADA_REPO_UID).
+    * [Alembic $ALEMBIC_VERSION] (from $ALEMBIC_SOURCE).\""
 
 if [ "$DO_SHOW_DEPS" = true ]; then
   PRINT ""
@@ -1118,7 +1152,7 @@ compile_Boost() {
   fi
 
   # To be changed each time we make edits that would modify the compiled result!
-  boost_magic=10
+  boost_magic=11
 
   _init_boost
 
@@ -2138,6 +2172,102 @@ compile_OPENVDB() {
   run_ldconfig "openvdb"
 }
 
+#### Build Alembic ####
+_init_alembic() {
+  _src=$SRC/alembic-$ALEMBIC_VERSION
+  _git=false
+  _inst=$INST/alembic-$ALEMBIC_VERSION
+  _inst_shortcut=$INST/alembic
+}
+
+clean_ALEMBIC() {
+  _init_alembic
+  _clean
+}
+
+compile_ALEMBIC() {
+  if [ "$NO_BUILD" = true ]; then
+    WARNING "--no-build enabled, Alembic will not be compiled!"
+    return
+  fi
+
+  compile_HDF5
+  PRINT ""
+
+  # To be changed each time we make edits that would modify the compiled result!
+  alembic_magic=2
+  _init_alembic
+
+  # Clean install if needed!
+  magic_compile_check alembic-$ALEMBIC_VERSION $alembic_magic
+  if [ $? -eq 1 -o "$ALEMBIC_FORCE_REBUILD" = true ]; then
+    clean_ALEMBIC
+  fi
+
+  if [ ! -d $_inst ]; then
+    INFO "Building Alembic-$ALEMBIC_VERSION"
+
+    prepare_opt
+
+    if [ ! -d $_src -o true ]; then
+      mkdir -p $SRC
+      download ALEMBIC_SOURCE[@] "$_src.tar.gz"
+
+      INFO "Unpacking Alembic-$ALEMBIC_VERSION"
+      tar -C $SRC -xf $_src.tar.gz
+    fi
+
+    cd $_src
+
+    cmake_d="-D CMAKE_INSTALL_PREFIX=$_inst"
+
+    if [ -d $INST/boost ]; then
+      cmake_d="$cmake_d -D BOOST_ROOT=$INST/boost"
+      cmake_d="$cmake_d -D USE_STATIC_BOOST=ON"
+    else
+      cmake_d="$cmake_d -D USE_STATIC_BOOST=OFF"
+    fi
+
+    if [ "$_with_built_openexr" = true ]; then
+      cmake_d="$cmake_d -D ILMBASE_ROOT=$INST/openexr"
+      cmake_d="$cmake_d -D USE_ARNOLD=OFF"
+      cmake_d="$cmake_d -D USE_BINARIES=OFF"
+      cmake_d="$cmake_d -D USE_EXAMPLES=OFF"
+      cmake_d="$cmake_d -D USE_HDF5=OFF"
+      cmake_d="$cmake_d -D USE_MAYA=OFF"
+      cmake_d="$cmake_d -D USE_PRMAN=OFF"
+      cmake_d="$cmake_d -D USE_PYALEMBIC=OFF"
+      cmake_d="$cmake_d -D USE_STATIC_HDF5=OFF"
+      cmake_d="$cmake_d -D ALEMBIC_ILMBASE_LINK_STATIC=OFF"
+      cmake_d="$cmake_d -D ALEMBIC_SHARED_LIBS=OFF"
+      cmake_d="$cmake_d -D ALEMBIC_LIB_USES_BOOST=ON"
+      cmake_d="$cmake_d -D ALEMBIC_LIB_USES_TR1=OFF"
+      INFO "ILMBASE_ROOT=$INST/openexr"
+    fi
+
+    cmake $cmake_d ./
+    make -j$THREADS install
+    make clean
+
+    if [ -d $_inst ]; then
+      _create_inst_shortcut
+    else
+      ERROR "Alembic-$ALEMBIC_VERSION failed to compile, exiting"
+      exit 1
+    fi
+
+    magic_compile_set alembic-$ALEMBIC_VERSION $alembic_magic
+
+    cd $CWD
+    INFO "Done compiling Alembic-$ALEMBIC_VERSION!"
+  else
+    INFO "Own Alembic-$ALEMBIC_VERSION is up to date, nothing to do!"
+    INFO "If you want to force rebuild of this lib, use the --force-alembic option."
+  fi
+
+  run_ldconfig "alembic"
+}
+
 #### Build OpenCOLLADA ####
 _init_opencollada() {
   _src=$SRC/OpenCOLLADA-$OPENCOLLADA_VERSION
@@ -2746,6 +2876,17 @@ install_DEB() {
     fi
   fi
 
+  PRINT ""
+  if [ "$ALEMBIC_SKIP" = true ]; then
+    WARNING "Skipping Alembic installation, as requested..."
+  elif [ "$ALEMBIC_FORCE_BUILD" = true ]; then
+    INFO "Forced Alembic building, as requested..."
+    compile_ALEMBIC
+  else
+    # No package currently, only HDF5!
+    compile_ALEMBIC
+  fi
+
 
   if [ "$WITH_OPENCOLLADA" = true ]; then
     _do_compile_collada=false
@@ -3283,6 +3424,17 @@ install_RPM() {
     compile_OPENVDB
   fi
 
+  PRINT ""
+  if [ "$ALEMBIC_SKIP" = true ]; then
+    WARNING "Skipping Alembic installation, as requested..."
+  elif [ "$ALEMBIC_FORCE_BUILD" = true ]; then
+    INFO "Forced Alembic building, as requested..."
+    compile_ALEMBIC
+  else
+    # No package currently!
+    compile_ALEMBIC
+  fi
+
 
   if [ "$WITH_OPENCOLLADA" = true ]; then
     PRINT ""
@@ -3693,6 +3845,16 @@ install_ARCH() {
     fi
   fi
 
+  PRINT ""
+  if [ "$ALEMBIC_SKIP" = true ]; then
+    WARNING "Skipping Alembic installation, as requested..."
+  elif [ "$ALEMBIC_FORCE_BUILD" = true ]; then
+    INFO "Forced Alembic building, as requested..."
+    compile_ALEMBIC
+  else
+    compile_ALEMBIC
+  fi
+
 
   if [ "$WITH_OPENCOLLADA" = true ]; then
     PRINT ""
@@ -4000,7 +4162,7 @@ print_info() {
 
   _buildargs="-U *SNDFILE* -U *PYTHON* -U *BOOST* -U *Boost*"
   _buildargs="$_buildargs -U *OPENCOLORIO* -U *OPENEXR* -U *OPENIMAGEIO* -U *LLVM* -U *CYCLES*"
-  _buildargs="$_buildargs -U *OPENSUBDIV* -U *OPENVDB* -U *COLLADA* -U *FFMPEG*"
+  _buildargs="$_buildargs -U *OPENSUBDIV* -U *OPENVDB* -U *COLLADA* -U *FFMPEG* -U *ALEMBIC*"
 
   _1="-D WITH_CODEC_SNDFILE=ON"
   PRINT "  $_1"
@@ -4106,6 +4268,17 @@ print_info() {
     _buildargs="$_buildargs $_1"
   fi
 
+  if [ "$ALEMBIC_SKIP" = false ]; then
+    _1="-D WITH_ALEMBIC=ON"
+    PRINT "  $_1"
+    _buildargs="$_buildargs $_1"
+    if [ -d $INST/alembic ]; then
+      _1="-D ALEMBIC_ROOT_DIR=$INST/alembic"
+      PRINT "  $_1"
+      _buildargs="$_buildargs $_1"
+    fi
+  fi
+
   if [ "$NO_SYSTEM_GLEW" = true ]; then
     _1="-D WITH_SYSTEM_GLEW=OFF"
     PRINT "  $_1"
diff --git a/build_files/cmake/Modules/FindAlembic.cmake b/build_files/cmake/Modules/FindAlembic.cmake
new file mode 100644 (file)
index 0000000..1f61b5e
--- /dev/null
@@ -0,0 +1,70 @@
+# - Find Alembic library
+# Find the native Alembic includes and libraries
+# This module defines
+#  ALEMBIC_INCLUDE_DIRS, where to find Alembic headers, Set when
+#                        ALEMBIC_INCLUDE_DIR is found.
+#  ALEMBIC_LIBRARIES, libraries to link against to use Alembic.
+#  ALEMBIC_ROOT_DIR, The base directory to search for Alembic.
+#                    This can also be an environment variable.
+#  ALEMBIC_FOUND, If false, do not try to use Alembic.
+#
+
+#=============================================================================
+# Copyright 2016 Blender Foundation.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+
+# If ALEMBIC_ROOT_DIR was defined in the environment, use it.
+IF(NOT ALEMBIC_ROOT_DIR AND NOT $ENV{ALEMBIC_ROOT_DIR} STREQUAL "")
+  SET(ALEMBIC_ROOT_DIR $ENV{ALEMBIC_ROOT_DIR})
+ENDIF()
+
+SET(_alembic_SEARCH_DIRS
+  ${ALEMBIC_ROOT_DIR}
+  /usr/local
+  /sw # Fink
+  /opt/local # DarwinPorts
+  /opt/csw # Blastwave
+  /opt/lib/alembic
+)
+
+FIND_PATH(ALEMBIC_INCLUDE_DIR
+  NAMES
+    Alembic/Abc/All.h
+  HINTS
+    ${_alembic_SEARCH_DIRS}
+  PATH_SUFFIXES
+    include
+)
+
+FIND_LIBRARY(ALEMBIC_LIBRARY
+  NAMES
+    Alembic
+  HINTS
+    ${_alembic_SEARCH_DIRS}
+  PATH_SUFFIXES
+    lib64 lib lib/static
+)
+
+# handle the QUIETLY and REQUIRED arguments and set ALEMBIC_FOUND to TRUE if 
+# all listed variables are TRUE
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(ALEMBIC DEFAULT_MSG ALEMBIC_LIBRARY ALEMBIC_INCLUDE_DIR)
+
+IF(ALEMBIC_FOUND)
+  SET(ALEMBIC_LIBRARIES ${ALEMBIC_LIBRARY})
+  SET(ALEMBIC_INCLUDE_DIRS ${ALEMBIC_INCLUDE_DIR})
+ENDIF(ALEMBIC_FOUND)
+
+MARK_AS_ADVANCED(
+  ALEMBIC_INCLUDE_DIR
+  ALEMBIC_LIBRARY
+)
+
+UNSET(_alembic_SEARCH_DIRS)
diff --git a/build_files/cmake/Modules/FindHDF5.cmake b/build_files/cmake/Modules/FindHDF5.cmake
new file mode 100644 (file)
index 0000000..56ceda8
--- /dev/null
@@ -0,0 +1,69 @@
+# - Find HDF5 library
+# Find the native HDF5 includes and libraries
+# This module defines
+#  HDF5_INCLUDE_DIRS, where to find hdf5.h, Set when HDF5_INCLUDE_DIR is found.
+#  HDF5_LIBRARIES, libraries to link against to use HDF5.
+#  HDF5_ROOT_DIR, The base directory to search for HDF5.
+#                 This can also be an environment variable.
+#  HDF5_FOUND, If false, do not try to use HDF5.
+#
+
+#=============================================================================
+# Copyright 2016 Blender Foundation.
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+
+# If HDF5_ROOT_DIR was defined in the environment, use it.
+IF(NOT HDF5_ROOT_DIR AND NOT $ENV{HDF5_ROOT_DIR} STREQUAL "")
+  SET(HDF5_ROOT_DIR $ENV{HDF5_ROOT_DIR})
+ENDIF()
+
+SET(_hdf5_SEARCH_DIRS
+  ${HDF5_ROOT_DIR}
+  /usr/local
+  /sw # Fink
+  /opt/local # DarwinPorts
+  /opt/csw # Blastwave
+  /opt/lib/hdf5
+)
+
+FIND_LIBRARY(HDF5_LIBRARY
+  NAMES
+    hdf5
+  HINTS
+    ${_hdf5_SEARCH_DIRS}
+  PATH_SUFFIXES
+    lib64 lib
+)
+
+FIND_PATH(HDF5_INCLUDE_DIR
+  NAMES
+    hdf5.h
+  HINTS
+    ${_hdf5_SEARCH_DIRS}
+  PATH_SUFFIXES
+    include
+)
+
+# handle the QUIETLY and REQUIRED arguments and set HDF5_FOUND to TRUE if
+# all listed variables are TRUE
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(HDF5 DEFAULT_MSG HDF5_LIBRARY HDF5_INCLUDE_DIR)
+
+IF(HDF5_FOUND)
+  SET(HDF5_LIBRARIES ${HDF5_LIBRARY})
+  SET(HDF5_INCLUDE_DIRS ${HDF5_INCLUDE_DIR})
+ENDIF(HDF5_FOUND)
+
+MARK_AS_ADVANCED(
+  HDF5_INCLUDE_DIR
+  HDF5_LIBRARY
+)
+
+UNSET(_hdf5_SEARCH_DIRS)
index b50b42416fb10d9eabe1c458fa76f1d8a71bb387..634d4f431d4200222367a7903a47190fce8e451e 100644 (file)
@@ -4,6 +4,7 @@
 #   cmake -C../blender/build_files/cmake/config/blender_full.cmake  ../blender
 #
 
+set(WITH_ALEMBIC             ON  CACHE BOOL "" FORCE)
 set(WITH_BUILDINFO           ON  CACHE BOOL "" FORCE)
 set(WITH_BULLET              ON  CACHE BOOL "" FORCE)
 set(WITH_CODEC_AVI           ON  CACHE BOOL "" FORCE)
index 3c53ee7ae23bd066a3adbf98e9ae2883e609fd24..46b7d48b494c793749580a3f3efd2a0e43290e1c 100644 (file)
@@ -8,6 +8,7 @@
 set(WITH_INSTALL_PORTABLE    ON  CACHE BOOL "" FORCE)
 set(WITH_SYSTEM_GLEW         ON  CACHE BOOL "" FORCE)
 
+set(WITH_ALEMBIC             OFF CACHE BOOL "" FORCE)
 set(WITH_BUILDINFO           OFF CACHE BOOL "" FORCE)
 set(WITH_BULLET              OFF CACHE BOOL "" FORCE)
 set(WITH_CODEC_AVI           OFF CACHE BOOL "" FORCE)
index 41140151f0470cee12b9424a8855ec104dee1370..854d6e4937083365ff8f335dad2b9c2da8396b8b 100644 (file)
@@ -32,3 +32,4 @@ set(WITH_OPENCOLLADA         OFF CACHE BOOL "" FORCE)
 set(WITH_INTERNATIONAL       OFF CACHE BOOL "" FORCE)
 set(WITH_BULLET              OFF CACHE BOOL "" FORCE)
 set(WITH_OPENVDB             OFF CACHE BOOL "" FORCE)
+set(WITH_ALEMBIC             OFF CACHE BOOL "" FORCE)
index f57a6952164935a46078831f133bcabd9562408a..c4e1c56699ec4966854d11aeafedb5bda7ce1585 100644 (file)
@@ -333,6 +333,11 @@ function(SETUP_LIBDIRS)
                link_directories(${LLVM_LIBPATH})
        endif()
 
+       if(WITH_ALEMBIC)
+               link_directories(${ALEMBIC_LIBPATH})
+               link_directories(${HDF5_LIBPATH})
+       endif()
+
        if(WIN32 AND NOT UNIX)
                link_directories(${PTHREADS_LIBPATH})
        endif()
@@ -434,6 +439,9 @@ function(setup_liblinks
                endif()
        endif()
        target_link_libraries(${target} ${JPEG_LIBRARIES})
+       if(WITH_ALEMBIC)
+               target_link_libraries(${target} ${ALEMBIC_LIBRARIES} ${HDF5_LIBRARIES})
+       endif()
        if(WITH_IMAGE_OPENEXR)
                target_link_libraries(${target} ${OPENEXR_LIBRARIES})
        endif()
@@ -607,6 +615,7 @@ function(SETUP_BLENDER_SORTED_LIBS)
                bf_imbuf_openimageio
                bf_imbuf_dds
                bf_collada
+               bf_alembic
                bf_intern_elbeem
                bf_intern_memutil
                bf_intern_guardedalloc
index b225325ba276bf105d3ebcb4353a356d8f1a73f3..30b9cdfaf37b574636972483cba2bfabfec7904c 100644 (file)
@@ -158,6 +158,13 @@ def write_sysinfo(filepath):
     else:
         output.write("Blender was built without OpenVDB support\n")
 
+    alembic = bpy.app.alembic
+    output.write("Alembic: ")
+    if alembic.supported:
+        output.write("%s\n" % alembic.version_string)
+    else:
+        output.write("Blender was built without Alembic support\n")
+
     if not bpy.app.build_options.sdl:
         output.write("SDL: Blender was built without SDL support\n")
 
index 4ca2f773dcc55123e67b3266ee2be8a05f9882cb..cb5f1595ff333ef31db04d3ee24510e01c71aa8d 100644 (file)
@@ -880,6 +880,19 @@ class ConstraintButtonsPanel:
 
         layout.operator("clip.constraint_to_fcurve")
 
+    def TRANSFORM_CACHE(self, context, layout, con):
+        layout.label(text="Cache File Properties:")
+        box = layout.box()
+        box.template_cache_file(con, "cache_file")
+
+        cache_file = con.cache_file
+        
+        layout.label(text="Constraint Properties:")
+        box = layout.box()
+        
+        if cache_file is not None:
+            box.prop_search(con, "object_path", cache_file, "object_paths")
+
     def SCRIPT(self, context, layout, con):
         layout.label("Blender 2.6 doesn't support python constraints yet")
 
index de3e98611d76ca8b4226806667f35e27d2b2a879..50221d57b4928d817030501b921284e6c98c7cdb 100644 (file)
@@ -181,6 +181,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
         layout.prop(md, "cache_format")
         layout.prop(md, "filepath")
 
+        if md.cache_format == 'ABC':
+            layout.prop(md, "sub_object")
+
         layout.label(text="Evaluation:")
         layout.prop(md, "factor", slider=True)
         layout.prop(md, "deform_mode")
@@ -215,6 +218,22 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
         row = split.row()
         row.prop(md, "flip_axis")
 
+    def MESH_SEQUENCE_CACHE(self, layout, ob, md):
+        layout.label(text="Cache File Properties:")
+        box = layout.box()
+        box.template_cache_file(md, "cache_file")
+
+        cache_file = md.cache_file
+
+        layout.label(text="Modifier Properties:")
+        box = layout.box()
+
+        if cache_file is not None:
+            box.prop_search(md, "object_path", cache_file, "object_paths")
+
+        if ob.type == 'MESH':
+            box.row().prop(md, "read_data")
+
     def CAST(self, layout, ob, md):
         split = layout.split(percentage=0.25)
 
index 97ef37fa5e050ed0d34c63e9b2e9fe23f2259036..780dc4cf98290e4e5a4da28b7f570a89b21f3fae 100644 (file)
@@ -158,6 +158,8 @@ class INFO_MT_file_import(Menu):
     def draw(self, context):
         if bpy.app.build_options.collada:
             self.layout.operator("wm.collada_import", text="Collada (Default) (.dae)")
+        if bpy.app.build_options.alembic:
+            self.layout.operator("wm.alembic_import", text="Alembic (.abc)")
 
 
 class INFO_MT_file_export(Menu):
@@ -167,6 +169,8 @@ class INFO_MT_file_export(Menu):
     def draw(self, context):
         if bpy.app.build_options.collada:
             self.layout.operator("wm.collada_export", text="Collada (Default) (.dae)")
+        if bpy.app.build_options.alembic:
+            self.layout.operator("wm.alembic_export", text="Alembic (.abc)")
 
 
 class INFO_MT_file_external_data(Menu):
index e36f9e2b43eac72472861171427685209955e795..6f2b78e084529a69af4bef6dc81f39a06ed00e40 100644 (file)
@@ -31,6 +31,7 @@ set(SRC_DNA_INC
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_armature_types.h
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_boid_types.h
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_brush_types.h
+       ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_cachefile_types.h
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_camera_types.h
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_cloth_types.h
        ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_color_types.h
@@ -153,3 +154,6 @@ if(WITH_FREESTYLE)
        add_subdirectory(freestyle)
 endif()
 
+if(WITH_ALEMBIC)
+       add_subdirectory(alembic)
+endif()
diff --git a/source/blender/alembic/ABC_alembic.h b/source/blender/alembic/ABC_alembic.h
new file mode 100644 (file)
index 0000000..cf121f8
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_ALEMBIC_H__
+#define __ABC_ALEMBIC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct bContext;
+struct DerivedMesh;
+struct ListBase;
+struct Object;
+struct Scene;
+
+typedef struct AbcArchiveHandle AbcArchiveHandle;
+
+enum {
+       ABC_ARCHIVE_OGAWA = 0,
+       ABC_ARCHIVE_HDF5  = 1,
+};
+
+int ABC_get_version(void);
+
+struct AlembicExportParams {
+       double frame_start;
+       double frame_end;
+
+       double frame_step_xform;
+       double frame_step_shape;
+
+       double shutter_open;
+       double shutter_close;
+
+       /* bools */
+       unsigned int selected_only : 1;
+       unsigned int uvs : 1;
+       unsigned int normals : 1;
+       unsigned int vcolors : 1;
+       unsigned int apply_subdiv : 1;
+       unsigned int flatten_hierarchy : 1;
+       unsigned int visible_layers_only : 1;
+       unsigned int renderable_only : 1;
+       unsigned int face_sets : 1;
+       unsigned int use_subdiv_schema : 1;
+       unsigned int packuv : 1;
+
+       unsigned int compression_type : 1;
+       float global_scale;
+};
+
+void ABC_export(
+        struct Scene *scene,
+        struct bContext *C,
+        const char *filepath,
+        const struct AlembicExportParams *params);
+
+void ABC_import(struct bContext *C,
+                const char *filepath,
+                float scale,
+                bool is_sequence,
+                bool set_frame_range,
+                int sequence_len,
+                int offset,
+                bool validate_meshes);
+
+AbcArchiveHandle *ABC_create_handle(const char *filename, struct ListBase *object_paths);
+
+void ABC_free_handle(AbcArchiveHandle *handle);
+
+void ABC_get_transform(AbcArchiveHandle *handle,
+                       struct Object *ob,
+                       const char *object_path,
+                       float r_mat[4][4],
+                       float time,
+                       float scale);
+
+struct DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle,
+                                  struct Object *ob,
+                                  struct DerivedMesh *dm,
+                                  const char *object_path,
+                                  const float time,
+                                  const char **err_str,
+                                  int flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __ABC_ALEMBIC_H__ */
diff --git a/source/blender/alembic/CMakeLists.txt b/source/blender/alembic/CMakeLists.txt
new file mode 100644 (file)
index 0000000..f579a3b
--- /dev/null
@@ -0,0 +1,81 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2006, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): Kevin Dietrich.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+       .
+       ../blenkernel
+       ../blenlib
+       ../blenloader
+       ../editors/include
+       ../makesdna
+       ../makesrna
+       ../windowmanager
+       ../../../intern/guardedalloc
+)
+
+set(INC_SYS
+       ${ALEMBIC_INCLUDE_DIRS}
+       ${HDF5_INCLUDE_DIRS}
+       ${OPENEXR_INCLUDE_DIRS}
+)
+if(APPLE)
+       list(APPEND INC_SYS
+               ${BOOST_INCLUDE_DIR}
+       )
+endif()
+
+set(SRC
+       intern/abc_camera.cc
+       intern/abc_customdata.cc
+       intern/abc_curves.cc
+       intern/abc_exporter.cc
+       intern/abc_hair.cc
+       intern/abc_mesh.cc
+       intern/abc_nurbs.cc
+       intern/abc_object.cc
+       intern/abc_points.cc
+       intern/abc_transform.cc
+       intern/abc_util.cc
+       intern/alembic_capi.cc
+
+       ABC_alembic.h
+       intern/abc_camera.h
+       intern/abc_customdata.h
+       intern/abc_curves.h
+       intern/abc_exporter.h
+       intern/abc_hair.h
+       intern/abc_mesh.h
+       intern/abc_nurbs.h
+       intern/abc_object.h
+       intern/abc_points.h
+       intern/abc_transform.h
+       intern/abc_util.h
+)
+
+if(WITH_ALEMBIC_HDF5)
+       add_definitions(-DWITH_ALEMBIC_HDF5)
+endif()
+
+blender_add_lib(bf_alembic "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/source/blender/alembic/intern/abc_camera.cc b/source/blender/alembic/intern/abc_camera.cc
new file mode 100644 (file)
index 0000000..38a6d3d
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_camera.h"
+
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_camera_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_camera.h"
+#include "BKE_object.h"
+
+#include "BLI_math.h"
+#include "BLI_string.h"
+}
+
+using Alembic::AbcGeom::ICamera;
+using Alembic::AbcGeom::ICompoundProperty;
+using Alembic::AbcGeom::IFloatProperty;
+using Alembic::AbcGeom::ISampleSelector;
+
+using Alembic::AbcGeom::OCamera;
+using Alembic::AbcGeom::OFloatProperty;
+
+using Alembic::AbcGeom::CameraSample;
+using Alembic::AbcGeom::kWrapExisting;
+
+/* ************************************************************************** */
+
+AbcCameraWriter::AbcCameraWriter(Scene *scene,
+                                 Object *ob,
+                                 AbcTransformWriter *parent,
+                                 uint32_t time_sampling,
+                                 ExportSettings &settings)
+    : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+       OCamera camera(parent->alembicXform(), m_name, m_time_sampling);
+       m_camera_schema = camera.getSchema();
+
+       m_custom_data_container = m_camera_schema.getUserProperties();
+       m_stereo_distance = OFloatProperty(m_custom_data_container, "stereoDistance", m_time_sampling);
+       m_eye_separation = OFloatProperty(m_custom_data_container, "eyeSeparation", m_time_sampling);
+}
+
+void AbcCameraWriter::do_write()
+{
+       Camera *cam = static_cast<Camera *>(m_object->data);
+
+       m_stereo_distance.set(cam->stereo.convergence_distance);
+       m_eye_separation.set(cam->stereo.interocular_distance);
+
+       const double apperture_x = cam->sensor_x / 10.0;
+       const double apperture_y = cam->sensor_y / 10.0;
+       const double film_aspect = apperture_x / apperture_y;
+
+       m_camera_sample.setFocalLength(cam->lens);
+       m_camera_sample.setHorizontalAperture(apperture_x);
+       m_camera_sample.setVerticalAperture(apperture_y);
+       m_camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx);
+       m_camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect);
+       m_camera_sample.setNearClippingPlane(cam->clipsta);
+       m_camera_sample.setFarClippingPlane(cam->clipend);
+
+       if (cam->dof_ob) {
+               Imath::V3f v(m_object->loc[0] - cam->dof_ob->loc[0],
+                       m_object->loc[1] - cam->dof_ob->loc[1],
+                       m_object->loc[2] - cam->dof_ob->loc[2]);
+               m_camera_sample.setFocusDistance(v.length());
+       }
+       else {
+               m_camera_sample.setFocusDistance(cam->gpu_dof.focus_distance);
+       }
+
+       /* Blender camera does not have an fstop param, so try to find a custom prop
+        * instead. */
+       m_camera_sample.setFStop(cam->gpu_dof.fstop);
+
+       m_camera_sample.setLensSqueezeRatio(1.0);
+       m_camera_schema.set(m_camera_sample);
+}
+
+/* ************************************************************************** */
+
+AbcCameraReader::AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
+    : AbcObjectReader(object, settings)
+{
+       ICamera abc_cam(m_iobject, kWrapExisting);
+       m_schema = abc_cam.getSchema();
+
+       get_min_max_time(m_schema, m_min_time, m_max_time);
+}
+
+bool AbcCameraReader::valid() const
+{
+       return m_schema.valid();
+}
+
+void AbcCameraReader::readObjectData(Main *bmain, float time)
+{
+       Camera *bcam = static_cast<Camera *>(BKE_camera_add(bmain, "abc_camera"));
+
+       ISampleSelector sample_sel(time);
+       CameraSample cam_sample;
+       m_schema.get(cam_sample, sample_sel);
+
+       ICompoundProperty customDataContainer = m_schema.getUserProperties();
+
+       if (customDataContainer.valid() &&
+           customDataContainer.getPropertyHeader("stereoDistance") &&
+           customDataContainer.getPropertyHeader("eyeSeparation"))
+       {
+               IFloatProperty convergence_plane(customDataContainer, "stereoDistance");
+               IFloatProperty eye_separation(customDataContainer, "eyeSeparation");
+
+               bcam->stereo.interocular_distance = eye_separation.getValue(sample_sel);
+               bcam->stereo.convergence_distance = convergence_plane.getValue(sample_sel);
+       }
+
+       const float lens = cam_sample.getFocalLength();
+       const float apperture_x = cam_sample.getHorizontalAperture();
+       const float apperture_y = cam_sample.getVerticalAperture();
+       const float h_film_offset = cam_sample.getHorizontalFilmOffset();
+       const float v_film_offset = cam_sample.getVerticalFilmOffset();
+       const float film_aspect = apperture_x / apperture_y;
+
+       bcam->lens = lens;
+       bcam->sensor_x = apperture_x * 10;
+       bcam->sensor_y = apperture_y * 10;
+       bcam->shiftx = h_film_offset / apperture_x;
+       bcam->shifty = v_film_offset / apperture_y / film_aspect;
+       bcam->clipsta = max_ff(0.1f, cam_sample.getNearClippingPlane());
+       bcam->clipend = cam_sample.getFarClippingPlane();
+       bcam->gpu_dof.focus_distance = cam_sample.getFocusDistance();
+       bcam->gpu_dof.fstop = cam_sample.getFStop();
+
+       BLI_strncpy(bcam->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1);
+
+       m_object = BKE_object_add_only_object(bmain, OB_CAMERA, m_object_name.c_str());
+       m_object->data = bcam;
+}
diff --git a/source/blender/alembic/intern/abc_camera.h b/source/blender/alembic/intern/abc_camera.h
new file mode 100644 (file)
index 0000000..fafb4d3
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_CAMERA_H__
+#define __ABC_CAMERA_H__
+
+#include "abc_object.h"
+
+/* ************************************************************************** */
+
+class AbcCameraWriter : public AbcObjectWriter {
+       Alembic::AbcGeom::OCameraSchema m_camera_schema;
+       Alembic::AbcGeom::CameraSample m_camera_sample;
+       Alembic::AbcGeom::OCompoundProperty m_custom_data_container;
+       Alembic::AbcGeom::OFloatProperty m_stereo_distance;
+       Alembic::AbcGeom::OFloatProperty m_eye_separation;
+
+public:
+       AbcCameraWriter(Scene *scene,
+                       Object *ob,
+                       AbcTransformWriter *parent,
+                       uint32_t time_sampling,
+                       ExportSettings &settings);
+
+private:
+       virtual void do_write();
+};
+
+/* ************************************************************************** */
+
+class AbcCameraReader : public AbcObjectReader {
+       Alembic::AbcGeom::ICameraSchema m_schema;
+
+public:
+       AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+       bool valid() const;
+
+       void readObjectData(Main *bmain, float time);
+};
+
+#endif  /* __ABC_CAMERA_H__ */
\ No newline at end of file
diff --git a/source/blender/alembic/intern/abc_curves.cc b/source/blender/alembic/intern/abc_curves.cc
new file mode 100644 (file)
index 0000000..4e1e4e7
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software  Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#include "abc_curves.h"
+
+#include <cstdio>
+
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_listbase.h"
+
+#include "BKE_curve.h"
+#include "BKE_object.h"
+
+#include "ED_curve.h"
+}
+
+using Alembic::Abc::IInt32ArrayProperty;
+using Alembic::Abc::Int32ArraySamplePtr;
+using Alembic::Abc::FloatArraySamplePtr;
+using Alembic::Abc::P3fArraySamplePtr;
+using Alembic::Abc::UcharArraySamplePtr;
+
+using Alembic::AbcGeom::ICurves;
+using Alembic::AbcGeom::ICurvesSchema;
+using Alembic::AbcGeom::IFloatGeomParam;
+using Alembic::AbcGeom::ISampleSelector;
+using Alembic::AbcGeom::kWrapExisting;
+using Alembic::AbcGeom::CurvePeriodicity;
+
+using Alembic::AbcGeom::OCurves;
+using Alembic::AbcGeom::OCurvesSchema;
+using Alembic::AbcGeom::ON3fGeomParam;
+using Alembic::AbcGeom::OV2fGeomParam;
+
+/* ************************************************************************** */
+
+AbcCurveWriter::AbcCurveWriter(Scene *scene,
+                               Object *ob,
+                               AbcTransformWriter *parent,
+                               uint32_t time_sampling,
+                               ExportSettings &settings)
+    : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+       OCurves curves(parent->alembicXform(), m_name, m_time_sampling);
+       m_schema = curves.getSchema();
+}
+
+void AbcCurveWriter::do_write()
+{
+       Curve *curve = static_cast<Curve *>(m_object->data);
+
+       std::vector<Imath::V3f> verts;
+       std::vector<int32_t> vert_counts;
+       std::vector<float> widths;
+       std::vector<float> weights;
+       std::vector<float> knots;
+       std::vector<uint8_t> orders;
+       Imath::V3f temp_vert;
+
+       Alembic::AbcGeom::BasisType curve_basis;
+       Alembic::AbcGeom::CurveType curve_type;
+       Alembic::AbcGeom::CurvePeriodicity periodicity;
+
+       Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
+       for (; nurbs; nurbs = nurbs->next) {
+               if (nurbs->bp) {
+                       curve_basis = Alembic::AbcGeom::kNoBasis;
+                       curve_type = Alembic::AbcGeom::kLinear;
+
+                       const int totpoint = nurbs->pntsu * nurbs->pntsv;
+
+                       const BPoint *point = nurbs->bp;
+
+                       for (int i = 0; i < totpoint; ++i, ++point) {
+                               copy_zup_yup(temp_vert.getValue(), point->vec);
+                               verts.push_back(temp_vert);
+                               weights.push_back(point->vec[3]);
+                               widths.push_back(point->radius);
+                       }
+               }
+               else if (nurbs->bezt) {
+                       curve_basis = Alembic::AbcGeom::kBezierBasis;
+                       curve_type = Alembic::AbcGeom::kCubic;
+
+                       const int totpoint = nurbs->pntsu;
+
+                       const BezTriple *bezier = nurbs->bezt;
+
+                       /* TODO(kevin): store info about handles, Alembic doesn't have this. */
+                       for (int i = 0; i < totpoint; ++i, ++bezier) {
+                               copy_zup_yup(temp_vert.getValue(), bezier->vec[1]);
+                               verts.push_back(temp_vert);
+                               widths.push_back(bezier->radius);
+                       }
+               }
+
+               if ((nurbs->flagu & CU_NURB_ENDPOINT) != 0) {
+                       periodicity = Alembic::AbcGeom::kNonPeriodic;
+               }
+               else if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) {
+                       periodicity = Alembic::AbcGeom::kPeriodic;
+
+                       /* Duplicate the start points to indicate that the curve is actually
+                        * cyclic since other software need those.
+                        */
+
+                       for (int i = 0; i < nurbs->orderu; ++i) {
+                               verts.push_back(verts[i]);
+                       }
+               }
+
+               if (nurbs->knotsu != NULL) {
+                       const size_t num_knots = KNOTSU(nurbs);
+
+                       /* Add an extra knot at the beggining and end of the array since most apps
+                        * require/expect them. */
+                       knots.resize(num_knots + 2);
+
+                       for (int i = 0; i < num_knots; ++i) {
+                               knots[i + 1] = nurbs->knotsu[i];
+                       }
+
+                       if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) {
+                               knots[0] = nurbs->knotsu[0];
+                               knots[num_knots - 1] = nurbs->knotsu[num_knots - 1];
+                       }
+                       else {
+                               knots[0] = (2.0f * nurbs->knotsu[0] - nurbs->knotsu[1]);
+                               knots[num_knots - 1] = (2.0f * nurbs->knotsu[num_knots - 1] - nurbs->knotsu[num_knots - 2]);
+                       }
+               }
+
+               orders.push_back(nurbs->orderu + 1);
+               vert_counts.push_back(verts.size());
+       }
+
+       Alembic::AbcGeom::OFloatGeomParam::Sample width_sample;
+       width_sample.setVals(widths);
+
+       m_sample = OCurvesSchema::Sample(verts,
+                                        vert_counts,
+                                        curve_type,
+                                        periodicity,
+                                        width_sample,
+                                        OV2fGeomParam::Sample(),  /* UVs */
+                                        ON3fGeomParam::Sample(),  /* normals */
+                                        curve_basis,
+                                        weights,
+                                        orders,
+                                        knots);
+
+       m_sample.setSelfBounds(bounds());
+       m_schema.set(m_sample);
+}
+
+/* ************************************************************************** */
+
+AbcCurveReader::AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
+    : AbcObjectReader(object, settings)
+{
+       ICurves abc_curves(object, kWrapExisting);
+       m_curves_schema = abc_curves.getSchema();
+
+       get_min_max_time(m_curves_schema, m_min_time, m_max_time);
+}
+
+bool AbcCurveReader::valid() const
+{
+       return m_curves_schema.valid();
+}
+
+void AbcCurveReader::readObjectData(Main *bmain, float time)
+{
+       Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVE);
+
+       cu->flag |= CU_DEFORM_FILL | CU_3D;
+       cu->actvert = CU_ACT_NONE;
+
+       m_object = BKE_object_add_only_object(bmain, OB_CURVE, m_object_name.c_str());
+       m_object->data = cu;
+
+       read_curve_sample(cu, m_curves_schema, time);
+
+       if (has_animations(m_curves_schema, m_settings)) {
+               addCacheModifier();
+       }
+}
+
+/* ************************************************************************** */
+
+void read_curve_sample(Curve *cu, const ICurvesSchema &schema, const float time)
+{
+       const ISampleSelector sample_sel(time);
+       ICurvesSchema::Sample smp = schema.getValue(sample_sel);
+       const Int32ArraySamplePtr num_vertices = smp.getCurvesNumVertices();
+       const P3fArraySamplePtr positions = smp.getPositions();
+       const FloatArraySamplePtr weights = smp.getPositionWeights();
+       const FloatArraySamplePtr knots = smp.getKnots();
+       const CurvePeriodicity periodicity = smp.getWrap();
+       const UcharArraySamplePtr orders = smp.getOrders();
+
+       const IFloatGeomParam widths_param = schema.getWidthsParam();
+       FloatArraySamplePtr radiuses;
+
+       if (widths_param.valid()) {
+               IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(sample_sel);
+               radiuses = wsample.getVals();
+       }
+
+       int knot_offset = 0;
+
+       size_t idx = 0;
+       for (size_t i = 0; i < num_vertices->size(); ++i) {
+               const int num_verts = (*num_vertices)[i];
+
+               Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), "abc_getnurb"));
+               nu->resolu = cu->resolu;
+               nu->resolv = cu->resolv;
+               nu->pntsu = num_verts;
+               nu->pntsv = 1;
+               nu->flag |= CU_SMOOTH;
+
+               nu->orderu = num_verts;
+
+               if (smp.getType() == Alembic::AbcGeom::kCubic) {
+                       nu->orderu = 3;
+               }
+               else if (orders && orders->size() > i) {
+                       nu->orderu = static_cast<short>((*orders)[i] - 1);
+               }
+
+               if (periodicity == Alembic::AbcGeom::kNonPeriodic) {
+                       nu->flagu |= CU_NURB_ENDPOINT;
+               }
+               else if (periodicity == Alembic::AbcGeom::kPeriodic) {
+                       nu->flagu |= CU_NURB_CYCLIC;
+
+                       /* Check the number of points which overlap, we don't have
+                        * overlapping points in Blender, but other software do use them to
+                        * indicate that a curve is actually cyclic. Usually the number of
+                        * overlapping points is equal to the order/degree of the curve.
+                        */
+
+                       const int start = idx;
+                       const int end = idx + num_verts;
+                       int overlap = 0;
+
+                       for (int j = start, k = end - nu->orderu; j < nu->orderu; ++j, ++k) {
+                               const Imath::V3f &p1 = (*positions)[j];
+                               const Imath::V3f &p2 = (*positions)[k];
+
+                               if (p1 != p2) {
+                                       break;
+                               }
+
+                               ++overlap;
+                       }
+
+                       /* TODO: Special case, need to figure out how it coincides with knots. */
+                       if (overlap == 0 && num_verts > 2 && (*positions)[start] == (*positions)[end - 1]) {
+                               overlap = 1;
+                       }
+
+                       /* There is no real cycles. */
+                       if (overlap == 0) {
+                               nu->flagu &= ~CU_NURB_CYCLIC;
+                               nu->flagu |= CU_NURB_ENDPOINT;
+                       }
+
+                       nu->pntsu -= overlap;
+               }
+
+               const bool do_weights = (weights != NULL) && (weights->size() > 1);
+               float weight = 1.0f;
+
+               const bool do_radius = (radiuses != NULL) && (radiuses->size() > 1);
+               float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : 1.0f;
+
+               nu->type = CU_NURBS;
+
+               nu->bp = static_cast<BPoint *>(MEM_callocN(sizeof(BPoint) * nu->pntsu, "abc_getnurb"));
+               BPoint *bp = nu->bp;
+
+               for (int j = 0; j < nu->pntsu; ++j, ++bp, ++idx) {
+                       const Imath::V3f &pos = (*positions)[idx];
+
+                       if (do_radius) {
+                               radius = (*radiuses)[idx];
+                       }
+
+                       if (do_weights) {
+                               weight = (*weights)[idx];
+                       }
+
+                       copy_yup_zup(bp->vec, pos.getValue());
+                       bp->vec[3] = weight;
+                       bp->f1 = SELECT;
+                       bp->radius = radius;
+                       bp->weight = 1.0f;
+               }
+
+               if (knots && knots->size() != 0) {
+                       nu->knotsu = static_cast<float *>(MEM_callocN(KNOTSU(nu) * sizeof(float), "abc_setsplineknotsu"));
+
+                       /* TODO: second check is temporary, for until the check for cycles is rock solid. */
+                       if (periodicity == Alembic::AbcGeom::kPeriodic && (KNOTSU(nu) == knots->size() - 2)) {
+                               /* Skip first and last knots. */
+                               for (size_t i = 1; i < knots->size() - 1; ++i) {
+                                       nu->knotsu[i - 1] = (*knots)[knot_offset + i];
+                               }
+                       }
+                       else {
+                               /* TODO: figure out how to use the knots array from other
+                                * software in this case. */
+                               BKE_nurb_knot_calc_u(nu);
+                       }
+
+                       knot_offset += knots->size();
+               }
+               else {
+                       BKE_nurb_knot_calc_u(nu);
+               }
+
+               BLI_addtail(BKE_curve_nurbs_get(cu), nu);
+       }
+}
diff --git a/source/blender/alembic/intern/abc_curves.h b/source/blender/alembic/intern/abc_curves.h
new file mode 100644 (file)
index 0000000..ee47f19
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software  Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#ifndef __ABC_CURVES_H__
+#define __ABC_CURVES_H__
+
+#include "abc_object.h"
+
+struct Curve;
+
+/* ************************************************************************** */
+
+class AbcCurveWriter : public AbcObjectWriter {
+       Alembic::AbcGeom::OCurvesSchema m_schema;
+       Alembic::AbcGeom::OCurvesSchema::Sample m_sample;
+
+public:
+       AbcCurveWriter(Scene *scene,
+                      Object *ob,
+                      AbcTransformWriter *parent,
+                      uint32_t time_sampling,
+                      ExportSettings &settings);
+
+       void do_write();
+};
+
+/* ************************************************************************** */
+
+class AbcCurveReader : public AbcObjectReader {
+       Alembic::AbcGeom::ICurvesSchema m_curves_schema;
+
+public:
+       AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+       bool valid() const;
+
+       void readObjectData(Main *bmain, float time);
+};
+
+/* ************************************************************************** */
+
+void read_curve_sample(Curve *cu, const Alembic::AbcGeom::ICurvesSchema &schema, const float time);
+
+#endif  /* __ABC_CURVES_H__ */
\ No newline at end of file
diff --git a/source/blender/alembic/intern/abc_customdata.cc b/source/blender/alembic/intern/abc_customdata.cc
new file mode 100644 (file)
index 0000000..ebf1b2b
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software  Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#include "abc_customdata.h"
+
+#include <Alembic/AbcGeom/All.h>
+#include <algorithm>
+
+extern "C" {
+#include "DNA_customdata_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_customdata.h"
+}
+
+/* NOTE: for now only UVs and Vertex Colors are supported for streaming.
+ * Although Alembic only allows for a single UV layer per {I|O}Schema, and does
+ * not have a vertex color concept, there is a convention between DCCs to write
+ * such data in a way that lets other DCC know what they are for. See comments
+ * in the write code for the conventions. */
+
+using Alembic::AbcGeom::kVertexScope;
+using Alembic::AbcGeom::kFacevaryingScope;
+
+using Alembic::Abc::C4fArraySample;
+using Alembic::Abc::UInt32ArraySample;
+using Alembic::Abc::V2fArraySample;
+
+using Alembic::AbcGeom::OV2fGeomParam;
+using Alembic::AbcGeom::OC4fGeomParam;
+
+static void get_uvs(const CDStreamConfig &config,
+                    std::vector<Imath::V2f> &uvs,
+                    std::vector<uint32_t> &uvidx,
+                    void *cd_data)
+{
+       MLoopUV *mloopuv_array = static_cast<MLoopUV *>(cd_data);
+
+       if (!mloopuv_array) {
+               return;
+       }
+
+       const int num_poly = config.totpoly;
+       MPoly *polygons = config.mpoly;
+
+       if (!config.pack_uvs) {
+               int cnt = 0;
+               uvidx.resize(config.totloop);
+               uvs.resize(config.totloop);
+
+               for (int i = 0; i < num_poly; ++i) {
+                       MPoly &current_poly = polygons[i];
+                       MLoopUV *loopuvpoly = mloopuv_array + current_poly.loopstart + current_poly.totloop;
+
+                       for (int j = 0; j < current_poly.totloop; ++j, ++cnt) {
+                               --loopuvpoly;
+
+                               uvidx[cnt] = cnt;
+                               uvs[cnt][0] = loopuvpoly->uv[0];
+                               uvs[cnt][1] = loopuvpoly->uv[1];
+                       }
+               }
+       }
+       else {
+               for (int i = 0; i < num_poly; ++i) {
+                       MPoly &current_poly = polygons[i];
+                       MLoopUV *loopuvpoly = mloopuv_array + current_poly.loopstart + current_poly.totloop;
+
+                       for (int j = 0; j < current_poly.totloop; ++j) {
+                               loopuvpoly--;
+                               Imath::V2f uv(loopuvpoly->uv[0], loopuvpoly->uv[1]);
+
+                               std::vector<Imath::V2f>::iterator it = std::find(uvs.begin(), uvs.end(), uv);
+
+                               if (it == uvs.end()) {
+                                       uvidx.push_back(uvs.size());
+                                       uvs.push_back(uv);
+                               }
+                               else {
+                                       uvidx.push_back(std::distance(uvs.begin(), it));
+                               }
+                       }
+               }
+       }
+}
+
+const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data)
+{
+       const int active_uvlayer = CustomData_get_active_layer(data, CD_MLOOPUV);
+
+       if (active_uvlayer < 0) {
+               return "";
+       }
+
+       void *cd_data = CustomData_get_layer_n(data, CD_MLOOPUV, active_uvlayer);
+
+       get_uvs(config, sample.uvs, sample.indices, cd_data);
+
+       return CustomData_get_layer_name(data, CD_MLOOPUV, active_uvlayer);
+}
+
+/* Convention to write UVs:
+ * - V2fGeomParam on the arbGeomParam
+ * - set scope as face varying
+ * - (optional due to its behaviour) tag as UV using Alembic::AbcGeom::SetIsUV
+ */
+static void write_uv(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name)
+{
+       std::vector<uint32_t> indices;
+       std::vector<Imath::V2f> uvs;
+
+       get_uvs(config, uvs, indices, data);
+
+       if (indices.empty() || uvs.empty()) {
+               return;
+       }
+
+       OV2fGeomParam param(prop, name, true, kFacevaryingScope, 1);
+
+       OV2fGeomParam::Sample sample(
+               V2fArraySample(&uvs.front(), uvs.size()),
+               UInt32ArraySample(&indices.front(), indices.size()),
+               kFacevaryingScope);
+
+       param.set(sample);
+}
+
+/* Convention to write Vertex Colors:
+ * - C3fGeomParam/C4fGeomParam on the arbGeomParam
+ * - set scope as vertex varying
+ */
+static void write_mcol(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name)
+{
+       const float cscale = 1.0f / 255.0f;
+       MPoly *polys = config.mpoly;
+       MLoop *mloops = config.mloop;
+       MCol *cfaces = static_cast<MCol *>(data);
+
+       std::vector<Imath::C4f> buffer(config.totvert);
+
+       Imath::C4f col;
+
+       for (int i = 0; i < config.totpoly; ++i) {
+               MPoly *p = &polys[i];
+               MCol *cface = &cfaces[p->loopstart + p->totloop];
+               MLoop *mloop = &mloops[p->loopstart + p->totloop];
+
+               for (int j = 0; j < p->totloop; ++j) {
+                       cface--;
+                       mloop--;
+
+                       col[0] = cface->a * cscale;
+                       col[1] = cface->r * cscale;
+                       col[2] = cface->g * cscale;
+                       col[3] = cface->b * cscale;
+
+                       buffer[mloop->v] = col;
+               }
+       }
+
+       OC4fGeomParam param(prop, name, true, kFacevaryingScope, 1);
+
+       OC4fGeomParam::Sample sample(
+               C4fArraySample(&buffer.front(), buffer.size()),
+               kVertexScope);
+
+       param.set(sample);
+}
+
+void write_custom_data(const OCompoundProperty &prop, const CDStreamConfig &config, CustomData *data, int data_type)
+{
+       CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
+
+       if (!CustomData_has_layer(data, cd_data_type)) {
+               return;
+       }
+
+       const int active_layer = CustomData_get_active_layer(data, cd_data_type);
+       const int tot_layers = CustomData_number_of_layers(data, cd_data_type);
+
+       for (int i = 0; i < tot_layers; ++i) {
+               void *cd_data = CustomData_get_layer_n(data, cd_data_type, i);
+               const char *name = CustomData_get_layer_name(data, cd_data_type, i);
+
+               if (cd_data_type == CD_MLOOPUV) {
+                       /* Already exported. */
+                       if (i == active_layer) {
+                               continue;
+                       }
+
+                       write_uv(prop, config, cd_data, name);
+               }
+               else if (cd_data_type == CD_MLOOPCOL) {
+                       write_mcol(prop, config, cd_data, name);
+               }
+       }
+}
+
+/* ************************************************************************** */
+
+using Alembic::Abc::C3fArraySamplePtr;
+using Alembic::Abc::C4fArraySamplePtr;
+using Alembic::Abc::PropertyHeader;
+
+using Alembic::AbcGeom::IC3fGeomParam;
+using Alembic::AbcGeom::IC4fGeomParam;
+using Alembic::AbcGeom::IV2fGeomParam;
+
+static void read_mcols(const CDStreamConfig &config, void *data,
+                       const C3fArraySamplePtr &c3f_ptr, const C4fArraySamplePtr &c4f_ptr)
+{
+       MCol *cfaces = static_cast<MCol *>(data);
+       MPoly *polys = config.mpoly;
+       MLoop *mloops = config.mloop;
+
+       if (c3f_ptr) {
+               for (int i = 0; i < config.totpoly; ++i) {
+                       MPoly *p = &polys[i];
+                       MCol *cface = &cfaces[p->loopstart + p->totloop];
+                       MLoop *mloop = &mloops[p->loopstart + p->totloop];
+
+                       for (int j = 0; j < p->totloop; ++j) {
+                               cface--;
+                               mloop--;
+                               const Imath::C3f &color = (*c3f_ptr)[mloop->v];
+                               cface->a = FTOCHAR(color[0]);
+                               cface->r = FTOCHAR(color[1]);
+                               cface->g = FTOCHAR(color[2]);
+                               cface->b = 255;
+                       }
+               }
+       }
+       else if (c4f_ptr) {
+               for (int i = 0; i < config.totpoly; ++i) {
+                       MPoly *p = &polys[i];
+                       MCol *cface = &cfaces[p->loopstart + p->totloop];
+                       MLoop *mloop = &mloops[p->loopstart + p->totloop];
+
+                       for (int j = 0; j < p->totloop; ++j) {
+                               cface--;
+                               mloop--;
+                               const Imath::C4f &color = (*c4f_ptr)[mloop->v];
+                               cface->a = FTOCHAR(color[0]);
+                               cface->r = FTOCHAR(color[1]);
+                               cface->g = FTOCHAR(color[2]);
+                               cface->b = FTOCHAR(color[3]);
+                       }
+               }
+       }
+}
+
+static void read_uvs(const CDStreamConfig &config, void *data,
+                     const Alembic::AbcGeom::V2fArraySamplePtr &uvs,
+                     const Alembic::AbcGeom::UInt32ArraySamplePtr &indices)
+{
+       MPoly *mpolys = config.mpoly;
+       MLoopUV *mloopuvs = static_cast<MLoopUV *>(data);
+
+       unsigned int uv_index, loop_index;
+
+       for (int i = 0; i < config.totpoly; ++i) {
+               MPoly &poly = mpolys[i];
+
+               for (int f = 0; f < poly.totloop; ++f) {
+                       loop_index = poly.loopstart + f;
+                       uv_index = (*indices)[loop_index];
+                       const Imath::V2f &uv = (*uvs)[uv_index];
+
+                       MLoopUV &loopuv = mloopuvs[loop_index];
+                       loopuv.uv[0] = uv[0];
+                       loopuv.uv[1] = uv[1];
+               }
+       }
+}
+
+static void read_custom_data_ex(const ICompoundProperty &prop,
+                                const PropertyHeader &prop_header,
+                                const CDStreamConfig &config,
+                                const Alembic::Abc::ISampleSelector &iss,
+                                int data_type)
+{
+       if (data_type == CD_MLOOPCOL) {
+               C3fArraySamplePtr c3f_ptr = C3fArraySamplePtr();
+               C4fArraySamplePtr c4f_ptr = C4fArraySamplePtr();
+
+               if (IC3fGeomParam::matches(prop_header)) {
+                       IC3fGeomParam color_param(prop, prop_header.getName());
+                       IC3fGeomParam::Sample sample;
+                       color_param.getIndexed(sample, iss);
+
+                       c3f_ptr = sample.getVals();
+               }
+               else if (IC4fGeomParam::matches(prop_header)) {
+                       IC4fGeomParam color_param(prop, prop_header.getName());
+                       IC4fGeomParam::Sample sample;
+                       color_param.getIndexed(sample, iss);
+
+                       c4f_ptr = sample.getVals();
+               }
+
+               void *cd_data = config.add_customdata_cb(config.user_data,
+                                                        prop_header.getName().c_str(),
+                                                        data_type);
+
+               read_mcols(config, cd_data, c3f_ptr, c4f_ptr);
+       }
+       else if (data_type == CD_MLOOPUV) {
+               IV2fGeomParam uv_param(prop, prop_header.getName());
+               IV2fGeomParam::Sample sample;
+               uv_param.getIndexed(sample, iss);
+
+               if (uv_param.getScope() != kFacevaryingScope) {
+                       return;
+               }
+
+               void *cd_data = config.add_customdata_cb(config.user_data,
+                                                        prop_header.getName().c_str(),
+                                                        data_type);
+
+               read_uvs(config, cd_data, sample.getVals(), sample.getIndices());
+       }
+}
+
+void read_custom_data(const ICompoundProperty &prop, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss)
+{
+       if (!prop.valid()) {
+               return;
+       }
+
+       int num_uvs = 0;
+       int num_colors = 0;
+
+       const size_t num_props = prop.getNumProperties();
+
+       for (size_t i = 0; i < num_props; ++i) {
+               const Alembic::Abc::PropertyHeader &prop_header = prop.getPropertyHeader(i);
+
+               /* Read UVs according to convention. */
+               if (IV2fGeomParam::matches(prop_header) && Alembic::AbcGeom::isUV(prop_header)) {
+                       if (++num_uvs > MAX_MTFACE) {
+                               continue;
+                       }
+
+                       read_custom_data_ex(prop, prop_header, config, iss, CD_MLOOPUV);
+                       continue;
+               }
+
+               /* Read vertex colors according to convention. */
+               if (IC3fGeomParam::matches(prop_header) || IC4fGeomParam::matches(prop_header)) {
+                       if (++num_colors > MAX_MCOL) {
+                               continue;
+                       }
+
+                       read_custom_data_ex(prop, prop_header, config, iss, CD_MLOOPCOL);
+                       continue;
+               }
+       }
+}
diff --git a/source/blender/alembic/intern/abc_customdata.h b/source/blender/alembic/intern/abc_customdata.h
new file mode 100644 (file)
index 0000000..3b16c0d
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software  Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#ifndef __ABC_CUSTOMDATA_H__
+#define __ABC_CUSTOMDATA_H__
+
+#include <Alembic/Abc/All.h>
+
+struct CustomData;
+struct MLoop;
+struct MLoopUV;
+struct MPoly;
+struct MVert;
+
+using Alembic::Abc::ICompoundProperty;
+using Alembic::Abc::OCompoundProperty;
+
+struct UVSample {
+       std::vector<Imath::V2f> uvs;
+       std::vector<uint32_t> indices;
+};
+
+struct CDStreamConfig {
+       MLoop *mloop;
+       int totloop;
+
+       MPoly *mpoly;
+       int totpoly;
+
+       MVert *mvert;
+       int totvert;
+
+       MLoopUV *mloopuv;
+
+       CustomData *loopdata;
+
+       bool pack_uvs;
+
+       /* TODO(kevin): might need a better way to handle adding and/or updating
+        * custom datas such that it updates the custom data holder and its pointers
+        * properly. */
+       void *user_data;
+       void *(*add_customdata_cb)(void *user_data, const char *name, int data_type);
+
+       CDStreamConfig()
+           : mloop(NULL)
+           , totloop(0)
+           , mpoly(NULL)
+           , totpoly(0)
+           , totvert(0)
+           , pack_uvs(false)
+           , user_data(NULL)
+           , add_customdata_cb(NULL)
+       {}
+};
+
+/* Get the UVs for the main UV property on a OSchema.
+ * Returns the name of the UV layer.
+ *
+ * For now the active layer is used, maybe needs a better way to choose this. */
+const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data);
+
+void write_custom_data(const OCompoundProperty &prop,
+                       const CDStreamConfig &config,
+                       CustomData *data,
+                       int data_type);
+
+void read_custom_data(const ICompoundProperty &prop,
+                      const CDStreamConfig &config,
+                      const Alembic::Abc::ISampleSelector &iss);
+
+#endif  /* __ABC_CUSTOMDATA_H__ */
diff --git a/source/blender/alembic/intern/abc_exporter.cc b/source/blender/alembic/intern/abc_exporter.cc
new file mode 100644 (file)
index 0000000..127e885
--- /dev/null
@@ -0,0 +1,600 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_exporter.h"
+
+#include <cmath>
+
+#ifdef WITH_ALEMBIC_HDF5
+#  include <Alembic/AbcCoreHDF5/All.h>
+#endif
+
+#include <Alembic/AbcCoreOgawa/All.h>
+
+#include "abc_camera.h"
+#include "abc_curves.h"
+#include "abc_hair.h"
+#include "abc_mesh.h"
+#include "abc_nurbs.h"
+#include "abc_points.h"
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_camera_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_space_types.h"  /* for FILE_MAX */
+
+#include "BLI_string.h"
+
+#ifdef WIN32
+/* needed for MSCV because of snprintf from BLI_string */
+#      include "BLI_winstuff.h"
+#endif
+
+#include "BKE_anim.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_main.h"
+#include "BKE_modifier.h"
+#include "BKE_particle.h"
+#include "BKE_scene.h"
+}
+
+using Alembic::Abc::TimeSamplingPtr;
+using Alembic::Abc::OBox3dProperty;
+
+/* ************************************************************************** */
+
+ExportSettings::ExportSettings()
+    : scene(NULL)
+    , selected_only(false)
+    , visible_layers_only(false)
+    , renderable_only(false)
+    , frame_start(1)
+    , frame_end(1)
+    , frame_step_xform(1)
+    , frame_step_shape(1)
+    , shutter_open(0.0)
+    , shutter_close(1.0)
+    , global_scale(1.0f)
+    , flatten_hierarchy(false)
+    , export_normals(false)
+    , export_uvs(false)
+    , export_vcols(false)
+    , export_face_sets(false)
+    , export_vweigths(false)
+    , apply_subdiv(false)
+    , use_subdiv_schema(false)
+    , export_child_hairs(true)
+    , export_ogawa(true)
+    , pack_uv(false)
+    , do_convert_axis(false)
+{}
+
+static bool object_is_smoke_sim(Object *ob)
+{
+       ModifierData *md = modifiers_findByType(ob, eModifierType_Smoke);
+
+       if (md) {
+               SmokeModifierData *smd = reinterpret_cast<SmokeModifierData *>(md);
+               return (smd->type == MOD_SMOKE_TYPE_DOMAIN);
+       }
+
+       return false;
+}
+
+static bool object_is_shape(Object *ob)
+{
+       switch (ob->type) {
+               case OB_MESH:
+                       if (object_is_smoke_sim(ob)) {
+                               return false;
+                       }
+
+                       return true;
+               case OB_CURVE:
+               case OB_SURF:
+               case OB_CAMERA:
+                       return true;
+               default:
+                       return false;
+       }
+}
+
+static bool export_object(const ExportSettings * const settings, Object *ob)
+{
+       if (settings->selected_only && !parent_selected(ob)) {
+               return false;
+       }
+
+       if (settings->visible_layers_only && !(settings->scene->lay & ob->lay)) {
+               return false;
+       }
+
+       if (settings->renderable_only && (ob->restrictflag & OB_RESTRICT_RENDER)) {
+               return false;
+       }
+
+       return true;
+}
+
+/* ************************************************************************** */
+
+AbcExporter::AbcExporter(Scene *scene, const char *filename, ExportSettings &settings)
+    : m_settings(settings)
+    , m_filename(filename)
+    , m_trans_sampling_index(0)
+    , m_shape_sampling_index(0)
+    , m_scene(scene)
+{}
+
+AbcExporter::~AbcExporter()
+{
+       std::map<std::string, AbcTransformWriter*>::iterator it, e;
+       for (it = m_xforms.begin(), e = m_xforms.end(); it != e; ++it) {
+               delete it->second;
+       }
+
+       for (int i = 0, e = m_shapes.size(); i != e; ++i) {
+               delete m_shapes[i];
+       }
+}
+
+void AbcExporter::getShutterSamples(double step, bool time_relative,
+                                    std::vector<double> &samples)
+{
+       samples.clear();
+
+       const double time_factor = time_relative ? m_scene->r.frs_sec : 1.0;
+       const double shutter_open = m_settings.shutter_open;
+       const double shutter_close = m_settings.shutter_close;
+
+       /* sample all frame */
+       if (shutter_open == 0.0 && shutter_close == 1.0) {
+               for (double t = 0; t < 1.0; t += step) {
+                       samples.push_back(t / time_factor);
+               }
+       }
+       else {
+               /* sample between shutter open & close */
+               const int nsamples = std::max((1.0 / step) - 1.0, 1.0);
+               const double time_inc = (shutter_close - shutter_open) / nsamples;
+
+               for (double t = shutter_open; t <= shutter_close; t += time_inc) {
+                       samples.push_back(t / time_factor);
+               }
+       }
+}
+
+Alembic::Abc::TimeSamplingPtr AbcExporter::createTimeSampling(double step)
+{
+       TimeSamplingPtr time_sampling;
+       std::vector<double> samples;
+
+       if (m_settings.frame_start == m_settings.frame_end) {
+               time_sampling.reset(new Alembic::Abc::TimeSampling());
+               return time_sampling;
+       }
+
+       getShutterSamples(step, true, samples);
+
+       Alembic::Abc::TimeSamplingType ts(static_cast<uint32_t>(samples.size()), 1.0 / m_scene->r.frs_sec);
+       time_sampling.reset(new Alembic::Abc::TimeSampling(ts, samples));
+
+       return time_sampling;
+}
+
+void AbcExporter::getFrameSet(double step, std::set<double> &frames)
+{
+       frames.clear();
+
+       std::vector<double> shutter_samples;
+
+       getShutterSamples(step, false, shutter_samples);
+
+       for (int frame = m_settings.frame_start; frame <= m_settings.frame_end; ++frame) {
+               for (int j = 0, e = shutter_samples.size(); j < e; ++j) {
+                       frames.insert(frame + shutter_samples[j]);
+               }
+       }
+}
+
+void AbcExporter::operator()(Main *bmain, float &progress, bool &was_canceled)
+{
+       std::string scene_name;
+
+       if (bmain->name[0] != '\0') {
+               char scene_file_name[FILE_MAX];
+               BLI_strncpy(scene_file_name, bmain->name, FILE_MAX);
+               scene_name = scene_file_name;
+       }
+       else {
+               scene_name = "untitled";
+       }
+
+       Scene *scene = m_scene;
+       const int fps = FPS;
+       char buf[16];
+       snprintf(buf, 15, "%d", fps);
+       const std::string str_fps = buf;
+
+       Alembic::AbcCoreAbstract::MetaData md;
+       md.set("FramesPerTimeUnit", str_fps);
+
+       Alembic::Abc::Argument arg(md);
+
+#ifdef WITH_ALEMBIC_HDF5
+       if (!m_settings.export_ogawa) {
+               m_archive = Alembic::Abc::CreateArchiveWithInfo(Alembic::AbcCoreHDF5::WriteArchive(),
+                                                               m_filename,
+                                                               "Blender",
+                                                               scene_name,
+                                                               Alembic::Abc::ErrorHandler::kThrowPolicy,
+                                                               arg);
+       }
+       else
+#endif
+       {
+               m_archive = Alembic::Abc::CreateArchiveWithInfo(Alembic::AbcCoreOgawa::WriteArchive(),
+                                                               m_filename,
+                                                               "Blender",
+                                                               scene_name,
+                                                               Alembic::Abc::ErrorHandler::kThrowPolicy,
+                                                               arg);
+       }
+
+       /* Create time samplings for transforms and shapes. */
+
+       TimeSamplingPtr trans_time = createTimeSampling(m_settings.frame_step_xform);
+
+       m_trans_sampling_index = m_archive.addTimeSampling(*trans_time);
+
+       TimeSamplingPtr shape_time;
+
+       if ((m_settings.frame_step_shape == m_settings.frame_step_xform) ||
+           (m_settings.frame_start == m_settings.frame_end))
+       {
+               shape_time = trans_time;
+               m_shape_sampling_index = m_trans_sampling_index;
+       }
+       else {
+               shape_time = createTimeSampling(m_settings.frame_step_shape);
+               m_shape_sampling_index = m_archive.addTimeSampling(*shape_time);
+       }
+
+       OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds(m_archive, m_trans_sampling_index);
+
+       if (m_settings.flatten_hierarchy) {
+               createTransformWritersFlat();
+       }
+       else {
+               createTransformWritersHierarchy(bmain->eval_ctx);
+       }
+
+       createShapeWriters(bmain->eval_ctx);
+
+       /* Make a list of frames to export. */
+
+       std::set<double> xform_frames;
+       getFrameSet(m_settings.frame_step_xform, xform_frames);
+
+       std::set<double> shape_frames;
+       getFrameSet(m_settings.frame_step_shape, shape_frames);
+
+       /* Merge all frames needed. */
+
+       std::set<double> frames(xform_frames);
+       frames.insert(shape_frames.begin(), shape_frames.end());
+
+       /* Export all frames. */
+
+       std::set<double>::const_iterator begin = frames.begin();
+       std::set<double>::const_iterator end = frames.end();
+
+       const float size = static_cast<float>(frames.size());
+       size_t i = 0;
+
+       for (; begin != end; ++begin) {
+               progress = (++i / size);
+
+               if (G.is_break) {
+                       was_canceled = true;
+                       break;
+               }
+
+               double f = *begin;
+               setCurrentFrame(bmain, f);
+
+               if (shape_frames.count(f) != 0) {
+                       for (int i = 0, e = m_shapes.size(); i != e; ++i) {
+                               m_shapes[i]->write();
+                       }
+               }
+
+               if (xform_frames.count(f) == 0) {
+                       continue;
+               }
+
+               std::map<std::string, AbcTransformWriter *>::iterator xit, xe;
+               for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) {
+                       xit->second->write();
+               }
+
+               /* Save the archive 's bounding box. */
+               Imath::Box3d bounds;
+
+               for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) {
+                       Imath::Box3d box = xit->second->bounds();
+                       bounds.extendBy(box);
+               }
+
+               archive_bounds_prop.set(bounds);
+       }
+}
+
+void AbcExporter::createTransformWritersHierarchy(EvaluationContext *eval_ctx)
+{
+       Base *base = static_cast<Base *>(m_scene->base.first);
+
+       while (base) {
+               Object *ob = base->object;
+
+               if (export_object(&m_settings, ob)) {
+                       switch(ob->type) {
+                               case OB_LAMP:
+                               case OB_LATTICE:
+                               case OB_MBALL:
+                               case OB_SPEAKER:
+                                       /* We do not export transforms for objects of these classes. */
+                                       break;
+
+                               default:
+                                       exploreTransform(eval_ctx, ob, ob->parent, NULL);
+                       }
+               }
+
+               base = base->next;
+       }
+}
+
+void AbcExporter::createTransformWritersFlat()
+{
+       Base *base = static_cast<Base *>(m_scene->base.first);
+
+       while (base) {
+               Object *ob = base->object;
+
+               if (export_object(&m_settings, ob) && object_is_shape(ob)) {
+                       std::string name = get_id_name(ob);
+                       m_xforms[name] = new AbcTransformWriter(ob, m_archive.getTop(), 0, m_trans_sampling_index, m_settings);
+               }
+
+               base = base->next;
+       }
+}
+
+void AbcExporter::exploreTransform(EvaluationContext *eval_ctx, Object *ob, Object *parent, Object *dupliObParent)
+{
+       createTransformWriter(ob, parent, dupliObParent);
+
+       ListBase *lb = object_duplilist(eval_ctx, m_scene, ob);
+
+       if (lb) {
+               DupliObject *link = static_cast<DupliObject *>(lb->first);
+               Object *dupli_ob = NULL;
+               Object *dupli_parent = NULL;
+               
+               while (link) {
+                       if (link->type == OB_DUPLIGROUP) {
+                               dupli_ob = link->ob;
+                               dupli_parent = (dupli_ob->parent) ? dupli_ob->parent : ob;
+
+                               exploreTransform(eval_ctx, dupli_ob, dupli_parent, ob);
+                       }
+
+                       link = link->next;
+               }
+       }
+
+       free_object_duplilist(lb);
+}
+
+void AbcExporter::createTransformWriter(Object *ob, Object *parent, Object *dupliObParent)
+{
+       const std::string name = get_object_dag_path_name(ob, dupliObParent);
+
+       /* check if we have already created a transform writer for this object */
+       if (m_xforms.find(name) != m_xforms.end()){
+               std::cerr << "xform " << name << " already exists\n";
+               return;
+       }
+
+       AbcTransformWriter *parent_xform = NULL;
+
+       if (parent) {
+               const std::string parentname = get_object_dag_path_name(parent, dupliObParent);
+               parent_xform = getXForm(parentname);
+
+               if (!parent_xform) {
+                       if (parent->parent) {
+                               createTransformWriter(parent, parent->parent, dupliObParent);
+                       }
+                       else {
+                               createTransformWriter(parent, dupliObParent, dupliObParent);
+                       }
+
+                       parent_xform = getXForm(parentname);
+               }
+       }
+
+       if (parent_xform) {
+               m_xforms[name] = new AbcTransformWriter(ob, parent_xform->alembicXform(), parent_xform, m_trans_sampling_index, m_settings);
+               m_xforms[name]->setParent(parent);
+       }
+       else {
+               m_xforms[name] = new AbcTransformWriter(ob, m_archive.getTop(), NULL, m_trans_sampling_index, m_settings);
+       }
+}
+
+void AbcExporter::createShapeWriters(EvaluationContext *eval_ctx)
+{
+       Base *base = static_cast<Base *>(m_scene->base.first);
+
+       while (base) {
+               Object *ob = base->object;
+               exploreObject(eval_ctx, ob, NULL);
+
+               base = base->next;
+       }
+}
+
+void AbcExporter::exploreObject(EvaluationContext *eval_ctx, Object *ob, Object *dupliObParent)
+{
+       ListBase *lb = object_duplilist(eval_ctx, m_scene, ob);
+       
+       createShapeWriter(ob, dupliObParent);
+       
+       if (lb) {
+               DupliObject *dupliob = static_cast<DupliObject *>(lb->first);
+
+               while (dupliob) {
+                       if (dupliob->type == OB_DUPLIGROUP) {
+                               exploreObject(eval_ctx, dupliob->ob, ob);
+                       }
+
+                       dupliob = dupliob->next;
+               }
+       }
+
+       free_object_duplilist(lb);
+}
+
+void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent)
+{
+       if (!object_is_shape(ob)) {
+               return;
+       }
+
+       if (!export_object(&m_settings, ob)) {
+               return;
+       }
+
+       std::string name;
+
+       if (m_settings.flatten_hierarchy) {
+               name = get_id_name(ob);
+       }
+       else {
+               name = get_object_dag_path_name(ob, dupliObParent);
+       }
+       
+       AbcTransformWriter *xform = getXForm(name);
+
+       if (!xform) {
+               std::cerr << __func__ << ": xform " << name << " is NULL\n";
+               return;
+       }
+
+       ParticleSystem *psys = static_cast<ParticleSystem *>(ob->particlesystem.first);
+
+       for (; psys; psys = psys->next) {
+               if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) {
+                       continue;
+               }
+
+               if (psys->part->type == PART_HAIR) {
+                       m_settings.export_child_hairs = true;
+                       m_shapes.push_back(new AbcHairWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys));
+               }
+               else if (psys->part->type == PART_EMITTER) {
+                       m_shapes.push_back(new AbcPointsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys));
+               }
+       }
+
+       switch(ob->type) {
+               case OB_MESH:
+               {
+                       Mesh *me = static_cast<Mesh *>(ob->data);
+
+                       if (!me || me->totvert == 0) {
+                               return;
+                       }
+
+                       m_shapes.push_back(new AbcMeshWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings));
+                       break;
+               }
+               case OB_SURF:
+               {
+                       Curve *cu = static_cast<Curve *>(ob->data);
+
+                       if (!cu) {
+                               return;
+                       }
+
+                       m_shapes.push_back(new AbcNurbsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings));
+                       break;
+               }
+               case OB_CURVE:
+               {
+                       Curve *cu = static_cast<Curve *>(ob->data);
+
+                       if (!cu) {
+                               return;
+                       }
+
+                       m_shapes.push_back(new AbcCurveWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings));
+                       break;
+               }
+               case OB_CAMERA:
+               {
+                       Camera *cam = static_cast<Camera *>(ob->data);
+
+                       if (cam->type == CAM_PERSP) {
+                               m_shapes.push_back(new AbcCameraWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings));
+                       }
+
+                       break;
+               }
+       }
+}
+
+AbcTransformWriter *AbcExporter::getXForm(const std::string &name)
+{
+       std::map<std::string, AbcTransformWriter *>::iterator it = m_xforms.find(name);
+
+       if (it == m_xforms.end()) {
+               return NULL;
+       }
+
+       return it->second;
+}
+
+void AbcExporter::setCurrentFrame(Main *bmain, double t)
+{
+       m_scene->r.cfra = std::floor(t);
+       m_scene->r.subframe = t - m_scene->r.cfra;
+       BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, m_scene, m_scene->lay);
+}
diff --git a/source/blender/alembic/intern/abc_exporter.h b/source/blender/alembic/intern/abc_exporter.h
new file mode 100644 (file)
index 0000000..070eb9e
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_EXPORTER_H__
+#define __ABC_EXPORTER_H__
+
+#include <Alembic/Abc/All.h>
+#include <map>
+#include <set>
+#include <vector>
+
+class AbcObjectWriter;
+class AbcTransformWriter;
+
+struct EvaluationContext;
+struct Main;
+struct Object;
+struct Scene;
+
+struct ExportSettings {
+       ExportSettings();
+
+       Scene *scene;
+
+       bool selected_only;
+       bool visible_layers_only;
+       bool renderable_only;
+
+       double frame_start, frame_end;
+       double frame_step_xform;
+       double frame_step_shape;
+       double shutter_open;
+       double shutter_close;
+       float global_scale;
+
+       bool flatten_hierarchy;
+
+       bool export_normals;
+       bool export_uvs;
+       bool export_vcols;
+       bool export_face_sets;
+       bool export_vweigths;
+
+       bool apply_subdiv;
+       bool use_subdiv_schema;
+       bool export_child_hairs;
+       bool export_ogawa;
+       bool pack_uv;
+
+       bool do_convert_axis;
+       float convert_matrix[3][3];
+};
+
+class AbcExporter {
+       ExportSettings &m_settings;
+
+       const char *m_filename;
+
+       Alembic::Abc::OArchive m_archive;
+       unsigned int m_trans_sampling_index, m_shape_sampling_index;
+
+       Scene *m_scene;
+
+       std::map<std::string, AbcTransformWriter *> m_xforms;
+       std::vector<AbcObjectWriter *> m_shapes;
+
+public:
+       AbcExporter(Scene *scene, const char *filename, ExportSettings &settings);
+       ~AbcExporter();
+
+       void operator()(Main *bmain, float &progress, bool &was_canceled);
+
+private:
+       void getShutterSamples(double step, bool time_relative, std::vector<double> &samples);
+
+       Alembic::Abc::TimeSamplingPtr createTimeSampling(double step);
+
+       void getFrameSet(double step, std::set<double> &frames);
+
+       void createTransformWritersHierarchy(EvaluationContext *eval_ctx);
+       void createTransformWritersFlat();
+       void createTransformWriter(Object *ob,  Object *parent, Object *dupliObParent);
+       void exploreTransform(EvaluationContext *eval_ctx, Object *ob, Object *parent, Object *dupliObParent = NULL);
+       void exploreObject(EvaluationContext *eval_ctx, Object *ob, Object *dupliObParent);
+       void createShapeWriters(EvaluationContext *eval_ctx);
+       void createShapeWriter(Object *ob, Object *dupliObParent);
+
+       AbcTransformWriter *getXForm(const std::string &name);
+
+       void setCurrentFrame(Main *bmain, double t);
+};
+
+#endif  /* __ABC_EXPORTER_H__ */
diff --git a/source/blender/alembic/intern/abc_hair.cc b/source/blender/alembic/intern/abc_hair.cc
new file mode 100644 (file)
index 0000000..45bf9b7
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_hair.h"
+
+#include <cstdio>
+
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "DNA_modifier_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math_geom.h"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_object.h"
+#include "BKE_particle.h"
+}
+
+using Alembic::Abc::P3fArraySamplePtr;
+
+using Alembic::AbcGeom::OCurves;
+using Alembic::AbcGeom::OCurvesSchema;
+using Alembic::AbcGeom::ON3fGeomParam;
+using Alembic::AbcGeom::OV2fGeomParam;
+
+/* ************************************************************************** */
+
+AbcHairWriter::AbcHairWriter(Scene *scene,
+                             Object *ob,
+                             AbcTransformWriter *parent,
+                             uint32_t time_sampling,
+                             ExportSettings &settings,
+                             ParticleSystem *psys)
+    : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+       m_psys = psys;
+
+       OCurves curves(parent->alembicXform(), m_name, m_time_sampling);
+       m_schema = curves.getSchema();
+}
+
+void AbcHairWriter::do_write()
+{
+       if (!m_psys) {
+               return;
+       }
+
+       ParticleSystemModifierData *psmd = psys_get_modifier(m_object, m_psys);
+
+       if (!psmd->dm_final) {
+               return;
+       }
+
+       DerivedMesh *dm = mesh_create_derived_view(m_scene, m_object, CD_MASK_MESH);
+       DM_ensure_tessface(dm);
+       DM_update_tessface_data(dm);
+
+       std::vector<Imath::V3f> verts;
+       std::vector<int32_t> hvertices;
+       std::vector<Imath::V2f> uv_values;
+       std::vector<Imath::V3f> norm_values;
+
+       if (m_psys->pathcache) {
+               ParticleSettings *part = m_psys->part;
+
+               write_hair_sample(dm, part, verts, norm_values, uv_values, hvertices);
+
+               if (m_settings.export_child_hairs && m_psys->childcache) {
+                       write_hair_child_sample(dm, part, verts, norm_values, uv_values, hvertices);
+               }
+       }
+
+       dm->release(dm);
+
+       Alembic::Abc::P3fArraySample iPos(verts);
+       m_sample = OCurvesSchema::Sample(iPos, hvertices);
+       m_sample.setBasis(Alembic::AbcGeom::kNoBasis);
+       m_sample.setType(Alembic::AbcGeom::kLinear);
+       m_sample.setWrap(Alembic::AbcGeom::kNonPeriodic);
+
+       if (!uv_values.empty()) {
+               OV2fGeomParam::Sample uv_smp;
+               uv_smp.setVals(uv_values);
+               m_sample.setUVs(uv_smp);
+       }
+
+       if (!norm_values.empty()) {
+               ON3fGeomParam::Sample norm_smp;
+               norm_smp.setVals(norm_values);
+               m_sample.setNormals(norm_smp);
+       }
+
+       m_sample.setSelfBounds(bounds());
+       m_schema.set(m_sample);
+}
+
+void AbcHairWriter::write_hair_sample(DerivedMesh *dm,
+                                      ParticleSettings *part,
+                                      std::vector<Imath::V3f> &verts,
+                                      std::vector<Imath::V3f> &norm_values,
+                                      std::vector<Imath::V2f> &uv_values,
+                                      std::vector<int32_t> &hvertices)
+{
+       /* Get untransformed vertices, there's a xform under the hair. */
+       float inv_mat[4][4];
+       invert_m4_m4_safe(inv_mat, m_object->obmat);
+
+       MTFace *mtface = static_cast<MTFace *>(CustomData_get_layer(&dm->faceData, CD_MTFACE));
+       MFace *mface = dm->getTessFaceArray(dm);
+       MVert *mverts = dm->getVertArray(dm);
+
+       if (!mtface || !mface) {
+               std::fprintf(stderr, "Warning, no UV set found for underlying geometry.\n");
+       }
+
+       ParticleData * pa = m_psys->particles;
+       int k;
+
+       ParticleCacheKey **cache = m_psys->pathcache;
+       ParticleCacheKey *path;
+       float normal[3];
+       Imath::V3f tmp_nor;
+
+       for (int p = 0; p < m_psys->totpart; ++p, ++pa) {
+               /* underlying info for faces-only emission */
+               path = cache[p];
+
+               if (part->from == PART_FROM_FACE && mtface) {
+                       const int num = pa->num_dmcache >= 0 ? pa->num_dmcache : pa->num;
+
+                       if (num < dm->getNumTessFaces(dm)) {
+                               MFace *face = static_cast<MFace *>(dm->getTessFaceData(dm, num, CD_MFACE));
+                               MTFace *tface = mtface + num;
+
+                               if (mface) {
+                                       float r_uv[2], mapfw[4], vec[3];
+
+                                       psys_interpolate_uvs(tface, face->v4, pa->fuv, r_uv);
+                                       uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1]));
+
+                                       psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, normal, NULL, NULL, NULL, NULL);
+
+                                       copy_zup_yup(tmp_nor.getValue(), normal);
+                                       norm_values.push_back(tmp_nor);
+                               }
+                       }
+                       else {
+                               std::fprintf(stderr, "Particle to faces overflow (%d/%d)\n", num, dm->getNumTessFaces(dm));
+                       }
+               }
+               else if (part->from == PART_FROM_VERT && mtface) {
+                       /* vertex id */
+                       const int num = (pa->num_dmcache >= 0) ? pa->num_dmcache : pa->num;
+
+                       /* iterate over all faces to find a corresponding underlying UV */
+                       for (int n = 0; n < dm->getNumTessFaces(dm); ++n) {
+                               MFace *face  = static_cast<MFace *>(dm->getTessFaceData(dm, n, CD_MFACE));
+                               MTFace *tface = mtface + n;
+                               unsigned int vtx[4];
+                               vtx[0] = face->v1;
+                               vtx[1] = face->v2;
+                               vtx[2] = face->v3;
+                               vtx[3] = face->v4;
+                               bool found = false;
+
+                               for (int o = 0; o < 4; ++o) {
+                                       if (o > 2 && vtx[o] == 0) {
+                                               break;
+                                       }
+
+                                       if (vtx[o] == num) {
+                                               uv_values.push_back(Imath::V2f(tface->uv[o][0], tface->uv[o][1]));
+
+                                               MVert *mv = mverts + vtx[o];
+
+                                               normal_short_to_float_v3(normal, mv->no);
+                                               copy_zup_yup(tmp_nor.getValue(), normal);
+                                               norm_values.push_back(tmp_nor);
+                                               found = true;
+                                               break;
+                                       }
+                               }
+
+                               if (found) {
+                                       break;
+                               }
+                       }
+               }
+
+               int steps = path->segments + 1;
+               hvertices.push_back(steps);
+
+               for (k = 0; k < steps; ++k) {
+                       float vert[3];
+                       copy_v3_v3(vert, path->co);
+                       mul_m4_v3(inv_mat, vert);
+
+                       /* Convert Z-up to Y-up. */
+                       verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1]));
+
+                       ++path;
+               }
+       }
+}
+
+void AbcHairWriter::write_hair_child_sample(DerivedMesh *dm,
+                                            ParticleSettings *part,
+                                            std::vector<Imath::V3f> &verts,
+                                            std::vector<Imath::V3f> &norm_values,
+                                            std::vector<Imath::V2f> &uv_values,
+                                            std::vector<int32_t> &hvertices)
+{
+       /* Get untransformed vertices, there's a xform under the hair. */
+       float inv_mat[4][4];
+       invert_m4_m4_safe(inv_mat, m_object->obmat);
+
+       MTFace *mtface = static_cast<MTFace *>(CustomData_get_layer(&dm->faceData, CD_MTFACE));
+       MFace *mface = dm->getTessFaceArray(dm);
+       MVert *mverts = dm->getVertArray(dm);
+
+       if (!mtface || !mface) {
+               std::fprintf(stderr, "Warning, no UV set found for underlying geometry.\n");
+       }
+
+       ParticleCacheKey **cache = m_psys->childcache;
+       ParticleCacheKey *path;
+
+       ChildParticle *pc = m_psys->child;
+
+       for (int p = 0; p < m_psys->totchild; ++p, ++pc) {
+               path = cache[p];
+
+               if (part->from == PART_FROM_FACE) {
+                       const int num = pc->num;
+
+                       MFace *face = static_cast<MFace *>(dm->getTessFaceData(dm, num, CD_MFACE));
+                       MTFace *tface = mtface + num;
+
+                       if (mface && mtface) {
+                               float r_uv[2], tmpnor[3], mapfw[4], vec[3];
+
+                               psys_interpolate_uvs(tface, face->v4, pc->fuv, r_uv);
+                               uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1]));
+
+                               psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, tmpnor, NULL, NULL, NULL, NULL);
+
+                               /* Convert Z-up to Y-up. */
+                               norm_values.push_back(Imath::V3f(tmpnor[0], tmpnor[2], -tmpnor[1]));
+                       }
+               }
+
+               int steps = path->segments + 1;
+               hvertices.push_back(steps);
+
+               for (int k = 0; k < steps; ++k) {
+                       float vert[3];
+                       copy_v3_v3(vert, path->co);
+                       mul_m4_v3(inv_mat, vert);
+
+                       /* Convert Z-up to Y-up. */
+                       verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1]));
+
+                       ++path;
+               }
+       }
+}
diff --git a/source/blender/alembic/intern/abc_hair.h b/source/blender/alembic/intern/abc_hair.h
new file mode 100644 (file)
index 0000000..d132b60
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_HAIR_H__
+#define __ABC_HAIR_H__
+
+#include "abc_object.h"
+
+struct DerivedMesh;
+struct ParticleSettings;
+struct ParticleSystem;
+
+/* ************************************************************************** */
+
+class AbcHairWriter : public AbcObjectWriter {
+       ParticleSystem *m_psys;
+
+       Alembic::AbcGeom::OCurvesSchema m_schema;
+       Alembic::AbcGeom::OCurvesSchema::Sample m_sample;
+
+public:
+       AbcHairWriter(Scene *scene,
+                     Object *ob,
+                     AbcTransformWriter *parent,
+                     uint32_t time_sampling,
+                     ExportSettings &settings,
+                     ParticleSystem *psys);
+
+private:
+       virtual void do_write();
+
+       void write_hair_sample(DerivedMesh *dm,
+                              ParticleSettings *part,
+                              std::vector<Imath::V3f> &verts,
+                              std::vector<Imath::V3f> &norm_values,
+                              std::vector<Imath::V2f> &uv_values,
+                              std::vector<int32_t> &hvertices);
+
+       void write_hair_child_sample(DerivedMesh *dm,
+                                    ParticleSettings *part,
+                                    std::vector<Imath::V3f> &verts,
+                                    std::vector<Imath::V3f> &norm_values,
+                                    std::vector<Imath::V2f> &uv_values,
+                                    std::vector<int32_t> &hvertices);
+};
+
+#endif  /* __ABC_HAIR_H__ */
diff --git a/source/blender/alembic/intern/abc_mesh.cc b/source/blender/alembic/intern/abc_mesh.cc
new file mode 100644 (file)
index 0000000..f1c7b6b
--- /dev/null
@@ -0,0 +1,1213 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_mesh.h"
+
+#include <algorithm>
+
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_fluidsim.h"
+#include "DNA_object_types.h"
+
+#include "BLI_math_geom.h"
+#include "BLI_string.h"
+
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_mesh.h"
+}
+
+using Alembic::Abc::FloatArraySample;
+using Alembic::Abc::ICompoundProperty;
+using Alembic::Abc::Int32ArraySample;
+using Alembic::Abc::Int32ArraySamplePtr;
+using Alembic::Abc::P3fArraySamplePtr;
+using Alembic::Abc::V2fArraySample;
+using Alembic::Abc::V3fArraySample;
+using Alembic::Abc::C4fArraySample;
+
+using Alembic::AbcGeom::IFaceSet;
+using Alembic::AbcGeom::IFaceSetSchema;
+using Alembic::AbcGeom::IObject;
+using Alembic::AbcGeom::IPolyMesh;
+using Alembic::AbcGeom::IPolyMeshSchema;
+using Alembic::AbcGeom::ISampleSelector;
+using Alembic::AbcGeom::ISubD;
+using Alembic::AbcGeom::ISubDSchema;
+using Alembic::AbcGeom::IV2fGeomParam;
+
+using Alembic::AbcGeom::OArrayProperty;
+using Alembic::AbcGeom::OBoolProperty;
+using Alembic::AbcGeom::OC3fArrayProperty;
+using Alembic::AbcGeom::OC3fGeomParam;
+using Alembic::AbcGeom::OC4fGeomParam;
+using Alembic::AbcGeom::OCompoundProperty;
+using Alembic::AbcGeom::OFaceSet;
+using Alembic::AbcGeom::OFaceSetSchema;
+using Alembic::AbcGeom::OFloatGeomParam;
+using Alembic::AbcGeom::OInt32GeomParam;
+using Alembic::AbcGeom::ON3fArrayProperty;
+using Alembic::AbcGeom::ON3fGeomParam;
+using Alembic::AbcGeom::OPolyMesh;
+using Alembic::AbcGeom::OPolyMeshSchema;
+using Alembic::AbcGeom::OSubD;
+using Alembic::AbcGeom::OSubDSchema;
+using Alembic::AbcGeom::OV2fGeomParam;
+using Alembic::AbcGeom::OV3fGeomParam;
+
+using Alembic::AbcGeom::kFacevaryingScope;
+using Alembic::AbcGeom::kVaryingScope;
+using Alembic::AbcGeom::kVertexScope;
+using Alembic::AbcGeom::kWrapExisting;
+using Alembic::AbcGeom::UInt32ArraySample;
+using Alembic::AbcGeom::N3fArraySamplePtr;
+using Alembic::AbcGeom::IN3fGeomParam;
+
+/* ************************************************************************** */
+
+/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */
+
+static void get_vertices(DerivedMesh *dm, std::vector<Imath::V3f> &points)
+{
+       points.clear();
+       points.resize(dm->getNumVerts(dm));
+
+       MVert *verts = dm->getVertArray(dm);
+
+       for (int i = 0, e = dm->getNumVerts(dm); i < e; ++i) {
+               copy_zup_yup(points[i].getValue(), verts[i].co);
+       }
+}
+
+static void get_topology(DerivedMesh *dm,
+                         std::vector<int32_t> &poly_verts,
+                         std::vector<int32_t> &loop_counts,
+                         bool &smooth_normal)
+{
+       const int num_poly = dm->getNumPolys(dm);
+       const int num_loops = dm->getNumLoops(dm);
+       MLoop *mloop = dm->getLoopArray(dm);
+       MPoly *mpoly = dm->getPolyArray(dm);
+
+       poly_verts.clear();
+       loop_counts.clear();
+       poly_verts.reserve(num_loops);
+       loop_counts.reserve(num_poly);
+
+       /* NOTE: data needs to be written in the reverse order. */
+       for (int i = 0; i < num_poly; ++i) {
+               MPoly &poly = mpoly[i];
+               loop_counts.push_back(poly.totloop);
+
+               smooth_normal |= ((poly.flag & ME_SMOOTH) != 0);
+
+               MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1);
+
+               for (int j = 0; j < poly.totloop; ++j, --loop) {
+                       poly_verts.push_back(loop->v);
+               }
+       }
+}
+
+static void get_material_indices(DerivedMesh *dm, std::vector<int32_t> &indices)
+{
+       indices.clear();
+       indices.reserve(dm->getNumTessFaces(dm));
+
+       MPoly *mpolys = dm->getPolyArray(dm);
+
+       for (int i = 1, e = dm->getNumPolys(dm); i < e; ++i) {
+               MPoly *mpoly = &mpolys[i];
+               indices.push_back(mpoly->mat_nr);
+       }
+}
+
+static void get_creases(DerivedMesh *dm,
+                        std::vector<int32_t> &indices,
+                        std::vector<int32_t> &lengths,
+                        std::vector<float> &sharpnesses)
+{
+       const float factor = 1.0f / 255.0f;
+
+       indices.clear();
+       lengths.clear();
+       sharpnesses.clear();
+
+       MEdge *edge = dm->getEdgeArray(dm);
+
+       for (int i = 0, e = dm->getNumEdges(dm); i < e; ++i) {
+               const float sharpness = static_cast<float>(edge[i].crease) * factor;
+
+               if (sharpness != 0.0f) {
+                       indices.push_back(edge[i].v1);
+                       indices.push_back(edge[i].v2);
+                       sharpnesses.push_back(sharpness);
+               }
+       }
+
+       lengths.resize(sharpnesses.size(), 2);
+}
+
+static void get_vertex_normals(DerivedMesh *dm, std::vector<Imath::V3f> &normals)
+{
+       normals.clear();
+       normals.resize(dm->getNumVerts(dm));
+
+       MVert *verts = dm->getVertArray(dm);
+       float no[3];
+
+       for (int i = 0, e = dm->getNumVerts(dm); i < e; ++i) {
+               normal_short_to_float_v3(no, verts[i].no);
+               copy_zup_yup(normals[i].getValue(), no);
+       }
+}
+
+static void get_loop_normals(DerivedMesh *dm, std::vector<Imath::V3f> &normals)
+{
+       MPoly *mpoly = dm->getPolyArray(dm);
+       MPoly *mp = mpoly;
+
+       MLoop *mloop = dm->getLoopArray(dm);
+       MLoop *ml = mloop;
+
+       MVert *verts = dm->getVertArray(dm);
+
+       const float (*lnors)[3] = static_cast<float(*)[3]>(dm->getLoopDataArray(dm, CD_NORMAL));
+
+       normals.clear();
+       normals.resize(dm->getNumLoops(dm));
+
+       unsigned loop_index = 0;
+
+       /* NOTE: data needs to be written in the reverse order. */
+
+       if (lnors) {
+               for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i, ++mp) {
+                       ml = mloop + mp->loopstart + (mp->totloop - 1);
+
+                       for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) {
+                               const int index = ml->v;
+                               copy_zup_yup(normals[loop_index].getValue(), lnors[index]);
+                       }
+               }
+       }
+       else {
+               float no[3];
+
+               for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i, ++mp) {
+                       ml = mloop + mp->loopstart + (mp->totloop - 1);
+
+                       /* Flat shaded, use common normal for all verts. */
+                       if ((mp->flag & ME_SMOOTH) == 0) {
+                               BKE_mesh_calc_poly_normal(mp, ml - (mp->totloop - 1), verts, no);
+
+                               for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) {
+                                       copy_zup_yup(normals[loop_index].getValue(), no);
+                               }
+                       }
+                       else {
+                               /* Smooth shaded, use individual vert normals. */
+                               for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) {
+                                       normal_short_to_float_v3(no, verts[ml->v].no);
+                                       copy_zup_yup(normals[loop_index].getValue(), no);
+                               }
+                       }
+               }
+       }
+}
+
+/* *************** Modifiers *************** */
+
+/* check if the mesh is a subsurf, ignoring disabled modifiers and
+ * displace if it's after subsurf. */
+static ModifierData *get_subsurf_modifier(Scene *scene, Object *ob)
+{
+       ModifierData *md = static_cast<ModifierData *>(ob->modifiers.last);
+
+       for (; md; md = md->prev) {
+               if (!modifier_isEnabled(scene, md, eModifierMode_Render)) {
+                       continue;
+               }
+
+               if (md->type == eModifierType_Subsurf) {
+                       SubsurfModifierData *smd = reinterpret_cast<SubsurfModifierData*>(md);
+
+                       if (smd->subdivType == ME_CC_SUBSURF) {
+                               return md;
+                       }
+               }
+
+               /* mesh is not a subsurf. break */
+               if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) {
+                       return NULL;
+               }
+       }
+
+       return NULL;
+}
+
+static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob)
+{
+       ModifierData *md = modifiers_findByType(ob, eModifierType_Fluidsim);
+
+       if (md && (modifier_isEnabled(scene, md, eModifierMode_Render))) {
+               FluidsimModifierData *fsmd = reinterpret_cast<FluidsimModifierData *>(md);
+
+               if (fsmd->fss && fsmd->fss->type == OB_FLUIDSIM_DOMAIN) {
+                       return md;
+               }
+       }
+
+       return NULL;
+}
+
+/* ************************************************************************** */
+
+AbcMeshWriter::AbcMeshWriter(Scene *scene,
+                             Object *ob,
+                             AbcTransformWriter *parent,
+                             uint32_t time_sampling,
+                             ExportSettings &settings)
+    : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+       m_is_animated = isAnimated();
+       m_subsurf_mod = NULL;
+       m_has_per_face_materials = false;
+       m_is_subd = false;
+
+       /* If the object is static, use the default static time sampling. */
+       if (!m_is_animated) {
+               time_sampling = 0;
+       }
+
+       if (!m_settings.apply_subdiv) {
+               m_subsurf_mod = get_subsurf_modifier(m_scene, m_object);
+               m_is_subd = (m_subsurf_mod != NULL);
+       }
+
+       m_is_liquid = (get_liquid_sim_modifier(m_scene, m_object) != NULL);
+
+       while (parent->alembicXform().getChildHeader(m_name)) {
+               m_name.append("_");
+       }
+
+       if (m_settings.use_subdiv_schema && m_is_subd) {
+               OSubD subd(parent->alembicXform(), m_name, m_time_sampling);
+               m_subdiv_schema = subd.getSchema();
+       }
+       else {
+               OPolyMesh mesh(parent->alembicXform(), m_name, m_time_sampling);
+               m_mesh_schema = mesh.getSchema();
+
+               OCompoundProperty typeContainer = m_mesh_schema.getUserProperties();
+               OBoolProperty type(typeContainer, "meshtype");
+               type.set(m_is_subd);
+       }
+}
+
+AbcMeshWriter::~AbcMeshWriter()
+{
+       if (m_subsurf_mod) {
+               m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary;
+       }
+}
+
+bool AbcMeshWriter::isAnimated() const
+{
+       /* Check if object has shape keys. */
+       Mesh *me = static_cast<Mesh *>(m_object->data);
+
+       if (me->key) {
+               return true;
+       }
+
+       /* Test modifiers. */
+       ModifierData *md = static_cast<ModifierData *>(m_object->modifiers.first);
+
+       while (md) {
+               if (md->type != eModifierType_Subsurf) {
+                       return true;
+               }
+
+               md = md->next;
+       }
+
+       return false;
+}
+
+void AbcMeshWriter::do_write()
+{
+       /* We have already stored a sample for this object. */
+       if (!m_first_frame && !m_is_animated)
+               return;
+
+       DerivedMesh *dm = getFinalMesh();
+
+       try {
+               if (m_settings.use_subdiv_schema && m_subdiv_schema.valid()) {
+                       writeSubD(dm);
+               }
+               else {
+                       writeMesh(dm);
+               }
+
+               freeMesh(dm);
+       }
+       catch (...) {
+               freeMesh(dm);
+               throw;
+       }
+}
+
+void AbcMeshWriter::writeMesh(DerivedMesh *dm)
+{
+       std::vector<Imath::V3f> points, normals;
+       std::vector<int32_t> poly_verts, loop_counts;
+
+       bool smooth_normal = false;
+
+       get_vertices(dm, points);
+       get_topology(dm, poly_verts, loop_counts, smooth_normal);
+
+       if (m_first_frame) {
+               writeCommonData(dm, m_mesh_schema);
+       }
+
+       m_mesh_sample = OPolyMeshSchema::Sample(V3fArraySample(points),
+                                               Int32ArraySample(poly_verts),
+                                               Int32ArraySample(loop_counts));
+
+       UVSample sample;
+       if (m_settings.export_uvs) {
+               const char *name = get_uv_sample(sample, m_custom_data_config, &dm->loopData);
+
+               if (!sample.indices.empty() && !sample.uvs.empty()) {
+                       OV2fGeomParam::Sample uv_sample;
+                       uv_sample.setVals(V2fArraySample(sample.uvs));
+                       uv_sample.setIndices(UInt32ArraySample(sample.indices));
+                       uv_sample.setScope(kFacevaryingScope);
+
+                       m_mesh_schema.setUVSourceName(name);
+                       m_mesh_sample.setUVs(uv_sample);
+               }
+
+               write_custom_data(m_mesh_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPUV);
+       }
+
+       if (m_settings.export_normals) {
+               if (smooth_normal) {
+                       get_loop_normals(dm, normals);
+               }
+               else {
+                       get_vertex_normals(dm, normals);
+               }
+
+               ON3fGeomParam::Sample normals_sample;
+               if (!normals.empty()) {
+                       normals_sample.setScope((smooth_normal) ? kFacevaryingScope : kVertexScope);
+                       normals_sample.setVals(V3fArraySample(normals));
+               }
+
+               m_mesh_sample.setNormals(normals_sample);
+       }
+
+       if (m_is_liquid) {
+               std::vector<Imath::V3f> velocities;
+               getVelocities(dm, velocities);
+
+               m_mesh_sample.setVelocities(V3fArraySample(velocities));
+       }
+
+       m_mesh_sample.setSelfBounds(bounds());
+
+       m_mesh_schema.set(m_mesh_sample);
+
+       writeArbGeoParams(dm);
+}
+
+void AbcMeshWriter::writeSubD(DerivedMesh *dm)
+{
+       std::vector<float> crease_sharpness;
+       std::vector<Imath::V3f> points;
+       std::vector<int32_t> poly_verts, loop_counts;
+       std::vector<int32_t> crease_indices, crease_lengths;
+
+       bool smooth_normal = false;
+
+       get_vertices(dm, points);
+       get_topology(dm, poly_verts, loop_counts, smooth_normal);
+       get_creases(dm, crease_indices, crease_lengths, crease_sharpness);
+
+       if (m_first_frame) {
+               /* create materials' face_sets */
+               writeCommonData(dm, m_subdiv_schema);
+       }
+
+       m_subdiv_sample = OSubDSchema::Sample(V3fArraySample(points),
+                                             Int32ArraySample(poly_verts),
+                                             Int32ArraySample(loop_counts));
+
+       UVSample sample;
+       if (m_settings.export_uvs) {
+               const char *name = get_uv_sample(sample, m_custom_data_config, &dm->loopData);
+
+               if (!sample.indices.empty() && !sample.uvs.empty()) {
+                       OV2fGeomParam::Sample uv_sample;
+                       uv_sample.setVals(V2fArraySample(sample.uvs));
+                       uv_sample.setIndices(UInt32ArraySample(sample.indices));
+                       uv_sample.setScope(kFacevaryingScope);
+
+                       m_subdiv_schema.setUVSourceName(name);
+                       m_subdiv_sample.setUVs(uv_sample);
+               }
+
+               write_custom_data(m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPUV);
+       }
+
+       if (!crease_indices.empty()) {
+               m_subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices));
+               m_subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths));
+               m_subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness));
+       }
+
+       m_subdiv_sample.setSelfBounds(bounds());
+       m_subdiv_schema.set(m_subdiv_sample);
+
+       writeArbGeoParams(dm);
+}
+
+template <typename Schema>
+void AbcMeshWriter::writeCommonData(DerivedMesh *dm, Schema &schema)
+{
+       std::map< std::string, std::vector<int32_t> > geo_groups;
+       getGeoGroups(dm, geo_groups);
+
+       std::map< std::string, std::vector<int32_t>  >::iterator it;
+       for (it = geo_groups.begin(); it != geo_groups.end(); ++it) {
+               OFaceSet face_set = schema.createFaceSet(it->first);
+               OFaceSetSchema::Sample samp;
+               samp.setFaces(Int32ArraySample(it->second));
+               face_set.getSchema().set(samp);
+       }
+}
+
+DerivedMesh *AbcMeshWriter::getFinalMesh()
+{
+       /* We don't want subdivided mesh data */
+       if (m_subsurf_mod) {
+               m_subsurf_mod->mode |= eModifierMode_DisableTemporary;
+       }
+
+       DerivedMesh *dm = mesh_create_derived_render(m_scene, m_object, CD_MASK_MESH);
+
+       if (m_subsurf_mod) {
+               m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary;
+       }
+
+       m_custom_data_config.pack_uvs = m_settings.pack_uv;
+       m_custom_data_config.mpoly = dm->getPolyArray(dm);
+       m_custom_data_config.mloop = dm->getLoopArray(dm);
+       m_custom_data_config.totpoly = dm->getNumPolys(dm);
+       m_custom_data_config.totloop = dm->getNumLoops(dm);
+       m_custom_data_config.totvert = dm->getNumVerts(dm);
+
+       return dm;
+}
+
+void AbcMeshWriter::freeMesh(DerivedMesh *dm)
+{
+       dm->release(dm);
+}
+
+void AbcMeshWriter::writeArbGeoParams(DerivedMesh *dm)
+{
+       if (m_is_liquid) {
+               /* We don't need anything more for liquid meshes. */
+               return;
+       }
+
+       if (m_settings.export_vcols) {
+               if (m_subdiv_schema.valid()) {
+                       write_custom_data(m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPCOL);
+               }
+               else {
+                       write_custom_data(m_mesh_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPCOL);
+               }
+       }
+
+       if (m_first_frame && m_has_per_face_materials) {
+               std::vector<int32_t> material_indices;
+
+               if (m_settings.export_face_sets) {
+                       get_material_indices(dm, material_indices);
+
+                       OFaceSetSchema::Sample samp;
+                       samp.setFaces(Int32ArraySample(material_indices));
+                       m_face_set.getSchema().set(samp);
+               }
+       }
+}
+
+void AbcMeshWriter::getVelocities(DerivedMesh *dm, std::vector<Imath::V3f> &vels)
+{
+       const int totverts = dm->getNumVerts(dm);
+
+       vels.clear();
+       vels.resize(totverts);
+
+       ModifierData *md = get_liquid_sim_modifier(m_scene, m_object);
+       FluidsimModifierData *fmd = reinterpret_cast<FluidsimModifierData *>(md);
+       FluidsimSettings *fss = fmd->fss;
+
+       if (fss->meshVelocities) {
+               float *mesh_vels = reinterpret_cast<float *>(fss->meshVelocities);
+
+               for (int i = 0; i < totverts; ++i) {
+                       copy_zup_yup(vels[i].getValue(), mesh_vels);
+                       mesh_vels += 3;
+               }
+       }
+       else {
+               std::fill(vels.begin(), vels.end(), Imath::V3f(0.0f));
+       }
+}
+
+void AbcMeshWriter::getGeoGroups(
+        DerivedMesh *dm,
+        std::map<std::string, std::vector<int32_t> > &geo_groups)
+{
+       const int num_poly = dm->getNumPolys(dm);
+       MPoly *polygons = dm->getPolyArray(dm);
+
+       for (int i = 0; i < num_poly; ++i) {
+               MPoly &current_poly = polygons[i];
+               short mnr = current_poly.mat_nr;
+
+               Material *mat = give_current_material(m_object, mnr + 1);
+
+               if (!mat) {
+                       continue;
+               }
+
+               std::string name = get_id_name(&mat->id);
+
+               if (geo_groups.find(name) == geo_groups.end()) {
+                       std::vector<int32_t> faceArray;
+                       geo_groups[name] = faceArray;
+               }
+
+               geo_groups[name].push_back(i);
+       }
+
+       if (geo_groups.size() == 0) {
+               Material *mat = give_current_material(m_object, 1);
+
+               std::string name = (mat) ? get_id_name(&mat->id) : "default";
+
+               std::vector<int32_t> faceArray;
+
+               for (int i = 0, e = dm->getNumTessFaces(dm); i < e; ++i) {
+                       faceArray.push_back(i);
+               }
+
+               geo_groups[name] = faceArray;
+       }
+}
+
+/* ************************************************************************** */
+
+/* Some helpers for mesh generation */
+namespace utils {
+
+void mesh_add_verts(Mesh *mesh, size_t len)
+{
+       if (len == 0) {
+               return;
+       }
+
+       const int totvert = mesh->totvert + len;
+       CustomData vdata;
+       CustomData_copy(&mesh->vdata, &vdata, CD_MASK_MESH, CD_DEFAULT, totvert);
+       CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert);
+
+       if (!CustomData_has_layer(&vdata, CD_MVERT)) {
+               CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert);
+       }
+
+       CustomData_free(&mesh->vdata, mesh->totvert);
+       mesh->vdata = vdata;
+       BKE_mesh_update_customdata_pointers(mesh, false);
+
+       mesh->totvert = totvert;
+}
+
+static void mesh_add_mloops(Mesh *mesh, size_t len)
+{
+       if (len == 0) {
+               return;
+       }
+
+       /* new face count */
+       const int totloops = mesh->totloop + len;
+
+       CustomData ldata;
+       CustomData_copy(&mesh->ldata, &ldata, CD_MASK_MESH, CD_DEFAULT, totloops);
+       CustomData_copy_data(&mesh->ldata, &ldata, 0, 0, mesh->totloop);
+
+       if (!CustomData_has_layer(&ldata, CD_MLOOP)) {
+               CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloops);
+       }
+
+       CustomData_free(&mesh->ldata, mesh->totloop);
+       mesh->ldata = ldata;
+       BKE_mesh_update_customdata_pointers(mesh, false);
+
+       mesh->totloop = totloops;
+}
+
+static void mesh_add_mpolygons(Mesh *mesh, size_t len)
+{
+       if (len == 0) {
+               return;
+       }
+
+       const int totpolys = mesh->totpoly + len;
+
+       CustomData pdata;
+       CustomData_copy(&mesh->pdata, &pdata, CD_MASK_MESH, CD_DEFAULT, totpolys);
+       CustomData_copy_data(&mesh->pdata, &pdata, 0, 0, mesh->totpoly);
+
+       if (!CustomData_has_layer(&pdata, CD_MPOLY)) {
+               CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpolys);
+       }
+
+       CustomData_free(&mesh->pdata, mesh->totpoly);
+       mesh->pdata = pdata;
+       BKE_mesh_update_customdata_pointers(mesh, false);
+
+       mesh->totpoly = totpolys;
+}
+
+static void build_mat_map(const Main *bmain, std::map<std::string, Material *> &mat_map)
+{
+       Material *material = static_cast<Material *>(bmain->mat.first);
+
+       for (; material; material = static_cast<Material *>(material->id.next)) {
+               mat_map[material->id.name + 2] = material;
+       }
+}
+
+static void assign_materials(Main *bmain, Object *ob, const std::map<std::string, int> &mat_index_map)
+{
+       bool can_assign = true;
+       std::map<std::string, int>::const_iterator it = mat_index_map.begin();
+
+       int matcount = 0;
+       for (; it != mat_index_map.end(); ++it, ++matcount) {
+               if (!BKE_object_material_slot_add(ob)) {
+                       can_assign = false;
+                       break;
+               }
+       }
+
+       /* TODO(kevin): use global map? */
+       std::map<std::string, Material *> mat_map;
+       build_mat_map(bmain, mat_map);
+
+       std::map<std::string, Material *>::iterator mat_iter;
+
+       if (can_assign) {
+               it = mat_index_map.begin();
+
+               for (; it != mat_index_map.end(); ++it) {
+                       std::string mat_name = it->first;
+                       mat_iter = mat_map.find(mat_name.c_str());
+
+                       Material *assigned_name;
+
+                       if (mat_iter == mat_map.end()) {
+                               assigned_name = BKE_material_add(bmain, mat_name.c_str());
+                               mat_map[mat_name] = assigned_name;
+                       }
+                       else {
+                               assigned_name = mat_iter->second;
+                       }
+
+                       assign_material(ob, assigned_name, it->second, BKE_MAT_ASSIGN_OBJECT);
+               }
+       }
+}
+
+}  /* namespace utils */
+
+/* ************************************************************************** */
+
+using Alembic::AbcGeom::UInt32ArraySamplePtr;
+using Alembic::AbcGeom::V2fArraySamplePtr;
+
+struct AbcMeshData {
+       Int32ArraySamplePtr face_indices;
+       Int32ArraySamplePtr face_counts;
+
+       P3fArraySamplePtr positions;
+
+       N3fArraySamplePtr vertex_normals;
+       N3fArraySamplePtr face_normals;
+
+       V2fArraySamplePtr uvs;
+       UInt32ArraySamplePtr uvs_indices;
+};
+
+static void *add_customdata_cb(void *user_data, const char *name, int data_type)
+{
+       Mesh *mesh = static_cast<Mesh *>(user_data);
+       CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
+       void *cd_ptr = NULL;
+
+       int index = -1;
+       if (cd_data_type == CD_MLOOPUV) {
+               index = ED_mesh_uv_texture_add(mesh, name, true);
+               cd_ptr = CustomData_get_layer(&mesh->ldata, cd_data_type);
+       }
+       else if (cd_data_type == CD_MLOOPCOL) {
+               index = ED_mesh_color_add(mesh, name, true);
+               cd_ptr = CustomData_get_layer(&mesh->ldata, cd_data_type);
+       }
+
+       if (index == -1) {
+               return NULL;
+       }
+
+       return cd_ptr;
+}
+
+CDStreamConfig create_config(Mesh *mesh)
+{
+       CDStreamConfig config;
+
+       config.mvert = mesh->mvert;
+       config.mpoly = mesh->mpoly;
+       config.mloop = mesh->mloop;
+       config.totpoly = mesh->totpoly;
+       config.totloop = mesh->totloop;
+       config.user_data = mesh;
+       config.loopdata = &mesh->ldata;
+       config.add_customdata_cb = add_customdata_cb;
+
+       return config;
+}
+
+static void read_mverts(CDStreamConfig &config, const AbcMeshData &mesh_data)
+{
+       MVert *mverts = config.mvert;
+       const P3fArraySamplePtr &positions = mesh_data.positions;
+       const N3fArraySamplePtr &normals = mesh_data.vertex_normals;
+
+       read_mverts(mverts, positions, normals);
+}
+
+void read_mverts(MVert *mverts, const P3fArraySamplePtr &positions, const N3fArraySamplePtr &normals)
+{
+       for (int i = 0; i < positions->size(); ++i) {
+               MVert &mvert = mverts[i];
+               Imath::V3f pos_in = (*positions)[i];
+
+               copy_yup_zup(mvert.co, pos_in.getValue());
+
+               mvert.bweight = 0;
+
+               if (normals) {
+                       Imath::V3f nor_in = (*normals)[i];
+
+                       short no[3];
+                       normal_float_to_short_v3(no, nor_in.getValue());
+
+                       copy_yup_zup(mvert.no, no);
+               }
+       }
+}
+
+static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data)
+{
+       MPoly *mpolys = config.mpoly;
+       MLoop *mloops = config.mloop;
+       MLoopUV *mloopuvs = config.mloopuv;
+
+       const Int32ArraySamplePtr &face_indices = mesh_data.face_indices;
+       const Int32ArraySamplePtr &face_counts = mesh_data.face_counts;
+       const V2fArraySamplePtr &uvs = mesh_data.uvs;
+       const UInt32ArraySamplePtr &uvs_indices = mesh_data.uvs_indices;
+       const N3fArraySamplePtr &normals = mesh_data.face_normals;
+
+       const bool do_uvs = (mloopuvs && uvs && uvs_indices) && (uvs_indices->size() == face_indices->size());
+       unsigned int loop_index = 0;
+       unsigned int rev_loop_index = 0;
+       unsigned int uv_index = 0;
+
+       for (int i = 0; i < face_counts->size(); ++i) {
+               const int face_size = (*face_counts)[i];
+
+               MPoly &poly = mpolys[i];
+               poly.loopstart = loop_index;
+               poly.totloop = face_size;
+
+               if (normals != NULL) {
+                       poly.flag |= ME_SMOOTH;
+               }
+
+               /* NOTE: Alembic data is stored in the reverse order. */
+               rev_loop_index = loop_index + (face_size - 1);
+
+               for (int f = 0; f < face_size; ++f, ++loop_index, --rev_loop_index) {
+                       MLoop &loop = mloops[rev_loop_index];
+                       loop.v = (*face_indices)[loop_index];
+
+                       if (do_uvs) {
+                               MLoopUV &loopuv = mloopuvs[rev_loop_index];
+
+                               uv_index = (*uvs_indices)[loop_index];
+                               loopuv.uv[0] = (*uvs)[uv_index][0];
+                               loopuv.uv[1] = (*uvs)[uv_index][1];
+                       }
+               }
+       }
+}
+
+ABC_INLINE void read_uvs_params(CDStreamConfig &config,
+                                AbcMeshData &abc_data,
+                                const IV2fGeomParam &uv,
+                                const ISampleSelector &selector)
+{
+       if (!uv.valid()) {
+               return;
+       }
+
+       IV2fGeomParam::Sample uvsamp;
+       uv.getIndexed(uvsamp, selector);
+
+       abc_data.uvs = uvsamp.getVals();
+       abc_data.uvs_indices = uvsamp.getIndices();
+
+       if (abc_data.uvs_indices->size() == config.totloop) {
+               std::string name = Alembic::Abc::GetSourceName(uv.getMetaData());
+
+               /* According to the convention, primary UVs should have had their name
+                * set using Alembic::Abc::SetSourceName, but you can't expect everyone
+                * to follow it! :) */
+               if (name.empty()) {
+                       name = uv.getName();
+               }
+
+               void *cd_ptr = config.add_customdata_cb(config.user_data, name.c_str(), CD_MLOOPUV);
+               config.mloopuv = static_cast<MLoopUV *>(cd_ptr);
+       }
+}
+
+/* TODO(kevin): normals from Alembic files are not read in anymore, this is due
+ * to the fact that there are many issues that are not so easy to solve, mainly
+ * regarding the way normals are handled in Blender (MPoly.flag vs loop normals).
+ */
+ABC_INLINE void read_normals_params(AbcMeshData &abc_data,
+                                    const IN3fGeomParam &normals,
+                                    const ISampleSelector &selector)
+{
+       if (!normals.valid()) {
+               return;
+       }
+
+       IN3fGeomParam::Sample normsamp = normals.getExpandedValue(selector);
+
+       if (normals.getScope() == kFacevaryingScope) {
+               abc_data.face_normals = normsamp.getVals();
+       }
+       else if ((normals.getScope() == kVertexScope) || (normals.getScope() == kVaryingScope)) {
+               abc_data.vertex_normals = N3fArraySamplePtr();
+       }
+}
+
+/* ************************************************************************** */
+
+AbcMeshReader::AbcMeshReader(const IObject &object, ImportSettings &settings)
+    : AbcObjectReader(object, settings)
+{
+       m_settings->read_flag |= MOD_MESHSEQ_READ_ALL;
+
+       IPolyMesh ipoly_mesh(m_iobject, kWrapExisting);
+       m_schema = ipoly_mesh.getSchema();
+       get_min_max_time(m_schema, m_min_time, m_max_time);
+}
+
+bool AbcMeshReader::valid() const
+{
+       return m_schema.valid();
+}
+
+void AbcMeshReader::readObjectData(Main *bmain, float time)
+{
+       Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
+
+       m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str());
+       m_object->data = mesh;
+
+       const ISampleSelector sample_sel(time);
+       const IPolyMeshSchema::Sample sample = m_schema.getValue(sample_sel);
+
+       const P3fArraySamplePtr &positions = sample.getPositions();
+       const Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
+    const Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
+
+       utils::mesh_add_verts(mesh, positions->size());
+       utils::mesh_add_mpolygons(mesh, face_counts->size());
+       utils::mesh_add_mloops(mesh, face_indices->size());
+
+       m_mesh_data = create_config(mesh);
+
+       bool has_smooth_normals = false;
+       read_mesh_sample(m_settings, m_schema, sample_sel, m_mesh_data, has_smooth_normals);
+
+       BKE_mesh_calc_normals(mesh);
+       BKE_mesh_calc_edges(mesh, false, false);
+
+       if (m_settings->validate_meshes) {
+               BKE_mesh_validate(mesh, false, false);
+       }
+
+       readFaceSetsSample(bmain, mesh, 0, sample_sel);
+
+       if (has_animations(m_schema, m_settings)) {
+               addCacheModifier();
+       }
+}
+
+void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, size_t poly_start,
+                                       const ISampleSelector &sample_sel)
+{
+       std::vector<std::string> face_sets;
+       m_schema.getFaceSetNames(face_sets);
+
+       if (face_sets.empty()) {
+               return;
+       }
+
+       std::map<std::string, int> mat_map;
+       int current_mat = 0;
+
+       for (int i = 0; i < face_sets.size(); ++i) {
+               const std::string &grp_name = face_sets[i];
+
+               if (mat_map.find(grp_name) == mat_map.end()) {
+                       mat_map[grp_name] = 1 + current_mat++;
+               }
+
+               const int assigned_mat = mat_map[grp_name];
+
+               const IFaceSet faceset = m_schema.getFaceSet(grp_name);
+
+               if (!faceset.valid()) {
+                       continue;
+               }
+
+               const IFaceSetSchema face_schem = faceset.getSchema();
+               const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel);
+               const Int32ArraySamplePtr group_faces = face_sample.getFaces();
+               const size_t num_group_faces = group_faces->size();
+
+               for (size_t l = 0; l < num_group_faces; l++) {
+                       size_t pos = (*group_faces)[l] + poly_start;
+
+                       if (pos >= mesh->totpoly) {
+                               std::cerr << "Faceset overflow on " << faceset.getName() << '\n';
+                               break;
+                       }
+
+                       MPoly &poly = mesh->mpoly[pos];
+                       poly.mat_nr = assigned_mat - 1;
+               }
+       }
+
+       utils::assign_materials(bmain, m_object, mat_map);
+}
+
+void read_mesh_sample(ImportSettings *settings,
+                      const IPolyMeshSchema &schema,
+                      const ISampleSelector &selector,
+                      CDStreamConfig &config,
+                      bool &do_normals)
+{
+       const IPolyMeshSchema::Sample sample = schema.getValue(selector);
+
+       AbcMeshData abc_mesh_data;
+       abc_mesh_data.face_counts = sample.getFaceCounts();
+       abc_mesh_data.face_indices = sample.getFaceIndices();
+       abc_mesh_data.positions = sample.getPositions();
+
+       read_normals_params(abc_mesh_data, schema.getNormalsParam(), selector);
+
+       do_normals = (abc_mesh_data.face_normals != NULL);
+
+       if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
+               read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
+       }
+
+       if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
+               read_mverts(config, abc_mesh_data);
+       }
+
+       if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
+               read_mpolys(config, abc_mesh_data);
+       }
+
+       if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
+               read_custom_data(schema.getArbGeomParams(), config, selector);
+       }
+
+       /* TODO: face sets */
+}
+
+/* ************************************************************************** */
+
+ABC_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2)
+{
+       for (int i = 0, e = totedge; i < e; ++i) {
+               MEdge &edge = edges[i];
+
+               if (edge.v1 == v1 && edge.v2 == v2) {
+                       return &edge;
+               }
+       }
+
+       return NULL;
+}
+
+AbcSubDReader::AbcSubDReader(const IObject &object, ImportSettings &settings)
+    : AbcObjectReader(object, settings)
+{
+       m_settings->read_flag |= MOD_MESHSEQ_READ_ALL;
+
+       ISubD isubd_mesh(m_iobject, kWrapExisting);
+       m_schema = isubd_mesh.getSchema();
+       get_min_max_time(m_schema, m_min_time, m_max_time);
+}
+
+bool AbcSubDReader::valid() const
+{
+       return m_schema.valid();
+}
+
+void AbcSubDReader::readObjectData(Main *bmain, float time)
+{
+       Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
+
+       m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str());
+       m_object->data = mesh;
+
+       const ISampleSelector sample_sel(time);
+       const ISubDSchema::Sample sample = m_schema.getValue(sample_sel);
+
+       const P3fArraySamplePtr &positions = sample.getPositions();
+       const Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
+    const Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
+
+       utils::mesh_add_verts(mesh, positions->size());
+       utils::mesh_add_mpolygons(mesh, face_counts->size());
+       utils::mesh_add_mloops(mesh, face_indices->size());
+
+       m_mesh_data = create_config(mesh);
+
+       read_subd_sample(m_settings, m_schema, sample_sel, m_mesh_data);
+
+       Int32ArraySamplePtr indices = sample.getCreaseIndices();
+       Alembic::Abc::FloatArraySamplePtr sharpnesses = sample.getCreaseSharpnesses();
+
+       MEdge *edges = mesh->medge;
+
+       if (indices && sharpnesses) {
+               for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, ++s) {
+                       MEdge *edge = find_edge(edges, mesh->totedge, (*indices)[i], (*indices)[i + 1]);
+
+                       if (edge) {
+                               edge->crease = FTOCHAR((*sharpnesses)[s]);
+                       }
+               }
+
+               mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE;
+       }
+
+       BKE_mesh_calc_normals(mesh);
+       BKE_mesh_calc_edges(mesh, false, false);
+
+       if (m_settings->validate_meshes) {
+               BKE_mesh_validate(mesh, false, false);
+       }
+
+       if (has_animations(m_schema, m_settings)) {
+               addCacheModifier();
+       }
+}
+
+void read_subd_sample(ImportSettings *settings,
+                      const ISubDSchema &schema,
+                      const ISampleSelector &selector,
+                      CDStreamConfig &config)
+{
+       const ISubDSchema::Sample sample = schema.getValue(selector);
+
+       AbcMeshData abc_mesh_data;
+       abc_mesh_data.face_counts = sample.getFaceCounts();
+       abc_mesh_data.face_indices = sample.getFaceIndices();
+       abc_mesh_data.vertex_normals = N3fArraySamplePtr();
+       abc_mesh_data.face_normals = N3fArraySamplePtr();
+       abc_mesh_data.positions = sample.getPositions();
+
+       if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) {
+               read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector);
+       }
+
+       if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
+               read_mverts(config, abc_mesh_data);
+       }
+
+       if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) {
+               read_mpolys(config, abc_mesh_data);
+       }
+
+       if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) {
+               read_custom_data(schema.getArbGeomParams(), config, selector);
+       }
+
+       /* TODO: face sets */
+}
diff --git a/source/blender/alembic/intern/abc_mesh.h b/source/blender/alembic/intern/abc_mesh.h
new file mode 100644 (file)
index 0000000..9dc222e
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_MESH_H__
+#define __ABC_MESH_H__
+
+#include "abc_customdata.h"
+#include "abc_object.h"
+
+struct DerivedMesh;
+struct Mesh;
+struct ModifierData;
+
+/* ************************************************************************** */
+
+class AbcMeshWriter : public AbcObjectWriter {
+       Alembic::AbcGeom::OPolyMeshSchema m_mesh_schema;
+       Alembic::AbcGeom::OPolyMeshSchema::Sample m_mesh_sample;
+
+       Alembic::AbcGeom::OSubDSchema m_subdiv_schema;
+       Alembic::AbcGeom::OSubDSchema::Sample m_subdiv_sample;
+
+       bool m_has_per_face_materials;
+       Alembic::AbcGeom::OFaceSet m_face_set;
+       Alembic::Abc::OArrayProperty m_mat_indices;
+
+       bool m_is_animated;
+       ModifierData *m_subsurf_mod;
+
+       CDStreamConfig m_custom_data_config;
+
+       bool m_is_liquid;
+       bool m_is_subd;
+
+public:
+       AbcMeshWriter(Scene *scene,
+                     Object *ob,
+                     AbcTransformWriter *parent,
+                     uint32_t time_sampling,
+                     ExportSettings &settings);
+
+       ~AbcMeshWriter();
+
+private:
+       virtual void do_write();
+
+       bool isAnimated() const;
+
+       void writeMesh(DerivedMesh *dm);
+       void writeSubD(DerivedMesh *dm);
+
+       void getMeshInfo(DerivedMesh *dm, std::vector<float> &points,
+                        std::vector<int32_t> &facePoints,
+                        std::vector<int32_t> &faceCounts,
+                        std::vector<int32_t> &creaseIndices,
+                        std::vector<int32_t> &creaseLengths,
+                        std::vector<float> &creaseSharpness);
+
+       DerivedMesh *getFinalMesh();
+       void freeMesh(DerivedMesh *dm);
+
+       void getMaterialIndices(DerivedMesh *dm, std::vector<int32_t> &indices);
+
+       void writeArbGeoParams(DerivedMesh *dm);
+       void getGeoGroups(DerivedMesh *dm, std::map<std::string, std::vector<int32_t> > &geoGroups);
+       
+       /* fluid surfaces support */
+       void getVelocities(DerivedMesh *dm, std::vector<Imath::V3f> &vels);
+
+       template <typename Schema>
+       void writeCommonData(DerivedMesh *dm, Schema &schema);
+};
+
+/* ************************************************************************** */
+
+class AbcMeshReader : public AbcObjectReader {
+       Alembic::AbcGeom::IPolyMeshSchema m_schema;
+
+       CDStreamConfig m_mesh_data;
+
+public:
+       AbcMeshReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+       bool valid() const;
+
+       void readObjectData(Main *bmain, float time);
+
+private:
+       void readFaceSetsSample(Main *bmain, Mesh *mesh, size_t poly_start,
+                               const Alembic::AbcGeom::ISampleSelector &sample_sel);
+};
+
+void read_mesh_sample(ImportSettings *settings,
+                      const Alembic::AbcGeom::IPolyMeshSchema &schema,
+                      const Alembic::AbcGeom::ISampleSelector &selector,
+                      CDStreamConfig &config,
+                      bool &do_normals);
+
+/* ************************************************************************** */
+
+class AbcSubDReader : public AbcObjectReader {
+       Alembic::AbcGeom::ISubDSchema m_schema;
+
+       CDStreamConfig m_mesh_data;
+
+public:
+       AbcSubDReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+       bool valid() const;
+
+       void readObjectData(Main *bmain, float time);
+};
+
+void read_subd_sample(ImportSettings *settings,
+                      const Alembic::AbcGeom::ISubDSchema &schema,
+                      const Alembic::AbcGeom::ISampleSelector &selector,
+                      CDStreamConfig &config);
+
+/* ************************************************************************** */
+
+namespace utils {
+
+void mesh_add_verts(struct Mesh *mesh, size_t len);
+
+}
+
+void read_mverts(MVert *mverts,
+                 const Alembic::AbcGeom::P3fArraySamplePtr &positions,
+                 const Alembic::AbcGeom::N3fArraySamplePtr &normals);
+
+CDStreamConfig create_config(Mesh *mesh);
+
+#endif  /* __ABC_MESH_H__ */
diff --git a/source/blender/alembic/intern/abc_nurbs.cc b/source/blender/alembic/intern/abc_nurbs.cc
new file mode 100644 (file)
index 0000000..a3c18ad
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_nurbs.h"
+
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+
+#include "BKE_curve.h"
+#include "BKE_object.h"
+}
+
+using Alembic::AbcGeom::bool_t;
+using Alembic::AbcGeom::FloatArraySample;
+using Alembic::AbcGeom::FloatArraySamplePtr;
+using Alembic::AbcGeom::MetaData;
+using Alembic::AbcGeom::P3fArraySamplePtr;
+using Alembic::AbcGeom::kWrapExisting;
+
+using Alembic::AbcGeom::IBoolProperty;
+using Alembic::AbcGeom::ICompoundProperty;
+using Alembic::AbcGeom::INuPatch;
+using Alembic::AbcGeom::INuPatchSchema;
+using Alembic::AbcGeom::IObject;
+using Alembic::AbcGeom::ISampleSelector;
+
+using Alembic::AbcGeom::OBoolProperty;
+using Alembic::AbcGeom::OCompoundProperty;
+using Alembic::AbcGeom::ONuPatch;
+using Alembic::AbcGeom::ONuPatchSchema;
+
+/* ************************************************************************** */
+
+AbcNurbsWriter::AbcNurbsWriter(Scene *scene,
+                               Object *ob,
+                               AbcTransformWriter *parent,
+                               uint32_t time_sampling,
+                               ExportSettings &settings)
+    : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+       m_is_animated = isAnimated();
+
+       /* if the object is static, use the default static time sampling */
+       if (!m_is_animated) {
+               m_time_sampling = 0;
+       }
+
+       Curve *curve = static_cast<Curve *>(m_object->data);
+       size_t numNurbs = BLI_listbase_count(&curve->nurb);
+
+       for (size_t i = 0; i < numNurbs; ++i) {
+               std::stringstream str;
+               str << m_name << '_' << i;
+
+               while (parent->alembicXform().getChildHeader(str.str())) {
+                       str << "_";
+               }
+
+               ONuPatch nurbs(parent->alembicXform(), str.str().c_str(), m_time_sampling);
+               m_nurbs_schema.push_back(nurbs.getSchema());
+       }
+}
+
+bool AbcNurbsWriter::isAnimated() const
+{
+       /* check if object has shape keys */
+       Curve *cu = static_cast<Curve *>(m_object->data);
+       return (cu->key != NULL);
+}
+
+static void get_knots(std::vector<float> &knots, const int num_knots, float *nu_knots)
+{
+       if (num_knots <= 1) {
+               return;
+       }
+
+       /* Add an extra knot at the beggining and end of the array since most apps
+        * require/expect them. */
+       knots.reserve(num_knots + 2);
+
+       knots.push_back(0.0f);
+
+       for (int i = 0; i < num_knots; ++i) {
+               knots.push_back(nu_knots[i]);
+       }
+
+       knots[0] = 2.0f * knots[1] - knots[2];
+       knots.push_back(2.0f * knots[num_knots] - knots[num_knots - 1]);
+}
+
+void AbcNurbsWriter::do_write()
+{
+       /* we have already stored a sample for this object. */
+       if (!m_first_frame && !m_is_animated) {
+               return;
+       }
+
+       if (!ELEM(m_object->type, OB_SURF, OB_CURVE)) {
+               return;
+       }
+
+       Curve *curve = static_cast<Curve *>(m_object->data);
+       ListBase *nulb;
+
+       if (m_object->curve_cache->deformed_nurbs.first != NULL) {
+               nulb = &m_object->curve_cache->deformed_nurbs;
+       }
+       else {
+               nulb = BKE_curve_nurbs_get(curve);
+       }
+
+       size_t count = 0;
+       for (Nurb *nu = static_cast<Nurb *>(nulb->first); nu; nu = nu->next, ++count) {
+               std::vector<float> knotsU;
+               get_knots(knotsU, KNOTSU(nu), nu->knotsu);
+
+               std::vector<float> knotsV;
+               get_knots(knotsV, KNOTSV(nu), nu->knotsv);
+
+               const int size = nu->pntsu * nu->pntsv;
+               std::vector<Imath::V3f> positions(size);
+               std::vector<float> weights(size);
+
+               const BPoint *bp = nu->bp;
+
+               for (int i = 0; i < size; ++i, ++bp) {
+                       copy_zup_yup(positions[i].getValue(), bp->vec);
+                       weights[i] = bp->vec[3];
+               }
+
+               ONuPatchSchema::Sample sample;
+               sample.setUOrder(nu->orderu + 1);
+               sample.setVOrder(nu->orderv + 1);
+               sample.setPositions(positions);
+               sample.setPositionWeights(weights);
+               sample.setUKnot(FloatArraySample(knotsU));
+               sample.setVKnot(FloatArraySample(knotsV));
+               sample.setNu(nu->pntsu);
+               sample.setNv(nu->pntsv);
+
+               /* TODO(kevin): to accomodate other software we should duplicate control
+                * points to indicate that a NURBS is cyclic. */
+               OCompoundProperty user_props = m_nurbs_schema[count].getUserProperties();
+
+               if ((nu->flagu & CU_NURB_ENDPOINT) != 0) {
+                       OBoolProperty prop(user_props, "endpoint_u");
+                       prop.set(true);
+               }
+
+               if ((nu->flagv & CU_NURB_ENDPOINT) != 0) {
+                       OBoolProperty prop(user_props, "endpoint_v");
+                       prop.set(true);
+               }
+
+               if ((nu->flagu & CU_NURB_CYCLIC) != 0) {
+                       OBoolProperty prop(user_props, "cyclic_u");
+                       prop.set(true);
+               }
+
+               if ((nu->flagv & CU_NURB_CYCLIC) != 0) {
+                       OBoolProperty prop(user_props, "cyclic_v");
+                       prop.set(true);
+               }
+
+               m_nurbs_schema[count].set(sample);
+       }
+}
+
+/* ************************************************************************** */
+
+AbcNurbsReader::AbcNurbsReader(const IObject &object, ImportSettings &settings)
+    : AbcObjectReader(object, settings)
+{
+       getNurbsPatches(m_iobject);
+       get_min_max_time(m_schemas[0].first, m_min_time, m_max_time);
+}
+
+bool AbcNurbsReader::valid() const
+{
+       if (m_schemas.empty()) {
+               return false;
+       }
+
+       std::vector< std::pair<INuPatchSchema, IObject> >::const_iterator it;
+       for (it = m_schemas.begin(); it != m_schemas.end(); ++it) {
+               const INuPatchSchema &schema = it->first;
+
+               if (!schema.valid()) {
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots)
+{
+       if (!knots || knots->size() == 0) {
+               return false;
+       }
+
+       /* Skip first and last knots, as they are used for padding. */
+       const size_t num_knots = knots->size() - 2;
+       nu_knots = static_cast<float *>(MEM_callocN(num_knots * sizeof(float), "abc_setsplineknotsu"));
+
+       for (size_t i = 0; i < num_knots; ++i) {
+               nu_knots[i] = (*knots)[i + 1];
+       }
+
+       return true;
+}
+
+void AbcNurbsReader::readObjectData(Main *bmain, float time)
+{
+       Curve *cu = static_cast<Curve *>(BKE_curve_add(bmain, "abc_curve", OB_SURF));
+       cu->actvert = CU_ACT_NONE;
+
+       std::vector< std::pair<INuPatchSchema, IObject> >::iterator it;
+
+       for (it = m_schemas.begin(); it != m_schemas.end(); ++it) {
+               Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), "abc_getnurb"));
+               nu->flag  = CU_SMOOTH;
+               nu->type = CU_NURBS;
+               nu->resolu = cu->resolu;
+               nu->resolv = cu->resolv;
+
+               const ISampleSelector sample_sel(time);
+               const INuPatchSchema &schema = it->first;
+               const INuPatchSchema::Sample smp = schema.getValue(sample_sel);
+
+               nu->orderu = smp.getUOrder() - 1;
+               nu->orderv = smp.getVOrder() - 1;
+               nu->pntsu = smp.getNumU();
+               nu->pntsv = smp.getNumV();
+
+               /* Read positions and weights. */
+
+               const P3fArraySamplePtr positions = smp.getPositions();
+               const FloatArraySamplePtr weights = smp.getPositionWeights();
+
+               const size_t num_points = positions->size();
+
+               nu->bp = static_cast<BPoint *>(MEM_callocN(num_points * sizeof(BPoint), "abc_setsplinetype"));
+
+               BPoint *bp = nu->bp;
+               float posw_in = 1.0f;
+
+               for (int i = 0; i < num_points; ++i, ++bp) {
+                       const Imath::V3f &pos_in = (*positions)[i];
+
+                       if (weights) {
+                               posw_in = (*weights)[i];
+                       }
+
+                       copy_yup_zup(bp->vec, pos_in.getValue());
+                       bp->vec[3] = posw_in;
+                       bp->f1 = SELECT;
+                       bp->radius = 1.0f;
+                       bp->weight = 1.0f;
+               }
+
+               /* Read knots. */
+
+               if (!set_knots(smp.getUKnot(), nu->knotsu)) {
+                       BKE_nurb_knot_calc_u(nu);
+               }
+
+               if (!set_knots(smp.getVKnot(), nu->knotsv)) {
+                       BKE_nurb_knot_calc_v(nu);
+               }
+
+               /* Read flags. */
+
+               ICompoundProperty user_props = schema.getUserProperties();
+
+               if (has_property(user_props, "enpoint_u")) {
+                       nu->flagu |= CU_NURB_ENDPOINT;
+               }
+
+               if (has_property(user_props, "enpoint_v")) {
+                       nu->flagv |= CU_NURB_ENDPOINT;
+               }
+
+               if (has_property(user_props, "cyclic_u")) {
+                       nu->flagu |= CU_NURB_CYCLIC;
+               }
+
+               if (has_property(user_props, "cyclic_v")) {
+                       nu->flagv |= CU_NURB_CYCLIC;
+               }
+
+               BLI_addtail(BKE_curve_nurbs_get(cu), nu);
+       }
+
+       BLI_strncpy(cu->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1);
+
+       m_object = BKE_object_add_only_object(bmain, OB_SURF, m_object_name.c_str());
+       m_object->data = cu;
+}
+
+void AbcNurbsReader::getNurbsPatches(const IObject &obj)
+{
+       if (!obj.valid()) {
+               return;
+       }
+
+       const int num_children = obj.getNumChildren();
+
+       if (num_children == 0) {
+               INuPatch abc_nurb(obj, kWrapExisting);
+               INuPatchSchema schem = abc_nurb.getSchema();
+               m_schemas.push_back(std::pair<INuPatchSchema, IObject>(schem, obj));
+               return;
+       }
+
+       for (int i = 0; i < num_children; ++i) {
+               bool ok = true;
+               IObject child(obj, obj.getChildHeader(i).getName());
+
+               if (!m_name.empty() && child.valid() && !begins_with(child.getFullName(), m_name)) {
+                       ok = false;
+               }
+
+               if (!child.valid()) {
+                       continue;
+               }
+
+               const MetaData &md = child.getMetaData();
+
+               if (INuPatch::matches(md) && ok) {
+                       INuPatch abc_nurb(child, kWrapExisting);
+                       INuPatchSchema schem = abc_nurb.getSchema();
+                       m_schemas.push_back(std::pair<INuPatchSchema, IObject>(schem, child));
+               }
+
+               getNurbsPatches(child);
+       }
+}
diff --git a/source/blender/alembic/intern/abc_nurbs.h b/source/blender/alembic/intern/abc_nurbs.h
new file mode 100644 (file)
index 0000000..1b2e7a8
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_NURBS_H__
+#define __ABC_NURBS_H__
+
+#include "abc_object.h"
+
+/* ************************************************************************** */
+
+class AbcNurbsWriter : public AbcObjectWriter {
+       std::vector<Alembic::AbcGeom::ONuPatchSchema> m_nurbs_schema;
+       bool m_is_animated;
+
+public:
+       AbcNurbsWriter(Scene *scene,
+                      Object *ob,
+                      AbcTransformWriter *parent,
+                      uint32_t time_sampling,
+                      ExportSettings &settings);
+
+private:
+       virtual void do_write();
+
+       bool isAnimated() const;
+};
+
+/* ************************************************************************** */
+
+class AbcNurbsReader : public AbcObjectReader {
+       std::vector< std::pair<Alembic::AbcGeom::INuPatchSchema, Alembic::Abc::IObject> > m_schemas;
+
+public:
+       AbcNurbsReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+       bool valid() const;
+
+       void readObjectData(Main *bmain, float time);
+
+private:
+       void getNurbsPatches(const Alembic::Abc::IObject &obj);
+};
+
+#endif  /* __ABC_NURBS_H__ */
diff --git a/source/blender/alembic/intern/abc_object.cc b/source/blender/alembic/intern/abc_object.cc
new file mode 100644 (file)
index 0000000..5b7b85f
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_object.h"
+
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_cachefile_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_space_types.h"  /* for FILE_MAX */
+
+#include "BKE_constraint.h"
+#include "BKE_depsgraph.h"
+#include "BKE_idprop.h"
+#include "BKE_library.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+}
+
+using Alembic::AbcGeom::IObject;
+using Alembic::AbcGeom::IXform;
+using Alembic::AbcGeom::IXformSchema;
+
+using Alembic::AbcGeom::OCompoundProperty;
+using Alembic::AbcGeom::ODoubleArrayProperty;
+using Alembic::AbcGeom::ODoubleProperty;
+using Alembic::AbcGeom::OFloatArrayProperty;
+using Alembic::AbcGeom::OFloatProperty;
+using Alembic::AbcGeom::OInt32ArrayProperty;
+using Alembic::AbcGeom::OInt32Property;
+using Alembic::AbcGeom::OStringArrayProperty;
+using Alembic::AbcGeom::OStringProperty;
+
+/* ************************************************************************** */
+
+AbcObjectWriter::AbcObjectWriter(Scene *scene,
+                                 Object *ob,
+                                 uint32_t time_sampling,
+                                 ExportSettings &settings,
+                                 AbcObjectWriter *parent)
+    : m_object(ob)
+    , m_settings(settings)
+    , m_scene(scene)
+    , m_time_sampling(time_sampling)
+    , m_first_frame(true)
+{
+       m_name = get_id_name(m_object) + "Shape";
+
+       if (parent) {
+               parent->addChild(this);
+       }
+}
+
+AbcObjectWriter::~AbcObjectWriter()
+{}
+
+void AbcObjectWriter::addChild(AbcObjectWriter *child)
+{
+       m_children.push_back(child);
+}
+
+Imath::Box3d AbcObjectWriter::bounds()
+{
+       BoundBox *bb = BKE_object_boundbox_get(this->m_object);
+
+       if (!bb) {
+               if (this->m_object->type != OB_CAMERA) {
+                       std::cerr << "Boundbox is null!\n";
+               }
+
+               return Imath::Box3d();
+       }
+
+       /* Convert Z-up to Y-up. */
+       this->m_bounds.min.x = bb->vec[0][0];
+       this->m_bounds.min.y = bb->vec[0][2];
+       this->m_bounds.min.z = -bb->vec[0][1];
+
+       this->m_bounds.max.x = bb->vec[6][0];
+       this->m_bounds.max.y = bb->vec[6][2];
+       this->m_bounds.max.z = -bb->vec[6][1];
+
+       return this->m_bounds;
+}
+
+void AbcObjectWriter::write()
+{
+       do_write();
+       m_first_frame = false;
+}
+
+/* ************************************************************************** */
+
+AbcObjectReader::AbcObjectReader(const IObject &object, ImportSettings &settings)
+    : m_name("")
+    , m_object_name("")
+    , m_data_name("")
+    , m_object(NULL)
+    , m_iobject(object)
+    , m_settings(&settings)
+    , m_min_time(std::numeric_limits<chrono_t>::max())
+    , m_max_time(std::numeric_limits<chrono_t>::min())
+{
+       m_name = object.getFullName();
+       std::vector<std::string> parts;
+       split(m_name, '/', parts);
+
+       if (parts.size() >= 2) {
+               m_object_name = parts[parts.size() - 2];
+               m_data_name = parts[parts.size() - 1];
+       }
+       else {
+               m_object_name = m_data_name = parts[parts.size() - 1];
+       }
+}
+
+AbcObjectReader::~AbcObjectReader()
+{}
+
+const IObject &AbcObjectReader::iobject() const
+{
+       return m_iobject;
+}
+
+Object *AbcObjectReader::object() const
+{
+       return m_object;
+}
+
+void AbcObjectReader::readObjectMatrix(const float time)
+{
+       IXform ixform;
+       bool has_alembic_parent = false;
+
+       /* Check that we have an empty object (locator, bone head/tail...).  */
+       if (IXform::matches(m_iobject.getMetaData())) {
+               ixform = IXform(m_iobject, Alembic::AbcGeom::kWrapExisting);
+
+               /* See comment below. */
+               has_alembic_parent = m_iobject.getParent().getParent().valid();
+       }
+       /* Check that we have an object with actual data. */
+       else if (IXform::matches(m_iobject.getParent().getMetaData())) {
+               ixform = IXform(m_iobject.getParent(), Alembic::AbcGeom::kWrapExisting);
+
+               /* This is a bit hackish, but we need to make sure that extra
+                * transformations added to the matrix (rotation/scale) are only applied
+                * to root objects. The way objects and their hierarchy are created will
+                * need to be revisited at some point but for now this seems to do the
+                * trick.
+                *
+                * Explanation of the trick:
+                * The first getParent() will return this object's transformation matrix.
+                * The second getParent() will get the parent of the transform, but this
+                * might be the archive root ('/') which is valid, so we go passed it to
+                * make sure that there is no parent.
+                */
+               has_alembic_parent = m_iobject.getParent().getParent().getParent().valid();
+       }
+       /* Should not happen. */
+       else {
+               return;
+       }
+
+       const IXformSchema &schema(ixform.getSchema());
+
+       if (!schema.valid()) {
+               return;
+       }
+
+       Alembic::AbcGeom::ISampleSelector sample_sel(time);
+       Alembic::AbcGeom::XformSample xs;
+       schema.get(xs, sample_sel);
+
+       create_input_transform(sample_sel, ixform, m_object, m_object->obmat, m_settings->scale, has_alembic_parent);
+
+       invert_m4_m4(m_object->imat, m_object->obmat);
+
+       BKE_object_apply_mat4(m_object, m_object->obmat, false,  false);
+
+       if (!schema.isConstant()) {
+               bConstraint *con = BKE_constraint_add_for_object(m_object, NULL, CONSTRAINT_TYPE_TRANSFORM_CACHE);
+               bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data);
+               BLI_strncpy(data->object_path, m_iobject.getFullName().c_str(), FILE_MAX);
+
+               data->cache_file = m_settings->cache_file;
+               id_us_plus(&data->cache_file->id);
+       }
+}
+
+void AbcObjectReader::addCacheModifier() const
+{
+       ModifierData *md = modifier_new(eModifierType_MeshSequenceCache);
+       BLI_addtail(&m_object->modifiers, md);
+
+       MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
+
+       mcmd->cache_file = m_settings->cache_file;
+       id_us_plus(&mcmd->cache_file->id);
+
+       BLI_strncpy(mcmd->object_path, m_iobject.getFullName().c_str(), FILE_MAX);
+}
+
+chrono_t AbcObjectReader::minTime() const
+{
+       return m_min_time;
+}
+
+chrono_t AbcObjectReader::maxTime() const
+{
+       return m_max_time;
+}
diff --git a/source/blender/alembic/intern/abc_object.h b/source/blender/alembic/intern/abc_object.h
new file mode 100644 (file)
index 0000000..2e885f2
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_OBJECT_H__
+#define __ABC_OBJECT_H__
+
+#include <Alembic/Abc/All.h>
+#include <Alembic/AbcGeom/All.h>
+
+#include "abc_exporter.h"
+
+extern "C" {
+#include "DNA_ID.h"
+}
+
+class AbcTransformWriter;
+
+struct Main;
+struct Object;
+
+/* ************************************************************************** */
+
+class AbcObjectWriter {
+protected:
+       Object *m_object;
+       ExportSettings &m_settings;
+
+       Scene *m_scene;
+       uint32_t m_time_sampling;
+
+       Imath::Box3d m_bounds;
+       std::vector<AbcObjectWriter *> m_children;
+
+       std::vector< std::pair<std::string, IDProperty *> > m_props;
+
+       bool m_first_frame;
+       std::string m_name;
+
+public:
+       AbcObjectWriter(Scene *scene,
+                       Object *ob,
+                       uint32_t time_sampling,
+                       ExportSettings &settings,
+                       AbcObjectWriter *parent = NULL);
+
+       virtual ~AbcObjectWriter();
+
+       void addChild(AbcObjectWriter *child);
+
+       virtual Imath::Box3d bounds();
+
+       void write();
+
+private:
+       virtual void do_write() = 0;
+};
+
+/* ************************************************************************** */
+
+class CacheFile;
+
+struct ImportSettings {
+       bool do_convert_mat;
+       float conversion_mat[4][4];
+
+       int from_up;
+       int from_forward;
+       float scale;
+       bool is_sequence;
+       bool set_frame_range;
+
+       /* Length and frame offset of file sequences. */
+       int sequence_len;
+       int offset;
+
+       /* From MeshSeqCacheModifierData.read_flag */
+       int read_flag;
+
+       bool validate_meshes;
+
+       CacheFile *cache_file;
+
+       ImportSettings()
+           : do_convert_mat(false)
+           , from_up(0)
+           , from_forward(0)
+           , scale(1.0f)
+           , is_sequence(false)
+           , set_frame_range(false)
+           , sequence_len(1)
+           , offset(0)
+           , read_flag(0)
+           , validate_meshes(false)
+           , cache_file(NULL)
+       {}
+};
+
+template <typename Schema>
+static bool has_animations(Schema &schema, ImportSettings *settings)
+{
+       if (settings->is_sequence) {
+               return true;
+       }
+
+       if (!schema.isConstant()) {
+               return true;
+       }
+
+       return false;
+}
+
+/* ************************************************************************** */
+
+using Alembic::AbcCoreAbstract::chrono_t;
+
+class AbcObjectReader {
+protected:
+       std::string m_name;
+       std::string m_object_name;
+       std::string m_data_name;
+       Object *m_object;
+       Alembic::Abc::IObject m_iobject;
+
+       ImportSettings *m_settings;
+
+       chrono_t m_min_time;
+       chrono_t m_max_time;
+
+public:
+       explicit AbcObjectReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+       virtual ~AbcObjectReader();
+
+       const Alembic::Abc::IObject &iobject() const;
+
+       Object *object() const;
+
+       virtual bool valid() const = 0;
+
+       virtual void readObjectData(Main *bmain, float time) = 0;
+
+       void readObjectMatrix(const float time);
+
+       void addCacheModifier() const;
+
+       chrono_t minTime() const;
+       chrono_t maxTime() const;
+};
+
+#endif  /* __ABC_OBJECT_H__ */
diff --git a/source/blender/alembic/intern/abc_points.cc b/source/blender/alembic/intern/abc_points.cc
new file mode 100644 (file)
index 0000000..fa5b71a
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software  Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#include "abc_points.h"
+
+#include "abc_mesh.h"
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_mesh_types.h"
+
+#include "BKE_lattice.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+#include "BKE_particle.h"
+#include "BKE_scene.h"
+
+#include "BLI_math.h"
+}
+
+using Alembic::AbcGeom::kVertexScope;
+using Alembic::AbcGeom::kWrapExisting;
+using Alembic::AbcGeom::P3fArraySamplePtr;
+using Alembic::AbcGeom::N3fArraySamplePtr;
+
+using Alembic::AbcGeom::ICompoundProperty;
+using Alembic::AbcGeom::IN3fArrayProperty;
+using Alembic::AbcGeom::IPoints;
+using Alembic::AbcGeom::IPointsSchema;
+using Alembic::AbcGeom::ISampleSelector;
+
+using Alembic::AbcGeom::OPoints;
+using Alembic::AbcGeom::OPointsSchema;
+
+/* ************************************************************************** */
+
+AbcPointsWriter::AbcPointsWriter(Scene *scene,
+                                 Object *ob,
+                                    AbcTransformWriter *parent,
+                                    uint32_t time_sampling,
+                                    ExportSettings &settings,
+                                    ParticleSystem *psys)
+    : AbcObjectWriter(scene, ob, time_sampling, settings, parent)
+{
+       m_psys = psys;
+
+       OPoints points(parent->alembicXform(), m_name, m_time_sampling);
+       m_schema = points.getSchema();
+}
+
+void AbcPointsWriter::do_write()
+{
+       if (!m_psys) {
+               return;
+       }
+
+       std::vector<Imath::V3f> points;
+       std::vector<Imath::V3f> velocities;
+       std::vector<float> widths;
+       std::vector<uint64_t> ids;
+
+       ParticleKey state;
+
+       ParticleSimulationData sim;
+       sim.scene = m_scene;
+       sim.ob = m_object;
+       sim.psys = m_psys;
+
+       m_psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
+
+       uint64_t index = 0;
+       for (int p = 0; p < m_psys->totpart; p++) {
+               float pos[3], vel[3];
+
+               if (m_psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) {
+                       continue;
+               }
+
+               state.time = BKE_scene_frame_get(m_scene);
+
+               if (psys_get_particle_state(&sim, p, &state, 0) == 0) {
+                       continue;
+               }
+
+               /* location */
+               mul_v3_m4v3(pos, m_object->imat, state.co);
+
+               /* velocity */
+               sub_v3_v3v3(vel, state.co, m_psys->particles[p].prev_state.co);
+
+               /* Convert Z-up to Y-up. */
+               points.push_back(Imath::V3f(pos[0], pos[2], -pos[1]));
+               velocities.push_back(Imath::V3f(vel[0], vel[2], -vel[1]));
+               widths.push_back(m_psys->particles[p].size);
+               ids.push_back(index++);
+       }
+
+       if (m_psys->lattice_deform_data) {
+               end_latt_deform(m_psys->lattice_deform_data);
+               m_psys->lattice_deform_data = NULL;
+       }
+
+       Alembic::Abc::P3fArraySample psample(points);
+       Alembic::Abc::UInt64ArraySample idsample(ids);
+       Alembic::Abc::V3fArraySample vsample(velocities);
+       Alembic::Abc::FloatArraySample wsample_array(widths);
+       Alembic::AbcGeom::OFloatGeomParam::Sample wsample(wsample_array, kVertexScope);
+
+       m_sample = OPointsSchema::Sample(psample, idsample, vsample, wsample);
+       m_sample.setSelfBounds(bounds());
+
+       m_schema.set(m_sample);
+}
+
+/* ************************************************************************** */
+
+AbcPointsReader::AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
+    : AbcObjectReader(object, settings)
+{
+       IPoints ipoints(m_iobject, kWrapExisting);
+       m_schema = ipoints.getSchema();
+       get_min_max_time(m_schema, m_min_time, m_max_time);
+}
+
+bool AbcPointsReader::valid() const
+{
+       return m_schema.valid();
+}
+
+void AbcPointsReader::readObjectData(Main *bmain, float time)
+{
+       Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str());
+
+       const ISampleSelector sample_sel(time);
+       m_sample = m_schema.getValue(sample_sel);
+
+       const P3fArraySamplePtr &positions = m_sample.getPositions();
+       utils::mesh_add_verts(mesh, positions->size());
+
+       CDStreamConfig config = create_config(mesh);
+       read_points_sample(m_schema, sample_sel, config, time);
+
+       if (m_settings->validate_meshes) {
+               BKE_mesh_validate(mesh, false, false);
+       }
+
+       m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str());
+       m_object->data = mesh;
+
+       if (has_animations(m_schema, m_settings)) {
+               addCacheModifier();
+       }
+}
+
+void read_points_sample(const IPointsSchema &schema,
+                        const ISampleSelector &selector,
+                        CDStreamConfig &config,
+                        float time)
+{
+       Alembic::AbcGeom::IPointsSchema::Sample sample = schema.getValue(selector);
+
+       const P3fArraySamplePtr &positions = sample.getPositions();
+
+       ICompoundProperty prop = schema.getArbGeomParams();
+       N3fArraySamplePtr vnormals;
+
+       if (has_property(prop, "N")) {
+               const IN3fArrayProperty &normals_prop = IN3fArrayProperty(prop, "N", time);
+
+               if (normals_prop) {
+                       vnormals = normals_prop.getValue(selector);
+               }
+       }
+
+       read_mverts(config.mvert, positions, vnormals);
+}
diff --git a/source/blender/alembic/intern/abc_points.h b/source/blender/alembic/intern/abc_points.h
new file mode 100644 (file)
index 0000000..51f3103
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software  Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Kévin Dietrich.
+ * All rights reserved.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ *
+ */
+
+#ifndef __ABC_POINTS_H__
+#define __ABC_POINTS_H__
+
+#include "abc_object.h"
+#include "abc_customdata.h"
+
+class ParticleSystem;
+
+/* ************************************************************************** */
+
+class AbcPointsWriter : public AbcObjectWriter {
+       Alembic::AbcGeom::OPointsSchema m_schema;
+       Alembic::AbcGeom::OPointsSchema::Sample m_sample;
+       ParticleSystem *m_psys;
+
+public:
+       AbcPointsWriter(Scene *scene,
+                       Object *ob,
+                       AbcTransformWriter *parent,
+                       uint32_t time_sampling,
+                       ExportSettings &settings,
+                       ParticleSystem *psys);
+
+       void do_write();
+};
+
+/* ************************************************************************** */
+
+class AbcPointsReader : public AbcObjectReader {
+       Alembic::AbcGeom::IPointsSchema m_schema;
+       Alembic::AbcGeom::IPointsSchema::Sample m_sample;
+
+public:
+       AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+       bool valid() const;
+
+       void readObjectData(Main *bmain, float time);
+};
+
+void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema,
+                        const Alembic::AbcGeom::ISampleSelector &selector,
+                        CDStreamConfig &config,
+                        float time);
+
+#endif  /* __ABC_POINTS_H__ */
diff --git a/source/blender/alembic/intern/abc_transform.cc b/source/blender/alembic/intern/abc_transform.cc
new file mode 100644 (file)
index 0000000..3326ae0
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_transform.h"
+
+#include <OpenEXR/ImathBoxAlgo.h>
+
+#include "abc_util.h"
+
+extern "C" {
+#include "DNA_object_types.h"
+
+#include "BLI_math.h"
+
+#include "BKE_object.h"
+}
+
+using Alembic::AbcGeom::OObject;
+using Alembic::AbcGeom::OXform;
+
+/* ************************************************************************** */
+
+static bool has_parent_camera(Object *ob)
+{
+       if (!ob->parent) {
+               return false;
+       }
+
+       Object *parent = ob->parent;
+
+       if (parent->type == OB_CAMERA) {
+               return true;
+       }
+
+       return has_parent_camera(parent);
+}
+
+/* ************************************************************************** */
+
+AbcTransformWriter::AbcTransformWriter(Object *ob,
+                                       const OObject &abc_parent,
+                                       AbcTransformWriter *parent,
+                                       unsigned int time_sampling,
+                                       ExportSettings &settings)
+    : AbcObjectWriter(NULL, ob, time_sampling, settings, parent)
+{
+       m_is_animated = hasAnimation(m_object);
+       m_parent = NULL;
+
+       if (!m_is_animated) {
+               time_sampling = 0;
+       }
+
+       m_xform = OXform(abc_parent, get_id_name(m_object), time_sampling);
+       m_schema = m_xform.getSchema();
+}
+
+void AbcTransformWriter::do_write()
+{
+       if (m_first_frame) {
+               m_visibility = Alembic::AbcGeom::CreateVisibilityProperty(m_xform, m_xform.getSchema().getTimeSampling());
+       }
+
+       m_visibility.set(!(m_object->restrictflag & OB_RESTRICT_VIEW));
+
+       if (!m_first_frame && !m_is_animated) {
+               return;
+       }
+
+       float mat[4][4];
+       create_transform_matrix(m_object, mat);
+
+       /* Only apply rotation to root camera, parenting will propagate it. */
+       if (m_object->type == OB_CAMERA && !has_parent_camera(m_object)) {
+               float rot_mat[4][4];
+               unit_m4(rot_mat);
+               rotate_m4(rot_mat, 'X', -M_PI_2);
+               mul_m4_m4m4(mat, mat, rot_mat);
+       }
+
+       if (!m_object->parent) {
+               /* Only apply scaling to root objects, parenting will propagate it. */
+               float scale_mat[4][4];
+               scale_m4_fl(scale_mat, m_settings.global_scale);
+               mul_m4_m4m4(mat, mat, scale_mat);
+               mul_v3_fl(mat[3], m_settings.global_scale);
+       }
+
+       m_matrix = convert_matrix(mat);
+
+       m_sample.setMatrix(m_matrix);
+       m_schema.set(m_sample);
+}
+
+Imath::Box3d AbcTransformWriter::bounds()
+{
+       Imath::Box3d bounds;
+
+       for (int i = 0; i < m_children.size(); ++i) {
+               Imath::Box3d box(m_children[i]->bounds());
+               bounds.extendBy(box);
+       }
+
+       return Imath::transform(bounds, m_matrix);
+}
+
+bool AbcTransformWriter::hasAnimation(Object */*ob*/) const
+{
+       /* TODO(kevin): implement this. */
+       return true;
+}
+
+/* ************************************************************************** */
+
+AbcEmptyReader::AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings)
+    : AbcObjectReader(object, settings)
+{
+       Alembic::AbcGeom::IXform xform(object, Alembic::AbcGeom::kWrapExisting);
+       m_schema = xform.getSchema();
+
+       get_min_max_time(m_schema, m_min_time, m_max_time);
+}
+
+bool AbcEmptyReader::valid() const
+{
+       return m_schema.valid();
+}
+
+void AbcEmptyReader::readObjectData(Main *bmain, float /*time*/)
+{
+       m_object = BKE_object_add_only_object(bmain, OB_EMPTY, m_object_name.c_str());
+       m_object->data = NULL;
+}
diff --git a/source/blender/alembic/intern/abc_transform.h b/source/blender/alembic/intern/abc_transform.h
new file mode 100644 (file)
index 0000000..6a3aae2
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_TRANSFORM_H__
+#define __ABC_TRANSFORM_H__
+
+#include "abc_object.h"
+
+#include <Alembic/AbcGeom/All.h>
+
+/* ************************************************************************** */
+
+class AbcTransformWriter : public AbcObjectWriter {
+       Alembic::AbcGeom::OXform m_xform;
+       Alembic::AbcGeom::OXformSchema m_schema;
+       Alembic::AbcGeom::XformSample m_sample;
+       Alembic::AbcGeom::OVisibilityProperty m_visibility;
+       Alembic::Abc::M44d m_matrix;
+
+       bool m_is_animated;
+       Object *m_parent;
+       bool m_visible;
+
+public:
+       AbcTransformWriter(Object *ob,
+                          const Alembic::AbcGeom::OObject &abc_parent,
+                          AbcTransformWriter *parent,
+                          unsigned int time_sampling,
+                          ExportSettings &settings);
+
+       Alembic::AbcGeom::OXform &alembicXform() { return m_xform;}
+       virtual Imath::Box3d bounds();
+       void setParent(Object *p) { m_parent = p; }
+
+private:
+       virtual void do_write();
+
+       bool hasAnimation(Object *ob) const;
+};
+
+/* ************************************************************************** */
+
+class AbcEmptyReader : public AbcObjectReader {
+       Alembic::AbcGeom::IXformSchema m_schema;
+
+public:
+       AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings);
+
+       bool valid() const;
+
+       void readObjectData(Main *bmain, float time);
+};
+
+#endif  /* __ABC_TRANSFORM_H__ */
diff --git a/source/blender/alembic/intern/abc_util.cc b/source/blender/alembic/intern/abc_util.cc
new file mode 100644 (file)
index 0000000..fbab0bc
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "abc_util.h"
+
+#include <algorithm>
+
+extern "C" {
+#include "DNA_object_types.h"
+
+#include "BLI_math.h"
+}
+
+std::string get_id_name(Object *ob)
+{
+       if (!ob) {
+               return "";
+       }
+
+       return get_id_name(&ob->id);
+}
+
+std::string get_id_name(ID *id)
+{
+       std::string name(id->name + 2);
+       std::replace(name.begin(), name.end(), ' ', '_');
+       std::replace(name.begin(), name.end(), '.', '_');
+       std::replace(name.begin(), name.end(), ':', '_');
+
+       return name;
+}
+
+std::string get_object_dag_path_name(Object *ob, Object *dupli_parent)
+{
+       std::string name = get_id_name(ob);
+
+       Object *p = ob->parent;
+
+       while (p) {
+               name = get_id_name(p) + "/" + name;
+               p = p->parent;
+       }
+
+       if (dupli_parent && (ob != dupli_parent)) {
+               name = get_id_name(dupli_parent) + "/" + name;
+       }
+
+       return name;
+}
+
+bool object_selected(Object *ob)
+{
+       return ob->flag & SELECT;
+}
+
+bool parent_selected(Object *ob)
+{
+       if (object_selected(ob)) {
+               return true;
+       }
+
+       bool do_export = false;
+
+       Object *parent = ob->parent;
+
+       while (parent != NULL) {
+               if (object_selected(parent)) {
+                       do_export = true;
+                       break;
+               }
+
+               parent = parent->parent;
+       }
+
+       return do_export;
+}
+
+Imath::M44d convert_matrix(float mat[4][4])
+{
+       Imath::M44d m;
+
+       for (int i = 0; i < 4; ++i) {
+               for (int j = 0; j < 4; ++j) {
+                       m[i][j] = mat[i][j];
+               }
+       }
+
+       return m;
+}
+
+void split(const std::string &s, const char delim, std::vector<std::string> &tokens)
+{
+       tokens.clear();
+
+       std::stringstream ss(s);
+       std::string item;
+
+       while (std::getline(ss, item, delim)) {
+               if (!item.empty()) {
+                       tokens.push_back(item);
+               }
+       }
+}
+
+/* Create a rotation matrix for each axis from euler angles.
+ * Euler angles are swaped to change coordinate system. */
+static void create_rotation_matrix(
+        float rot_x_mat[3][3], float rot_y_mat[3][3],
+        float rot_z_mat[3][3], const float euler[3], const bool to_yup)
+{
+       const float rx = euler[0];
+       const float ry = (to_yup) ?  euler[2] : -euler[2];
+       const float rz = (to_yup) ? -euler[1] :  euler[1];
+
+       unit_m3(rot_x_mat);
+       unit_m3(rot_y_mat);
+       unit_m3(rot_z_mat);
+
+       rot_x_mat[1][1] = cos(rx);
+       rot_x_mat[2][1] = -sin(rx);
+       rot_x_mat[1][2] = sin(rx);
+       rot_x_mat[2][2] = cos(rx);
+
+       rot_y_mat[2][2] = cos(ry);
+       rot_y_mat[0][2] = -sin(ry);
+       rot_y_mat[2][0] = sin(ry);
+       rot_y_mat[0][0] = cos(ry);
+
+       rot_z_mat[0][0] = cos(rz);
+       rot_z_mat[1][0] = -sin(rz);
+       rot_z_mat[0][1] = sin(rz);
+       rot_z_mat[1][1] = cos(rz);
+}
+
+/* Recompute transform matrix of object in new coordinate system
+ * (from Y-Up to Z-Up). */
+void create_transform_matrix(float r_mat[4][4])
+{
+       float rot_mat[3][3], rot[3][3], scale_mat[4][4], invmat[4][4], transform_mat[4][4];
+       float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3];
+       float loc[3], scale[3], euler[3];
+
+       zero_v3(loc);
+       zero_v3(scale);
+       zero_v3(euler);
+       unit_m3(rot);
+       unit_m3(rot_mat);
+       unit_m4(scale_mat);
+       unit_m4(transform_mat);
+       unit_m4(invmat);
+
+       /* Compute rotation matrix. */
+
+       /* Extract location, rotation, and scale from matrix. */
+       mat4_to_loc_rot_size(loc, rot, scale, r_mat);
+
+       /* Get euler angles from rotation matrix. */
+       mat3_to_eulO(euler, ROT_MODE_XYZ, rot);
+
+       /* Create X, Y, Z rotation matrices from euler angles. */
+       create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, false);
+
+       /* Concatenate rotation matrices. */
+       mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+       mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+       mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+
+       /* Add rotation matrix to transformation matrix. */
+       copy_m4_m3(transform_mat, rot_mat);
+
+       /* Add translation to transformation matrix. */
+       copy_yup_zup(transform_mat[3], loc);
+
+       /* Create scale matrix. */
+       scale_mat[0][0] = scale[0];
+       scale_mat[1][1] = scale[2];
+       scale_mat[2][2] = scale[1];
+
+       /* Add scale to transformation matrix. */
+       mul_m4_m4m4(transform_mat, transform_mat, scale_mat);
+
+       copy_m4_m4(r_mat, transform_mat);
+}
+
+void create_input_transform(const Alembic::AbcGeom::ISampleSelector &sample_sel,
+                            const Alembic::AbcGeom::IXform &ixform, Object *ob,
+                            float r_mat[4][4], float scale, bool has_alembic_parent)
+{
+
+       const Alembic::AbcGeom::IXformSchema &ixform_schema = ixform.getSchema();
+       Alembic::AbcGeom::XformSample xs;
+       ixform_schema.get(xs, sample_sel);
+       const Imath::M44d &xform = xs.getMatrix();
+
+       for (int i = 0; i < 4; ++i) {
+               for (int j = 0; j < 4; ++j) {
+                       r_mat[i][j] = xform[i][j];
+               }
+       }
+
+       if (ob->type == OB_CAMERA) {
+               float cam_to_yup[4][4];
+               unit_m4(cam_to_yup);
+               rotate_m4(cam_to_yup, 'X', M_PI_2);
+               mul_m4_m4m4(r_mat, r_mat, cam_to_yup);
+       }
+
+       create_transform_matrix(r_mat);
+
+       if (ob->parent) {
+               mul_m4_m4m4(r_mat, ob->parent->obmat, r_mat);
+       }
+       /* TODO(kevin) */
+       else if (!has_alembic_parent) {
+               /* Only apply scaling to root objects, parenting will propagate it. */
+               float scale_mat[4][4];
+               scale_m4_fl(scale_mat, scale);
+               mul_m4_m4m4(r_mat, r_mat, scale_mat);
+               mul_v3_fl(r_mat[3], scale);
+       }
+}
+
+/* Recompute transform matrix of object in new coordinate system (from Z-Up to Y-Up). */
+void create_transform_matrix(Object *obj, float transform_mat[4][4])
+{
+       float rot_mat[3][3], rot[3][3], scale_mat[4][4], invmat[4][4], mat[4][4];
+       float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3];
+       float loc[3], scale[3], euler[3];
+
+       zero_v3(loc);
+       zero_v3(scale);
+       zero_v3(euler);
+       unit_m3(rot);
+       unit_m3(rot_mat);
+       unit_m4(scale_mat);
+       unit_m4(transform_mat);
+       unit_m4(invmat);
+       unit_m4(mat);
+
+       /* get local matrix. */
+       if (obj->parent) {
+               invert_m4_m4(invmat, obj->parent->obmat);
+               mul_m4_m4m4(mat, invmat, obj->obmat);
+       }
+       else {
+               copy_m4_m4(mat, obj->obmat);
+       }
+
+       /* Compute rotation matrix. */
+       switch (obj->rotmode) {
+               case ROT_MODE_AXISANGLE:
+               {
+                       /* Get euler angles from axis angle rotation. */
+                       axis_angle_to_eulO(euler, ROT_MODE_XYZ, obj->rotAxis, obj->rotAngle);
+
+                       /* Create X, Y, Z rotation matrices from euler angles. */
+                       create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+                       /* Concatenate rotation matrices. */
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+
+                       /* Extract location and scale from matrix. */
+                       mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+                       break;
+               }
+               case ROT_MODE_QUAT:
+               {
+                       float q[4];
+                       copy_v4_v4(q, obj->quat);
+
+                       /* Swap axis. */
+                       q[2] = obj->quat[3];
+                       q[3] = -obj->quat[2];
+
+                       /* Compute rotation matrix from quaternion. */
+                       quat_to_mat3(rot_mat, q);
+
+                       /* Extract location and scale from matrix. */
+                       mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+                       break;
+               }
+               case ROT_MODE_XYZ:
+               {
+                       /* Extract location, rotation, and scale form matrix. */
+                       mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+                       /* Get euler angles from rotation matrix. */
+                       mat3_to_eulO(euler, ROT_MODE_XYZ, rot);
+
+                       /* Create X, Y, Z rotation matrices from euler angles. */
+                       create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+                       /* Concatenate rotation matrices. */
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+
+                       break;
+               }
+               case ROT_MODE_XZY:
+               {
+                       /* Extract location, rotation, and scale form matrix. */
+                       mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+                       /* Get euler angles from rotation matrix. */
+                       mat3_to_eulO(euler, ROT_MODE_XZY, rot);
+
+                       /* Create X, Y, Z rotation matrices from euler angles. */
+                       create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+                       /* Concatenate rotation matrices. */
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+
+                       break;
+               }
+               case ROT_MODE_YXZ:
+               {
+                       /* Extract location, rotation, and scale form matrix. */
+                       mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+                       /* Get euler angles from rotation matrix. */
+                       mat3_to_eulO(euler, ROT_MODE_YXZ, rot);
+
+                       /* Create X, Y, Z rotation matrices from euler angles. */
+                       create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+                       /* Concatenate rotation matrices. */
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+
+                       break;
+               }
+               case ROT_MODE_YZX:
+               {
+                       /* Extract location, rotation, and scale form matrix. */
+                       mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+                       /* Get euler angles from rotation matrix. */
+                       mat3_to_eulO(euler, ROT_MODE_YZX, rot);
+
+                       /* Create X, Y, Z rotation matrices from euler angles. */
+                       create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+                       /* Concatenate rotation matrices. */
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+
+                       break;
+               }
+               case ROT_MODE_ZXY:
+               {
+                       /* Extract location, rotation, and scale form matrix. */
+                       mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+                       /* Get euler angles from rotation matrix. */
+                       mat3_to_eulO(euler, ROT_MODE_ZXY, rot);
+
+                       /* Create X, Y, Z rotation matrices from euler angles. */
+                       create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+                       /* Concatenate rotation matrices. */
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+
+                       break;
+               }
+               case ROT_MODE_ZYX:
+               {
+                       /* Extract location, rotation, and scale form matrix. */
+                       mat4_to_loc_rot_size(loc, rot, scale, mat);
+
+                       /* Get euler angles from rotation matrix. */
+                       mat3_to_eulO(euler, ROT_MODE_ZYX, rot);
+
+                       /* Create X, Y, Z rotation matrices from euler angles. */
+                       create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true);
+
+                       /* Concatenate rotation matrices. */
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat);
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat);
+                       mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat);
+
+                       break;
+               }
+       }
+
+       /* Add rotation matrix to transformation matrix. */
+       copy_m4_m3(transform_mat, rot_mat);
+
+       /* Add translation to transformation matrix. */
+       copy_zup_yup(transform_mat[3], loc);
+
+       /* Create scale matrix. */
+       scale_mat[0][0] = scale[0];
+       scale_mat[1][1] = scale[2];
+       scale_mat[2][2] = scale[1];
+
+       /* Add scale to transformation matrix. */
+       mul_m4_m4m4(transform_mat, transform_mat, scale_mat);
+}
+
+bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name)
+{
+       if (!prop.valid()) {
+               return false;
+       }
+
+       return prop.getPropertyHeader(name) != NULL;
+}
diff --git a/source/blender/alembic/intern/abc_util.h b/source/blender/alembic/intern/abc_util.h
new file mode 100644 (file)
index 0000000..688a25d
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __ABC_UTIL_H__
+#define __ABC_UTIL_H__
+
+#include <Alembic/Abc/All.h>
+#include <Alembic/AbcGeom/All.h>
+
+#ifdef _MSC_VER
+#  define ABC_INLINE static __forceinline
+#else
+#  define ABC_INLINE static inline
+#endif
+
+using Alembic::Abc::chrono_t;
+
+class ImportSettings;
+
+struct ID;
+struct Object;
+
+std::string get_id_name(ID *id);
+std::string get_id_name(Object *ob);
+std::string get_object_dag_path_name(Object *ob, Object *dupli_parent);
+
+bool object_selected(Object *ob);
+bool parent_selected(Object *ob);
+
+Imath::M44d convert_matrix(float mat[4][4]);
+void create_transform_matrix(float r_mat[4][4]);
+void create_transform_matrix(Object *obj, float transform_mat[4][4]);
+
+void split(const std::string &s, const char delim, std::vector<std::string> &tokens);
+
+template<class TContainer>
+bool begins_with(const TContainer &input, const TContainer &match)
+{
+       return input.size() >= match.size()
+               && std::equal(match.begin(), match.end(), input.begin());
+}
+
+void create_input_transform(const Alembic::AbcGeom::ISampleSelector &sample_sel,
+                            const Alembic::AbcGeom::IXform &ixform, Object *ob,
+                            float r_mat[4][4], float scale, bool has_alembic_parent = false);
+
+template <typename Schema>
+void get_min_max_time(const Schema &schema, chrono_t &min, chrono_t &max)
+{
+       const Alembic::Abc::TimeSamplingPtr &time_samp = schema.getTimeSampling();
+
+       if (!schema.isConstant()) {
+               const size_t num_samps = schema.getNumSamples();
+
+               if (num_samps > 0) {
+                       const chrono_t min_time = time_samp->getSampleTime(0);
+                       min = std::min(min, min_time);
+
+                       const chrono_t max_time = time_samp->getSampleTime(num_samps - 1);
+                       max = std::max(max, max_time);
+               }
+       }
+}
+
+bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name);
+
+/* ************************** */
+
+/* TODO(kevin): for now keeping these transformations hardcoded to make sure
+ * everything works properly, and also because Alembic is almost exclusively
+ * used in Y-up software, but eventually they'll be set by the user in the UI
+ * like other importers/exporters do, to support other axis. */
+
+/* Copy from Y-up to Z-up. */
+
+ABC_INLINE void copy_yup_zup(float zup[3], const float yup[3])
+{
+       zup[0] = yup[0];
+       zup[1] = -yup[2];
+       zup[2] = yup[1];
+}
+
+ABC_INLINE void copy_yup_zup(short zup[3], const short yup[3])
+{
+       zup[0] = yup[0];
+       zup[1] = -yup[2];
+       zup[2] = yup[1];
+}
+
+/* Copy from Z-up to Y-up. */
+
+ABC_INLINE void copy_zup_yup(float yup[3], const float zup[3])
+{
+       yup[0] = zup[0];
+       yup[1] = zup[2];
+       yup[2] = -zup[1];
+}
+
+ABC_INLINE void copy_zup_yup(short yup[3], const short zup[3])
+{
+       yup[0] = zup[0];
+       yup[1] = zup[2];
+       yup[2] = -zup[1];
+}
+
+#endif  /* __ABC_UTIL_H__ */
diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc
new file mode 100644 (file)
index 0000000..0e96ac2
--- /dev/null
@@ -0,0 +1,1136 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "../ABC_alembic.h"
+
+#ifdef WITH_ALEMBIC_HDF5
+#  include <Alembic/AbcCoreHDF5/All.h>
+#endif
+
+#include <Alembic/AbcCoreOgawa/All.h>
+#include <Alembic/AbcMaterial/IMaterial.h>
+
+#include "abc_camera.h"
+#include "abc_curves.h"
+#include "abc_hair.h"
+#include "abc_mesh.h"
+#include "abc_nurbs.h"
+#include "abc_points.h"
+#include "abc_transform.h"
+#include "abc_util.h"
+
+extern "C" {
+#include "MEM_guardedalloc.h"
+
+#include "DNA_cachefile_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_cachefile.h"
+#include "BKE_cdderivedmesh.h"
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_depsgraph.h"
+#include "BKE_global.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_scene.h"
+
+/* SpaceType struct has a member called 'new' which obviously conflicts with C++
+ * so temporarily redefining the new keyword to make it compile. */
+#define new extern_new
+#include "BKE_screen.h"
+#undef new
+
+#include "BLI_fileops.h"
+#include "BLI_ghash.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+}
+
+using Alembic::Abc::Int32ArraySamplePtr;
+using Alembic::Abc::ObjectHeader;
+
+using Alembic::AbcGeom::ErrorHandler;
+using Alembic::AbcGeom::Exception;
+using Alembic::AbcGeom::MetaData;
+using Alembic::AbcGeom::P3fArraySamplePtr;
+using Alembic::AbcGeom::kWrapExisting;
+
+using Alembic::AbcGeom::IArchive;
+using Alembic::AbcGeom::ICamera;
+using Alembic::AbcGeom::ICurves;
+using Alembic::AbcGeom::ICurvesSchema;
+using Alembic::AbcGeom::IFaceSet;
+using Alembic::AbcGeom::ILight;
+using Alembic::AbcGeom::INuPatch;
+using Alembic::AbcGeom::IObject;
+using Alembic::AbcGeom::IPoints;
+using Alembic::AbcGeom::IPointsSchema;
+using Alembic::AbcGeom::IPolyMesh;
+using Alembic::AbcGeom::IPolyMeshSchema;
+using Alembic::AbcGeom::ISampleSelector;
+using Alembic::AbcGeom::ISubD;
+using Alembic::AbcGeom::IV2fGeomParam;
+using Alembic::AbcGeom::IXform;
+using Alembic::AbcGeom::IXformSchema;
+using Alembic::AbcGeom::N3fArraySamplePtr;
+using Alembic::AbcGeom::XformSample;
+using Alembic::AbcGeom::ICompoundProperty;
+using Alembic::AbcGeom::IN3fArrayProperty;
+using Alembic::AbcGeom::IN3fGeomParam;
+using Alembic::AbcGeom::V3fArraySamplePtr;
+
+using Alembic::AbcMaterial::IMaterial;
+
+struct AbcArchiveHandle {
+       int unused;
+};
+
+ABC_INLINE IArchive *archive_from_handle(AbcArchiveHandle *handle)
+{
+       return reinterpret_cast<IArchive *>(handle);
+}
+
+ABC_INLINE AbcArchiveHandle *handle_from_archive(IArchive *archive)
+{
+       return reinterpret_cast<AbcArchiveHandle *>(archive);
+}
+
+static IArchive *open_archive(const std::string &filename)
+{
+       Alembic::AbcCoreAbstract::ReadArraySampleCachePtr cache_ptr;
+       IArchive *archive;
+
+       try {
+               archive = new IArchive(Alembic::AbcCoreOgawa::ReadArchive(),
+                                      filename.c_str(), ErrorHandler::kThrowPolicy,
+                                      cache_ptr);
+       }
+       catch (const Exception &e) {
+               std::cerr << e.what() << '\n';
+
+#ifdef WITH_ALEMBIC_HDF5
+               try {
+                       archive = new IArchive(Alembic::AbcCoreHDF5::ReadArchive(),
+                                              filename.c_str(), ErrorHandler::kThrowPolicy,
+                                              cache_ptr);
+               }
+               catch (const Exception &) {
+                       std::cerr << e.what() << '\n';
+                       return NULL;
+               }
+#else
+               return NULL;
+#endif
+       }
+
+       return archive;
+}
+
+//#define USE_NURBS
+
+/* NOTE: this function is similar to visit_objects below, need to keep them in
+ * sync. */
+static void gather_objects_paths(const IObject &object, ListBase *object_paths)
+{
+       if (!object.valid()) {
+               return;
+       }
+
+       for (int i = 0; i < object.getNumChildren(); ++i) {
+               IObject child = object.getChild(i);
+
+               if (!child.valid()) {
+                       continue;
+               }
+
+               bool get_path = false;
+
+               const MetaData &md = child.getMetaData();
+
+               if (IXform::matches(md)) {
+                       /* Check whether or not this object is a Maya locator, which is
+                        * similar to empties used as parent object in Blender. */
+                       if (has_property(child.getProperties(), "locator")) {
+                               get_path = true;
+                       }
+                       else {
+                               /* Avoid creating an empty object if the child of this transform
+                                * is not a transform (that is an empty). */
+                               if (child.getNumChildren() == 1) {
+                                       if (IXform::matches(child.getChild(0).getMetaData())) {
+                                               get_path = true;
+                                       }
+#if 0
+                                       else {
+                                               std::cerr << "Skipping " << child.getFullName() << '\n';
+                                       }
+#endif
+                               }
+                               else {
+                                       get_path = true;
+                               }
+                       }
+               }
+               else if (IPolyMesh::matches(md)) {
+                       get_path = true;
+               }
+               else if (ISubD::matches(md)) {
+                       get_path = true;
+               }
+               else if (INuPatch::matches(md)) {
+#ifdef USE_NURBS
+                       get_path = true;
+#endif
+               }
+               else if (ICamera::matches(md)) {
+                       get_path = true;
+               }
+               else if (IPoints::matches(md)) {
+                       get_path = true;
+               }
+               else if (IMaterial::matches(md)) {
+                       /* Pass for now. */
+               }
+               else if (ILight::matches(md)) {
+                       /* Pass for now. */
+               }
+               else if (IFaceSet::matches(md)) {
+                       /* Pass, those are handled in the mesh reader. */
+               }
+               else if (ICurves::matches(md)) {
+                       get_path = true;
+               }
+               else {
+                       assert(false);
+               }
+
+               if (get_path) {
+                       AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(
+                                                         MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"));
+
+                       BLI_strncpy(abc_path->path, child.getFullName().c_str(), PATH_MAX);
+
+                       BLI_addtail(object_paths, abc_path);
+               }
+
+               gather_objects_paths(child, object_paths);
+       }
+}
+
+AbcArchiveHandle *ABC_create_handle(const char *filename, ListBase *object_paths)
+{
+       IArchive *archive = open_archive(filename);
+
+       if (!archive) {
+               return NULL;
+       }
+
+       if (object_paths) {
+               gather_objects_paths(archive->getTop(), object_paths);
+       }
+
+       return handle_from_archive(archive);
+}
+
+void ABC_free_handle(AbcArchiveHandle *handle)
+{
+       delete archive_from_handle(handle);
+}
+
+int ABC_get_version()
+{
+       return ALEMBIC_LIBRARY_VERSION;
+}
+
+static void find_iobject(const IObject &object, IObject &ret,
+                         const std::string &path)
+{
+       if (!object.valid()) {
+               return;
+       }
+
+       std::vector<std::string> tokens;
+       split(path, '/', tokens);
+
+       IObject tmp = object;
+
+       std::vector<std::string>::iterator iter;
+       for (iter = tokens.begin(); iter != tokens.end(); ++iter) {
+               IObject child = tmp.getChild(*iter);
+               tmp = child;
+       }
+
+       ret = tmp;
+}
+
+struct ExportJobData {
+       Scene *scene;
+       Main *bmain;
+
+       char filename[1024];
+       ExportSettings settings;
+
+       short *stop;
+       short *do_update;
+       float *progress;
+
+       bool was_canceled;
+};
+
+static void export_startjob(void *customdata, short *stop, short *do_update, float *progress)
+{
+       ExportJobData *data = static_cast<ExportJobData *>(customdata);
+
+       data->stop = stop;
+       data->do_update = do_update;
+       data->progress = progress;
+
+       /* XXX annoying hack: needed to prevent data corruption when changing
+        * scene frame in separate threads
+        */
+       G.is_rendering = true;
+       BKE_spacedata_draw_locks(true);
+
+       G.is_break = false;
+
+       try {
+               Scene *scene = data->scene;
+               AbcExporter exporter(scene, data->filename, data->settings);
+
+               const int orig_frame = CFRA;
+
+               data->was_canceled = false;
+               exporter(data->bmain, *data->progress, data->was_canceled);
+
+               if (CFRA != orig_frame) {
+                       CFRA = orig_frame;
+
+                       BKE_scene_update_for_newframe(data->bmain->eval_ctx, data->bmain,
+                                                     scene, scene->lay);
+               }
+       }
+       catch (const std::exception &e) {
+               std::cerr << "Abc Export error: " << e.what() << '\n';
+       }
+       catch (...) {
+               std::cerr << "Abc Export error\n";
+       }
+}
+
+static void export_endjob(void *customdata)
+{
+       ExportJobData *data = static_cast<ExportJobData *>(customdata);
+
+       if (data->was_canceled && BLI_exists(data->filename)) {
+               BLI_delete(data->filename, false, false);
+       }
+
+       G.is_rendering = false;
+       BKE_spacedata_draw_locks(false);
+}
+
+void ABC_export(
+        Scene *scene,
+        bContext *C,
+        const char *filepath,
+        const struct AlembicExportParams *params)
+{
+       ExportJobData *job = static_cast<ExportJobData *>(MEM_mallocN(sizeof(ExportJobData), "ExportJobData"));
+       job->scene = scene;
+       job->bmain = CTX_data_main(C);
+       BLI_strncpy(job->filename, filepath, 1024);
+
+       job->settings.scene = job->scene;
+       job->settings.frame_start = params->frame_start;
+       job->settings.frame_end = params->frame_end;
+       job->settings.frame_step_xform = params->frame_step_xform;
+       job->settings.frame_step_shape = params->frame_step_shape;
+       job->settings.shutter_open = params->shutter_open;
+       job->settings.shutter_close = params->shutter_close;
+       job->settings.selected_only = params->selected_only;
+       job->settings.export_face_sets = params->face_sets;
+       job->settings.export_normals = params->normals;
+       job->settings.export_uvs = params->uvs;
+       job->settings.export_vcols = params->vcolors;
+       job->settings.apply_subdiv = params->apply_subdiv;
+       job->settings.flatten_hierarchy = params->flatten_hierarchy;
+       job->settings.visible_layers_only = params->visible_layers_only;
+       job->settings.renderable_only = params->renderable_only;
+       job->settings.use_subdiv_schema = params->use_subdiv_schema;
+       job->settings.export_ogawa = (params->compression_type == ABC_ARCHIVE_OGAWA);
+       job->settings.pack_uv = params->packuv;
+       job->settings.global_scale = params->global_scale;
+
+       if (job->settings.frame_start > job->settings.frame_end) {
+               std::swap(job->settings.frame_start, job->settings.frame_end);
+       }
+
+       wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
+                                   CTX_wm_window(C),
+                                   job->scene,
+                                   "Alembic Export",
+                                   WM_JOB_PROGRESS,
+                                   WM_JOB_TYPE_ALEMBIC);
+
+       /* setup job */
+       WM_jobs_customdata_set(wm_job, job, MEM_freeN);
+       WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
+       WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob);
+
+       WM_jobs_start(CTX_wm_manager(C), wm_job);
+}
+
+/* ********************** Import file ********************** */
+
+static void visit_object(const IObject &object,
+                         std::vector<AbcObjectReader *> &readers,
+                         GHash *parent_map,
+                         ImportSettings &settings)
+{
+       if (!object.valid()) {
+               return;
+       }
+
+       for (int i = 0; i < object.getNumChildren(); ++i) {
+               IObject child = object.getChild(i);
+
+               if (!child.valid()) {
+                       continue;
+               }
+
+               AbcObjectReader *reader = NULL;
+
+               const MetaData &md = child.getMetaData();
+
+               if (IXform::matches(md)) {
+                       bool create_xform = false;
+
+                       /* Check whether or not this object is a Maya locator, which is
+                        * similar to empties used as parent object in Blender. */
+                       if (has_property(child.getProperties(), "locator")) {
+                               create_xform = true;
+                       }
+                       else {
+                               /* Avoid creating an empty object if the child of this transform
+                                * is not a transform (that is an empty). */
+                               if (child.getNumChildren() == 1) {
+                                       if (IXform::matches(child.getChild(0).getMetaData())) {
+                                               create_xform = true;
+                                       }
+#if 0
+                                       else {
+                                               std::cerr << "Skipping " << child.getFullName() << '\n';
+                                       }
+#endif
+                               }
+                               else {
+                                       create_xform = true;
+                               }
+                       }
+
+                       if (create_xform) {
+                               reader = new AbcEmptyReader(child, settings);
+                       }
+               }
+               else if (IPolyMesh::matches(md)) {
+                       reader = new AbcMeshReader(child, settings);
+               }
+               else if (ISubD::matches(md)) {
+                       reader = new AbcSubDReader(child, settings);
+               }
+               else if (INuPatch::matches(md)) {
+#ifdef USE_NURBS
+                       /* TODO(kevin): importing cyclic NURBS from other software crashes
+                        * at the moment. This is due to the fact that NURBS in other
+                        * software have duplicated points which causes buffer overflows in
+                        * Blender. Need to figure out exactly how these points are
+                        * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV).
+                        * Until this is fixed, disabling NURBS reading. */
+                       reader = new AbcNurbsReader(child, settings);
+#endif
+               }
+               else if (ICamera::matches(md)) {
+                       reader = new AbcCameraReader(child, settings);
+               }
+               else if (IPoints::matches(md)) {
+                       reader = new AbcPointsReader(child, settings);
+               }
+               else if (IMaterial::matches(md)) {
+                       /* Pass for now. */
+               }
+               else if (ILight::matches(md)) {
+                       /* Pass for now. */
+               }
+               else if (IFaceSet::matches(md)) {
+                       /* Pass, those are handled in the mesh reader. */
+               }
+               else if (ICurves::matches(md)) {
+                       reader = new AbcCurveReader(child, settings);
+               }
+               else {
+                       assert(false);
+               }
+
+               if (reader) {
+                       readers.push_back(reader);
+
+                       AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>(
+                                                         MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"));
+
+                       BLI_strncpy(abc_path->path, child.getFullName().c_str(), PATH_MAX);
+
+                       BLI_addtail(&settings.cache_file->object_paths, abc_path);
+
+                       /* Cast to `void *` explicitly to avoid compiler errors because it
+                        * is a `const char *` which the compiler cast to `const void *`
+                        * instead of the expected `void *`. */
+                       BLI_ghash_insert(parent_map, (void *)child.getFullName().c_str(), reader);
+               }
+
+               visit_object(child, readers, parent_map, settings);
+       }
+}
+
+enum {
+       ABC_NO_ERROR = 0,
+       ABC_ARCHIVE_FAIL,
+};
+
+struct ImportJobData {
+       Main *bmain;
+       Scene *scene;
+
+       char filename[1024];
+       ImportSettings settings;
+
+       GHash *parent_map;
+       std::vector<AbcObjectReader *> readers;
+
+       short *stop;
+       short *do_update;
+       float *progress;
+
+       char error_code;
+       bool was_cancelled;
+};
+
+ABC_INLINE bool is_mesh_and_strands(const IObject &object)
+{
+       if (object.getNumChildren() != 2) {
+               return false;
+       }
+
+       bool has_mesh = false;
+       bool has_curve = false;
+
+       for (int i = 0; i < object.getNumChildren(); ++i) {
+               const IObject &child = object.getChild(i);
+
+               if (!child.valid()) {
+                       continue;
+               }
+
+               const MetaData &md = child.getMetaData();
+
+               if (IPolyMesh::matches(md)) {
+                       has_mesh = true;
+               }
+               else if (ISubD::matches(md)) {
+                       has_mesh = true;
+               }
+               else if (ICurves::matches(md)) {
+                       has_curve = true;
+               }
+       }
+
+       return has_mesh && has_curve;
+}
+
+static void import_startjob(void *user_data, short *stop, short *do_update, float *progress)
+{
+       ImportJobData *data = static_cast<ImportJobData *>(user_data);
+
+       data->stop = stop;
+       data->do_update = do_update;
+       data->progress = progress;
+
+       IArchive *archive = open_archive(data->filename);
+
+       if (!archive || !archive->valid()) {
+               data->error_code = ABC_ARCHIVE_FAIL;
+               return;
+       }
+
+       CacheFile *cache_file = static_cast<CacheFile *>(BKE_cachefile_add(data->bmain, BLI_path_basename(data->filename)));
+
+       /* Decrement the ID ref-count because it is going to be incremented for each
+        * modifier and constraint that it will be attached to, so since currently
+        * it is not used by anyone, its use count will off by one. */
+       id_us_min(&cache_file->id);
+
+       cache_file->is_sequence = data->settings.is_sequence;
+       cache_file->scale = data->settings.scale;
+       cache_file->handle = handle_from_archive(archive);
+       BLI_strncpy(cache_file->filepath, data->filename, 1024);
+
+       data->settings.cache_file = cache_file;
+
+       *data->do_update = true;
+       *data->progress = 0.05f;
+
+       data->parent_map = BLI_ghash_str_new("alembic parent ghash");
+
+       /* Parse Alembic Archive. */
+
+       visit_object(archive->getTop(), data->readers, data->parent_map, data->settings);
+
+       if (G.is_break) {
+               data->was_cancelled = true;
+               return;
+       }
+
+       *data->do_update = true;
+       *data->progress = 0.1f;
+
+       /* Create objects and set scene frame range. */
+
+       const float size = static_cast<float>(data->readers.size());
+       size_t i = 0;
+
+       chrono_t min_time = std::numeric_limits<chrono_t>::max();
+       chrono_t max_time = std::numeric_limits<chrono_t>::min();
+
+       std::vector<AbcObjectReader *>::iterator iter;
+       for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
+               AbcObjectReader *reader = *iter;
+
+               if (reader->valid()) {
+                       reader->readObjectData(data->bmain, 0.0f);
+                       reader->readObjectMatrix(0.0f);
+
+                       min_time = std::min(min_time, reader->minTime());
+                       max_time = std::max(max_time, reader->maxTime());
+               }
+
+               *data->progress = 0.1f + 0.6f * (++i / size);
+               *data->do_update = true;
+
+               if (G.is_break) {
+                       data->was_cancelled = true;
+                       return;
+               }
+       }
+
+       if (data->settings.set_frame_range) {
+               Scene *scene = data->scene;
+
+               if (data->settings.is_sequence) {
+                       SFRA = data->settings.offset;
+                       EFRA = SFRA + (data->settings.sequence_len - 1);
+                       CFRA = SFRA;
+               }
+               else if (min_time < max_time) {
+                       SFRA = min_time * FPS;
+                       EFRA = max_time * FPS;
+                       CFRA = SFRA;
+               }
+       }
+
+       /* Setup parentship. */
+
+       i = 0;
+       for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
+               const AbcObjectReader *reader = *iter;
+               const AbcObjectReader *parent_reader = NULL;
+               const IObject &iobject = reader->iobject();
+
+               IObject parent = iobject.getParent();
+
+               if (!IXform::matches(iobject.getHeader())) {
+                       /* In the case of an non XForm node, the parent is the transform
+                        * matrix of the data itself, so we get the its grand parent.
+                        */
+
+                       /* Special case with object only containing a mesh and some strands,
+                        * we want both objects to be parented to the same object. */
+                       if (!is_mesh_and_strands(parent)) {
+                               parent = parent.getParent();
+                       }
+               }
+
+               parent_reader = reinterpret_cast<AbcObjectReader *>(
+                                   BLI_ghash_lookup(data->parent_map, parent.getFullName().c_str()));
+
+               if (parent_reader) {
+                       Object *parent = parent_reader->object();
+
+                       if (parent != NULL && reader->object() != parent) {
+                               Object *ob = reader->object();
+                               ob->parent = parent;
+                       }
+               }
+
+               *data->progress = 0.7f + 0.3f * (++i / size);
+               *data->do_update = true;
+
+               if (G.is_break) {
+                       data->was_cancelled = true;
+                       return;
+               }
+       }
+}
+
+static void import_endjob(void *user_data)
+{
+       ImportJobData *data = static_cast<ImportJobData *>(user_data);
+
+       std::vector<AbcObjectReader *>::iterator iter;
+
+       /* Delete objects on cancelation. */
+       if (data->was_cancelled) {
+               for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
+                       Object *ob = (*iter)->object();
+
+                       if (ob->data) {
+                               BKE_libblock_free_us(data->bmain, ob->data);
+                               ob->data = NULL;
+                       }
+
+                       BKE_libblock_free_us(data->bmain, ob);
+               }
+       }
+       else {
+               /* Add object to scene. */
+               BKE_scene_base_deselect_all(data->scene);
+
+               for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
+                       Object *ob = (*iter)->object();
+                       ob->lay = data->scene->lay;
+
+                       BKE_scene_base_add(data->scene, ob);
+
+                       DAG_id_tag_update_ex(data->bmain, &ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME);
+               }
+
+               DAG_relations_tag_update(data->bmain);
+       }
+
+       for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) {
+               delete *iter;
+       }
+
+       if (data->parent_map) {
+               BLI_ghash_free(data->parent_map, NULL, NULL);
+       }
+
+       switch (data->error_code) {
+               default:
+               case ABC_NO_ERROR:
+                       break;
+               case ABC_ARCHIVE_FAIL:
+                       WM_report(RPT_ERROR, "Could not open Alembic archive for reading! See console for detail.");
+                       break;
+       }
+
+       WM_main_add_notifier(NC_SCENE | ND_FRAME, data->scene);
+}
+
+static void import_freejob(void *user_data)
+{
+       ImportJobData *data = static_cast<ImportJobData *>(user_data);
+       delete data;
+}
+
+void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence, bool set_frame_range, int sequence_len, int offset, bool validate_meshes)
+{
+       /* Using new here since MEM_* funcs do not call ctor to properly initialize
+        * data. */
+       ImportJobData *job = new ImportJobData();
+       job->bmain = CTX_data_main(C);
+       job->scene = CTX_data_scene(C);
+       BLI_strncpy(job->filename, filepath, 1024);
+
+       job->settings.scale = scale;
+       job->settings.is_sequence = is_sequence;
+       job->settings.set_frame_range = set_frame_range;
+       job->settings.sequence_len = sequence_len;
+       job->settings.offset = offset;
+       job->settings.validate_meshes = validate_meshes;
+       job->parent_map = NULL;
+       job->error_code = ABC_NO_ERROR;
+       job->was_cancelled = false;
+
+       G.is_break = false;
+
+       wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
+                                   CTX_wm_window(C),
+                                   job->scene,
+                                   "Alembic Import",
+                                   WM_JOB_PROGRESS,
+                                   WM_JOB_TYPE_ALEMBIC);
+
+       /* setup job */
+       WM_jobs_customdata_set(wm_job, job, import_freejob);
+       WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME);
+       WM_jobs_callbacks(wm_job, import_startjob, NULL, NULL, import_endjob);
+
+       WM_jobs_start(CTX_wm_manager(C), wm_job);
+}
+
+/* ******************************* */
+
+void ABC_get_transform(AbcArchiveHandle *handle, Object *ob, const char *object_path, float r_mat[4][4], float time, float scale)
+{
+       IArchive *archive = archive_from_handle(handle);
+
+       if (!archive || !archive->valid()) {
+               return;
+       }
+
+       IObject tmp;
+       find_iobject(archive->getTop(), tmp, object_path);
+
+       IXform ixform;
+
+       if (IXform::matches(tmp.getHeader())) {
+               ixform = IXform(tmp, kWrapExisting);
+       }
+       else {
+               ixform = IXform(tmp.getParent(), kWrapExisting);
+       }
+
+       IXformSchema schema = ixform.getSchema();
+
+       if (!schema.valid()) {
+               return;
+       }
+
+       ISampleSelector sample_sel(time);
+
+       create_input_transform(sample_sel, ixform, ob, r_mat, scale);
+}
+
+/* ***************************************** */
+
+static bool check_smooth_poly_flag(DerivedMesh *dm)
+{
+       MPoly *mpolys = dm->getPolyArray(dm);
+
+       for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) {
+               MPoly &poly = mpolys[i];
+
+               if ((poly.flag & ME_SMOOTH) != 0) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+static void set_smooth_poly_flag(DerivedMesh *dm)
+{
+       MPoly *mpolys = dm->getPolyArray(dm);
+
+       for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) {
+               MPoly &poly = mpolys[i];
+               poly.flag |= ME_SMOOTH;
+       }
+}
+
+static void *add_customdata_cb(void *user_data, const char *name, int data_type)
+{
+       DerivedMesh *dm = static_cast<DerivedMesh *>(user_data);
+       CustomDataType cd_data_type = static_cast<CustomDataType>(data_type);
+       void *cd_ptr = NULL;
+
+       if (ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) {
+               cd_ptr = CustomData_get_layer_named(dm->getLoopDataLayout(dm), cd_data_type, name);
+
+               if (cd_ptr == NULL) {
+                       cd_ptr = CustomData_add_layer_named(dm->getLoopDataLayout(dm),
+                                                           cd_data_type,
+                                                           CD_DEFAULT,
+                                                           NULL,
+                                                           dm->getNumLoops(dm),
+                                                           name);
+               }
+       }
+
+       return cd_ptr;
+}
+
+ABC_INLINE CDStreamConfig get_config(DerivedMesh *dm)
+{
+       CDStreamConfig config;
+
+       config.user_data = dm;
+       config.mvert = dm->getVertArray(dm);
+       config.mloop = dm->getLoopArray(dm);
+       config.mpoly = dm->getPolyArray(dm);
+       config.totloop = dm->getNumLoops(dm);
+       config.totpoly = dm->getNumPolys(dm);
+       config.loopdata = dm->getLoopDataLayout(dm);
+       config.add_customdata_cb = add_customdata_cb;
+
+       return config;
+}
+
+static DerivedMesh *read_mesh_sample(DerivedMesh *dm, const IObject &iobject, const float time, int read_flag)
+{
+       IPolyMesh mesh(iobject, kWrapExisting);
+       IPolyMeshSchema schema = mesh.getSchema();
+       ISampleSelector sample_sel(time);
+       const IPolyMeshSchema::Sample sample = schema.getValue(sample_sel);
+
+       const P3fArraySamplePtr &positions = sample.getPositions();
+       const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
+       const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
+
+       DerivedMesh *new_dm = NULL;
+
+       /* Only read point data when streaming meshes, unless we need to create new ones. */
+       ImportSettings settings;
+       settings.read_flag |= read_flag;
+
+       if (dm->getNumVerts(dm) != positions->size()) {
+               new_dm = CDDM_from_template(dm,
+                                           positions->size(),
+                                           0,
+                                           0,
+                                           face_indices->size(),
+                                           face_counts->size());
+
+               settings.read_flag |= MOD_MESHSEQ_READ_ALL;
+       }
+
+       CDStreamConfig config = get_config(new_dm ? new_dm : dm);
+
+       bool has_loop_normals = false;
+       read_mesh_sample(&settings, schema, sample_sel, config, has_loop_normals);
+
+       if (new_dm) {
+               /* Check if we had ME_SMOOTH flag set to restore it. */
+               if (!has_loop_normals && check_smooth_poly_flag(dm)) {
+                       set_smooth_poly_flag(new_dm);
+               }
+
+               CDDM_calc_normals(new_dm);
+               CDDM_calc_edges(new_dm);
+
+               return new_dm;
+       }
+
+       return dm;
+}
+
+using Alembic::AbcGeom::ISubDSchema;
+
+static DerivedMesh *read_subd_sample(DerivedMesh *dm, const IObject &iobject, const float time, int read_flag)
+{
+       ISubD mesh(iobject, kWrapExisting);
+       ISubDSchema schema = mesh.getSchema();
+       ISampleSelector sample_sel(time);
+       const ISubDSchema::Sample sample = schema.getValue(sample_sel);
+
+       const P3fArraySamplePtr &positions = sample.getPositions();
+       const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices();
+       const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts();
+
+       DerivedMesh *new_dm = NULL;
+
+       ImportSettings settings;
+       settings.read_flag |= read_flag;
+
+       if (dm->getNumVerts(dm) != positions->size()) {
+               new_dm = CDDM_from_template(dm,
+                                           positions->size(),
+                                           0,
+                                           0,
+                                           face_indices->size(),
+                                           face_counts->size());
+
+               settings.read_flag |= MOD_MESHSEQ_READ_ALL;
+       }
+
+       /* Only read point data when streaming meshes, unless we need to create new ones. */
+       CDStreamConfig config = get_config(new_dm ? new_dm : dm);
+       read_subd_sample(&settings, schema, sample_sel, config);
+
+       if (new_dm) {
+               /* Check if we had ME_SMOOTH flag set to restore it. */
+               if (check_smooth_poly_flag(dm)) {
+                       set_smooth_poly_flag(new_dm);
+               }
+
+               CDDM_calc_normals(new_dm);
+               CDDM_calc_edges(new_dm);
+
+               return new_dm;
+       }
+
+       return dm;
+}
+
+static DerivedMesh *read_points_sample(DerivedMesh *dm, const IObject &iobject, const float time)
+{
+       IPoints points(iobject, kWrapExisting);
+       IPointsSchema schema = points.getSchema();
+       ISampleSelector sample_sel(time);
+       const IPointsSchema::Sample sample = schema.getValue(sample_sel);
+
+       const P3fArraySamplePtr &positions = sample.getPositions();
+
+       DerivedMesh *new_dm = NULL;
+
+       if (dm->getNumVerts(dm) != positions->size()) {
+               new_dm = CDDM_new(positions->size(), 0, 0, 0, 0);
+       }
+
+       CDStreamConfig config = get_config(new_dm ? new_dm : dm);
+       read_points_sample(schema, sample_sel, config, time);
+
+       return new_dm ? new_dm : dm;
+}
+
+/* NOTE: Alembic only stores data about control points, but the DerivedMesh
+ * passed from the cache modifier contains the displist, which has more data
+ * than the control points, so to avoid corrupting the displist we modify the
+ * object directly and create a new DerivedMesh from that. Also we might need to
+ * create new or delete existing NURBS in the curve.
+ */
+static DerivedMesh *read_curves_sample(Object *ob, const IObject &iobject, const float time)
+{
+       ICurves points(iobject, kWrapExisting);
+       ICurvesSchema schema = points.getSchema();
+       ISampleSelector sample_sel(time);
+       const ICurvesSchema::Sample sample = schema.getValue(sample_sel);
+
+       const P3fArraySamplePtr &positions = sample.getPositions();
+       const Int32ArraySamplePtr num_vertices = sample.getCurvesNumVertices();
+
+       int vertex_idx = 0;
+       int curve_idx = 0;
+       Curve *curve = static_cast<Curve *>(ob->data);
+
+       const int curve_count = BLI_listbase_count(&curve->nurb);
+
+       if (curve_count != num_vertices->size()) {
+               BKE_nurbList_free(&curve->nurb);
+               read_curve_sample(curve, schema, time);
+       }
+       else {
+               Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first);
+               for (; nurbs; nurbs = nurbs->next, ++curve_idx) {
+                       const int totpoint = (*num_vertices)[curve_idx];
+
+                       if (nurbs->bp) {
+                               BPoint *point = nurbs->bp;
+
+                               for (int i = 0; i < totpoint; ++i, ++point, ++vertex_idx) {
+                                       const Imath::V3f &pos = (*positions)[vertex_idx];
+                                       copy_yup_zup(point->vec, pos.getValue());
+                               }
+                       }
+                       else if (nurbs->bezt) {
+                               BezTriple *bezier = nurbs->bezt;
+
+                               for (int i = 0; i < totpoint; ++i, ++bezier, ++vertex_idx) {
+                                       const Imath::V3f &pos = (*positions)[vertex_idx];
+                                       copy_yup_zup(bezier->vec[1], pos.getValue());
+                               }
+                       }
+               }
+       }
+
+       return CDDM_from_curve(ob);
+}
+
+DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle,
+                           Object *ob,
+                           DerivedMesh *dm,
+                           const char *object_path,
+                           const float time,
+                           const char **err_str,
+                           int read_flag)
+{
+       IArchive *archive = archive_from_handle(handle);
+
+       if (!archive || !archive->valid()) {
+               *err_str = "Invalid archive!";
+               return NULL;
+       }
+
+       IObject iobject;
+       find_iobject(archive->getTop(), iobject, object_path);
+
+       if (!iobject.valid()) {
+               *err_str = "Invalid object: verify object path";
+               return NULL;
+       }
+
+       const ObjectHeader &header = iobject.getHeader();
+
+       if (IPolyMesh::matches(header)) {
+               if (ob->type != OB_MESH) {
+                       *err_str = "Object type mismatch: object path points to a mesh!";
+                       return NULL;
+               }
+
+               return read_mesh_sample(dm, iobject, time, read_flag);
+       }
+       else if (ISubD::matches(header)) {
+               if (ob->type != OB_MESH) {
+                       *err_str = "Object type mismatch: object path points to a subdivision mesh!";
+                       return NULL;
+               }
+
+               return read_subd_sample(dm, iobject, time, read_flag);
+       }
+       else if (IPoints::matches(header)) {
+               if (ob->type != OB_MESH) {
+                       *err_str = "Object type mismatch: object path points to a point cloud (requires a mesh object)!";
+                       return NULL;
+               }
+
+               return read_points_sample(dm, iobject, time);
+       }
+       else if (ICurves::matches(header)) {
+               if (ob->type != OB_CURVE) {
+                       *err_str = "Object type mismatch: object path points to a curve!";
+                       return NULL;
+               }
+
+               return read_curves_sample(ob, iobject, time);
+       }
+
+       *err_str = "Unsupported object type: verify object path"; // or poke developer
+       return NULL;
+}
diff --git a/source/blender/blenkernel/BKE_cachefile.h b/source/blender/blenkernel/BKE_cachefile.h
new file mode 100644 (file)
index 0000000..51b161f
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_CACHEFILE_H__
+#define __BKE_CACHEFILE_H__
+
+/** \file BKE_cachefile.h
+ *  \ingroup bke
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct CacheFile;
+struct Main;
+struct Scene;
+
+void *BKE_cachefile_add(struct Main *bmain, const char *name);
+
+void BKE_cachefile_init(struct CacheFile *cache_file);
+
+void BKE_cachefile_free(struct CacheFile *cache_file);
+
+struct CacheFile *BKE_cachefile_copy(struct Main *bmain, struct CacheFile *cache_file);
+
+void BKE_cachefile_make_local(struct Main *bmain, struct CacheFile *cache_file, const bool lib_local);
+
+void BKE_cachefile_reload(const struct Main *bmain, struct CacheFile *cache_file);
+
+void BKE_cachefile_ensure_handle(const struct Main *bmain, struct CacheFile *cache_file);
+
+void BKE_cachefile_update_frame(struct Main *bmain, struct Scene *scene, float ctime, const float fps);
+
+bool BKE_cachefile_filepath_get(
+        const struct Main *bmain, const struct CacheFile *cache_file, float frame,
+        char r_filename[1024]);
+
+float BKE_cachefile_time_offset(struct CacheFile *cache_file, const float time, const float fps);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* __BKE_CACHEFILE_H__ */
index 30ac310c2df62ea8d6a041fd5e089cc6a70a50e1..4da6a61cbfacb8eaa933c9f7e272f8c3c1bd3d28 100644 (file)
@@ -39,6 +39,7 @@ extern "C" {
 
 struct ARegion;
 struct bScreen;
+struct CacheFile;
 struct ListBase;
 struct Main;
 struct Object;
@@ -271,6 +272,8 @@ struct Text *CTX_data_edit_text(const bContext *C);
 struct MovieClip *CTX_data_edit_movieclip(const bContext *C);
 struct Mask *CTX_data_edit_mask(const bContext *C);
 
+struct CacheFile *CTX_data_edit_cachefile(const bContext *C);
+
 int CTX_data_selected_nodes(const bContext *C, ListBase *list);
 
 struct EditBone *CTX_data_active_bone(const bContext *C);
index e32bc2ffb206829b343ebc23aaae9dade90fd111..e49019fcfaecd252f512717fc54c2ecc2f4c67dd 100644 (file)
@@ -94,7 +94,7 @@ void id_clear_lib_data_ex(struct Main *bmain, struct ID *id, const bool id_in_ma
 
 struct ListBase *which_libbase(struct Main *mainlib, short type);
 
-#define MAX_LIBARRAY    34
+#define MAX_LIBARRAY    35
 int set_listbasepointers(struct Main *main, struct ListBase *lb[MAX_LIBARRAY]);
 
 /* Main API */
index 44e4da4e0a3c16da77ba3bd18d88e75c4fc061e1..29219a4f56efaee1be6f0919b4afd8eacbb8a070 100644 (file)
@@ -102,6 +102,7 @@ typedef struct Main {
        ListBase movieclip;
        ListBase mask;
        ListBase linestyle;
+       ListBase cachefiles;
 
        char id_tag_update[256];
 
index b7ff81d603ba5bc614d788f08c79841fecab6bb1..157c4408d6ac8713ebfe8b9bc959ed499da62e5a 100644 (file)
@@ -81,6 +81,7 @@ set(SRC
        intern/brush.c
        intern/bullet.c
        intern/bvhutils.c
+       intern/cachefile.c
        intern/camera.c
        intern/cdderivedmesh.c
        intern/cloth.c
@@ -207,6 +208,7 @@ set(SRC
        BKE_brush.h
        BKE_bullet.h
        BKE_bvhutils.h
+       BKE_cachefile.h
        BKE_camera.h
        BKE_ccg.h
        BKE_cdderivedmesh.h
@@ -500,6 +502,13 @@ if(WITH_FREESTYLE)
        add_definitions(-DWITH_FREESTYLE)
 endif()
 
+if(WITH_ALEMBIC)
+       list(APPEND INC
+               ../alembic
+       )
+       add_definitions(-DWITH_ALEMBIC)
+endif()
+
 if(WITH_OPENSUBDIV)
        add_definitions(-DWITH_OPENSUBDIV)
        list(APPEND INC_SYS
index 477c703676290f5d156e21a3f196bdf347619661..1a5d77bbc07a625833a38c416f76e155cab2a8a3 100644 (file)
@@ -97,6 +97,7 @@ bool id_type_can_have_animdata(const short id_type)
                case ID_MC:
                case ID_MSK:
                case ID_GD:
+               case ID_CF:
                        return true;
                
                /* no AnimData */
@@ -1160,6 +1161,9 @@ void BKE_animdata_main_cb(Main *mainptr, ID_AnimData_Edit_Callback func, void *u
        
        /* grease pencil */
        ANIMDATA_IDS_CB(mainptr->gpencil.first);
+
+       /* cache files */
+       ANIMDATA_IDS_CB(mainptr->cachefiles.first);
 }
 
 /* Fix all RNA-Paths throughout the database (directly access the Global.main version)
@@ -1250,6 +1254,9 @@ void BKE_animdata_fix_paths_rename_all(ID *ref_id, const char *prefix, const cha
        
        /* grease pencil */
        RENAMEFIX_ANIM_IDS(mainptr->gpencil.first);
+
+       /* cache files */
+       RENAMEFIX_ANIM_IDS(mainptr->cachefiles.first);
        
        /* scenes */
        RENAMEFIX_ANIM_NODETREE_IDS(mainptr->scene.first, Scene);
@@ -2873,6 +2880,9 @@ void BKE_animsys_evaluate_all_animation(Main *main, Scene *scene, float ctime)
        
        /* grease pencil */
        EVAL_ANIM_IDS(main->gpencil.first, ADT_RECALC_ANIM);
+
+       /* cache files */
+       EVAL_ANIM_IDS(main->cachefiles.first, ADT_RECALC_ANIM);
        
        /* objects */
        /* ADT_RECALC_ANIM doesn't need to be supplied here, since object AnimData gets
index a708c59fa971820be3e3119f7277553852a233e2..487b8ffa2b561dfaf17e8ee6cd0a8b9087f2e36c 100644 (file)
@@ -48,6 +48,7 @@
 #include "MEM_guardedalloc.h"
 
 #include "DNA_brush_types.h"
+#include "DNA_cachefile_types.h"
 #include "DNA_image_types.h"
 #include "DNA_mesh_types.h"
 #include "DNA_modifier_types.h"
@@ -653,6 +654,12 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int
                        rewrite_path_fixed(clip->name, visit_cb, absbase, bpath_user_data);
                        break;
                }
+               case ID_CF:
+               {
+                       CacheFile *cache_file = (CacheFile *)id;
+                       rewrite_path_fixed(cache_file->filepath, visit_cb, absbase, bpath_user_data);
+                       break;
+               }
                default:
                        /* Nothing to do for other IDs that don't contain file paths. */
                        break;
diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c
new file mode 100644 (file)
index 0000000..16f2637
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2016 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): Kevin Dietrich.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/cachefile.c
+ *  \ingroup bke
+ */
+
+#include "DNA_anim_types.h"
+#include "DNA_cachefile_types.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_fileops.h"
+#include "BLI_listbase.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_animsys.h"
+#include "BKE_cachefile.h"
+#include "BKE_global.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_scene.h"
+
+#ifdef WITH_ALEMBIC
+#  include "ABC_alembic.h"
+#endif
+
+void *BKE_cachefile_add(Main *bmain, const char *name)
+{
+       CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, name);
+
+       BKE_cachefile_init(cache_file);
+
+       return cache_file;
+}
+
+void BKE_cachefile_init(CacheFile *cache_file)
+{
+       cache_file->handle = NULL;
+       cache_file->filepath[0] = '\0';
+       cache_file->override_frame = false;
+       cache_file->frame = 0.0f;
+       cache_file->is_sequence = false;
+       cache_file->scale = 1.0f;
+}
+
+/** Free (or release) any data used by this cachefile (does not free the cachefile itself). */
+void BKE_cachefile_free(CacheFile *cache_file)
+{
+       BKE_animdata_free((ID *)cache_file, false);
+
+#ifdef WITH_ALEMBIC
+       ABC_free_handle(cache_file->handle);
+#endif
+
+       BLI_freelistN(&cache_file->object_paths);
+}
+
+CacheFile *BKE_cachefile_copy(Main *bmain, CacheFile *cache_file)
+{
+       CacheFile *new_cache_file = BKE_libblock_copy(bmain, &cache_file->id);
+       new_cache_file->handle = NULL;
+
+       BLI_listbase_clear(&cache_file->object_paths);
+
+       BKE_id_copy_ensure_local(bmain, &cache_file->id, &new_cache_file->id);
+
+       return new_cache_file;
+}
+
+void BKE_cachefile_make_local(Main *bmain, CacheFile *cache_file, const bool lib_local)
+{
+       BKE_id_make_local_generic(bmain, &cache_file->id, true, lib_local);
+}
+
+void BKE_cachefile_reload(const Main *bmain, CacheFile *cache_file)
+{
+       char filepath[FILE_MAX];
+
+       BLI_strncpy(filepath, cache_file->filepath, sizeof(filepath));
+       BLI_path_abs(filepath, ID_BLEND_PATH(bmain, &cache_file->id));
+
+#ifdef WITH_ALEMBIC
+       if (cache_file->handle) {
+               ABC_free_handle(cache_file->handle);
+       }
+
+       cache_file->handle = ABC_create_handle(filepath, &cache_file->object_paths);
+#endif
+}
+
+void BKE_cachefile_ensure_handle(const Main *bmain, CacheFile *cache_file)
+{
+       if (cache_file->handle == NULL) {
+               BKE_cachefile_reload(bmain, cache_file);
+       }
+}
+
+void BKE_cachefile_update_frame(Main *bmain, Scene *scene, const float ctime, const float fps)
+{
+       CacheFile *cache_file;
+       char filename[FILE_MAX];
+
+       for (cache_file = bmain->cachefiles.first; cache_file; cache_file = cache_file->id.next) {
+               /* Execute drivers only, as animation has already been done. */
+               BKE_animsys_evaluate_animdata(scene, &cache_file->id, cache_file->adt, ctime, ADT_RECALC_DRIVERS);
+
+               if (!cache_file->is_sequence) {
+                       continue;
+               }
+
+               const float time = BKE_cachefile_time_offset(cache_file, ctime, fps);
+
+               if (BKE_cachefile_filepath_get(bmain, cache_file, time, filename)) {
+#ifdef WITH_ALEMBIC
+                       ABC_free_handle(cache_file->handle);
+                       cache_file->handle = ABC_create_handle(filename, NULL);
+#endif
+               }
+       }
+}
+
+bool BKE_cachefile_filepath_get(
+        const Main *bmain, const CacheFile *cache_file, float frame,
+        char r_filepath[FILE_MAX])
+{
+       BLI_strncpy(r_filepath, cache_file->filepath, FILE_MAX);
+       BLI_path_abs(r_filepath, ID_BLEND_PATH(bmain, &cache_file->id));
+
+       int fframe;
+       int frame_len;
+
+       if (cache_file->is_sequence && BLI_path_frame_get(r_filepath, &fframe, &frame_len)) {
+               char ext[32];
+               BLI_path_frame_strip(r_filepath, true, ext);
+               BLI_path_frame(r_filepath, frame, frame_len);
+               BLI_ensure_extension(r_filepath, FILE_MAX, ext);
+
+               /* TODO(kevin): store sequence range? */
+               return BLI_exists(r_filepath);
+       }
+
+       return true;
+}
+
+float BKE_cachefile_time_offset(CacheFile *cache_file, const float time, const float fps)
+{
+       const float frame = (cache_file->override_frame ? cache_file->frame : time);
+       return cache_file->is_sequence ? frame : frame / fps;
+}
index 159d5b82a6ce38e539f879edd368a4bb12bd2d46..d257a1cfcae5e5146bf9920c8d4675f46fb3c27a 100644 (file)
@@ -3402,7 +3402,7 @@ void CDDM_calc_edges(DerivedMesh *dm)
                BLI_edgehashIterator_getKey(ehi, &med->v1, &med->v2);
                j = GET_INT_FROM_POINTER(BLI_edgehashIterator_getValue(ehi));
 
-               if (j == 0) {
+               if (j == 0 || !eindex) {
                        med->flag = ME_EDGEDRAW | ME_EDGERENDER;