Basic Alembic support

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
This commit is contained in:
Kévin Dietrich 2016-08-06 06:20:37 +02:00
parent 4158737cb2
commit 61050f75b1
126 changed files with 9796 additions and 17 deletions

@ -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)

@ -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"

@ -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)

@ -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)

@ -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)

@ -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)

@ -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)

@ -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

@ -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")

@ -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")

@ -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)

@ -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):

@ -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()

@ -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__ */

@ -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}")

@ -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;
}

@ -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__ */

@ -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);
}
}

@ -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__ */

@ -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;
}
}
}

@ -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__ */

@ -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);
}

@ -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__ */

@ -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;
}
}
}

@ -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__ */

File diff suppressed because it is too large Load Diff

@ -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__ */

@ -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);
}
}

@ -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__ */

@ -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;
}

@ -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__ */

@ -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);
}

@ -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__ */

@ -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;
}

@ -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__ */

@ -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;
}

@ -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__ */

File diff suppressed because it is too large Load Diff

@ -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__ */

@ -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);

@ -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 */

@ -102,6 +102,7 @@ typedef struct Main {
ListBase movieclip;
ListBase mask;
ListBase linestyle;
ListBase cachefiles;
char id_tag_update[256];

@ -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

@ -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

@ -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;

@ -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;
}

@ -3402,7 +3402,7 @@ void CDDM_calc_edges(DerivedMesh *dm)
BLI_edgehashIterator_getKey(ehi, &med->v1, &med->v2);
j = GET_INT_FROM_POINTER(BLI_edgehashIterator_getValue(ehi));
if (j == 0) {
if (j == 0 || !eindex) {
med->flag = ME_EDGEDRAW | ME_EDGERENDER;
*index = ORIGINDEX_NONE;
}

@ -46,6 +46,7 @@
#include "BLT_translation.h"
#include "DNA_armature_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_constraint_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
@ -63,6 +64,7 @@
#include "BKE_anim.h" /* for the curve calculation part */
#include "BKE_armature.h"
#include "BKE_bvhutils.h"
#include "BKE_cachefile.h"
#include "BKE_camera.h"
#include "BKE_constraint.h"
#include "BKE_curve.h"
@ -86,6 +88,10 @@
# include "BPY_extern.h"
#endif
#ifdef WITH_ALEMBIC
# include "ABC_alembic.h"
#endif
/* ---------------------------------------------------------------------------- */
/* Useful macros for testing various common flag combinations */
@ -4333,6 +4339,73 @@ static bConstraintTypeInfo CTI_OBJECTSOLVER = {
objectsolver_evaluate /* evaluate */
};
/* ----------- Transform Cache ------------- */
static void transformcache_id_looper(bConstraint *con, ConstraintIDFunc func, void *userdata)
{
bTransformCacheConstraint *data = con->data;
func(con, (ID **)&data->cache_file, false, userdata);
}
static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targets)
{
#ifdef WITH_ALEMBIC
bTransformCacheConstraint *data = con->data;
Scene *scene = cob->scene;
const float frame = BKE_scene_frame_get(scene);
const float time = BKE_cachefile_time_offset(data->cache_file, frame, FPS);
CacheFile *cache_file = data->cache_file;
BKE_cachefile_ensure_handle(G.main, cache_file);
ABC_get_transform(cache_file->handle, cob->ob, data->object_path,
cob->matrix, time, cache_file->scale);
#else
UNUSED_VARS(con, cob);
#endif
UNUSED_VARS(targets);
}
static void transformcache_copy(bConstraint *con, bConstraint *srccon)
{
bTransformCacheConstraint *src = srccon->data;
bTransformCacheConstraint *dst = con->data;
BLI_strncpy(dst->object_path, src->object_path, sizeof(dst->object_path));
dst->cache_file = src->cache_file;
if (dst->cache_file) {
id_us_plus(&dst->cache_file->id);
}
}
static void transformcache_free(bConstraint *con)
{
bTransformCacheConstraint *data = con->data;
if (data->cache_file) {
id_us_min(&data->cache_file->id);
}
}
static bConstraintTypeInfo CTI_TRANSFORM_CACHE = {
CONSTRAINT_TYPE_TRANSFORM_CACHE, /* type */
sizeof(bTransformCacheConstraint), /* size */
"Transform Cache", /* name */
"bTransformCacheConstraint", /* struct name */
transformcache_free, /* free data */
transformcache_id_looper, /* id looper */
transformcache_copy, /* copy data */
NULL, /* new data */
NULL, /* get constraint targets */
NULL, /* flush constraint targets */
NULL, /* get target matrix */
transformcache_evaluate /* evaluate */
};
/* ************************* Constraints Type-Info *************************** */
/* All of the constraints api functions use bConstraintTypeInfo structs to carry out
* and operations that involve constraint specific code.
@ -4374,6 +4447,7 @@ static void constraints_init_typeinfo(void)
constraintsTypeInfo[26] = &CTI_FOLLOWTRACK; /* Follow Track Constraint */
constraintsTypeInfo[27] = &CTI_CAMERASOLVER; /* Camera Solver Constraint */
constraintsTypeInfo[28] = &CTI_OBJECTSOLVER; /* Object Solver Constraint */
constraintsTypeInfo[29] = &CTI_TRANSFORM_CACHE; /* Transform Cache Constraint */
}
/* This function should be used for getting the appropriate type-info when only

@ -1067,6 +1067,11 @@ struct EditBone *CTX_data_active_bone(const bContext *C)
return ctx_data_pointer_get(C, "active_bone");
}
struct CacheFile *CTX_data_edit_cachefile(const bContext *C)
{
return ctx_data_pointer_get(C, "edit_cachefile");
}
int CTX_data_selected_bones(const bContext *C, ListBase *list)
{
return ctx_data_collection_get(C, "selected_bones", list);

@ -46,6 +46,7 @@
#include "DNA_anim_types.h"
#include "DNA_camera_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_group_types.h"
#include "DNA_lamp_types.h"
#include "DNA_lattice_types.h"
@ -2173,7 +2174,12 @@ static void dag_object_time_update_flags(Main *bmain, Scene *scene, Object *ob)
if (cti) {
/* special case for camera tracking -- it doesn't use targets to define relations */
if (ELEM(cti->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_CAMERASOLVER, CONSTRAINT_TYPE_OBJECTSOLVER)) {
if (ELEM(cti->type,
CONSTRAINT_TYPE_FOLLOWTRACK,
CONSTRAINT_TYPE_CAMERASOLVER,
CONSTRAINT_TYPE_OBJECTSOLVER,
CONSTRAINT_TYPE_TRANSFORM_CACHE))
{
ob->recalc |= OB_RECALC_OB;
}
else if (cti->get_constraint_targets) {
@ -3001,6 +3007,33 @@ void DAG_id_tag_update_ex(Main *bmain, ID *id, short flag)
/* BLI_assert(!"invalid flag for this 'idtype'"); */
}
}
else if (GS(id->name) == ID_CF) {
for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
ModifierData *md = modifiers_findByType(ob, eModifierType_MeshSequenceCache);
if (md) {
MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md;
if (mcmd->cache_file && (&mcmd->cache_file->id == id)) {
ob->recalc |= OB_RECALC_DATA;
continue;
}
}
for (bConstraint *con = ob->constraints.first; con; con = con->next) {
if (con->type != CONSTRAINT_TYPE_TRANSFORM_CACHE) {
continue;
}
bTransformCacheConstraint *data = con->data;
if (data->cache_file && (&data->cache_file->id == id)) {
ob->recalc |= OB_RECALC_DATA;
break;
}
}
}
}
}
void DAG_id_tag_update(ID *id, short flag)

@ -60,6 +60,7 @@ static IDType idtypes[] = {
{ ID_AR, "Armature", "armatures", BLT_I18NCONTEXT_ID_ARMATURE, IDTYPE_FLAGS_ISLINKABLE },
{ ID_BR, "Brush", "brushes", BLT_I18NCONTEXT_ID_BRUSH, IDTYPE_FLAGS_ISLINKABLE },
{ ID_CA, "Camera", "cameras", BLT_I18NCONTEXT_ID_CAMERA, IDTYPE_FLAGS_ISLINKABLE },
{ ID_CF, "CacheFile", "cache_files", BLT_I18NCONTEXT_ID_CACHEFILE, IDTYPE_FLAGS_ISLINKABLE },
{ ID_CU, "Curve", "curves", BLT_I18NCONTEXT_ID_CURVE, IDTYPE_FLAGS_ISLINKABLE },
{ ID_GD, "GPencil", "grease_pencil", BLT_I18NCONTEXT_ID_GPENCIL, IDTYPE_FLAGS_ISLINKABLE }, /* rename gpencil */
{ ID_GR, "Group", "groups", BLT_I18NCONTEXT_ID_GROUP, IDTYPE_FLAGS_ISLINKABLE },
@ -184,6 +185,7 @@ int BKE_idcode_to_idfilter(const short idcode)
CASE_IDFILTER(AR);
CASE_IDFILTER(BR);
CASE_IDFILTER(CA);
CASE_IDFILTER(CF);
CASE_IDFILTER(CU);
CASE_IDFILTER(GD);
CASE_IDFILTER(GR);
@ -227,6 +229,7 @@ short BKE_idcode_from_idfilter(const int idfilter)
CASE_IDFILTER(AR);
CASE_IDFILTER(BR);
CASE_IDFILTER(CA);
CASE_IDFILTER(CF);
CASE_IDFILTER(CU);
CASE_IDFILTER(GD);
CASE_IDFILTER(GR);

@ -45,6 +45,7 @@
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_brush_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
#include "DNA_group_types.h"
#include "DNA_gpencil_types.h"
@ -56,6 +57,7 @@
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
#include "DNA_modifier_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_mask_types.h"
#include "DNA_node_types.h"
@ -81,6 +83,7 @@
#include "BKE_bpath.h"
#include "BKE_brush.h"
#include "BKE_camera.h"
#include "BKE_cachefile.h"
#include "BKE_context.h"
#include "BKE_curve.h"
#include "BKE_depsgraph.h"
@ -425,6 +428,9 @@ bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local)
case ID_PC:
if (!test) BKE_paint_curve_make_local(bmain, (PaintCurve *)id, lib_local);
return true;
case ID_CF:
if (!test) BKE_cachefile_make_local(bmain, (CacheFile *)id, lib_local);
return true;
case ID_SCR:
case ID_LI:
case ID_KE:
@ -529,6 +535,9 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test)
case ID_PC:
if (!test) *newid = (ID *)BKE_paint_curve_copy(bmain, (PaintCurve *)id);
return true;
case ID_CF:
if (!test) *newid = (ID *)BKE_cachefile_copy(bmain, (CacheFile *)id);
return true;
case ID_SCE:
case ID_LI:
case ID_SCR:
@ -641,6 +650,8 @@ ListBase *which_libbase(Main *mainlib, short type)
return &(mainlib->palettes);
case ID_PC:
return &(mainlib->paintcurves);
case ID_CF:
return &(mainlib->cachefiles);
}
return NULL;
}
@ -764,6 +775,7 @@ int set_listbasepointers(Main *main, ListBase **lb)
lb[a++] = &(main->armature);
lb[a++] = &(main->cachefiles);
lb[a++] = &(main->mesh);
lb[a++] = &(main->curve);
lb[a++] = &(main->mball);
@ -915,6 +927,9 @@ void *BKE_libblock_alloc_notest(short type)
case ID_PC:
id = MEM_callocN(sizeof(PaintCurve), "Paint Curve");
break;
case ID_CF:
id = MEM_callocN(sizeof(CacheFile), "Cache File");
break;
}
return id;
}
@ -1041,6 +1056,9 @@ void BKE_libblock_init_empty(ID *id)
case ID_LS:
BKE_linestyle_init((FreestyleLineStyle *)id);
break;
case ID_CF:
BKE_cachefile_init((CacheFile *)id);
break;
case ID_KE:
/* Shapekeys are a complex topic too - they depend on their 'user' data type...
* They are not linkable, though, so it should never reach here anyway. */
@ -1228,6 +1246,7 @@ void BKE_main_free(Main *mainvar)
case 31: BKE_libblock_free_ex(mainvar, id, false); break;
case 32: BKE_libblock_free_ex(mainvar, id, false); break;
case 33: BKE_libblock_free_ex(mainvar, id, false); break;
case 34: BKE_libblock_free_ex(mainvar, id, false); break;
default:
BLI_assert(0);
break;

@ -854,6 +854,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u
case ID_WM:
case ID_PAL:
case ID_PC:
case ID_CF:
break;
/* Deprecated. */

@ -38,6 +38,7 @@
#include "DNA_armature_types.h"
#include "DNA_brush_types.h"
#include "DNA_camera_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_group_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_ipo_types.h"
@ -69,6 +70,7 @@
#include "BKE_armature.h"
#include "BKE_brush.h"
#include "BKE_camera.h"
#include "BKE_cachefile.h"
#include "BKE_curve.h"
#include "BKE_depsgraph.h"
#include "BKE_fcurve.h"
@ -812,6 +814,9 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user)
case ID_PC:
BKE_paint_curve_free((PaintCurve *)id);
break;
case ID_CF:
BKE_cachefile_free((CacheFile *)id);
break;
}
/* avoid notifying on removed data */

@ -65,6 +65,7 @@
#include "BKE_animsys.h"
#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_cachefile.h"
#include "BKE_colortools.h"
#include "BKE_depsgraph.h"
#include "BKE_editmesh.h"
@ -1926,6 +1927,9 @@ void BKE_scene_update_for_newframe_ex(EvaluationContext *eval_ctx, Main *bmain,
BKE_mask_evaluate_all_masks(bmain, ctime, true);
/* Update animated cache files for modifiers. */
BKE_cachefile_update_frame(bmain, sce, ctime, (((double)sce->r.frs_sec) / (double)sce->r.frs_sec_base));
#ifdef POSE_ANIMATION_WORKAROUND
scene_armature_depsgraph_workaround(bmain);
#endif

@ -77,6 +77,13 @@ if(WITH_CODEC_FFMPEG)
add_definitions(-DWITH_FFMPEG)
endif()
if(WITH_ALEMBIC)
list(APPEND INC
../alembic
)
add_definitions(-DWITH_ALEMBIC)
endif()
blender_add_lib(bf_blenloader "${SRC}" "${INC}" "${INC_SYS}")
# needed so writefile.c can use dna_type_offsets.h

@ -59,6 +59,7 @@
#include "DNA_actuator_types.h"
#include "DNA_brush_types.h"
#include "DNA_camera_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_cloth_types.h"
#include "DNA_controller_types.h"
#include "DNA_constraint_types.h"
@ -114,6 +115,7 @@
#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_brush.h"
#include "BKE_cachefile.h"
#include "BKE_cloth.h"
#include "BKE_constraint.h"
#include "BKE_context.h"
@ -2691,6 +2693,36 @@ static void direct_link_animdata(FileData *fd, AnimData *adt)
adt->actstrip = newdataadr(fd, adt->actstrip);
}
/* ************ READ CACHEFILES *************** */
static void lib_link_cachefiles(FileData *fd, Main *bmain)
{
CacheFile *cache_file;
/* only link ID pointers */
for (cache_file = bmain->cachefiles.first; cache_file; cache_file = cache_file->id.next) {
if (cache_file->id.tag & LIB_TAG_NEED_LINK) {
cache_file->id.tag &= ~LIB_TAG_NEED_LINK;
}
BLI_listbase_clear(&cache_file->object_paths);
cache_file->handle = NULL;
if (cache_file->adt) {
lib_link_animdata(fd, &cache_file->id, cache_file->adt);
}
}
}
static void direct_link_cachefile(FileData *fd, CacheFile *cache_file)
{
cache_file->handle = NULL;
/* relink animdata */
cache_file->adt = newdataadr(fd, cache_file->adt);
direct_link_animdata(fd, cache_file->adt);
}
/* ************ READ MOTION PATHS *************** */
/* direct data for cache */
@ -7912,6 +7944,7 @@ static const char *dataname(short id_code)
case ID_MC: return "Data from MC";
case ID_MSK: return "Data from MSK";
case ID_LS: return "Data from LS";
case ID_CF: return "Data from CF";
}
return "Data from Lib Block";
@ -8163,6 +8196,9 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const short
case ID_PC:
direct_link_paint_curve(fd, (PaintCurve *)id);
break;
case ID_CF:
direct_link_cachefile(fd, (CacheFile *)id);
break;
}
oldnewmap_free_unused(fd->datamap);
@ -8356,6 +8392,7 @@ static void lib_link_all(FileData *fd, Main *main)
lib_link_mask(fd, main);
lib_link_linestyle(fd, main);
lib_link_gpencil(fd, main);
lib_link_cachefiles(fd, main);
lib_link_mesh(fd, main); /* as last: tpage images with users at zero */
@ -9462,6 +9499,13 @@ static void expand_camera(FileData *fd, Main *mainvar, Camera *ca)
expand_animdata(fd, mainvar, ca->adt);
}
static void expand_cachefile(FileData *fd, Main *mainvar, CacheFile *cache_file)
{
if (cache_file->adt) {
expand_animdata(fd, mainvar, cache_file->adt);
}
}
static void expand_speaker(FileData *fd, Main *mainvar, Speaker *spk)
{
expand_doit(fd, mainvar, spk->sound);
@ -9657,6 +9701,9 @@ void BLO_expand_main(void *fdhandle, Main *mainvar)
case ID_GD:
expand_gpencil(fd, mainvar, (bGPdata *)id);
break;
case ID_CF:
expand_cachefile(fd, mainvar, (CacheFile *)id);
break;
}
do_it = true;

@ -107,6 +107,7 @@
#include "DNA_armature_types.h"
#include "DNA_actuator_types.h"
#include "DNA_brush_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
#include "DNA_cloth_types.h"
#include "DNA_constraint_types.h"
@ -3882,6 +3883,21 @@ static void write_linestyles(WriteData *wd, ListBase *idbase)
}
}
static void write_cachefiles(WriteData *wd, ListBase *idbase)
{
CacheFile *cache_file;
for (cache_file = idbase->first; cache_file; cache_file = cache_file->id.next) {
if (cache_file->id.us > 0 || wd->current) {
writestruct(wd, ID_CF, CacheFile, 1, cache_file);
if (cache_file->adt) {
write_animdata(wd, cache_file->adt);
}
}
}
}
/* Keep it last of write_foodata functions. */
static void write_libraries(WriteData *wd, Main *main)
{
@ -4079,6 +4095,7 @@ static bool write_file_handle(
write_paintcurves(wd, &mainvar->paintcurves);
write_gpencils(wd, &mainvar->gpencil);
write_linestyles(wd, &mainvar->linestyle);
write_cachefiles(wd, &mainvar->cachefiles);
write_libraries(wd, mainvar->next);
/* So changes above don't cause a 'DNA1' to be detected as changed on undo. */

@ -120,6 +120,7 @@ bool BLT_lang_is_ime_supported(void);
#define BLT_I18NCONTEXT_ID_ARMATURE "Armature"
#define BLT_I18NCONTEXT_ID_BRUSH "Brush"
#define BLT_I18NCONTEXT_ID_CAMERA "Camera"
#define BLT_I18NCONTEXT_ID_CACHEFILE "CacheFile"
#define BLT_I18NCONTEXT_ID_CURVE "Curve"
#define BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE "FreestyleLineStyle"
#define BLT_I18NCONTEXT_ID_GPENCIL "GPencil"
@ -171,6 +172,7 @@ typedef struct {
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_ARMATURE, "id_armature"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_BRUSH, "id_brush"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CAMERA, "id_camera"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CACHEFILE, "id_cachefile"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVE, "id_curve"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, "id_fs_linestyle"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_GPENCIL, "id_gpencil"), \

@ -79,6 +79,7 @@ void DEG_scene_graph_free(struct Scene *scene);
*/
struct DepsNodeHandle;
struct CacheFile;
struct Object;
typedef enum eDepsSceneComponentType {
@ -100,11 +101,13 @@ typedef enum eDepsObjectComponentType {
DEG_OB_COMP_EVAL_PARTICLES, /* Particle Systems Component */
DEG_OB_COMP_SHADING, /* Material Shading Component */
DEG_OB_COMP_CACHE, /* Cache Component */
} eDepsObjectComponentType;
void DEG_add_scene_relation(struct DepsNodeHandle *node, struct Scene *scene, eDepsSceneComponentType component, const char *description);
void DEG_add_object_relation(struct DepsNodeHandle *node, struct Object *ob, eDepsObjectComponentType component, const char *description);
void DEG_add_bone_relation(struct DepsNodeHandle *handle, struct Object *ob, const char *bone_name, eDepsObjectComponentType component, const char *description);
void DEG_add_object_cache_relation(struct DepsNodeHandle *handle, struct CacheFile *cache_file, eDepsObjectComponentType component, const char *description);
/* TODO(sergey): Remove once all geometry update is granular. */
void DEG_add_special_eval_flag(struct Depsgraph *graph, struct ID *id, short flag);

@ -46,6 +46,7 @@ extern "C" {
#include "DNA_action_types.h"
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
@ -339,6 +340,14 @@ void DepsgraphNodeBuilder::build_scene(Main *bmain, Scene *scene)
if (scene->gpd) {
build_gpencil(scene->gpd);
}
/* cache files */
for (CacheFile *cachefile = static_cast<CacheFile *>(bmain->cachefiles.first);
cachefile;
cachefile = static_cast<CacheFile *>(cachefile->id.next))
{
build_cachefile(cachefile);
}
}
void DepsgraphNodeBuilder::build_group(Scene *scene,
@ -1259,4 +1268,18 @@ void DepsgraphNodeBuilder::build_gpencil(bGPdata *gpd)
build_animdata(gpd_id);
}
void DepsgraphNodeBuilder::build_cachefile(CacheFile *cache_file)
{
ID *cache_file_id = &cache_file->id;
add_component_node(cache_file_id, DEPSNODE_TYPE_CACHE);
add_operation_node(cache_file_id, DEPSNODE_TYPE_CACHE,
DEPSOP_TYPE_EXEC, NULL,
DEG_OPCODE_PLACEHOLDER, "Cache File Update");
add_id_node(cache_file_id);
build_animdata(cache_file_id);
}
} // namespace DEG

@ -33,6 +33,7 @@
#include "intern/depsgraph_types.h"
struct Base;
struct CacheFile;
struct bGPdata;
struct ListBase;
struct GHash;
@ -144,6 +145,7 @@ struct DepsgraphNodeBuilder {
void build_world(World *world);
void build_compositor(Scene *scene);
void build_gpencil(bGPdata *gpd);
void build_cachefile(CacheFile *cache_file);
protected:
Main *m_bmain;

@ -47,6 +47,7 @@ extern "C" {
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_camera_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
#include "DNA_effect_types.h"
@ -599,6 +600,18 @@ void DepsgraphRelationBuilder::build_constraints(Scene *scene, ID *id, eDepsNode
TimeSourceKey time_src_key;
add_relation(time_src_key, constraint_op_key, DEPSREL_TYPE_TIME, "[TimeSrc -> Animation]");
}
else if (cti->type == CONSTRAINT_TYPE_TRANSFORM_CACHE) {
/* TODO(kevin): This is more a TimeSource -> CacheFile -> Constraint dependency chain. */
TimeSourceKey time_src_key;
add_relation(time_src_key, constraint_op_key, DEPSREL_TYPE_TIME, "[TimeSrc -> Animation]");
bTransformCacheConstraint *data = (bTransformCacheConstraint *)con->data;
if (data->cache_file) {
ComponentKey cache_key(&data->cache_file->id, DEPSNODE_TYPE_CACHE);
add_relation(cache_key, constraint_op_key, DEPSREL_TYPE_CACHE, cti->name);
}
}
else if (cti->get_constraint_targets) {
ListBase targets = {NULL, NULL};
cti->get_constraint_targets(con, &targets);

@ -90,6 +90,7 @@ static const int deg_debug_node_type_color_map[][2] = {
{DEPSNODE_TYPE_GEOMETRY, 8},
{DEPSNODE_TYPE_SEQUENCER, 9},
{DEPSNODE_TYPE_SHADING, 10},
{DEPSNODE_TYPE_CACHE, 11},
{-1, 0}
};
#endif
@ -401,6 +402,7 @@ static void deg_debug_graphviz_node(const DebugContext &ctx,
case DEPSNODE_TYPE_EVAL_POSE:
case DEPSNODE_TYPE_BONE:
case DEPSNODE_TYPE_SHADING:
case DEPSNODE_TYPE_CACHE:
case DEPSNODE_TYPE_EVAL_PARTICLES:
{
ComponentDepsNode *comp_node = (ComponentDepsNode *)node;

@ -33,6 +33,7 @@
#include "MEM_guardedalloc.h"
extern "C" {
#include "DNA_cachefile_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@ -89,6 +90,7 @@ static DEG::eDepsNode_Type deg_build_object_component_type(
case DEG_OB_COMP_BONE: return DEG::DEPSNODE_TYPE_BONE;
case DEG_OB_COMP_EVAL_PARTICLES: return DEG::DEPSNODE_TYPE_EVAL_PARTICLES;
case DEG_OB_COMP_SHADING: return DEG::DEPSNODE_TYPE_SHADING;
case DEG_OB_COMP_CACHE: return DEG::DEPSNODE_TYPE_CACHE;
}
return DEG::DEPSNODE_TYPE_UNDEFINED;
}
@ -126,6 +128,20 @@ void DEG_add_object_relation(DepsNodeHandle *handle,
description);
}
void DEG_add_object_cache_relation(DepsNodeHandle *handle,
CacheFile *cache_file,
eDepsObjectComponentType component,
const char *description)
{
DEG::eDepsNode_Type type = deg_build_object_component_type(component);
DEG::ComponentKey comp_key(&cache_file->id, type);
DEG::DepsNodeHandle *deg_handle = get_handle(handle);
deg_handle->builder->add_node_handle_relation(comp_key,
deg_handle,
DEG::DEPSREL_TYPE_CACHE,
description);
}
void DEG_add_bone_relation(DepsNodeHandle *handle,
Object *ob,
const char *bone_name,

@ -133,6 +133,8 @@ typedef enum eDepsNode_Type {
DEPSNODE_TYPE_EVAL_PARTICLES = 23,
/* Material Shading Component */
DEPSNODE_TYPE_SHADING = 24,
/* Cache Component */
DEPSNODE_TYPE_CACHE = 25,
} eDepsNode_Type;
/* Identifiers for common operations (as an enum). */
@ -330,6 +332,9 @@ typedef enum eDepsRelation_Type {
/* relationship is used to trigger editor/screen updates */
DEPSREL_TYPE_UPDATE_UI,
/* cache dependency */
DEPSREL_TYPE_CACHE,
} eDepsRelation_Type;
} // namespace DEG

@ -366,6 +366,11 @@ static DepsNodeFactoryImpl<ParticlesComponentDepsNode> DNTI_EVAL_PARTICLES;
DEG_DEPSNODE_DEFINE(ShadingComponentDepsNode, DEPSNODE_TYPE_SHADING, "Shading Component");
static DepsNodeFactoryImpl<ShadingComponentDepsNode> DNTI_SHADING;
/* Cache Component Defines ============================ */
DEG_DEPSNODE_DEFINE(CacheComponentDepsNode, DEPSNODE_TYPE_CACHE, "Cache Component");
static DepsNodeFactoryImpl<CacheComponentDepsNode> DNTI_CACHE;
/* Node Types Register =================================== */
@ -383,6 +388,8 @@ void deg_register_component_depsnodes()
deg_register_node_typeinfo(&DNTI_EVAL_PARTICLES);
deg_register_node_typeinfo(&DNTI_SHADING);
deg_register_node_typeinfo(&DNTI_CACHE);
}
} // namespace DEG

@ -209,6 +209,10 @@ struct ShadingComponentDepsNode : public ComponentDepsNode {
DEG_DEPSNODE_DECLARE;
};
struct CacheComponentDepsNode : public ComponentDepsNode {
DEG_DEPSNODE_DECLARE;
};
void deg_register_component_depsnodes();

@ -40,6 +40,7 @@
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
#include "DNA_object_types.h"
#include "DNA_particle_types.h"
@ -1577,6 +1578,88 @@ static bAnimChannelType ACF_DSTEX =
/* Camera Expander ------------------------------------------- */
// TODO: just get this from RNA?
static int acf_dscachefile_icon(bAnimListElem *ale)
{
UNUSED_VARS(ale);
return ICON_FILE;
}
/* get the appropriate flag(s) for the setting when it is valid */
static int acf_dscachefile_setting_flag(bAnimContext *ac, eAnimChannel_Settings setting, bool *neg)
{
/* clear extra return data first */
*neg = false;
switch (setting) {
case ACHANNEL_SETTING_EXPAND: /* expanded */
return CACHEFILE_DS_EXPAND;
case ACHANNEL_SETTING_MUTE: /* mute (only in NLA) */
return ADT_NLA_EVAL_OFF;
case ACHANNEL_SETTING_VISIBLE: /* visible (only in Graph Editor) */
*neg = true;
return ADT_CURVES_NOT_VISIBLE;
case ACHANNEL_SETTING_SELECT: /* selected */
return ADT_UI_SELECTED;
default: /* unsupported */
return 0;
}
UNUSED_VARS(ac);
}
/* get pointer to the setting */
static void *acf_dscachefile_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings setting, short *type)
{
CacheFile *cache_file = (CacheFile *)ale->data;
/* clear extra return data first */
*type = 0;
switch (setting) {
case ACHANNEL_SETTING_EXPAND: /* expanded */
return GET_ACF_FLAG_PTR(cache_file->flag, type);
case ACHANNEL_SETTING_SELECT: /* selected */
case ACHANNEL_SETTING_MUTE: /* muted (for NLA only) */
case ACHANNEL_SETTING_VISIBLE: /* visible (for Graph Editor only) */
if (cache_file->adt) {
return GET_ACF_FLAG_PTR(cache_file->adt->flag, type);
}
return NULL;
default: /* unsupported */
return NULL;
}
}
/* CacheFile expander type define. */
static bAnimChannelType ACF_DSCACHEFILE =
{
"Cache File Expander", /* type name */
ACHANNEL_ROLE_EXPANDER, /* role */
acf_generic_dataexpand_color, /* backdrop color */
acf_generic_dataexpand_backdrop, /* backdrop */
acf_generic_indention_1, /* indent level */
acf_generic_basic_offset, /* offset */
acf_generic_idblock_name, /* name */
acf_generic_idfill_name_prop, /* name prop */
acf_dscachefile_icon, /* icon */
acf_generic_dataexpand_setting_valid, /* has setting */
acf_dscachefile_setting_flag, /* flag for setting */
acf_dscachefile_setting_ptr /* pointer for setting */
};
/* Camera Expander ------------------------------------------- */
// TODO: just get this from RNA?
static int acf_dscam_icon(bAnimListElem *UNUSED(ale))
{
@ -3388,6 +3471,7 @@ static void ANIM_init_channel_typeinfo_data(void)
animchannelTypeInfo[type++] = &ACF_DSMAT; /* Material Channel */
animchannelTypeInfo[type++] = &ACF_DSLAM; /* Lamp Channel */
animchannelTypeInfo[type++] = &ACF_DSCAM; /* Camera Channel */
animchannelTypeInfo[type++] = &ACF_DSCACHEFILE; /* CacheFile Channel */
animchannelTypeInfo[type++] = &ACF_DSCUR; /* Curve Channel */
animchannelTypeInfo[type++] = &ACF_DSSKEY; /* ShapeKey Channel */
animchannelTypeInfo[type++] = &ACF_DSWOR; /* World Channel */

@ -120,6 +120,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
case ANIMTYPE_DSCACHEFILE:
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
@ -175,6 +176,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
case ANIMTYPE_DSCACHEFILE:
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
@ -275,6 +277,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types d
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
case ANIMTYPE_DSCACHEFILE:
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
@ -370,6 +373,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types d
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
case ANIMTYPE_DSCACHEFILE:
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:
@ -2716,6 +2720,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
case ANIMTYPE_DSCACHEFILE:
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:

@ -53,6 +53,7 @@
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_camera_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_lamp_types.h"
#include "DNA_lattice_types.h"
#include "DNA_linestyle_types.h"
@ -199,6 +200,16 @@ static bool actedit_get_context(bAnimContext *ac, SpaceAction *saction)
ac->mode = saction->mode;
return true;
case SACTCONT_CACHEFILE: /* Cache File */ /* XXX review how this mode is handled... */
/* update scene-pointer (no need to check for pinning yet, as not implemented) */
saction->ads.source = (ID *)ac->scene;
ac->datatype = ANIMCONT_CHANNEL;
ac->data = &saction->ads;
ac->mode = saction->mode;
return true;
case SACTCONT_MASK: /* Mask */ /* XXX review how this mode is handled... */
{
@ -660,6 +671,19 @@ static bAnimListElem *make_new_animlistelem(void *data, short datatype, ID *owne
ale->adt = BKE_animdata_from_id(data);
break;
}
case ANIMTYPE_DSCACHEFILE:
{
CacheFile *cache_file = (CacheFile *)data;
AnimData *adt = cache_file->adt;
ale->flag = FILTER_CACHEFILE_OBJD(cache_file);
ale->key_data = (adt) ? adt->action : NULL;
ale->datatype = ALE_ACT;
ale->adt = BKE_animdata_from_id(data);
break;
}
case ANIMTYPE_DSCUR:
{
Curve *cu = (Curve *)data;
@ -1751,6 +1775,42 @@ static size_t animdata_filter_ds_gpencil(bAnimContext *ac, ListBase *anim_data,
return items;
}
/* Helper for Cache File data integrated with main DopeSheet */
static size_t animdata_filter_ds_cachefile(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, CacheFile *cache_file, int filter_mode)
{
ListBase tmp_data = {NULL, NULL};
size_t tmp_items = 0;
size_t items = 0;
/* add relevant animation channels for Cache File */
BEGIN_ANIMFILTER_SUBCHANNELS(FILTER_CACHEFILE_OBJD(cache_file))
{
/* add animation channels */
tmp_items += animfilter_block_data(ac, &tmp_data, ads, &cache_file->id, filter_mode);
}
END_ANIMFILTER_SUBCHANNELS;
/* did we find anything? */
if (tmp_items) {
/* include data-expand widget first */
if (filter_mode & ANIMFILTER_LIST_CHANNELS) {
/* check if filtering by active status */
// XXX: active check here needs checking
if (ANIMCHANNEL_ACTIVEOK(cache_file)) {
ANIMCHANNEL_NEW_CHANNEL(cache_file, ANIMTYPE_DSCACHEFILE, cache_file);
}
}
/* now add the list of collected channels */
BLI_movelisttolist(anim_data, &tmp_data);
BLI_assert(BLI_listbase_is_empty(&tmp_data));
items += tmp_items;
}
/* return the number of items added to the list */
return items;
}
/* Helper for Mask Editing - mask layers */
static size_t animdata_filter_mask_data(ListBase *anim_data, Mask *mask, const int filter_mode)
{
@ -2839,6 +2899,12 @@ static size_t animdata_filter_dopesheet(bAnimContext *ac, ListBase *anim_data, b
filter_mode |= ANIMFILTER_SELEDIT;
}
/* Cache files level animations (frame duration and such). */
CacheFile *cache_file = G.main->cachefiles.first;
for (; cache_file; cache_file = cache_file->id.next) {
items += animdata_filter_ds_cachefile(ac, anim_data, ads, cache_file, filter_mode);
}
/* scene-linked animation - e.g. world, compositing nodes, scene anim (including sequencer currently) */
items += animdata_filter_dopesheet_scene(ac, anim_data, ads, scene, filter_mode);
@ -2950,7 +3016,11 @@ static size_t animdata_filter_animchan(bAnimContext *ac, ListBase *anim_data, bD
case ANIMTYPE_OBJECT:
items += animdata_filter_dopesheet_ob(ac, anim_data, ads, channel->data, filter_mode);
break;
case ANIMTYPE_DSCACHEFILE:
items += animdata_filter_ds_cachefile(ac, anim_data, ads, channel->data, filter_mode);
break;
case ANIMTYPE_ANIMDATA:
items += animfilter_block_data(ac, anim_data, ads, channel->id, filter_mode);
break;

@ -44,6 +44,7 @@
#include "BLI_utildefines.h"
#include "DNA_anim_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_gpencil_types.h"
@ -965,6 +966,37 @@ void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, DLRBT_Tree *bl
ANIM_animdata_freelist(&anim_data);
}
void cachefile_to_keylist(bDopeSheet *ads, CacheFile *cache_file, DLRBT_Tree *keys, DLRBT_Tree *blocks)
{
if (cache_file == NULL) {
return;
}
/* create a dummy wrapper data to work with */
bAnimListElem dummychan = {NULL};
dummychan.type = ANIMTYPE_DSCACHEFILE;
dummychan.data = cache_file;
dummychan.id = &cache_file->id;
dummychan.adt = cache_file->adt;
bAnimContext ac = {NULL};
ac.ads = ads;
ac.data = &dummychan;
ac.datatype = ANIMCONT_CHANNEL;
/* get F-Curves to take keyframes from */
ListBase anim_data = { NULL, NULL };
int filter = ANIMFILTER_DATA_VISIBLE; // curves only
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* loop through each F-Curve, grabbing the keyframes */
for (bAnimListElem *ale = anim_data.first; ale; ale = ale->next) {
fcurve_to_keylist(ale->adt, ale->data, keys, blocks);
}
ANIM_animdata_freelist(&anim_data);
}
void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, DLRBT_Tree *blocks)
{
BezTriple *bezt;

@ -156,6 +156,7 @@ typedef enum eAnim_ChannelType {
ANIMTYPE_DSMAT,
ANIMTYPE_DSLAM,
ANIMTYPE_DSCAM,
ANIMTYPE_DSCACHEFILE,
ANIMTYPE_DSCUR,
ANIMTYPE_DSSKEY,
ANIMTYPE_DSWOR,
@ -275,6 +276,7 @@ typedef enum eAnimFilter_Flags {
#define FILTER_MAT_OBJD(ma) (CHECK_TYPE_INLINE(ma, Material *), ((ma->flag & MA_DS_EXPAND)))
#define FILTER_LAM_OBJD(la) (CHECK_TYPE_INLINE(la, Lamp *), ((la->flag & LA_DS_EXPAND)))
#define FILTER_CAM_OBJD(ca) (CHECK_TYPE_INLINE(ca, Camera *), ((ca->flag & CAM_DS_EXPAND)))
#define FILTER_CACHEFILE_OBJD(cf) (CHECK_TYPE_INLINE(cf, CacheFile *), ((cf->flag & CACHEFILE_DS_EXPAND)))
#define FILTER_CUR_OBJD(cu) (CHECK_TYPE_INLINE(cu, Curve *), ((cu->flag & CU_DS_EXPAND)))
#define FILTER_PART_OBJD(part) (CHECK_TYPE_INLINE(part, ParticleSettings *), ((part->flag & PART_DS_EXPAND)))
#define FILTER_MBALL_OBJD(mb) (CHECK_TYPE_INLINE(mb, MetaBall *), ((mb->flag2 & MB_DS_EXPAND)))

@ -34,6 +34,7 @@
struct bAnimContext;
struct AnimData;
struct CacheFile;
struct FCurve;
struct bDopeSheet;
struct bAction;
@ -141,6 +142,8 @@ void agroup_to_keylist(struct AnimData *adt, struct bActionGroup *agrp, struct D
void action_to_keylist(struct AnimData *adt, struct bAction *act, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks);
/* Object */
void ob_to_keylist(struct bDopeSheet *ads, struct Object *ob, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks);
/* Cache File */
void cachefile_to_keylist(struct bDopeSheet *ads, struct CacheFile *cache_file, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks);
/* Scene */
void scene_to_keylist(struct bDopeSheet *ads, struct Scene *sce, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks);
/* DopeSheet Summary */

@ -945,6 +945,7 @@ void uiTemplateReportsBanner(uiLayout *layout, struct bContext *C);
void uiTemplateKeymapItemProperties(uiLayout *layout, struct PointerRNA *ptr);
void uiTemplateComponentMenu(uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *name);
void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float *color);
void uiTemplateCacheFile(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname);
/* Default UIList class name, keep in sync with its declaration in bl_ui/__init__.py */
#define UI_UL_DEFAULT_CLASS_NAME "UI_UL_list"

@ -1541,6 +1541,8 @@ int UI_idcode_icon_get(const int idcode)
return ICON_BRUSH_DATA;
case ID_CA:
return ICON_CAMERA_DATA;
case ID_CF:
return ICON_FILE;
case ID_CU:
return ICON_CURVE_DATA;
case ID_GD:

@ -32,6 +32,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_cachefile_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
#include "DNA_object_types.h"
@ -368,6 +369,7 @@ static const char *template_id_browse_tip(StructRNA *type)
case ID_MSK: return N_("Browse Mask to be linked");
case ID_PAL: return N_("Browse Palette Data to be linked");
case ID_PC: return N_("Browse Paint Curve Data to be linked");
case ID_CF: return N_("Browse Cache Files to be linked");
}
}
return N_("Browse ID data to be linked");
@ -3848,3 +3850,73 @@ void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float *color)
UI_block_align_end(block);
}
/********************************* Cache File *********************************/
void uiTemplateCacheFile(uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname)
{
if (!ptr->data) {
return;
}
PropertyRNA *prop = RNA_struct_find_property(ptr, propname);
if (!prop) {
printf("%s: property not found: %s.%s\n",
__func__, RNA_struct_identifier(ptr->type), propname);
return;
}
if (RNA_property_type(prop) != PROP_POINTER) {
printf("%s: expected pointer property for %s.%s\n",
__func__, RNA_struct_identifier(ptr->type), propname);
return;
}
PointerRNA fileptr = RNA_property_pointer_get(ptr, prop);
CacheFile *file = fileptr.data;
uiLayoutSetContextPointer(layout, "edit_cachefile", &fileptr);
uiTemplateID(layout, C, ptr, propname, NULL, "CACHEFILE_OT_open", NULL);
if (!file) {
return;
}
uiLayout *row = uiLayoutRow(layout, false);
uiBlock *block = uiLayoutGetBlock(row);
uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("File Path:"), 0, 19, 145, 19, NULL, 0, 0, 0, 0, "");
row = uiLayoutRow(layout, false);
uiLayout *split = uiLayoutSplit(row, 0.0f, false);
row = uiLayoutRow(split, true);
uiItemR(row, &fileptr, "filepath", 0, "", ICON_NONE);
uiItemO(row, "", ICON_FILE_REFRESH, "cachefile.reload");
row = uiLayoutRow(layout, false);
uiItemR(row, &fileptr, "is_sequence", 0, "Is Sequence", ICON_NONE);
row = uiLayoutRow(layout, false);
uiItemR(row, &fileptr, "override_frame", 0, "Override Frame", ICON_NONE);
row = uiLayoutRow(layout, false);
uiLayoutSetEnabled(row, RNA_boolean_get(&fileptr, "override_frame"));
uiItemR(row, &fileptr, "frame", 0, "Frame", ICON_NONE);
row = uiLayoutRow(layout, false);
uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE);
row = uiLayoutRow(layout, false);
uiItemR(row, &fileptr, "scale", 0, "Scale", ICON_NONE);
/* TODO: unused for now, so no need to expose. */
#if 0
row = uiLayoutRow(layout, false);
uiItemR(row, &fileptr, "forward_axis", 0, "Forward Axis", ICON_NONE);
row = uiLayoutRow(layout, false);
uiItemR(row, &fileptr, "up_axis", 0, "Up Axis", ICON_NONE);
#endif
}

@ -28,6 +28,8 @@ set(INC
../../makesrna
../../windowmanager
../../collada
../../alembic
../../../../intern/guardedalloc
)
set(INC_SYS
@ -35,9 +37,13 @@ set(INC_SYS
)
set(SRC
io_alembic.c
io_cache.c
io_collada.c
io_ops.c
io_alembic.h
io_cache.h
io_collada.h
io_ops.h
)
@ -46,6 +52,14 @@ if(WITH_OPENCOLLADA)
add_definitions(-DWITH_COLLADA)
endif()
if(WITH_ALEMBIC)
add_definitions(-DWITH_ALEMBIC)
if(WITH_ALEMBIC_HDF5)
add_definitions(-DWITH_ALEMBIC_HDF5)
endif()
endif()
if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()

@ -0,0 +1,458 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2016 Blender Foundation.
* All rights reserved.
*
* ***** END GPL LICENSE BLOCK *****
*
*/
#ifdef WITH_ALEMBIC
/* needed for directory lookup */
#ifndef WIN32
# include <dirent.h>
#else
# include "BLI_winstuff.h"
#endif
#include "MEM_guardedalloc.h"
#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "WM_api.h"
#include "WM_types.h"
#include "io_alembic.h"
#include "ABC_alembic.h"
static int wm_alembic_export_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
char filepath[FILE_MAX];
BLI_strncpy(filepath, G.main->name, sizeof(filepath));
BLI_replace_extension(filepath, sizeof(filepath), ".abc");
RNA_string_set(op->ptr, "filepath", filepath);
}
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
UNUSED_VARS(event);
}
static int wm_alembic_export_exec(bContext *C, wmOperator *op)
{
if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
char filename[FILE_MAX];
RNA_string_get(op->ptr, "filepath", filename);
const struct AlembicExportParams params = {
.frame_start = RNA_int_get(op->ptr, "start"),
.frame_end = RNA_int_get(op->ptr, "end"),
.frame_step_xform = 1.0 / (double)RNA_int_get(op->ptr, "xsamples"),
.frame_step_shape = 1.0 / (double)RNA_int_get(op->ptr, "gsamples"),
.shutter_open = RNA_float_get(op->ptr, "sh_open"),
.shutter_close = RNA_float_get(op->ptr, "sh_close"),
.selected_only = RNA_boolean_get(op->ptr, "selected"),
.uvs = RNA_boolean_get(op->ptr, "uvs"),
.normals = RNA_boolean_get(op->ptr, "normals"),
.vcolors = RNA_boolean_get(op->ptr, "vcolors"),
.apply_subdiv = RNA_boolean_get(op->ptr, "apply_subdiv"),
.flatten_hierarchy = RNA_boolean_get(op->ptr, "flatten"),
.visible_layers_only = RNA_boolean_get(op->ptr, "visible_layers_only"),
.renderable_only = RNA_boolean_get(op->ptr, "renderable_only"),
.face_sets = RNA_boolean_get(op->ptr, "face_sets"),
.use_subdiv_schema = RNA_boolean_get(op->ptr, "subdiv_schema"),
.compression_type = RNA_enum_get(op->ptr, "compression_type"),
.packuv = RNA_boolean_get(op->ptr, "packuv"),
.global_scale = RNA_float_get(op->ptr, "global_scale"),
};
ABC_export(CTX_data_scene(C), C, filename, &params);
return OPERATOR_FINISHED;
}
static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr)
{
uiLayout *box = uiLayoutBox(layout);
uiLayout *row;
#ifdef WITH_ALEMBIC_HDF5
row = uiLayoutRow(box, false);
uiItemL(row, IFACE_("Archive Options:"), ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "compression_type", 0, NULL, ICON_NONE);
#endif
box = uiLayoutBox(layout);
row = uiLayoutRow(box, false);
uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "global_scale", 0, NULL, ICON_NONE);
/* Scene Options */
box = uiLayoutBox(layout);
row = uiLayoutRow(box, false);
uiItemL(row, IFACE_("Scene Options:"), ICON_SCENE_DATA);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "start", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "end", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "xsamples", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "gsamples", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "sh_open", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "sh_close", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "selected", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "renderable_only", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "visible_layers_only", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "flatten", 0, NULL, ICON_NONE);
/* Object Data */
box = uiLayoutBox(layout);
row = uiLayoutRow(box, false);
uiItemL(row, IFACE_("Object Options:"), ICON_OBJECT_DATA);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "uvs", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "packuv", 0, NULL, ICON_NONE);
uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "uvs"));
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "normals", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "vcolors", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "face_sets", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "subdiv_schema", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "apply_subdiv", 0, NULL, ICON_NONE);
}
static void wm_alembic_export_draw(bContext *UNUSED(C), wmOperator *op)
{
PointerRNA ptr;
RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
ui_alembic_export_settings(op->layout, &ptr);
}
void WM_OT_alembic_export(wmOperatorType *ot)
{
ot->name = "Export Alembic Archive";
ot->idname = "WM_OT_alembic_export";
ot->invoke = wm_alembic_export_invoke;
ot->exec = wm_alembic_export_exec;
ot->poll = WM_operator_winactive;
ot->ui = wm_alembic_export_draw;
WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC,
FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH,
FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
RNA_def_int(ot->srna, "start", 1, INT_MIN, INT_MAX,
"Start Frame", "Start Frame", INT_MIN, INT_MAX);
RNA_def_int(ot->srna, "end", 1, INT_MIN, INT_MAX,
"End Frame", "End Frame", INT_MIN, INT_MAX);
RNA_def_int(ot->srna, "xsamples", 1, 1, 128,
"Transform Samples", "Number of times per frame transformations are sampled", 1, 128);
RNA_def_int(ot->srna, "gsamples", 1, 1, 128,
"Geometry Samples", "Number of times per frame object datas are sampled", 1, 128);
RNA_def_float(ot->srna, "sh_open", 0.0f, -1.0f, 1.0f,
"Shutter Open", "Time at which the shutter is open", -1.0f, 1.0f);
RNA_def_float(ot->srna, "sh_close", 1.0f, -1.0f, 1.0f,
"Shutter Close", "Time at which the shutter is closed", -1.0f, 1.0f);
RNA_def_boolean(ot->srna, "selected", 0,
"Selected Objects Only", "Export only selected objects");
RNA_def_boolean(ot->srna, "renderable_only", 1,
"Renderable Objects Only",
"Export only objects marked renderable in the outliner");
RNA_def_boolean(ot->srna, "visible_layers_only", 0,
"Visible Layers Only", "Export only objects in visible layers");
RNA_def_boolean(ot->srna, "flatten", 0,
"Flatten Hierarchy",
"Do not preserve objects' parent/children relationship");
RNA_def_boolean(ot->srna, "uvs", 1, "UVs", "Export UVs");
RNA_def_boolean(ot->srna, "packuv", 1, "Pack UV Islands",
"Export UVs with packed island");
RNA_def_boolean(ot->srna, "normals", 1, "Normals", "Export normals");
RNA_def_boolean(ot->srna, "vcolors", 0, "Vertex colors", "Export vertex colors");
RNA_def_boolean(ot->srna, "face_sets", 0, "Face Sets", "Export per face shading group assignments");
RNA_def_boolean(ot->srna, "subdiv_schema", 0,
"Use Subdivision Schema",
"Export meshes using Alembic's subdivision schema");
RNA_def_boolean(ot->srna, "apply_subdiv", 0,
"Apply Subsurf", "Export subdivision surfaces as meshes");
RNA_def_enum(ot->srna, "compression_type", rna_enum_abc_compression_items,
ABC_ARCHIVE_OGAWA, "Compression", "");
RNA_def_float(ot->srna, "global_scale", 1.0f, 0.0001f, 1000.0f, "Scale",
"Value by which to enlarge or shrink the objects with respect to the world's origin",
0.0001f, 1000.0f);
}
/* ************************************************************************** */
/* TODO(kevin): check on de-duplicating all this with code in image_ops.c */
typedef struct CacheFrame {
struct CacheFrame *next, *prev;
int framenr;
} CacheFrame;
static int cmp_frame(const void *a, const void *b)
{
const CacheFrame *frame_a = a;
const CacheFrame *frame_b = b;
if (frame_a->framenr < frame_b->framenr) return -1;
if (frame_a->framenr > frame_b->framenr) return 1;
return 0;
}
static int get_sequence_len(char *filename, int *ofs)
{
int frame;
int numdigit;
if (!BLI_path_frame_get(filename, &frame, &numdigit)) {
return 1;
}
char path[FILE_MAX];
BLI_split_dir_part(filename, path, FILE_MAX);
DIR *dir = opendir(path);
const char *ext = ".abc";
const char *basename = BLI_path_basename(filename);
const int len = strlen(basename) - (numdigit + strlen(ext));
ListBase frames;
BLI_listbase_clear(&frames);
struct dirent *fname;
while ((fname = readdir(dir)) != NULL) {
/* do we have the right extension? */
if (!strstr(fname->d_name, ext)) {
continue;
}
if (!STREQLEN(basename, fname->d_name, len)) {
continue;
}
CacheFrame *cache_frame = MEM_callocN(sizeof(CacheFrame), "abc_frame");
BLI_path_frame_get(fname->d_name, &cache_frame->framenr, &numdigit);
BLI_addtail(&frames, cache_frame);
}
closedir(dir);
BLI_listbase_sort(&frames, cmp_frame);
CacheFrame *cache_frame = frames.first;
if (cache_frame) {
int frame_curr = cache_frame->framenr;
(*ofs) = frame_curr;
while (cache_frame && (cache_frame->framenr == frame_curr)) {
++frame_curr;
cache_frame = cache_frame->next;
}
BLI_freelistN(&frames);
return frame_curr - (*ofs);
}
return 1;
}
/* ************************************************************************** */
static void ui_alembic_import_settings(uiLayout *layout, PointerRNA *imfptr)
{
uiLayout *box = uiLayoutBox(layout);
uiLayout *row = uiLayoutRow(box, false);
uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "scale", 0, NULL, ICON_NONE);
box = uiLayoutBox(layout);
row = uiLayoutRow(box, false);
uiItemL(row, IFACE_("Options:"), ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "set_frame_range", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "is_sequence", 0, NULL, ICON_NONE);
row = uiLayoutRow(box, false);
uiItemR(row, imfptr, "validate_meshes", 0, NULL, ICON_NONE);
}
static void wm_alembic_import_draw(bContext *UNUSED(C), wmOperator *op)
{
PointerRNA ptr;
RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
ui_alembic_import_settings(op->layout, &ptr);
}
static int wm_alembic_import_exec(bContext *C, wmOperator *op)
{
if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
char filename[FILE_MAX];
RNA_string_get(op->ptr, "filepath", filename);
const float scale = RNA_float_get(op->ptr, "scale");
const bool is_sequence = RNA_boolean_get(op->ptr, "is_sequence");
const bool set_frame_range = RNA_boolean_get(op->ptr, "set_frame_range");
const bool validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes");
int offset = 0;
int sequence_len = 1;
if (is_sequence) {
sequence_len = get_sequence_len(filename, &offset);
}
ABC_import(C, filename, scale, is_sequence, set_frame_range, sequence_len, offset, validate_meshes);
return OPERATOR_FINISHED;
}
void WM_OT_alembic_import(wmOperatorType *ot)
{
ot->name = "Import Alembic Archive";
ot->idname = "WM_OT_alembic_import";
ot->invoke = WM_operator_filesel;
ot->exec = wm_alembic_import_exec;
ot->poll = WM_operator_winactive;
ot->ui = wm_alembic_import_draw;
WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC,
FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH,
FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
RNA_def_float(ot->srna, "scale", 1.0f, 0.0001f, 1000.0f, "Scale",
"Value by which to enlarge or shrink the objects with respect to the world's origin",
0.0001f, 1000.0f);
RNA_def_boolean(ot->srna, "set_frame_range", true,
"Set Frame Range",
"If checked, update scene's start and end frame to match those of the Alembic archive");
RNA_def_boolean(ot->srna, "validate_meshes", 0,
"Validate Meshes", "Check imported mesh objects for invalid data (slow)");
RNA_def_boolean(ot->srna, "is_sequence", false, "Is Sequence",
"Set to true if the cache is split into separate files");
}
#endif

@ -0,0 +1,37 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2016 Blender Foundation.
* All rights reserved.
*
* ***** END GPL LICENSE BLOCK *****
*
*/
#ifndef __IO_ALEMBIC_H__
#define __IO_ALEMBIC_H__
/** \file blender/editors/io/io_alembic.h
* \ingroup editor/io
*/
struct wmOperatorType;
void WM_OT_alembic_export(struct wmOperatorType *ot);
void WM_OT_alembic_import(struct wmOperatorType *ot);
#endif /* __IO_ALEMBIC_H__ */

@ -0,0 +1,162 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2016 Blender Foundation.
* All rights reserved.
*
* ***** END GPL LICENSE BLOCK *****
*
*/
#include "MEM_guardedalloc.h"
#include "DNA_cachefile_types.h"
#include "DNA_space_types.h"
#include "BLI_listbase.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BKE_cachefile.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_library.h"
#include "BKE_main.h"
#include "BKE_report.h"
#include "RNA_access.h"
#include "UI_interface.h"
#include "WM_api.h"
#include "WM_types.h"
#include "io_cache.h"
static void cachefile_init(bContext *C, wmOperator *op)
{
PropertyPointerRNA *pprop;
op->customdata = pprop = MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA");
UI_context_active_but_prop_get_templateID(C, &pprop->ptr, &pprop->prop);
}
static int cachefile_open_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
char filepath[FILE_MAX];
BLI_strncpy(filepath, G.main->name, sizeof(filepath));
BLI_replace_extension(filepath, sizeof(filepath), ".abc");
RNA_string_set(op->ptr, "filepath", filepath);
}
cachefile_init(C, op);
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
UNUSED_VARS(event);
}
static void open_cancel(bContext *UNUSED(C), wmOperator *op)
{
MEM_freeN(op->customdata);
op->customdata = NULL;
}
static int cachefile_open_exec(bContext *C, wmOperator *op)
{
if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
char filename[FILE_MAX];
RNA_string_get(op->ptr, "filepath", filename);
Main *bmain = CTX_data_main(C);
CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, BLI_path_basename(filename));
BLI_strncpy(cache_file->filepath, filename, FILE_MAX);
BKE_cachefile_reload(bmain, cache_file);
/* hook into UI */
PropertyPointerRNA *pprop = op->customdata;
if (pprop->prop) {
/* when creating new ID blocks, use is already 1, but RNA
* pointer se also increases user, so this compensates it */
id_us_min(&cache_file->id);
PointerRNA idptr;
RNA_id_pointer_create(&cache_file->id, &idptr);
RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr);
RNA_property_update(C, &pprop->ptr, pprop->prop);
}
MEM_freeN(op->customdata);
return OPERATOR_FINISHED;
}
void CACHEFILE_OT_open(wmOperatorType *ot)
{
ot->name = "Open Cache File";
ot->idname = "CACHEFILE_OT_open";
ot->invoke = cachefile_open_invoke;
ot->exec = cachefile_open_exec;
ot->cancel = open_cancel;
WM_operator_properties_filesel(ot, FILE_TYPE_ALEMBIC | FILE_TYPE_FOLDER,
FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH,
FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
}
/* ***************************** Reload Operator **************************** */
static int cachefile_reload_exec(bContext *C, wmOperator *op)
{
CacheFile *cache_file = CTX_data_edit_cachefile(C);
if (!cache_file) {
return OPERATOR_CANCELLED;
}
Main *bmain = CTX_data_main(C);
BLI_listbase_clear(&cache_file->object_paths);
BKE_cachefile_reload(bmain, cache_file);
return OPERATOR_FINISHED;
UNUSED_VARS(op);
}
void CACHEFILE_OT_reload(wmOperatorType *ot)
{
ot->name = "Refresh Archive";
ot->description = "Update objects paths list with new data from the archive";
ot->idname = "CACHEFILE_OT_reload";
/* api callbacks */
ot->exec = cachefile_reload_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}

@ -0,0 +1,37 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2016 Blender Foundation.
* All rights reserved.
*
* ***** END GPL LICENSE BLOCK *****
*
*/
#ifndef __IO_CACHE_H__
#define __IO_CACHE_H__
/** \file blender/editors/io/io_cache.h
* \ingroup editor/io
*/
struct wmOperatorType;
void CACHEFILE_OT_open(struct wmOperatorType *ot);
void CACHEFILE_OT_reload(struct wmOperatorType *ot);
#endif /* __IO_CACHE_H__ */

@ -30,11 +30,18 @@
#include "io_ops.h" /* own include */
#include "WM_api.h"
#ifdef WITH_COLLADA
# include "io_collada.h"
# include "WM_api.h"
#endif
#ifdef WITH_ALEMBIC
# include "io_alembic.h"
#endif
#include "io_cache.h"
void ED_operatortypes_io(void)
{
#ifdef WITH_COLLADA
@ -42,4 +49,11 @@ void ED_operatortypes_io(void)
WM_operatortype_append(WM_OT_collada_export);
WM_operatortype_append(WM_OT_collada_import);
#endif
#ifdef WITH_ALEMBIC
WM_operatortype_append(WM_OT_alembic_import);
WM_operatortype_append(WM_OT_alembic_export);
#endif
WM_operatortype_append(CACHEFILE_OT_open);
WM_operatortype_append(CACHEFILE_OT_reload);
}

@ -416,6 +416,13 @@ static void test_constraint(Object *owner, bPoseChannel *pchan, bConstraint *con
if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL))
con->flag |= CONSTRAINT_DISABLE;
}
else if (con->type == CONSTRAINT_TYPE_TRANSFORM_CACHE) {
bTransformCacheConstraint *data = con->data;
if ((data->cache_file == NULL) || (data->object_path[0] == '\0')) {
con->flag |= CONSTRAINT_DISABLE;
}
}
/* Check targets for constraints */
if (check_targets && cti && cti->get_constraint_targets) {

@ -920,6 +920,8 @@ static int filelist_geticon_ex(
return ICON_FILE_BLANK;
else if (typeflag & FILE_TYPE_COLLADA)
return ICON_FILE_BLANK;
else if (typeflag & FILE_TYPE_ALEMBIC)
return ICON_FILE_BLANK;
else if (typeflag & FILE_TYPE_TEXT)
return ICON_FILE_TEXT;
else if (typeflag & FILE_TYPE_BLENDERLIB) {
@ -1952,6 +1954,9 @@ int ED_path_extension_type(const char *path)
else if (BLI_testextensie(path, ".dae")) {
return FILE_TYPE_COLLADA;
}
else if (BLI_testextensie(path, ".abc")) {
return FILE_TYPE_ALEMBIC;
}
else if (BLI_testextensie_array(path, imb_ext_image) ||
(G.have_quicktime && BLI_testextensie_array(path, imb_ext_image_qt)))
{
@ -2004,6 +2009,8 @@ int ED_file_extension_icon(const char *path)
return ICON_FILE_BLANK;
case FILE_TYPE_COLLADA:
return ICON_FILE_BLANK;
case FILE_TYPE_ALEMBIC:
return ICON_FILE_BLANK;
case FILE_TYPE_TEXT:
return ICON_FILE_TEXT;
default:

@ -185,6 +185,8 @@ short ED_fileselect_set_params(SpaceFile *sfile)
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_BTX : 0;
if ((prop = RNA_struct_find_property(op->ptr, "filter_collada")))
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_COLLADA : 0;
if ((prop = RNA_struct_find_property(op->ptr, "filter_alembic")))
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_ALEMBIC : 0;
if ((prop = RNA_struct_find_property(op->ptr, "filter_glob"))) {
/* Protection against pyscripts not setting proper size limit... */
char *tmp = RNA_property_string_get_alloc(
@ -213,7 +215,7 @@ short ED_fileselect_set_params(SpaceFile *sfile)
FILTER_ID_GR | FILTER_ID_IM | FILTER_ID_LA | FILTER_ID_LS | FILTER_ID_LT | FILTER_ID_MA |
FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | FILTER_ID_OB |
FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | FILTER_ID_SO |
FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO;
FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF;
if (U.uiflag & USER_HIDE_DOT) {
params->flag |= FILE_HIDE_DOT;

@ -131,6 +131,7 @@ bool nla_panel_context(const bContext *C, PointerRNA *adt_ptr, PointerRNA *nlt_p
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
case ANIMTYPE_DSCACHEFILE:
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:

@ -170,6 +170,7 @@ static int mouse_nla_channels(bContext *C, bAnimContext *ac, float x, int channe
case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */
case ANIMTYPE_DSLAM:
case ANIMTYPE_DSCAM:
case ANIMTYPE_DSCACHEFILE:
case ANIMTYPE_DSCUR:
case ANIMTYPE_DSSKEY:
case ANIMTYPE_DSWOR:

@ -1173,6 +1173,8 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto
UI_icon_draw(x, y, ICON_MOD_TRIANGULATE); break;
case eModifierType_MeshCache:
UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */
case eModifierType_MeshSequenceCache:
UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */
case eModifierType_Wireframe:
UI_icon_draw(x, y, ICON_MOD_WIREFRAME); break;
case eModifierType_LaplacianDeform:

@ -62,7 +62,7 @@ typedef struct TreeElement {
#define TREESTORE_ID_TYPE(_id) \
(ELEM(GS((_id)->name), ID_SCE, ID_LI, ID_OB, ID_ME, ID_CU, ID_MB, ID_NT, ID_MA, ID_TE, ID_IM, ID_LT, ID_LA, ID_CA) || \
ELEM(GS((_id)->name), ID_KE, ID_WO, ID_SPK, ID_GR, ID_AR, ID_AC, ID_BR, ID_PA, ID_GD, ID_LS) || \
ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO)) /* Only in 'blendfile' mode ... :/ */
ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO, ID_CF)) /* Only in 'blendfile' mode ... :/ */
/* TreeElement->flag */
#define TE_ACTIVE 1

@ -38,6 +38,7 @@
#include "DNA_armature_types.h"
#include "DNA_constraint_types.h"
#include "DNA_camera_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_group_types.h"
#include "DNA_key_types.h"
@ -746,6 +747,16 @@ static void outliner_add_id_contents(SpaceOops *soops, TreeElement *te, TreeStor
outliner_add_element(soops, &te->subtree, ca, te, TSE_ANIM_DATA, 0);
break;
}
case ID_CF:
{
CacheFile *cache_file = (CacheFile *)id;
if (outliner_animdata_test(cache_file->adt)) {
outliner_add_element(soops, &te->subtree, cache_file, te, TSE_ANIM_DATA, 0);
}
break;
}
case ID_LA:
{
Lamp *la = (Lamp *)id;

@ -32,7 +32,10 @@
#include <string.h>
#include <stdio.h>
#include "DNA_cachefile_types.h"
#include "DNA_constraint_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@ -42,7 +45,10 @@
#include "BLI_dlrbTree.h"
#include "BLI_utildefines.h"
#include "BKE_constraint.h"
#include "BKE_context.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_screen.h"
#include "BKE_pointcache.h"
@ -320,6 +326,9 @@ static void time_draw_idblock_keyframes(View2D *v2d, ID *id, short onlysel)
case ID_GD:
gpencil_to_keylist(&ads, (bGPdata *)id, &keys);
break;
case ID_CF:
cachefile_to_keylist(&ads, (CacheFile *)id, &keys, NULL);
break;
}
/* build linked-list for searching */
@ -344,6 +353,56 @@ static void time_draw_idblock_keyframes(View2D *v2d, ID *id, short onlysel)
BLI_dlrbTree_free(&keys);
}
static void time_draw_caches_keyframes(Main *bmain, Scene *scene, View2D *v2d, bool onlysel)
{
CacheFile *cache_file;
for (cache_file = bmain->cachefiles.first;
cache_file;
cache_file = cache_file->id.next)
{
cache_file->draw_flag &= ~CACHEFILE_KEYFRAME_DRAWN;
}
for (Base *base = scene->base.first; base; base = base->next) {
Object *ob = base->object;
ModifierData *md = modifiers_findByType(ob, eModifierType_MeshSequenceCache);
if (md) {
MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md;
cache_file = mcmd->cache_file;
if (!cache_file || (cache_file->draw_flag & CACHEFILE_KEYFRAME_DRAWN) != 0) {
continue;
}
cache_file->draw_flag |= CACHEFILE_KEYFRAME_DRAWN;
time_draw_idblock_keyframes(v2d, (ID *)cache_file, onlysel);
}
for (bConstraint *con = ob->constraints.first; con; con = con->next) {
if (con->type != CONSTRAINT_TYPE_TRANSFORM_CACHE) {
continue;
}
bTransformCacheConstraint *data = con->data;
cache_file = data->cache_file;
if (!cache_file || (cache_file->draw_flag & CACHEFILE_KEYFRAME_DRAWN) != 0) {
continue;
}
cache_file->draw_flag |= CACHEFILE_KEYFRAME_DRAWN;
time_draw_idblock_keyframes(v2d, (ID *)cache_file, onlysel);
}
}
}
/* draw keyframe lines for timeline */
static void time_draw_keyframes(const bContext *C, ARegion *ar)
{
@ -354,7 +413,11 @@ static void time_draw_keyframes(const bContext *C, ARegion *ar)
/* set this for all keyframe lines once and for all */
glLineWidth(1.0);
/* draw cache files keyframes (if available) */
UI_ThemeColor(TH_TIME_KEYFRAME);
time_draw_caches_keyframes(CTX_data_main(C), scene, v2d, onlysel);
/* draw grease pencil keyframes (if available) */
UI_ThemeColor(TH_TIME_GP_KEYFRAME);
if (scene->gpd) {

@ -247,6 +247,7 @@ typedef enum ID_Type {
ID_LS = MAKE_ID2('L', 'S'), /* FreestyleLineStyle */
ID_PAL = MAKE_ID2('P', 'L'), /* Palette */
ID_PC = MAKE_ID2('P', 'C'), /* PaintCurve */
ID_CF = MAKE_ID2('C', 'F'), /* CacheFile */
} ID_Type;
/* Only used as 'placeholder' in .blend files for directly linked datablocks. */
@ -377,6 +378,7 @@ enum {
FILTER_ID_VF = (1 << 25),
FILTER_ID_WO = (1 << 26),
FILTER_ID_PA = (1 << 27),
FILTER_ID_CF = (1 << 28),
};
#ifdef __cplusplus

@ -694,7 +694,9 @@ typedef enum eAnimEdit_Context {
/* dopesheet (default) */
SACTCONT_DOPESHEET = 3,
/* mask */
SACTCONT_MASK = 4
SACTCONT_MASK = 4,
/* cache file */
SACTCONT_CACHEFILE = 5,
} eAnimEdit_Context;
/* SpaceAction AutoSnap Settings (also used by other Animation Editors) */

@ -0,0 +1,84 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2016 Blender Foundation.
* All rights reserved.
*
* Contributor(s): Kevin Dietrich.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file DNA_cachefile_types.h
* \ingroup DNA
*/
#ifndef __DNA_CACHEFILE_TYPES_H__
#define __DNA_CACHEFILE_TYPES_H__
#include "DNA_ID.h"
#ifdef __cplusplus
extern "C" {
#endif
/* CacheFile::flag */
enum {
CACHEFILE_DS_EXPAND = (1 << 0),
};
/* CacheFile::draw_flag */
enum {
CACHEFILE_KEYFRAME_DRAWN = (1 << 0),
};
typedef struct AlembicObjectPath {
struct AlembicObjectPath *next, *prev;
char path[1024]; /* 1024 = FILE_MAX, might use PATH_MAX in the future. */
} AlembicObjectPath;
typedef struct CacheFile {
ID id;
struct AnimData *adt;
struct AbcArchiveHandle *handle;
/* Paths of the objects inside of the Alembic archive referenced by this
* CacheFile. */
ListBase object_paths;
char filepath[1024]; /* 1024 = FILE_MAX */
char is_sequence;
char forward_axis;
char up_axis;
char override_frame;
float scale;
float frame; /* The frame/time to lookup in the cache file. */
short flag; /* Animation flag. */
short draw_flag;
} CacheFile;
#ifdef __cplusplus
}
#endif
#endif /* __DNA_CACHEFILE_TYPES_H__ */

@ -458,6 +458,12 @@ typedef struct bObjectSolverConstraint {
struct Object *camera;
} bObjectSolverConstraint;
/* Transform matrix cache constraint */
typedef struct bTransformCacheConstraint {
struct CacheFile *cache_file;
char object_path[1024]; /* FILE_MAX */
} bTransformCacheConstraint;
/* ------------------------------------------ */
/* bConstraint->type
@ -494,6 +500,7 @@ typedef enum eBConstraint_Types {
CONSTRAINT_TYPE_FOLLOWTRACK = 26, /* Follow Track Constraint */
CONSTRAINT_TYPE_CAMERASOLVER = 27, /* Camera Solver Constraint */
CONSTRAINT_TYPE_OBJECTSOLVER = 28, /* Object Solver Constraint */
CONSTRAINT_TYPE_TRANSFORM_CACHE = 29, /* Transform Cache Constraint */
/* NOTE: no constraints are allowed to be added after this */
NUM_CONSTRAINT_TYPES

@ -85,6 +85,7 @@ typedef enum ModifierType {
eModifierType_DataTransfer = 49,
eModifierType_NormalEdit = 50,
eModifierType_CorrectiveSmooth = 51,
eModifierType_MeshSequenceCache = 52,
NUM_MODIFIER_TYPES
} ModifierType;
@ -1541,4 +1542,25 @@ enum {
MOD_NORMALEDIT_MIX_MUL = 3,
};
typedef struct MeshSeqCacheModifierData {
ModifierData modifier;
struct CacheFile *cache_file;
char object_path[1024]; /* 1024 = FILE_MAX */
char read_flag;
char pad[7];
} MeshSeqCacheModifierData;
/* MeshSeqCacheModifierData.read_flag */
enum {
MOD_MESHSEQ_READ_VERT = (1 << 0),
MOD_MESHSEQ_READ_POLY = (1 << 1),
MOD_MESHSEQ_READ_UV = (1 << 2),
MOD_MESHSEQ_READ_COLOR = (1 << 3),
};
#define MOD_MESHSEQ_READ_ALL \
(MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)
#endif /* __DNA_MODIFIER_TYPES_H__ */

@ -738,6 +738,7 @@ typedef enum eFileSel_File_Types {
FILE_TYPE_COLLADA = (1 << 13),
FILE_TYPE_OPERATOR = (1 << 14), /* from filter_glob operator property */
FILE_TYPE_APPLICATIONBUNDLE = (1 << 15),
FILE_TYPE_ALEMBIC = (1 << 16),
FILE_TYPE_DIR = (1 << 30), /* An FS directory (i.e. S_ISDIR on its path is true). */
FILE_TYPE_BLENDERLIB = (1 << 31),

@ -129,6 +129,7 @@ static const char *includefiles[] = {
"DNA_rigidbody_types.h",
"DNA_freestyle_types.h",
"DNA_linestyle_types.h",
"DNA_cachefile_types.h",
/* see comment above before editing! */
/* empty string to indicate end of includefiles */
@ -1340,4 +1341,5 @@ int main(int argc, char **argv)
#include "DNA_rigidbody_types.h"
#include "DNA_freestyle_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_cachefile_types.h"
/* end of list */

@ -95,6 +95,8 @@ extern StructRNA RNA_Brush;
extern StructRNA RNA_BrushTextureSlot;
extern StructRNA RNA_BuildModifier;
extern StructRNA RNA_MeshCacheModifier;
extern StructRNA RNA_MeshSequenceCacheModifier;
extern StructRNA RNA_CacheFile;
extern StructRNA RNA_Camera;
extern StructRNA RNA_CastModifier;
extern StructRNA RNA_ChildOfConstraint;

Some files were not shown because too many files have changed in this diff Show More