diff options
author | Pascal Schoen <pascal_schoen@gmx.net> | 2016-08-16 16:22:32 +0300 |
---|---|---|
committer | Pascal Schoen <pascal_schoen@gmx.net> | 2016-08-16 16:22:32 +0300 |
commit | 9eed34c7d980e1b998df457c4f76021162c80f78 (patch) | |
tree | 0c47e10e97c2088d59a52c3802c35f7e9eb7901f | |
parent | ef29aaee1af8074e0228c480d962700e97ea5b36 (diff) | |
parent | ae475e355488db27c4b9fcc33385080578403833 (diff) |
Merge branch 'master' into cycles_disney_brdf
332 files changed, 22209 insertions, 2411 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 1dfa838a5a2..281aa52cae3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -323,6 +323,10 @@ option(WITH_CODEC_AVI "Enable Blenders own AVI file support (raw/jpeg) option(WITH_CODEC_FFMPEG "Enable FFMPeg Support (http://ffmpeg.org)" ${_init_CODEC_FFMPEG}) option(WITH_CODEC_SNDFILE "Enable libsndfile Support (http://www.mega-nerd.com/libsndfile)" OFF) +# Alembic support +option(WITH_ALEMBIC "Enable Alembic Support" OFF) +option(WITH_ALEMBIC_HDF5 "Enable Legacy Alembic Support (not officially supported)" OFF) + if(APPLE) option(WITH_CODEC_QUICKTIME "Enable Quicktime Support" ON) endif() @@ -393,6 +397,7 @@ option(WITH_CYCLES "Enable Cycles Render Engine" ON) option(WITH_CYCLES_STANDALONE "Build Cycles standalone application" OFF) option(WITH_CYCLES_STANDALONE_GUI "Build Cycles standalone with GUI" OFF) option(WITH_CYCLES_OSL "Build Cycles with OSL support" ${_init_CYCLES_OSL}) +option(WITH_CYCLES_OPENSUBDIV "Build Cycles with OpenSubdiv support" ON) option(WITH_CYCLES_CUDA_BINARIES "Build Cycles CUDA binaries" OFF) set(CYCLES_CUDA_BINARIES_ARCH sm_20 sm_21 sm_30 sm_35 sm_37 sm_50 sm_52 CACHE STRING "CUDA architectures to build binaries for") mark_as_advanced(CYCLES_CUDA_BINARIES_ARCH) @@ -720,6 +725,11 @@ if(WITH_OPENIMAGEIO) set(WITH_IMAGE_TIFF ON) endif() +# auto enable alembic linking dependencies +if(WITH_ALEMBIC) + set(WITH_IMAGE_OPENEXR ON) +endif() + # don't store paths to libs for portable distribution if(WITH_INSTALL_PORTABLE) set(CMAKE_SKIP_BUILD_RPATH TRUE) @@ -1091,6 +1101,20 @@ if(UNIX AND NOT APPLE) endif() endif() + if(WITH_ALEMBIC) + find_package_wrapper(Alembic) + + if(WITH_ALEMBIC_HDF5) + set(HDF5_ROOT_DIR ${LIBDIR}/hdf5) + find_package_wrapper(HDF5) + endif() + + if(NOT ALEMBIC_FOUND OR (WITH_ALEMBIC_HDF5 AND NOT HDF5_FOUND)) + set(WITH_ALEMBIC OFF) + set(WITH_ALEMBIC_HDF5 OFF) + endif() + endif() + if(WITH_BOOST) # uses in build instructions to override include and library variables if(NOT BOOST_CUSTOM) @@ -1326,10 +1350,8 @@ elseif(WIN32) # MSVC11 needs _ALLOW_KEYWORD_MACROS to build add_definitions(-D_ALLOW_KEYWORD_MACROS) - if(CMAKE_CL_64) - # We want to support Vista level ABI for x64 - add_definitions(-D_WIN32_WINNT=0x600) - endif() + # We want to support Vista level ABI + add_definitions(-D_WIN32_WINNT=0x600) # Make cmake find the msvc redistributables set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) @@ -1660,6 +1682,14 @@ elseif(WIN32) set(OPENVDB_LIBPATH ${LIBDIR}/openvdb/lib) endif() + if(WITH_ALEMBIC) + set(ALEMBIC ${LIBDIR}/alembic) + set(ALEMBIC_INCLUDE_DIR ${ALEMBIC}/include) + set(ALEMBIC_INCLUDE_DIRS ${ALEMBIC_INCLUDE_DIR}) + set(ALEMBIC_LIBPATH ${ALEMBIC}/lib) + set(ALEMBIC_LIBRARIES optimized alembic debug alembic_d) + endif() + if(WITH_MOD_CLOTH_ELTOPO) set(LAPACK ${LIBDIR}/lapack) # set(LAPACK_INCLUDE_DIR ${LAPACK}/include) @@ -1959,6 +1989,23 @@ elseif(WIN32) set(OPENVDB_DEFINITIONS) endif() + if(WITH_ALEMBIC) + # TODO(sergey): For until someone drops by and compiles libraries for + # MinGW we allow users to compile their own Alembic library and use + # that via find_package(), + # + # Once precompiled libraries are there we'll use hardcoded locations. + find_package_wrapper(Alembic) + if(WITH_ALEMBIC_HDF5) + set(HDF5_ROOT_DIR ${LIBDIR}/hdf5) + find_package_wrapper(HDF5) + endif() + if(NOT ALEMBIC_FOUND OR (WITH_ALEMBIC_HDF5 AND NOT HDF5_FOUND)) + set(WITH_ALEMBIC OFF) + set(WITH_ALEMBIC_HDF5 OFF) + endif() + endif() + set(PLATFORM_LINKFLAGS "-Xlinker --stack=2097152") ## DISABLE - causes linking errors @@ -2043,6 +2090,14 @@ elseif(APPLE) endif() endif() + if(WITH_ALEMBIC) + set(ALEMBIC ${LIBDIR}/alembic) + set(ALEMBIC_INCLUDE_DIR ${ALEMBIC}/include) + set(ALEMBIC_INCLUDE_DIRS ${ALEMBIC_INCLUDE_DIR}) + set(ALEMBIC_LIBPATH ${ALEMBIC}/lib) + set(ALEMBIC_LIBRARIES Alembic) + endif() + if(WITH_OPENSUBDIV) set(OPENSUBDIV ${LIBDIR}/opensubdiv) set(OPENSUBDIV_LIBPATH ${OPENSUBDIV}/lib) @@ -2452,6 +2507,11 @@ if(WITH_CYCLES) ) endif() endif() + + if(WITH_CYCLES_OPENSUBDIV AND NOT WITH_OPENSUBDIV) + message(STATUS "WITH_CYCLES_OPENSUBDIV requires WITH_OPENSUBDIV to be ON, turning OFF") + set(WITH_CYCLES_OPENSUBDIV OFF) + endif() endif() if(WITH_INTERNATIONAL) @@ -2504,8 +2564,6 @@ else() endif() unset(_SYSTEM_BIG_ENDIAN) endif() - - if(WITH_IMAGE_OPENJPEG) if(WITH_SYSTEM_OPENJPEG) # dealt with above @@ -3215,6 +3273,7 @@ if(FIRST_RUN) info_cfg_option(WITH_FREESTYLE) info_cfg_option(WITH_OPENCOLORIO) info_cfg_option(WITH_OPENVDB) + info_cfg_option(WITH_ALEMBIC) info_cfg_text("Compiler Options:") info_cfg_option(WITH_BUILDINFO) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index 068ac665623..51928b82d8c 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -29,13 +29,13 @@ getopt \ ver-ocio:,ver-oiio:,ver-llvm:,ver-osl:,ver-osd:,ver-openvdb:,\ force-all,force-python,force-numpy,force-boost,\ force-ocio,force-openexr,force-oiio,force-llvm,force-osl,force-osd,force-openvdb,\ -force-ffmpeg,force-opencollada,\ +force-ffmpeg,force-opencollada,force-alembic,\ build-all,build-python,build-numpy,build-boost,\ build-ocio,build-openexr,build-oiio,build-llvm,build-osl,build-osd,build-openvdb,\ -build-ffmpeg,build-opencollada,\ +build-ffmpeg,build-opencollada,build-alembic,\ skip-python,skip-numpy,skip-boost,\ skip-ocio,skip-openexr,skip-oiio,skip-llvm,skip-osl,skip-osd,skip-openvdb,\ -skip-ffmpeg,skip-opencollada \ +skip-ffmpeg,skip-opencollada,skip-alembic \ -- "$@" \ ) @@ -167,6 +167,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS: --build-openvdb Force the build of OpenVDB. + --build-alembic + Force the build of Alembic. + --build-opencollada Force the build of OpenCOLLADA. @@ -216,6 +219,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS: --force-openvdb Force the rebuild of OpenVDB. + --force-alembic + Force the rebuild of Alembic. + --force-opencollada Force the rebuild of OpenCOLLADA. @@ -258,6 +264,9 @@ ARGUMENTS_INFO="\"COMMAND LINE ARGUMENTS: --skip-openvdb Unconditionally skip OpenVDB installation/building. + --skip-alembic + Unconditionally skip Alembic installation/building. + --skip-opencollada Unconditionally skip OpenCOLLADA installation/building. @@ -328,7 +337,7 @@ OSL_FORCE_REBUILD=false OSL_SKIP=false # OpenSubdiv needs to be compiled for now -OSD_VERSION="3.0.2" +OSD_VERSION="3.0.5" OSD_VERSION_MIN=$OSD_VERSION OSD_FORCE_BUILD=false OSD_FORCE_REBUILD=false @@ -343,6 +352,13 @@ OPENVDB_FORCE_BUILD=false OPENVDB_FORCE_REBUILD=false OPENVDB_SKIP=false +# Alembic needs to be compiled for now +ALEMBIC_VERSION="1.6.0" +ALEMBIC_VERSION_MIN=$ALEMBIC_VERSION +ALEMBIC_FORCE_BUILD=false +ALEMBIC_FORCE_REBUILD=false +ALEMBIC_SKIP=false + # Version?? OPENCOLLADA_VERSION="1.3" OPENCOLLADA_FORCE_BUILD=false @@ -525,6 +541,7 @@ while true; do OPENVDB_FORCE_BUILD=true OPENCOLLADA_FORCE_BUILD=true FFMPEG_FORCE_BUILD=true + ALEMBIC_FORCE_BUILD=true shift; continue ;; --build-python) @@ -567,6 +584,9 @@ while true; do --build-ffmpeg) FFMPEG_FORCE_BUILD=true; shift; continue ;; + --build-alembic) + ALEMBIC_FORCE_BUILD=true; shift; continue + ;; --force-all) PYTHON_FORCE_REBUILD=true NUMPY_FORCE_REBUILD=true @@ -580,6 +600,7 @@ while true; do OPENVDB_FORCE_REBUILD=true OPENCOLLADA_FORCE_REBUILD=true FFMPEG_FORCE_REBUILD=true + ALEMBIC_FORCE_REBUILD=true shift; continue ;; --force-python) @@ -620,6 +641,9 @@ while true; do --force-ffmpeg) FFMPEG_FORCE_REBUILD=true; shift; continue ;; + --force-alembic) + ALEMBIC_FORCE_REBUILD=true; shift; continue + ;; --skip-python) PYTHON_SKIP=true; shift; continue ;; @@ -656,6 +680,9 @@ while true; do --skip-ffmpeg) FFMPEG_SKIP=true; shift; continue ;; + --skip-alembic) + ALEMBIC_SKIP=true; shift; continue + ;; --) # no more arguments to parse break @@ -683,7 +710,7 @@ NUMPY_SOURCE=( "http://sourceforge.net/projects/numpy/files/NumPy/$NUMPY_VERSION _boost_version_nodots=`echo "$BOOST_VERSION" | sed -r 's/\./_/g'` BOOST_SOURCE=( "http://sourceforge.net/projects/boost/files/boost/$BOOST_VERSION/boost_$_boost_version_nodots.tar.bz2/download" ) -BOOST_BUILD_MODULES="--with-system --with-filesystem --with-thread --with-regex --with-locale --with-date_time --with-wave --with-iostreams" +BOOST_BUILD_MODULES="--with-system --with-filesystem --with-thread --with-regex --with-locale --with-date_time --with-wave --with-iostreams --with-python --with-program_options" OCIO_SOURCE=( "https://github.com/imageworks/OpenColorIO/tarball/v$OCIO_VERSION" ) @@ -712,7 +739,7 @@ OSL_SOURCE_REPO=( "https://github.com/Nazg-Gul/OpenShadingLanguage.git" ) OSL_SOURCE_REPO_UID="7d40ff5fe8e47b030042afb92d0e955f5aa96f48" OSL_SOURCE_REPO_BRANCH="blender-fixes" -OSD_USE_REPO=true +OSD_USE_REPO=false # Script foo to make the version string compliant with the archive name: # ${Varname//SearchForThisChar/ReplaceWithThisChar} OSD_SOURCE=( "https://github.com/PixarAnimationStudios/OpenSubdiv/archive/v${OSD_VERSION//./_}.tar.gz" ) @@ -727,6 +754,12 @@ OPENVDB_SOURCE=( "https://github.com/dreamworksanimation/openvdb/archive/v${OPEN #~ OPENVDB_SOURCE_REPO_UID="404659fffa659da075d1c9416e4fc939139a84ee" #~ OPENVDB_SOURCE_REPO_BRANCH="dev" +ALEMBIC_USE_REPO=false +ALEMBIC_SOURCE=( "https://github.com/alembic/alembic/archive/${ALEMBIC_VERSION}.tar.gz" ) +# ALEMBIC_SOURCE_REPO=( "https://github.com/alembic/alembic.git" ) +# ALEMBIC_SOURCE_REPO_UID="e6c90d4faa32c4550adeaaf3f556dad4b73a92bb" +# ALEMBIC_SOURCE_REPO_BRANCH="master" + OPENCOLLADA_SOURCE=( "https://github.com/KhronosGroup/OpenCOLLADA.git" ) OPENCOLLADA_REPO_UID="3335ac164e68b2512a40914b14c74db260e6ff7d" OPENCOLLADA_REPO_BRANCH="master" @@ -767,7 +800,8 @@ You may also want to build them yourself (optional ones are [between brackets]): * [OpenShadingLanguage $OSL_VERSION_MIN] (from $OSL_SOURCE_REPO, branch $OSL_SOURCE_REPO_BRANCH, commit $OSL_SOURCE_REPO_UID). * [OpenSubDiv $OSD_VERSION_MIN] (from $OSD_SOURCE_REPO, branch $OSD_SOURCE_REPO_BRANCH, commit $OSD_SOURCE_REPO_UID). * [OpenVDB $OPENVDB_VERSION_MIN] (from $OPENVDB_SOURCE), [Blosc $OPENVDB_BLOSC_VERSION] (from $OPENVDB_BLOSC_SOURCE). - * [OpenCollada] (from $OPENCOLLADA_SOURCE, branch $OPENCOLLADA_REPO_BRANCH, commit $OPENCOLLADA_REPO_UID).\"" + * [OpenCollada] (from $OPENCOLLADA_SOURCE, branch $OPENCOLLADA_REPO_BRANCH, commit $OPENCOLLADA_REPO_UID). + * [Alembic $ALEMBIC_VERSION] (from $ALEMBIC_SOURCE).\"" if [ "$DO_SHOW_DEPS" = true ]; then PRINT "" @@ -1118,7 +1152,7 @@ compile_Boost() { fi # To be changed each time we make edits that would modify the compiled result! - boost_magic=10 + boost_magic=11 _init_boost @@ -1873,7 +1907,7 @@ compile_OSD() { fi # To be changed each time we make edits that would modify the compiled result! - osd_magic=1 + osd_magic=2 _init_osd # Clean install if needed! @@ -2138,6 +2172,102 @@ compile_OPENVDB() { run_ldconfig "openvdb" } +#### Build Alembic #### +_init_alembic() { + _src=$SRC/alembic-$ALEMBIC_VERSION + _git=false + _inst=$INST/alembic-$ALEMBIC_VERSION + _inst_shortcut=$INST/alembic +} + +clean_ALEMBIC() { + _init_alembic + _clean +} + +compile_ALEMBIC() { + if [ "$NO_BUILD" = true ]; then + WARNING "--no-build enabled, Alembic will not be compiled!" + return + fi + + compile_HDF5 + PRINT "" + + # To be changed each time we make edits that would modify the compiled result! + alembic_magic=2 + _init_alembic + + # Clean install if needed! + magic_compile_check alembic-$ALEMBIC_VERSION $alembic_magic + if [ $? -eq 1 -o "$ALEMBIC_FORCE_REBUILD" = true ]; then + clean_ALEMBIC + fi + + if [ ! -d $_inst ]; then + INFO "Building Alembic-$ALEMBIC_VERSION" + + prepare_opt + + if [ ! -d $_src -o true ]; then + mkdir -p $SRC + download ALEMBIC_SOURCE[@] "$_src.tar.gz" + + INFO "Unpacking Alembic-$ALEMBIC_VERSION" + tar -C $SRC -xf $_src.tar.gz + fi + + cd $_src + + cmake_d="-D CMAKE_INSTALL_PREFIX=$_inst" + + if [ -d $INST/boost ]; then + cmake_d="$cmake_d -D BOOST_ROOT=$INST/boost" + cmake_d="$cmake_d -D USE_STATIC_BOOST=ON" + else + cmake_d="$cmake_d -D USE_STATIC_BOOST=OFF" + fi + + if [ "$_with_built_openexr" = true ]; then + cmake_d="$cmake_d -D ILMBASE_ROOT=$INST/openexr" + cmake_d="$cmake_d -D USE_ARNOLD=OFF" + cmake_d="$cmake_d -D USE_BINARIES=OFF" + cmake_d="$cmake_d -D USE_EXAMPLES=OFF" + cmake_d="$cmake_d -D USE_HDF5=OFF" + cmake_d="$cmake_d -D USE_MAYA=OFF" + cmake_d="$cmake_d -D USE_PRMAN=OFF" + cmake_d="$cmake_d -D USE_PYALEMBIC=OFF" + cmake_d="$cmake_d -D USE_STATIC_HDF5=OFF" + cmake_d="$cmake_d -D ALEMBIC_ILMBASE_LINK_STATIC=OFF" + cmake_d="$cmake_d -D ALEMBIC_SHARED_LIBS=OFF" + cmake_d="$cmake_d -D ALEMBIC_LIB_USES_BOOST=ON" + cmake_d="$cmake_d -D ALEMBIC_LIB_USES_TR1=OFF" + INFO "ILMBASE_ROOT=$INST/openexr" + fi + + cmake $cmake_d ./ + make -j$THREADS install + make clean + + if [ -d $_inst ]; then + _create_inst_shortcut + else + ERROR "Alembic-$ALEMBIC_VERSION failed to compile, exiting" + exit 1 + fi + + magic_compile_set alembic-$ALEMBIC_VERSION $alembic_magic + + cd $CWD + INFO "Done compiling Alembic-$ALEMBIC_VERSION!" + else + INFO "Own Alembic-$ALEMBIC_VERSION is up to date, nothing to do!" + INFO "If you want to force rebuild of this lib, use the --force-alembic option." + fi + + run_ldconfig "alembic" +} + #### Build OpenCOLLADA #### _init_opencollada() { _src=$SRC/OpenCOLLADA-$OPENCOLLADA_VERSION @@ -2746,6 +2876,17 @@ install_DEB() { fi fi + PRINT "" + if [ "$ALEMBIC_SKIP" = true ]; then + WARNING "Skipping Alembic installation, as requested..." + elif [ "$ALEMBIC_FORCE_BUILD" = true ]; then + INFO "Forced Alembic building, as requested..." + compile_ALEMBIC + else + # No package currently, only HDF5! + compile_ALEMBIC + fi + if [ "$WITH_OPENCOLLADA" = true ]; then _do_compile_collada=false @@ -3283,6 +3424,17 @@ install_RPM() { compile_OPENVDB fi + PRINT "" + if [ "$ALEMBIC_SKIP" = true ]; then + WARNING "Skipping Alembic installation, as requested..." + elif [ "$ALEMBIC_FORCE_BUILD" = true ]; then + INFO "Forced Alembic building, as requested..." + compile_ALEMBIC + else + # No package currently! + compile_ALEMBIC + fi + if [ "$WITH_OPENCOLLADA" = true ]; then PRINT "" @@ -3693,6 +3845,16 @@ install_ARCH() { fi fi + PRINT "" + if [ "$ALEMBIC_SKIP" = true ]; then + WARNING "Skipping Alembic installation, as requested..." + elif [ "$ALEMBIC_FORCE_BUILD" = true ]; then + INFO "Forced Alembic building, as requested..." + compile_ALEMBIC + else + compile_ALEMBIC + fi + if [ "$WITH_OPENCOLLADA" = true ]; then PRINT "" @@ -4000,7 +4162,7 @@ print_info() { _buildargs="-U *SNDFILE* -U *PYTHON* -U *BOOST* -U *Boost*" _buildargs="$_buildargs -U *OPENCOLORIO* -U *OPENEXR* -U *OPENIMAGEIO* -U *LLVM* -U *CYCLES*" - _buildargs="$_buildargs -U *OPENSUBDIV* -U *OPENVDB* -U *COLLADA* -U *FFMPEG*" + _buildargs="$_buildargs -U *OPENSUBDIV* -U *OPENVDB* -U *COLLADA* -U *FFMPEG* -U *ALEMBIC*" _1="-D WITH_CODEC_SNDFILE=ON" PRINT " $_1" @@ -4106,6 +4268,17 @@ print_info() { _buildargs="$_buildargs $_1" fi + if [ "$ALEMBIC_SKIP" = false ]; then + _1="-D WITH_ALEMBIC=ON" + PRINT " $_1" + _buildargs="$_buildargs $_1" + if [ -d $INST/alembic ]; then + _1="-D ALEMBIC_ROOT_DIR=$INST/alembic" + PRINT " $_1" + _buildargs="$_buildargs $_1" + fi + fi + if [ "$NO_SYSTEM_GLEW" = true ]; then _1="-D WITH_SYSTEM_GLEW=OFF" PRINT " $_1" diff --git a/build_files/cmake/Modules/FindAlembic.cmake b/build_files/cmake/Modules/FindAlembic.cmake new file mode 100644 index 00000000000..1f61b5ef462 --- /dev/null +++ b/build_files/cmake/Modules/FindAlembic.cmake @@ -0,0 +1,70 @@ +# - Find Alembic library +# Find the native Alembic includes and libraries +# This module defines +# ALEMBIC_INCLUDE_DIRS, where to find Alembic headers, Set when +# ALEMBIC_INCLUDE_DIR is found. +# ALEMBIC_LIBRARIES, libraries to link against to use Alembic. +# ALEMBIC_ROOT_DIR, The base directory to search for Alembic. +# This can also be an environment variable. +# ALEMBIC_FOUND, If false, do not try to use Alembic. +# + +#============================================================================= +# Copyright 2016 Blender Foundation. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= + +# If ALEMBIC_ROOT_DIR was defined in the environment, use it. +IF(NOT ALEMBIC_ROOT_DIR AND NOT $ENV{ALEMBIC_ROOT_DIR} STREQUAL "") + SET(ALEMBIC_ROOT_DIR $ENV{ALEMBIC_ROOT_DIR}) +ENDIF() + +SET(_alembic_SEARCH_DIRS + ${ALEMBIC_ROOT_DIR} + /usr/local + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt/lib/alembic +) + +FIND_PATH(ALEMBIC_INCLUDE_DIR + NAMES + Alembic/Abc/All.h + HINTS + ${_alembic_SEARCH_DIRS} + PATH_SUFFIXES + include +) + +FIND_LIBRARY(ALEMBIC_LIBRARY + NAMES + Alembic + HINTS + ${_alembic_SEARCH_DIRS} + PATH_SUFFIXES + lib64 lib lib/static +) + +# handle the QUIETLY and REQUIRED arguments and set ALEMBIC_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(ALEMBIC DEFAULT_MSG ALEMBIC_LIBRARY ALEMBIC_INCLUDE_DIR) + +IF(ALEMBIC_FOUND) + SET(ALEMBIC_LIBRARIES ${ALEMBIC_LIBRARY}) + SET(ALEMBIC_INCLUDE_DIRS ${ALEMBIC_INCLUDE_DIR}) +ENDIF(ALEMBIC_FOUND) + +MARK_AS_ADVANCED( + ALEMBIC_INCLUDE_DIR + ALEMBIC_LIBRARY +) + +UNSET(_alembic_SEARCH_DIRS) diff --git a/build_files/cmake/Modules/FindHDF5.cmake b/build_files/cmake/Modules/FindHDF5.cmake new file mode 100644 index 00000000000..56ceda8fb5e --- /dev/null +++ b/build_files/cmake/Modules/FindHDF5.cmake @@ -0,0 +1,69 @@ +# - Find HDF5 library +# Find the native HDF5 includes and libraries +# This module defines +# HDF5_INCLUDE_DIRS, where to find hdf5.h, Set when HDF5_INCLUDE_DIR is found. +# HDF5_LIBRARIES, libraries to link against to use HDF5. +# HDF5_ROOT_DIR, The base directory to search for HDF5. +# This can also be an environment variable. +# HDF5_FOUND, If false, do not try to use HDF5. +# + +#============================================================================= +# Copyright 2016 Blender Foundation. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= + +# If HDF5_ROOT_DIR was defined in the environment, use it. +IF(NOT HDF5_ROOT_DIR AND NOT $ENV{HDF5_ROOT_DIR} STREQUAL "") + SET(HDF5_ROOT_DIR $ENV{HDF5_ROOT_DIR}) +ENDIF() + +SET(_hdf5_SEARCH_DIRS + ${HDF5_ROOT_DIR} + /usr/local + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt/lib/hdf5 +) + +FIND_LIBRARY(HDF5_LIBRARY + NAMES + hdf5 + HINTS + ${_hdf5_SEARCH_DIRS} + PATH_SUFFIXES + lib64 lib +) + +FIND_PATH(HDF5_INCLUDE_DIR + NAMES + hdf5.h + HINTS + ${_hdf5_SEARCH_DIRS} + PATH_SUFFIXES + include +) + +# handle the QUIETLY and REQUIRED arguments and set HDF5_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(HDF5 DEFAULT_MSG HDF5_LIBRARY HDF5_INCLUDE_DIR) + +IF(HDF5_FOUND) + SET(HDF5_LIBRARIES ${HDF5_LIBRARY}) + SET(HDF5_INCLUDE_DIRS ${HDF5_INCLUDE_DIR}) +ENDIF(HDF5_FOUND) + +MARK_AS_ADVANCED( + HDF5_INCLUDE_DIR + HDF5_LIBRARY +) + +UNSET(_hdf5_SEARCH_DIRS) diff --git a/build_files/cmake/config/blender_full.cmake b/build_files/cmake/config/blender_full.cmake index b50b42416fb..634d4f431d4 100644 --- a/build_files/cmake/config/blender_full.cmake +++ b/build_files/cmake/config/blender_full.cmake @@ -4,6 +4,7 @@ # cmake -C../blender/build_files/cmake/config/blender_full.cmake ../blender # +set(WITH_ALEMBIC ON CACHE BOOL "" FORCE) set(WITH_BUILDINFO ON CACHE BOOL "" FORCE) set(WITH_BULLET ON CACHE BOOL "" FORCE) set(WITH_CODEC_AVI ON CACHE BOOL "" FORCE) diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake index 3c53ee7ae23..46b7d48b494 100644 --- a/build_files/cmake/config/blender_lite.cmake +++ b/build_files/cmake/config/blender_lite.cmake @@ -8,6 +8,7 @@ set(WITH_INSTALL_PORTABLE ON CACHE BOOL "" FORCE) set(WITH_SYSTEM_GLEW ON CACHE BOOL "" FORCE) +set(WITH_ALEMBIC OFF CACHE BOOL "" FORCE) set(WITH_BUILDINFO OFF CACHE BOOL "" FORCE) set(WITH_BULLET OFF CACHE BOOL "" FORCE) set(WITH_CODEC_AVI OFF CACHE BOOL "" FORCE) diff --git a/build_files/cmake/config/bpy_module.cmake b/build_files/cmake/config/bpy_module.cmake index 41140151f04..854d6e49370 100644 --- a/build_files/cmake/config/bpy_module.cmake +++ b/build_files/cmake/config/bpy_module.cmake @@ -32,3 +32,4 @@ set(WITH_OPENCOLLADA OFF CACHE BOOL "" FORCE) set(WITH_INTERNATIONAL OFF CACHE BOOL "" FORCE) set(WITH_BULLET OFF CACHE BOOL "" FORCE) set(WITH_OPENVDB OFF CACHE BOOL "" FORCE) +set(WITH_ALEMBIC OFF CACHE BOOL "" FORCE) diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index f57a6952164..c4e1c56699e 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -333,6 +333,11 @@ function(SETUP_LIBDIRS) link_directories(${LLVM_LIBPATH}) endif() + if(WITH_ALEMBIC) + link_directories(${ALEMBIC_LIBPATH}) + link_directories(${HDF5_LIBPATH}) + endif() + if(WIN32 AND NOT UNIX) link_directories(${PTHREADS_LIBPATH}) endif() @@ -434,6 +439,9 @@ function(setup_liblinks endif() endif() target_link_libraries(${target} ${JPEG_LIBRARIES}) + if(WITH_ALEMBIC) + target_link_libraries(${target} ${ALEMBIC_LIBRARIES} ${HDF5_LIBRARIES}) + endif() if(WITH_IMAGE_OPENEXR) target_link_libraries(${target} ${OPENEXR_LIBRARIES}) endif() @@ -607,6 +615,7 @@ function(SETUP_BLENDER_SORTED_LIBS) bf_imbuf_openimageio bf_imbuf_dds bf_collada + bf_alembic bf_intern_elbeem bf_intern_memutil bf_intern_guardedalloc diff --git a/extern/curve_fit_nd/README.blender b/extern/curve_fit_nd/README.blender new file mode 100644 index 00000000000..db520ea524e --- /dev/null +++ b/extern/curve_fit_nd/README.blender @@ -0,0 +1,5 @@ +Project: Curve-Fit-nD +URL: https://github.com/ideasman42/curve-fit-nd +License: BSD 3-Clause +Upstream version: Unknown (Last Release) +Local modifications: None diff --git a/extern/curve_fit_nd/intern/curve_fit_cubic.c b/extern/curve_fit_nd/intern/curve_fit_cubic.c index 9c8ebcd098b..9b4f1869c02 100644 --- a/extern/curve_fit_nd/intern/curve_fit_cubic.c +++ b/extern/curve_fit_nd/intern/curve_fit_cubic.c @@ -614,7 +614,7 @@ static void cubic_from_points_offset_fallback( double dists[2] = {0, 0}; - const double *pt = points_offset; + const double *pt = &points_offset[dims]; for (uint i = 1; i < points_offset_len - 1; i++, pt += dims) { for (uint k = 0; k < 2; k++) { sub_vn_vnvn(tmp, p0, pt, dims); @@ -623,13 +623,13 @@ static void cubic_from_points_offset_fallback( } } - float alpha_l = (dists[0] / 0.75) / dot_vnvn(tan_l, a[0], dims); - float alpha_r = (dists[1] / 0.75) / -dot_vnvn(tan_r, a[1], dims); + double alpha_l = (dists[0] / 0.75) / dot_vnvn(tan_l, a[0], dims); + double alpha_r = (dists[1] / 0.75) / -dot_vnvn(tan_r, a[1], dims); - if (!(alpha_l > 0.0f)) { + if (!(alpha_l > 0.0)) { alpha_l = dir_dist / 3.0; } - if (!(alpha_r > 0.0f)) { + if (!(alpha_r > 0.0)) { alpha_r = dir_dist / 3.0; } @@ -896,7 +896,7 @@ static double points_calc_coord_length( } assert(!is_almost_zero(r_u[points_offset_len - 1])); const double w = r_u[points_offset_len - 1]; - for (uint i = 0; i < points_offset_len; i++) { + for (uint i = 1; i < points_offset_len; i++) { r_u[i] /= w; } return w; diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt index 3b410b2a1e1..97854a88e84 100644 --- a/intern/cycles/CMakeLists.txt +++ b/intern/cycles/CMakeLists.txt @@ -146,6 +146,14 @@ if(WITH_CYCLES_OSL) ) endif() +if(WITH_CYCLES_OPENSUBDIV) + add_definitions(-DWITH_OPENSUBDIV) + include_directories( + SYSTEM + ${OPENSUBDIV_INCLUDE_DIR} + ) +endif() + set(WITH_CYCLES_DEVICE_OPENCL TRUE) set(WITH_CYCLES_DEVICE_CUDA TRUE) set(WITH_CYCLES_DEVICE_MULTI TRUE) diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt index 73dbf16a3d3..8cd499b7ca6 100644 --- a/intern/cycles/app/CMakeLists.txt +++ b/intern/cycles/app/CMakeLists.txt @@ -88,6 +88,9 @@ macro(cycles_target_link_libraries target) if(WITH_CYCLES_OSL) target_link_libraries(${target} ${OSL_LIBRARIES} ${LLVM_LIBRARIES}) endif() + if(WITH_CYCLES_OPENSUBDIV) + target_link_libraries(${target} ${OPENSUBDIV_LIBRARIES}) + endif() target_link_libraries( ${target} ${OPENIMAGEIO_LIBRARIES} diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp index 726e9a51744..e8168bc15ff 100644 --- a/intern/cycles/app/cycles_standalone.cpp +++ b/intern/cycles/app/cycles_standalone.cpp @@ -375,6 +375,8 @@ static void options_parse(int argc, const char **argv) "--threads %d", &options.session_params.threads, "CPU Rendering Threads", "--width %d", &options.width, "Window width in pixel", "--height %d", &options.height, "Window height in pixel", + "--tile-width %d", &options.session_params.tile_size.x, "Tile width in pixels", + "--tile-height %d", &options.session_params.tile_size.y, "Tile height in pixels", "--list-devices", &list, "List information about all available devices", #ifdef WITH_CYCLES_LOGGING "--debug", &debug, "Enable debug logging", diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp index 3d3aca33881..a54022268bb 100644 --- a/intern/cycles/app/cycles_xml.cpp +++ b/intern/cycles/app/cycles_xml.cpp @@ -57,14 +57,12 @@ struct XMLReadState : public XMLReader { Shader *shader; /* current shader */ string base; /* base path to current file*/ float dicing_rate; /* current dicing rate */ - Mesh::DisplacementMethod displacement_method; XMLReadState() : scene(NULL), smooth(false), shader(NULL), - dicing_rate(0.0f), - displacement_method(Mesh::DISPLACE_BUMP) + dicing_rate(0.0f) { tfm = transform_identity(); } @@ -405,8 +403,6 @@ static void xml_read_mesh(const XMLReadState& state, pugi::xml_node node) int shader = 0; bool smooth = state.smooth; - mesh->displacement_method = state.displacement_method; - /* read vertices and polygons, RIB style */ vector<float3> P; vector<float> UV; @@ -653,14 +649,6 @@ static void xml_read_state(XMLReadState& state, pugi::xml_node node) state.smooth = true; else if(xml_equal_string(node, "interpolation", "flat")) state.smooth = false; - - /* read displacement method */ - if(xml_equal_string(node, "displacement_method", "true")) - state.displacement_method = Mesh::DISPLACE_TRUE; - else if(xml_equal_string(node, "displacement_method", "bump")) - state.displacement_method = Mesh::DISPLACE_BUMP; - else if(xml_equal_string(node, "displacement_method", "both")) - state.displacement_method = Mesh::DISPLACE_BOTH; } /* Scene */ diff --git a/intern/cycles/blender/addon/osl.py b/intern/cycles/blender/addon/osl.py index f4aaaab5eab..19f2ecc9d1a 100644 --- a/intern/cycles/blender/addon/osl.py +++ b/intern/cycles/blender/addon/osl.py @@ -41,6 +41,8 @@ def update_script_node(node, report): import shutil import tempfile + oso_file_remove = False + if node.mode == 'EXTERNAL': # compile external script file script_path = bpy.path.abspath(node.filepath, library=node.id_data.library) @@ -49,7 +51,6 @@ def update_script_node(node, report): if script_ext == ".oso": # it's a .oso file, no need to compile ok, oso_path = True, script_path - oso_file_remove = False elif script_ext == ".osl": # compile .osl file ok, oso_path = osl_compile(script_path, report) @@ -65,7 +66,6 @@ def update_script_node(node, report): elif os.path.dirname(node.filepath) == "": # module in search path oso_path = node.filepath - oso_file_remove = False ok = True else: # unknown @@ -88,12 +88,10 @@ def update_script_node(node, report): osl_file.close() ok, oso_path = osl_compile(osl_file.name, report) - oso_file_remove = False os.remove(osl_file.name) else: # compile text datablock from disk directly ok, oso_path = osl_compile(osl_path, report) - oso_file_remove = False if ok: # read bytecode diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 81204eb8ae0..8e82eac2b59 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -775,6 +775,13 @@ class CyclesMaterialSettings(bpy.types.PropertyGroup): default='LINEAR', ) + cls.displacement_method = EnumProperty( + name="Displacement Method", + description="Method to use for the displacement", + items=enum_displacement_methods, + default='BUMP', + ) + @classmethod def unregister(cls): del bpy.types.Material.cycles @@ -952,13 +959,6 @@ class CyclesMeshSettings(bpy.types.PropertyGroup): type=cls, ) - cls.displacement_method = EnumProperty( - name="Displacement Method", - description="Method to use for the displacement", - items=enum_displacement_methods, - default='BUMP', - ) - @classmethod def unregister(cls): del bpy.types.Mesh.cycles diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 42f7970769a..52872d2b83f 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -674,40 +674,6 @@ class Cycles_PT_context_material(CyclesButtonsPanel, Panel): split.separator() -class Cycles_PT_mesh_displacement(CyclesButtonsPanel, Panel): - bl_label = "Displacement" - bl_context = "data" - - @classmethod - def poll(cls, context): - if CyclesButtonsPanel.poll(context): - if context.mesh or context.curve or context.meta_ball: - if context.scene.cycles.feature_set == 'EXPERIMENTAL': - return True - - return False - - def draw(self, context): - layout = self.layout - - mesh = context.mesh - curve = context.curve - mball = context.meta_ball - - if mesh: - cdata = mesh.cycles - elif curve: - cdata = curve.cycles - elif mball: - cdata = mball.cycles - - split = layout.split() - - col = split.column() - sub = col.column(align=True) - sub.label(text="Displacement:") - sub.prop(cdata, "displacement_method", text="") - class CyclesObject_PT_motion_blur(CyclesButtonsPanel, Panel): bl_label = "Motion Blur" bl_context = "object" @@ -1219,6 +1185,11 @@ class CyclesMaterial_PT_settings(CyclesButtonsPanel, Panel): col.prop(cmat, "sample_as_light", text="Multiple Importance") col.prop(cmat, "use_transparent_shadow") + if context.scene.cycles.feature_set == 'EXPERIMENTAL': + col.separator() + col.label(text="Displacement:") + col.prop(cmat, "displacement_method", text="") + col = split.column() col.label(text="Volume:") sub = col.column() diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index 74fd4cb44a0..c33bc4c263f 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -409,7 +409,8 @@ static void attr_create_uv_map(Scene *scene, BL::Mesh& b_mesh, const vector<int>& nverts, const vector<int>& face_flags, - bool subdivision) + bool subdivision, + bool subdivide_uvs) { if(subdivision) { BL::Mesh::uv_layers_iterator l; @@ -429,6 +430,10 @@ static void attr_create_uv_map(Scene *scene, else attr = mesh->subd_attributes.add(name, TypeDesc::TypePoint, ATTR_ELEMENT_CORNER); + if(subdivide_uvs) { + attr->flags |= ATTR_SUBDIVIDED; + } + BL::Mesh::polygons_iterator p; float3 *fdata = attr->data_float3(); @@ -592,7 +597,8 @@ static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh& b_mesh, const vector<Shader*>& used_shaders, - bool subdivision=false) + bool subdivision=false, + bool subdivide_uvs=true) { /* count vertices and faces */ int numverts = b_mesh.vertices.length(); @@ -638,6 +644,7 @@ static void create_mesh(Scene *scene, /* create generated coordinates from undeformed coordinates */ if(mesh->need_attribute(scene, ATTR_STD_GENERATED)) { Attribute *attr = attributes.add(ATTR_STD_GENERATED); + attr->flags |= ATTR_SUBDIVIDED; float3 loc, size; mesh_texture_space(b_mesh, loc, size); @@ -746,7 +753,7 @@ static void create_mesh(Scene *scene, * The calculate functions will check whether they're needed or not. */ attr_create_vertex_color(scene, mesh, b_mesh, nverts, face_flags, subdivision); - attr_create_uv_map(scene, mesh, b_mesh, nverts, face_flags, subdivision); + attr_create_uv_map(scene, mesh, b_mesh, nverts, face_flags, subdivision, subdivide_uvs); /* for volume objects, create a matrix to transform from object space to * mesh texture space. this does not work with deformations but that can @@ -770,9 +777,39 @@ static void create_subd_mesh(Scene *scene, float dicing_rate, int max_subdivisions) { - create_mesh(scene, mesh, b_mesh, used_shaders, true); + BL::SubsurfModifier subsurf_mod(b_ob.modifiers[b_ob.modifiers.length()-1]); + bool subdivide_uvs = subsurf_mod.use_subsurf_uv(); + + create_mesh(scene, mesh, b_mesh, used_shaders, true, subdivide_uvs); + + /* export creases */ + size_t num_creases = 0; + BL::Mesh::edges_iterator e; + + for(b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e) { + if(e->crease() != 0.0f) { + num_creases++; + } + } + + mesh->subd_creases.resize(num_creases); + + Mesh::SubdEdgeCrease* crease = mesh->subd_creases.data(); + for(b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e) { + if(e->crease() != 0.0f) { + crease->v[0] = e->vertices()[0]; + crease->v[1] = e->vertices()[1]; + crease->crease = e->crease(); - SubdParams sdparams(mesh); + crease++; + } + } + + /* set subd params */ + if(!mesh->subd_params) { + mesh->subd_params = new SubdParams(mesh); + } + SubdParams& sdparams = *mesh->subd_params; PointerRNA cobj = RNA_pointer_get(&b_ob.ptr, "cycles"); @@ -782,10 +819,6 @@ static void create_subd_mesh(Scene *scene, scene->camera->update(); sdparams.camera = scene->camera; sdparams.objecttoworld = get_transform(b_ob.matrix_world()); - - /* tesselate */ - DiagSplit dsplit(sdparams); - mesh->tessellate(&dsplit); } /* Sync */ @@ -903,8 +936,6 @@ Mesh *BlenderSync::sync_mesh(BL::Object& b_ob, mesh_synced.insert(mesh); /* create derived mesh */ - PointerRNA cmesh = RNA_pointer_get(&b_ob_data.ptr, "cycles"); - array<int> oldtriangle = mesh->triangles; /* compares curve_keys rather than strands in order to handle quick hair @@ -936,7 +967,7 @@ Mesh *BlenderSync::sync_mesh(BL::Object& b_ob, BL::Modifier mod = b_ob.modifiers[b_ob.modifiers.length()-1]; bool enabled = preview ? mod.show_viewport() : mod.show_render(); - if(enabled && mod.type() == BL::Modifier::type_SUBSURF && RNA_int_get(&cobj, "use_adaptive_subdivision")) { + if(enabled && mod.type() == BL::Modifier::type_SUBSURF && RNA_boolean_get(&cobj, "use_adaptive_subdivision")) { BL::SubsurfModifier subsurf(mod); if(subsurf.subdivision_type() == BL::SubsurfModifier::subdivision_type_CATMULL_CLARK) { @@ -974,21 +1005,6 @@ Mesh *BlenderSync::sync_mesh(BL::Object& b_ob, } mesh->geometry_flags = requested_geometry_flags; - /* displacement method */ - if(cmesh.data) { - const int method = get_enum(cmesh, - "displacement_method", - Mesh::DISPLACE_NUM_METHODS, - Mesh::DISPLACE_BUMP); - - if(method == 0 || !experimental) - mesh->displacement_method = Mesh::DISPLACE_BUMP; - else if(method == 1) - mesh->displacement_method = Mesh::DISPLACE_TRUE; - else - mesh->displacement_method = Mesh::DISPLACE_BOTH; - } - /* fluid motion */ sync_mesh_fluid_motion(b_ob, scene, mesh); diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 4886735a18f..f305e8e17cc 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -329,16 +329,18 @@ Object *BlenderSync::sync_object(BL::Object& b_parent, /* object transformation */ if(tfm != object->tfm) { VLOG(1) << "Object " << b_ob.name() << " motion detected."; - if(motion_time == -1.0f) { - object->motion.pre = tfm; - object->use_motion = true; - } - else if(motion_time == 1.0f) { - object->motion.post = tfm; + if(motion_time == -1.0f || motion_time == 1.0f) { object->use_motion = true; } } + if(motion_time == -1.0f) { + object->motion.pre = tfm; + } + else if(motion_time == 1.0f) { + object->motion.post = tfm; + } + /* mesh deformation */ if(object->mesh) sync_mesh_motion(b_ob, object, motion_time); @@ -395,8 +397,8 @@ Object *BlenderSync::sync_object(BL::Object& b_parent, object->name = b_ob.name().c_str(); object->pass_id = b_ob.pass_index(); object->tfm = tfm; - object->motion.pre = tfm; - object->motion.post = tfm; + object->motion.pre = transform_empty(); + object->motion.post = transform_empty(); object->use_motion = false; /* motion blur */ diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 7b8317a50a7..171b8241280 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -64,6 +64,14 @@ static VolumeInterpolation get_volume_interpolation(PointerRNA& ptr) VOLUME_INTERPOLATION_LINEAR); } +static DisplacementMethod get_displacement_method(PointerRNA& ptr) +{ + return (DisplacementMethod)get_enum(ptr, + "displacement_method", + DISPLACE_NUM_METHODS, + DISPLACE_BUMP); +} + static int validate_enum_value(int value, int num_values, int default_value) { if(value >= num_values) { @@ -840,8 +848,10 @@ static ShaderNode *add_node(Scene *scene, } } - if(node) + if(node) { + node->name = b_node.name(); graph->add(node); + } return node; } @@ -1183,6 +1193,7 @@ void BlenderSync::sync_materials(bool update_all) shader->heterogeneous_volume = !get_boolean(cmat, "homogeneous_volume"); shader->volume_sampling_method = get_volume_sampling(cmat); shader->volume_interpolation_method = get_volume_interpolation(cmat); + shader->displacement_method = (experimental) ? get_displacement_method(cmat) : DISPLACE_BUMP; shader->set_graph(graph); shader->tag_update(scene); diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp index df01215c91a..85e736ad635 100644 --- a/intern/cycles/device/device.cpp +++ b/intern/cycles/device/device.cpp @@ -56,8 +56,14 @@ std::ostream& operator <<(std::ostream &os, << string_from_bool(requested_features.use_camera_motion) << std::endl; os << "Use Baking: " << string_from_bool(requested_features.use_baking) << std::endl; + os << "Use Subsurface: " + << string_from_bool(requested_features.use_subsurface) << std::endl; os << "Use Volume: " << string_from_bool(requested_features.use_volume) << std::endl; + os << "Use Branched Integrator: " + << string_from_bool(requested_features.use_integrator_branched) << std::endl; + os << "Use Patch Evaluation: " + << string_from_bool(requested_features.use_patch_evaluation) << std::endl; return os; } diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index e11bb7f76af..77dc1fa9713 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -109,6 +109,9 @@ public: /* Use branched integrator. */ bool use_integrator_branched; + /* Use OpenSubdiv patch evaluation */ + bool use_patch_evaluation; + DeviceRequestedFeatures() { /* TODO(sergey): Find more meaningful defaults. */ @@ -123,6 +126,7 @@ public: use_subsurface = false; use_volume = false; use_integrator_branched = false; + use_patch_evaluation = false; } bool modified(const DeviceRequestedFeatures& requested_features) @@ -137,7 +141,8 @@ public: use_baking == requested_features.use_baking && use_subsurface == requested_features.use_subsurface && use_volume == requested_features.use_volume && - use_integrator_branched == requested_features.use_integrator_branched); + use_integrator_branched == requested_features.use_integrator_branched && + use_patch_evaluation == requested_features.use_patch_evaluation); } /* Convert the requested features structure to a build options, @@ -175,6 +180,9 @@ public: if(!use_integrator_branched) { build_options += " -D__NO_BRANCHED_PATH__"; } + if(!use_patch_evaluation) { + build_options += " -D__NO_PATCH_EVAL__"; + } return build_options; } }; diff --git a/intern/cycles/device/device_cuda.cpp b/intern/cycles/device/device_cuda.cpp index 6a511ea7316..76e52498b42 100644 --- a/intern/cycles/device/device_cuda.cpp +++ b/intern/cycles/device/device_cuda.cpp @@ -297,7 +297,7 @@ public: cuda_error_message("CUDA nvcc compiler version could not be parsed."); return false; } - if(cuda_version < 60) { + if(cuda_version < 75) { printf("Unsupported CUDA version %d.%d detected, " "you need CUDA 7.5 or newer.\n", major, minor); @@ -576,6 +576,7 @@ public: case TYPE_UINT: format = CU_AD_FORMAT_UNSIGNED_INT32; break; case TYPE_INT: format = CU_AD_FORMAT_SIGNED_INT32; break; case TYPE_FLOAT: format = CU_AD_FORMAT_FLOAT; break; + case TYPE_HALF: format = CU_AD_FORMAT_HALF; break; default: assert(0); return; } @@ -747,8 +748,12 @@ public: } /* Resize once */ - if(flat_slot >= bindless_mapping.size()) - bindless_mapping.resize(4096); /*TODO(dingto): Make this a variable */ + if(flat_slot >= bindless_mapping.size()) { + /* Allocate some slots in advance, to reduce amount + * of re-allocations. + */ + bindless_mapping.resize(flat_slot + 128); + } /* Set Mapping and tag that we need to (re-)upload to device */ bindless_mapping.get_data()[flat_slot] = (uint)tex; diff --git a/intern/cycles/device/device_opencl.cpp b/intern/cycles/device/device_opencl.cpp index 50490f3a20e..5c05aeb5569 100644 --- a/intern/cycles/device/device_opencl.cpp +++ b/intern/cycles/device/device_opencl.cpp @@ -875,6 +875,7 @@ public: if(ciErr != CL_SUCCESS) { opencl_error("OpenCL build failed: errors in console"); + fprintf(stderr, "Build error: %s\n", clewErrorString(ciErr)); return false; } diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index f4d154ca19e..1bb93c7f922 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -166,6 +166,7 @@ set(SRC_GEOM_HEADERS geom/geom_motion_curve.h geom/geom_motion_triangle.h geom/geom_object.h + geom/geom_patch.h geom/geom_primitive.h geom/geom_subd_triangle.h geom/geom_triangle.h @@ -179,6 +180,7 @@ set(SRC_UTIL_HEADERS ../util/util_half.h ../util/util_math.h ../util/util_math_fast.h + ../util/util_static_assert.h ../util/util_transform.h ../util/util_texture.h ../util/util_types.h diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h index 633a16ca8e5..ac4f52818c9 100644 --- a/intern/cycles/kernel/closure/bsdf.h +++ b/intern/cycles/kernel/closure/bsdf.h @@ -166,7 +166,7 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg, return label; } -#ifndef __KERNEL_CUDS__ +#ifndef __KERNEL_CUDA__ ccl_device #else ccl_device_inline diff --git a/intern/cycles/kernel/geom/geom.h b/intern/cycles/kernel/geom/geom.h index 493afdc4f62..3605394f182 100644 --- a/intern/cycles/kernel/geom/geom.h +++ b/intern/cycles/kernel/geom/geom.h @@ -17,6 +17,9 @@ #include "geom_attribute.h" #include "geom_object.h" +#ifdef __PATCH_EVAL__ +# include "geom_patch.h" +#endif #include "geom_triangle.h" #include "geom_subd_triangle.h" #include "geom_triangle_intersect.h" diff --git a/intern/cycles/kernel/geom/geom_attribute.h b/intern/cycles/kernel/geom/geom_attribute.h index 5d78cf8f9fc..8604d30ad34 100644 --- a/intern/cycles/kernel/geom/geom_attribute.h +++ b/intern/cycles/kernel/geom/geom_attribute.h @@ -43,12 +43,19 @@ ccl_device_inline uint attribute_primitive_type(KernelGlobals *kg, const ShaderD } } +ccl_device_inline AttributeDescriptor attribute_not_found() +{ + const AttributeDescriptor desc = {ATTR_ELEMENT_NONE, (NodeAttributeType)0, 0, ATTR_STD_NOT_FOUND}; + return desc; +} + /* Find attribute based on ID */ -ccl_device_inline int find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id, AttributeElement *elem) +ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id) { - if(ccl_fetch(sd, object) == PRIM_NONE) - return (int)ATTR_STD_NOT_FOUND; + if(ccl_fetch(sd, object) == PRIM_NONE) { + return attribute_not_found(); + } /* for SVM, find attribute by unique id */ uint attr_offset = ccl_fetch(sd, object)*kernel_data.bvh.attributes_map_stride; @@ -57,31 +64,37 @@ ccl_device_inline int find_attribute(KernelGlobals *kg, const ShaderData *sd, ui while(attr_map.x != id) { if(UNLIKELY(attr_map.x == ATTR_STD_NONE)) { - return ATTR_STD_NOT_FOUND; + return attribute_not_found(); } attr_offset += ATTR_PRIM_TYPES; attr_map = kernel_tex_fetch(__attributes_map, attr_offset); } - *elem = (AttributeElement)attr_map.y; + AttributeDescriptor desc; + desc.element = (AttributeElement)attr_map.y; - if(ccl_fetch(sd, prim) == PRIM_NONE && (AttributeElement)attr_map.y != ATTR_ELEMENT_MESH) - return ATTR_STD_NOT_FOUND; + if(ccl_fetch(sd, prim) == PRIM_NONE && desc.element != ATTR_ELEMENT_MESH) { + return attribute_not_found(); + } /* return result */ - return (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z; + desc.offset = (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z; + desc.type = (NodeAttributeType)(attr_map.w & 0xff); + desc.flags = (AttributeFlag)(attr_map.w >> 8); + + return desc; } /* Transform matrix attribute on meshes */ -ccl_device Transform primitive_attribute_matrix(KernelGlobals *kg, const ShaderData *sd, int offset) +ccl_device Transform primitive_attribute_matrix(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc) { Transform tfm; - tfm.x = kernel_tex_fetch(__attributes_float3, offset + 0); - tfm.y = kernel_tex_fetch(__attributes_float3, offset + 1); - tfm.z = kernel_tex_fetch(__attributes_float3, offset + 2); - tfm.w = kernel_tex_fetch(__attributes_float3, offset + 3); + tfm.x = kernel_tex_fetch(__attributes_float3, desc.offset + 0); + tfm.y = kernel_tex_fetch(__attributes_float3, desc.offset + 1); + tfm.z = kernel_tex_fetch(__attributes_float3, desc.offset + 2); + tfm.w = kernel_tex_fetch(__attributes_float3, desc.offset + 3); return tfm; } diff --git a/intern/cycles/kernel/geom/geom_curve.h b/intern/cycles/kernel/geom/geom_curve.h index 292e1bfca0e..aa9cd295452 100644 --- a/intern/cycles/kernel/geom/geom_curve.h +++ b/intern/cycles/kernel/geom/geom_curve.h @@ -24,23 +24,23 @@ CCL_NAMESPACE_BEGIN /* Reading attributes on various curve elements */ -ccl_device float curve_attribute_float(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float *dx, float *dy) +ccl_device float curve_attribute_float(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy) { - if(elem == ATTR_ELEMENT_CURVE) { + if(desc.element == ATTR_ELEMENT_CURVE) { #ifdef __RAY_DIFFERENTIALS__ if(dx) *dx = 0.0f; if(dy) *dy = 0.0f; #endif - return kernel_tex_fetch(__attributes_float, offset + ccl_fetch(sd, prim)); + return kernel_tex_fetch(__attributes_float, desc.offset + ccl_fetch(sd, prim)); } - else if(elem == ATTR_ELEMENT_CURVE_KEY || elem == ATTR_ELEMENT_CURVE_KEY_MOTION) { + else if(desc.element == ATTR_ELEMENT_CURVE_KEY || desc.element == ATTR_ELEMENT_CURVE_KEY_MOTION) { float4 curvedata = kernel_tex_fetch(__curves, ccl_fetch(sd, prim)); int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(ccl_fetch(sd, type)); int k1 = k0 + 1; - float f0 = kernel_tex_fetch(__attributes_float, offset + k0); - float f1 = kernel_tex_fetch(__attributes_float, offset + k1); + float f0 = kernel_tex_fetch(__attributes_float, desc.offset + k0); + float f1 = kernel_tex_fetch(__attributes_float, desc.offset + k1); #ifdef __RAY_DIFFERENTIALS__ if(dx) *dx = ccl_fetch(sd, du).dx*(f1 - f0); @@ -59,9 +59,9 @@ ccl_device float curve_attribute_float(KernelGlobals *kg, const ShaderData *sd, } } -ccl_device float3 curve_attribute_float3(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float3 *dx, float3 *dy) +ccl_device float3 curve_attribute_float3(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, float3 *dy) { - if(elem == ATTR_ELEMENT_CURVE) { + if(desc.element == ATTR_ELEMENT_CURVE) { /* idea: we can't derive any useful differentials here, but for tiled * mipmap image caching it would be useful to avoid reading the highest * detail level always. maybe a derivative based on the hair density @@ -71,15 +71,15 @@ ccl_device float3 curve_attribute_float3(KernelGlobals *kg, const ShaderData *sd if(dy) *dy = make_float3(0.0f, 0.0f, 0.0f); #endif - return float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + ccl_fetch(sd, prim))); + return float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + ccl_fetch(sd, prim))); } - else if(elem == ATTR_ELEMENT_CURVE_KEY || elem == ATTR_ELEMENT_CURVE_KEY_MOTION) { + else if(desc.element == ATTR_ELEMENT_CURVE_KEY || desc.element == ATTR_ELEMENT_CURVE_KEY_MOTION) { float4 curvedata = kernel_tex_fetch(__curves, ccl_fetch(sd, prim)); int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(ccl_fetch(sd, type)); int k1 = k0 + 1; - float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + k0)); - float3 f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + k1)); + float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + k0)); + float3 f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + k1)); #ifdef __RAY_DIFFERENTIALS__ if(dx) *dx = ccl_fetch(sd, du).dx*(f1 - f0); diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index c0d15a95954..883c5dc100d 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -292,6 +292,18 @@ ccl_device_inline void object_motion_info(KernelGlobals *kg, int object, int *nu *numverts = __float_as_int(f.w); } +/* Offset to an objects patch map */ + +ccl_device_inline uint object_patch_map_offset(KernelGlobals *kg, int object) +{ + if(object == OBJECT_NONE) + return 0; + + int offset = object*OBJECT_SIZE + 11; + float4 f = kernel_tex_fetch(__objects, offset); + return __float_as_uint(f.x); +} + /* Pass ID for shader */ ccl_device int shader_pass_id(KernelGlobals *kg, const ShaderData *sd) diff --git a/intern/cycles/kernel/geom/geom_patch.h b/intern/cycles/kernel/geom/geom_patch.h new file mode 100644 index 00000000000..6a0ff5a4a04 --- /dev/null +++ b/intern/cycles/kernel/geom/geom_patch.h @@ -0,0 +1,343 @@ +/* + * Based on code from OpenSubdiv released under this license: + * + * Copyright 2013 Pixar + * + * Licensed under the Apache License, Version 2.0 (the "Apache License") + * with the following modification; you may not use this file except in + * compliance with the Apache License and the following modification to it: + * Section 6. Trademarks. is deleted and replaced with: + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor + * and its affiliates, except as required to comply with Section 4(c) of + * the License and to reproduce the content of the NOTICE file. + * + * You may obtain a copy of the Apache License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Apache License with the above modification is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the Apache License for the specific + * language governing permissions and limitations under the Apache License. + * + */ + +CCL_NAMESPACE_BEGIN + +typedef struct PatchHandle { + int array_index, patch_index, vert_index; +} PatchHandle; + +ccl_device_inline int patch_map_resolve_quadrant(float median, float *u, float *v) +{ + int quadrant = -1; + + if(*u < median) { + if(*v < median) { + quadrant = 0; + } + else { + quadrant = 1; + *v -= median; + } + } + else { + if(*v < median) { + quadrant = 3; + } + else { + quadrant = 2; + *v -= median; + } + *u -= median; + } + + return quadrant; +} + +/* retrieve PatchHandle from patch coords */ + +ccl_device_inline PatchHandle patch_map_find_patch(KernelGlobals *kg, int object, int patch, float u, float v) +{ + PatchHandle handle; + + kernel_assert((u >= 0.0f) && (u <= 1.0f) && (v >= 0.0f) && (v <= 1.0f)); + + int node = (object_patch_map_offset(kg, object) + patch)/2; + float median = 0.5f; + + for(int depth = 0; depth < 0xff; depth++) { + float delta = median * 0.5f; + + int quadrant = patch_map_resolve_quadrant(median, &u, &v); + kernel_assert(quadrant >= 0); + + uint child = kernel_tex_fetch(__patches, node + quadrant); + + /* is the quadrant a hole? */ + if(!(child & PATCH_MAP_NODE_IS_SET)) { + handle.array_index = -1; + return handle; + } + + uint index = child & PATCH_MAP_NODE_INDEX_MASK; + + if(child & PATCH_MAP_NODE_IS_LEAF) { + handle.array_index = kernel_tex_fetch(__patches, index + 0); + handle.patch_index = kernel_tex_fetch(__patches, index + 1); + handle.vert_index = kernel_tex_fetch(__patches, index + 2); + + return handle; + } else { + node = index; + } + + median = delta; + } + + /* no leaf found */ + kernel_assert(0); + + handle.array_index = -1; + return handle; +} + +ccl_device_inline void patch_eval_bspline_weights(float t, float *point, float *deriv) +{ + /* The four uniform cubic B-Spline basis functions evaluated at t */ + float inv_6 = 1.0f / 6.0f; + + float t2 = t * t; + float t3 = t * t2; + + point[0] = inv_6 * (1.0f - 3.0f*(t - t2) - t3); + point[1] = inv_6 * (4.0f - 6.0f*t2 + 3.0f*t3); + point[2] = inv_6 * (1.0f + 3.0f*(t + t2 - t3)); + point[3] = inv_6 * t3; + + /* Derivatives of the above four basis functions at t */ + deriv[0] = -0.5f*t2 + t - 0.5f; + deriv[1] = 1.5f*t2 - 2.0f*t; + deriv[2] = -1.5f*t2 + t + 0.5f; + deriv[3] = 0.5f*t2; +} + +ccl_device_inline void patch_eval_adjust_boundary_weights(uint bits, float *s, float *t) +{ + int boundary = ((bits >> 8) & 0xf); + + if(boundary & 1) { + t[2] -= t[0]; + t[1] += 2*t[0]; + t[0] = 0; + } + + if(boundary & 2) { + s[1] -= s[3]; + s[2] += 2*s[3]; + s[3] = 0; + } + + if(boundary & 4) { + t[1] -= t[3]; + t[2] += 2*t[3]; + t[3] = 0; + } + + if(boundary & 8) { + s[2] -= s[0]; + s[1] += 2*s[0]; + s[0] = 0; + } +} + +ccl_device_inline int patch_eval_depth(uint patch_bits) +{ + return (patch_bits & 0xf); +} + +ccl_device_inline float patch_eval_param_fraction(uint patch_bits) +{ + bool non_quad_root = (patch_bits >> 4) & 0x1; + int depth = patch_eval_depth(patch_bits); + + if(non_quad_root) { + return 1.0f / (float)(1 << (depth-1)); + } + else { + return 1.0f / (float)(1 << depth); + } +} + +ccl_device_inline void patch_eval_normalize_coords(uint patch_bits, float *u, float *v) +{ + float frac = patch_eval_param_fraction(patch_bits); + + int iu = (patch_bits >> 22) & 0x3ff; + int iv = (patch_bits >> 12) & 0x3ff; + + /* top left corner */ + float pu = (float)iu*frac; + float pv = (float)iv*frac; + + /* normalize uv coordinates */ + *u = (*u - pu) / frac; + *v = (*v - pv) / frac; +} + +/* retrieve patch control indices */ + +ccl_device_inline int patch_eval_indices(KernelGlobals *kg, const PatchHandle *handle, int channel, + int indices[PATCH_MAX_CONTROL_VERTS]) +{ + int index_base = kernel_tex_fetch(__patches, handle->array_index + 2) + handle->vert_index; + + /* XXX: regular patches only */ + for(int i = 0; i < 16; i++) { + indices[i] = kernel_tex_fetch(__patches, index_base + i); + } + + return 16; +} + +/* evaluate patch basis functions */ + +ccl_device_inline void patch_eval_basis(KernelGlobals *kg, const PatchHandle *handle, float u, float v, + float weights[PATCH_MAX_CONTROL_VERTS], + float weights_du[PATCH_MAX_CONTROL_VERTS], + float weights_dv[PATCH_MAX_CONTROL_VERTS]) +{ + uint patch_bits = kernel_tex_fetch(__patches, handle->patch_index + 1); /* read patch param */ + float d_scale = 1 << patch_eval_depth(patch_bits); + + bool non_quad_root = (patch_bits >> 4) & 0x1; + if(non_quad_root) { + d_scale *= 0.5f; + } + + patch_eval_normalize_coords(patch_bits, &u, &v); + + /* XXX: regular patches only for now. */ + + float s[4], t[4], ds[4], dt[4]; + + patch_eval_bspline_weights(u, s, ds); + patch_eval_bspline_weights(v, t, dt); + + patch_eval_adjust_boundary_weights(patch_bits, s, t); + patch_eval_adjust_boundary_weights(patch_bits, ds, dt); + + for(int k = 0; k < 4; k++) { + for(int l = 0; l < 4; l++) { + weights[4*k+l] = s[l] * t[k]; + weights_du[4*k+l] = ds[l] * t[k] * d_scale; + weights_dv[4*k+l] = s[l] * dt[k] * d_scale; + } + } +} + +/* generic function for evaluating indices and weights from patch coords */ + +ccl_device_inline int patch_eval_control_verts(KernelGlobals *kg, int object, int patch, float u, float v, int channel, + int indices[PATCH_MAX_CONTROL_VERTS], + float weights[PATCH_MAX_CONTROL_VERTS], + float weights_du[PATCH_MAX_CONTROL_VERTS], + float weights_dv[PATCH_MAX_CONTROL_VERTS]) +{ + PatchHandle handle = patch_map_find_patch(kg, object, patch, u, v); + kernel_assert(handle.array_index >= 0); + + int num_control = patch_eval_indices(kg, &handle, channel, indices); + patch_eval_basis(kg, &handle, u, v, weights, weights_du, weights_dv); + + return num_control; +} + +/* functions for evaluating attributes on patches */ + +ccl_device float patch_eval_float(KernelGlobals *kg, const ShaderData *sd, int offset, + int patch, float u, float v, int channel, + float *du, float* dv) +{ + int indices[PATCH_MAX_CONTROL_VERTS]; + float weights[PATCH_MAX_CONTROL_VERTS]; + float weights_du[PATCH_MAX_CONTROL_VERTS]; + float weights_dv[PATCH_MAX_CONTROL_VERTS]; + + int num_control = patch_eval_control_verts(kg, ccl_fetch(sd, object), patch, u, v, channel, + indices, weights, weights_du, weights_dv); + + float val = 0.0f; + if(du) *du = 0.0f; + if(dv) *dv = 0.0f; + + for(int i = 0; i < num_control; i++) { + float v = kernel_tex_fetch(__attributes_float, offset + indices[i]); + + val += v * weights[i]; + if(du) *du += v * weights_du[i]; + if(dv) *dv += v * weights_dv[i]; + } + + return val; +} + +ccl_device float3 patch_eval_float3(KernelGlobals *kg, const ShaderData *sd, int offset, + int patch, float u, float v, int channel, + float3 *du, float3 *dv) +{ + int indices[PATCH_MAX_CONTROL_VERTS]; + float weights[PATCH_MAX_CONTROL_VERTS]; + float weights_du[PATCH_MAX_CONTROL_VERTS]; + float weights_dv[PATCH_MAX_CONTROL_VERTS]; + + int num_control = patch_eval_control_verts(kg, ccl_fetch(sd, object), patch, u, v, channel, + indices, weights, weights_du, weights_dv); + + float3 val = make_float3(0.0f, 0.0f, 0.0f); + if(du) *du = make_float3(0.0f, 0.0f, 0.0f); + if(dv) *dv = make_float3(0.0f, 0.0f, 0.0f); + + for(int i = 0; i < num_control; i++) { + float3 v = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + indices[i])); + + val += v * weights[i]; + if(du) *du += v * weights_du[i]; + if(dv) *dv += v * weights_dv[i]; + } + + return val; +} + +ccl_device float3 patch_eval_uchar4(KernelGlobals *kg, const ShaderData *sd, int offset, + int patch, float u, float v, int channel, + float3 *du, float3 *dv) +{ + int indices[PATCH_MAX_CONTROL_VERTS]; + float weights[PATCH_MAX_CONTROL_VERTS]; + float weights_du[PATCH_MAX_CONTROL_VERTS]; + float weights_dv[PATCH_MAX_CONTROL_VERTS]; + + int num_control = patch_eval_control_verts(kg, ccl_fetch(sd, object), patch, u, v, channel, + indices, weights, weights_du, weights_dv); + + float3 val = make_float3(0.0f, 0.0f, 0.0f); + if(du) *du = make_float3(0.0f, 0.0f, 0.0f); + if(dv) *dv = make_float3(0.0f, 0.0f, 0.0f); + + for(int i = 0; i < num_control; i++) { + float3 v = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, offset + indices[i])); + + val += v * weights[i]; + if(du) *du += v * weights_du[i]; + if(dv) *dv += v * weights_dv[i]; + } + + return val; +} + +CCL_NAMESPACE_END + diff --git a/intern/cycles/kernel/geom/geom_primitive.h b/intern/cycles/kernel/geom/geom_primitive.h index b16f0c9a99b..4384c2093e9 100644 --- a/intern/cycles/kernel/geom/geom_primitive.h +++ b/intern/cycles/kernel/geom/geom_primitive.h @@ -25,24 +25,23 @@ CCL_NAMESPACE_BEGIN ccl_device_inline float primitive_attribute_float(KernelGlobals *kg, const ShaderData *sd, - AttributeElement elem, - int offset, + const AttributeDescriptor desc, float *dx, float *dy) { if(ccl_fetch(sd, type) & PRIMITIVE_ALL_TRIANGLE) { if(subd_triangle_patch(kg, sd) == ~0) - return triangle_attribute_float(kg, sd, elem, offset, dx, dy); + return triangle_attribute_float(kg, sd, desc, dx, dy); else - return subd_triangle_attribute_float(kg, sd, elem, offset, dx, dy); + return subd_triangle_attribute_float(kg, sd, desc, dx, dy); } #ifdef __HAIR__ else if(ccl_fetch(sd, type) & PRIMITIVE_ALL_CURVE) { - return curve_attribute_float(kg, sd, elem, offset, dx, dy); + return curve_attribute_float(kg, sd, desc, dx, dy); } #endif #ifdef __VOLUME__ - else if(ccl_fetch(sd, object) != OBJECT_NONE && elem == ATTR_ELEMENT_VOXEL) { - return volume_attribute_float(kg, sd, elem, offset, dx, dy); + else if(ccl_fetch(sd, object) != OBJECT_NONE && desc.element == ATTR_ELEMENT_VOXEL) { + return volume_attribute_float(kg, sd, desc, dx, dy); } #endif else { @@ -54,25 +53,23 @@ ccl_device_inline float primitive_attribute_float(KernelGlobals *kg, ccl_device_inline float3 primitive_attribute_float3(KernelGlobals *kg, const ShaderData *sd, - AttributeElement elem, - int offset, - float3 *dx, - float3 *dy) + const AttributeDescriptor desc, + float3 *dx, float3 *dy) { if(ccl_fetch(sd, type) & PRIMITIVE_ALL_TRIANGLE) { if(subd_triangle_patch(kg, sd) == ~0) - return triangle_attribute_float3(kg, sd, elem, offset, dx, dy); + return triangle_attribute_float3(kg, sd, desc, dx, dy); else - return subd_triangle_attribute_float3(kg, sd, elem, offset, dx, dy); + return subd_triangle_attribute_float3(kg, sd, desc, dx, dy); } #ifdef __HAIR__ else if(ccl_fetch(sd, type) & PRIMITIVE_ALL_CURVE) { - return curve_attribute_float3(kg, sd, elem, offset, dx, dy); + return curve_attribute_float3(kg, sd, desc, dx, dy); } #endif #ifdef __VOLUME__ - else if(ccl_fetch(sd, object) != OBJECT_NONE && elem == ATTR_ELEMENT_VOXEL) { - return volume_attribute_float3(kg, sd, elem, offset, dx, dy); + else if(ccl_fetch(sd, object) != OBJECT_NONE && desc.element == ATTR_ELEMENT_VOXEL) { + return volume_attribute_float3(kg, sd, desc, dx, dy); } #endif else { @@ -86,13 +83,12 @@ ccl_device_inline float3 primitive_attribute_float3(KernelGlobals *kg, ccl_device_inline float3 primitive_uv(KernelGlobals *kg, ShaderData *sd) { - AttributeElement elem_uv; - int offset_uv = find_attribute(kg, sd, ATTR_STD_UV, &elem_uv); + const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_UV); - if(offset_uv == ATTR_STD_NOT_FOUND) + if(desc.offset == ATTR_STD_NOT_FOUND) return make_float3(0.0f, 0.0f, 0.0f); - float3 uv = primitive_attribute_float3(kg, sd, elem_uv, offset_uv, NULL, NULL); + float3 uv = primitive_attribute_float3(kg, sd, desc, NULL, NULL); uv.z = 1.0f; return uv; } @@ -102,15 +98,14 @@ ccl_device_inline float3 primitive_uv(KernelGlobals *kg, ShaderData *sd) ccl_device bool primitive_ptex(KernelGlobals *kg, ShaderData *sd, float2 *uv, int *face_id) { /* storing ptex data as attributes is not memory efficient but simple for tests */ - AttributeElement elem_face_id, elem_uv; - int offset_face_id = find_attribute(kg, sd, ATTR_STD_PTEX_FACE_ID, &elem_face_id); - int offset_uv = find_attribute(kg, sd, ATTR_STD_PTEX_UV, &elem_uv); + const AttributeDescriptor desc_face_id = find_attribute(kg, sd, ATTR_STD_PTEX_FACE_ID); + const AttributeDescriptor desc_uv = find_attribute(kg, sd, ATTR_STD_PTEX_UV); - if(offset_face_id == ATTR_STD_NOT_FOUND || offset_uv == ATTR_STD_NOT_FOUND) + if(desc_face_id.offset == ATTR_STD_NOT_FOUND || desc_uv.offset == ATTR_STD_NOT_FOUND) return false; - float3 uv3 = primitive_attribute_float3(kg, sd, elem_uv, offset_uv, NULL, NULL); - float face_id_f = primitive_attribute_float(kg, sd, elem_face_id, offset_face_id, NULL, NULL); + float3 uv3 = primitive_attribute_float3(kg, sd, desc_uv, NULL, NULL); + float face_id_f = primitive_attribute_float(kg, sd, desc_face_id, NULL, NULL); *uv = make_float2(uv3.x, uv3.y); *face_id = (int)face_id_f; @@ -132,11 +127,10 @@ ccl_device float3 primitive_tangent(KernelGlobals *kg, ShaderData *sd) #endif /* try to create spherical tangent from generated coordinates */ - AttributeElement attr_elem; - int attr_offset = find_attribute(kg, sd, ATTR_STD_GENERATED, &attr_elem); + const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_GENERATED); - if(attr_offset != ATTR_STD_NOT_FOUND) { - float3 data = primitive_attribute_float3(kg, sd, attr_elem, attr_offset, NULL, NULL); + if(desc.offset != ATTR_STD_NOT_FOUND) { + float3 data = primitive_attribute_float3(kg, sd, desc, NULL, NULL); data = make_float3(-(data.y - 0.5f), (data.x - 0.5f), 0.0f); object_normal_transform(kg, sd, &data); return cross(ccl_fetch(sd, N), normalize(cross(data, ccl_fetch(sd, N)))); @@ -173,19 +167,18 @@ ccl_device_inline float4 primitive_motion_vector(KernelGlobals *kg, ShaderData * float3 motion_pre = center, motion_post = center; /* deformation motion */ - AttributeElement elem; - int offset = find_attribute(kg, sd, ATTR_STD_MOTION_VERTEX_POSITION, &elem); + AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_MOTION_VERTEX_POSITION); - if(offset != ATTR_STD_NOT_FOUND) { + if(desc.offset != ATTR_STD_NOT_FOUND) { /* get motion info */ int numverts, numkeys; object_motion_info(kg, ccl_fetch(sd, object), NULL, &numverts, &numkeys); /* lookup attributes */ - int offset_next = (ccl_fetch(sd, type) & PRIMITIVE_ALL_TRIANGLE)? offset + numverts: offset + numkeys; + motion_pre = primitive_attribute_float3(kg, sd, desc, NULL, NULL); - motion_pre = primitive_attribute_float3(kg, sd, elem, offset, NULL, NULL); - motion_post = primitive_attribute_float3(kg, sd, elem, offset_next, NULL, NULL); + desc.offset += (ccl_fetch(sd, type) & PRIMITIVE_ALL_TRIANGLE)? numverts: numkeys; + motion_post = primitive_attribute_float3(kg, sd, desc, NULL, NULL); #ifdef __HAIR__ if(is_curve_primitive && (ccl_fetch(sd, flag) & SD_OBJECT_HAS_VERTEX_MOTION) == 0) { diff --git a/intern/cycles/kernel/geom/geom_subd_triangle.h b/intern/cycles/kernel/geom/geom_subd_triangle.h index bf9be182345..647840dc696 100644 --- a/intern/cycles/kernel/geom/geom_subd_triangle.h +++ b/intern/cycles/kernel/geom/geom_subd_triangle.h @@ -97,36 +97,81 @@ ccl_device_inline void subd_triangle_patch_corners(KernelGlobals *kg, int patch, /* Reading attributes on various subdivision triangle elements */ -ccl_device float subd_triangle_attribute_float(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float *dx, float *dy) +ccl_device_noinline float subd_triangle_attribute_float(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy) { int patch = subd_triangle_patch(kg, sd); - if(elem == ATTR_ELEMENT_FACE) { +#ifdef __PATCH_EVAL__ + if(desc.flags & ATTR_SUBDIVIDED) { + float2 uv[3]; + subd_triangle_patch_uv(kg, sd, uv); + + float2 dpdu = uv[0] - uv[2]; + float2 dpdv = uv[1] - uv[2]; + + /* p is [s, t] */ + float2 p = dpdu * ccl_fetch(sd, u) + dpdv * ccl_fetch(sd, v) + uv[2]; + + float a, dads, dadt; + a = patch_eval_float(kg, sd, desc.offset, patch, p.x, p.y, 0, &dads, &dadt); + +#ifdef __RAY_DIFFERENTIALS__ + if(dx || dy) { + float dsdu = dpdu.x; + float dtdu = dpdu.y; + float dsdv = dpdv.x; + float dtdv = dpdv.y; + + if(dx) { + float dudx = ccl_fetch(sd, du).dx; + float dvdx = ccl_fetch(sd, dv).dx; + + float dsdx = dsdu*dudx + dsdv*dvdx; + float dtdx = dtdu*dudx + dtdv*dvdx; + + *dx = dads*dsdx + dadt*dtdx; + } + if(dy) { + float dudy = ccl_fetch(sd, du).dy; + float dvdy = ccl_fetch(sd, dv).dy; + + float dsdy = dsdu*dudy + dsdv*dvdy; + float dtdy = dtdu*dudy + dtdv*dvdy; + + *dy = dads*dsdy + dadt*dtdy; + } + } +#endif + + return a; + } + else +#endif /* __PATCH_EVAL__ */ + if(desc.element == ATTR_ELEMENT_FACE) { if(dx) *dx = 0.0f; if(dy) *dy = 0.0f; - return kernel_tex_fetch(__attributes_float, offset + subd_triangle_patch_face(kg, patch)); + return kernel_tex_fetch(__attributes_float, desc.offset + subd_triangle_patch_face(kg, patch)); } - else if(elem == ATTR_ELEMENT_VERTEX || elem == ATTR_ELEMENT_VERTEX_MOTION) { + else if(desc.element == ATTR_ELEMENT_VERTEX || desc.element == ATTR_ELEMENT_VERTEX_MOTION) { float2 uv[3]; subd_triangle_patch_uv(kg, sd, uv); - uint4 v = subd_triangle_patch_indices(kg, patch); - float a, b, c; + uint4 v = subd_triangle_patch_indices(kg, patch); - float f0 = kernel_tex_fetch(__attributes_float, offset + v.x); - float f1 = kernel_tex_fetch(__attributes_float, offset + v.y); - float f2 = kernel_tex_fetch(__attributes_float, offset + v.z); - float f3 = kernel_tex_fetch(__attributes_float, offset + v.w); + float f0 = kernel_tex_fetch(__attributes_float, desc.offset + v.x); + float f1 = kernel_tex_fetch(__attributes_float, desc.offset + v.y); + float f2 = kernel_tex_fetch(__attributes_float, desc.offset + v.z); + float f3 = kernel_tex_fetch(__attributes_float, desc.offset + v.w); if(subd_triangle_patch_num_corners(kg, patch) != 4) { f1 = (f1+f0)*0.5f; f3 = (f3+f0)*0.5f; } - a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y); - b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y); - c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y); + float a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y); + float b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y); + float c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y); #ifdef __RAY_DIFFERENTIALS__ if(dx) *dx = ccl_fetch(sd, du).dx*a + ccl_fetch(sd, dv).dx*b - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*c; @@ -135,28 +180,26 @@ ccl_device float subd_triangle_attribute_float(KernelGlobals *kg, const ShaderDa return ccl_fetch(sd, u)*a + ccl_fetch(sd, v)*b + (1.0f - ccl_fetch(sd, u) - ccl_fetch(sd, v))*c; } - else if(elem == ATTR_ELEMENT_CORNER) { - int corners[4]; - subd_triangle_patch_corners(kg, patch, corners); - + else if(desc.element == ATTR_ELEMENT_CORNER) { float2 uv[3]; subd_triangle_patch_uv(kg, sd, uv); - float a, b, c; + int corners[4]; + subd_triangle_patch_corners(kg, patch, corners); - float f0 = kernel_tex_fetch(__attributes_float, corners[0] + offset); - float f1 = kernel_tex_fetch(__attributes_float, corners[1] + offset); - float f2 = kernel_tex_fetch(__attributes_float, corners[2] + offset); - float f3 = kernel_tex_fetch(__attributes_float, corners[3] + offset); + float f0 = kernel_tex_fetch(__attributes_float, corners[0] + desc.offset); + float f1 = kernel_tex_fetch(__attributes_float, corners[1] + desc.offset); + float f2 = kernel_tex_fetch(__attributes_float, corners[2] + desc.offset); + float f3 = kernel_tex_fetch(__attributes_float, corners[3] + desc.offset); if(subd_triangle_patch_num_corners(kg, patch) != 4) { f1 = (f1+f0)*0.5f; f3 = (f3+f0)*0.5f; } - a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y); - b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y); - c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y); + float a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y); + float b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y); + float c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y); #ifdef __RAY_DIFFERENTIALS__ if(dx) *dx = ccl_fetch(sd, du).dx*a + ccl_fetch(sd, dv).dx*b - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*c; @@ -173,36 +216,87 @@ ccl_device float subd_triangle_attribute_float(KernelGlobals *kg, const ShaderDa } } -ccl_device float3 subd_triangle_attribute_float3(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float3 *dx, float3 *dy) +ccl_device_noinline float3 subd_triangle_attribute_float3(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, float3 *dy) { int patch = subd_triangle_patch(kg, sd); - if(elem == ATTR_ELEMENT_FACE) { +#ifdef __PATCH_EVAL__ + if(desc.flags & ATTR_SUBDIVIDED) { + float2 uv[3]; + subd_triangle_patch_uv(kg, sd, uv); + + float2 dpdu = uv[0] - uv[2]; + float2 dpdv = uv[1] - uv[2]; + + /* p is [s, t] */ + float2 p = dpdu * ccl_fetch(sd, u) + dpdv * ccl_fetch(sd, v) + uv[2]; + + float3 a, dads, dadt; + + if(desc.element == ATTR_ELEMENT_CORNER_BYTE) { + a = patch_eval_uchar4(kg, sd, desc.offset, patch, p.x, p.y, 0, &dads, &dadt); + } + else { + a = patch_eval_float3(kg, sd, desc.offset, patch, p.x, p.y, 0, &dads, &dadt); + } + +#ifdef __RAY_DIFFERENTIALS__ + if(dx || dy) { + float dsdu = dpdu.x; + float dtdu = dpdu.y; + float dsdv = dpdv.x; + float dtdv = dpdv.y; + + if(dx) { + float dudx = ccl_fetch(sd, du).dx; + float dvdx = ccl_fetch(sd, dv).dx; + + float dsdx = dsdu*dudx + dsdv*dvdx; + float dtdx = dtdu*dudx + dtdv*dvdx; + + *dx = dads*dsdx + dadt*dtdx; + } + if(dy) { + float dudy = ccl_fetch(sd, du).dy; + float dvdy = ccl_fetch(sd, dv).dy; + + float dsdy = dsdu*dudy + dsdv*dvdy; + float dtdy = dtdu*dudy + dtdv*dvdy; + + *dy = dads*dsdy + dadt*dtdy; + } + } +#endif + + return a; + } + else +#endif /* __PATCH_EVAL__ */ + if(desc.element == ATTR_ELEMENT_FACE) { if(dx) *dx = make_float3(0.0f, 0.0f, 0.0f); if(dy) *dy = make_float3(0.0f, 0.0f, 0.0f); - return float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + subd_triangle_patch_face(kg, patch))); + return float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + subd_triangle_patch_face(kg, patch))); } - else if(elem == ATTR_ELEMENT_VERTEX || elem == ATTR_ELEMENT_VERTEX_MOTION) { + else if(desc.element == ATTR_ELEMENT_VERTEX || desc.element == ATTR_ELEMENT_VERTEX_MOTION) { float2 uv[3]; subd_triangle_patch_uv(kg, sd, uv); - uint4 v = subd_triangle_patch_indices(kg, patch); - float3 a, b, c; + uint4 v = subd_triangle_patch_indices(kg, patch); - float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + v.x)); - float3 f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + v.y)); - float3 f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + v.z)); - float3 f3 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + v.w)); + float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + v.x)); + float3 f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + v.y)); + float3 f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + v.z)); + float3 f3 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + v.w)); if(subd_triangle_patch_num_corners(kg, patch) != 4) { f1 = (f1+f0)*0.5f; f3 = (f3+f0)*0.5f; } - a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y); - b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y); - c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y); + float3 a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y); + float3 b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y); + float3 c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y); #ifdef __RAY_DIFFERENTIALS__ if(dx) *dx = ccl_fetch(sd, du).dx*a + ccl_fetch(sd, dv).dx*b - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*c; @@ -211,27 +305,26 @@ ccl_device float3 subd_triangle_attribute_float3(KernelGlobals *kg, const Shader return ccl_fetch(sd, u)*a + ccl_fetch(sd, v)*b + (1.0f - ccl_fetch(sd, u) - ccl_fetch(sd, v))*c; } - else if(elem == ATTR_ELEMENT_CORNER || elem == ATTR_ELEMENT_CORNER_BYTE) { - int corners[4]; - subd_triangle_patch_corners(kg, patch, corners); - + else if(desc.element == ATTR_ELEMENT_CORNER || desc.element == ATTR_ELEMENT_CORNER_BYTE) { float2 uv[3]; subd_triangle_patch_uv(kg, sd, uv); - float3 a, b, c; + int corners[4]; + subd_triangle_patch_corners(kg, patch, corners); + float3 f0, f1, f2, f3; - if(elem == ATTR_ELEMENT_CORNER) { - f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[0] + offset)); - f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[1] + offset)); - f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[2] + offset)); - f3 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[3] + offset)); + if(desc.element == ATTR_ELEMENT_CORNER) { + f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[0] + desc.offset)); + f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[1] + desc.offset)); + f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[2] + desc.offset)); + f3 = float4_to_float3(kernel_tex_fetch(__attributes_float3, corners[3] + desc.offset)); } else { - f0 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, corners[0] + offset)); - f1 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, corners[1] + offset)); - f2 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, corners[2] + offset)); - f3 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, corners[3] + offset)); + f0 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, corners[0] + desc.offset)); + f1 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, corners[1] + desc.offset)); + f2 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, corners[2] + desc.offset)); + f3 = color_byte_to_float(kernel_tex_fetch(__attributes_uchar4, corners[3] + desc.offset)); } if(subd_triangle_patch_num_corners(kg, patch) != 4) { @@ -239,9 +332,9 @@ ccl_device float3 subd_triangle_attribute_float3(KernelGlobals *kg, const Shader f3 = (f3+f0)*0.5f; } - a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y); - b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y); - c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y); + float3 a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y); + float3 b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y); + float3 c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y); #ifdef __RAY_DIFFERENTIALS__ if(dx) *dx = ccl_fetch(sd, du).dx*a + ccl_fetch(sd, dv).dx*b - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*c; diff --git a/intern/cycles/kernel/geom/geom_triangle.h b/intern/cycles/kernel/geom/geom_triangle.h index 0c2351e1d1b..d3289d6572c 100644 --- a/intern/cycles/kernel/geom/geom_triangle.h +++ b/intern/cycles/kernel/geom/geom_triangle.h @@ -105,20 +105,20 @@ ccl_device_inline void triangle_dPdudv(KernelGlobals *kg, int prim, ccl_addr_spa /* Reading attributes on various triangle elements */ -ccl_device float triangle_attribute_float(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float *dx, float *dy) +ccl_device float triangle_attribute_float(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy) { - if(elem == ATTR_ELEMENT_FACE) { + if(desc.element == ATTR_ELEMENT_FACE) { if(dx) *dx = 0.0f; if(dy) *dy = 0.0f; - return kernel_tex_fetch(__attributes_float, offset + ccl_fetch(sd, prim)); + return kernel_tex_fetch(__attributes_float, desc.offset + ccl_fetch(sd, prim)); } - else if(elem == ATTR_ELEMENT_VERTEX || elem == ATTR_ELEMENT_VERTEX_MOTION) { + else if(desc.element == ATTR_ELEMENT_VERTEX || desc.element == ATTR_ELEMENT_VERTEX_MOTION) { uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, ccl_fetch(sd, prim)); - float f0 = kernel_tex_fetch(__attributes_float, offset + tri_vindex.x); - float f1 = kernel_tex_fetch(__attributes_float, offset + tri_vindex.y); - float f2 = kernel_tex_fetch(__attributes_float, offset + tri_vindex.z); + float f0 = kernel_tex_fetch(__attributes_float, desc.offset + tri_vindex.x); + float f1 = kernel_tex_fetch(__attributes_float, desc.offset + tri_vindex.y); + float f2 = kernel_tex_fetch(__attributes_float, desc.offset + tri_vindex.z); #ifdef __RAY_DIFFERENTIALS__ if(dx) *dx = ccl_fetch(sd, du).dx*f0 + ccl_fetch(sd, dv).dx*f1 - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*f2; @@ -127,8 +127,8 @@ ccl_device float triangle_attribute_float(KernelGlobals *kg, const ShaderData *s return ccl_fetch(sd, u)*f0 + ccl_fetch(sd, v)*f1 + (1.0f - ccl_fetch(sd, u) - ccl_fetch(sd, v))*f2; } - else if(elem == ATTR_ELEMENT_CORNER) { - int tri = offset + ccl_fetch(sd, prim)*3; + else if(desc.element == ATTR_ELEMENT_CORNER) { + int tri = desc.offset + ccl_fetch(sd, prim)*3; float f0 = kernel_tex_fetch(__attributes_float, tri + 0); float f1 = kernel_tex_fetch(__attributes_float, tri + 1); float f2 = kernel_tex_fetch(__attributes_float, tri + 2); @@ -148,20 +148,20 @@ ccl_device float triangle_attribute_float(KernelGlobals *kg, const ShaderData *s } } -ccl_device float3 triangle_attribute_float3(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float3 *dx, float3 *dy) +ccl_device float3 triangle_attribute_float3(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, float3 *dy) { - if(elem == ATTR_ELEMENT_FACE) { + if(desc.element == ATTR_ELEMENT_FACE) { if(dx) *dx = make_float3(0.0f, 0.0f, 0.0f); if(dy) *dy = make_float3(0.0f, 0.0f, 0.0f); - return float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + ccl_fetch(sd, prim))); + return float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + ccl_fetch(sd, prim))); } - else if(elem == ATTR_ELEMENT_VERTEX || elem == ATTR_ELEMENT_VERTEX_MOTION) { + else if(desc.element == ATTR_ELEMENT_VERTEX || desc.element == ATTR_ELEMENT_VERTEX_MOTION) { uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, ccl_fetch(sd, prim)); - float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + tri_vindex.x)); - float3 f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + tri_vindex.y)); - float3 f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, offset + tri_vindex.z)); + float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + tri_vindex.x)); + float3 f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + tri_vindex.y)); + float3 f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + tri_vindex.z)); #ifdef __RAY_DIFFERENTIALS__ if(dx) *dx = ccl_fetch(sd, du).dx*f0 + ccl_fetch(sd, dv).dx*f1 - (ccl_fetch(sd, du).dx + ccl_fetch(sd, dv).dx)*f2; @@ -170,11 +170,11 @@ ccl_device float3 triangle_attribute_float3(KernelGlobals *kg, const ShaderData return ccl_fetch(sd, u)*f0 + ccl_fetch(sd, v)*f1 + (1.0f - ccl_fetch(sd, u) - ccl_fetch(sd, v))*f2; } - else if(elem == ATTR_ELEMENT_CORNER || elem == ATTR_ELEMENT_CORNER_BYTE) { - int tri = offset + ccl_fetch(sd, prim)*3; + else if(desc.element == ATTR_ELEMENT_CORNER || desc.element == ATTR_ELEMENT_CORNER_BYTE) { + int tri = desc.offset + ccl_fetch(sd, prim)*3; float3 f0, f1, f2; - if(elem == ATTR_ELEMENT_CORNER) { + if(desc.element == ATTR_ELEMENT_CORNER) { f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, tri + 0)); f1 = float4_to_float3(kernel_tex_fetch(__attributes_float3, tri + 1)); f2 = float4_to_float3(kernel_tex_fetch(__attributes_float3, tri + 2)); diff --git a/intern/cycles/kernel/geom/geom_volume.h b/intern/cycles/kernel/geom/geom_volume.h index 7c8182bc430..efe540a8518 100644 --- a/intern/cycles/kernel/geom/geom_volume.h +++ b/intern/cycles/kernel/geom/geom_volume.h @@ -50,36 +50,35 @@ ccl_device_inline float3 volume_normalized_position(KernelGlobals *kg, { /* todo: optimize this so it's just a single matrix multiplication when * possible (not motion blur), or perhaps even just translation + scale */ - AttributeElement attr_elem; - int attr_offset = find_attribute(kg, sd, ATTR_STD_GENERATED_TRANSFORM, &attr_elem); + const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_GENERATED_TRANSFORM); object_inverse_position_transform(kg, sd, &P); - if(attr_offset != ATTR_STD_NOT_FOUND) { - Transform tfm = primitive_attribute_matrix(kg, sd, attr_offset); + if(desc.offset != ATTR_STD_NOT_FOUND) { + Transform tfm = primitive_attribute_matrix(kg, sd, desc); P = transform_point(&tfm, P); } return P; } -ccl_device float volume_attribute_float(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int id, float *dx, float *dy) +ccl_device float volume_attribute_float(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy) { float3 P = volume_normalized_position(kg, sd, sd->P); #ifdef __KERNEL_GPU__ # if __CUDA_ARCH__ >= 300 - CUtexObject tex = kernel_tex_fetch(__bindless_mapping, id); + CUtexObject tex = kernel_tex_fetch(__bindless_mapping, desc.offset); float f = kernel_tex_image_interp_3d_float(tex, P.x, P.y, P.z); float4 r = make_float4(f, f, f, 1.0); # else - float4 r = volume_image_texture_3d(id, P.x, P.y, P.z); + float4 r = volume_image_texture_3d(desc.offset, P.x, P.y, P.z); # endif #else float4 r; if(sd->flag & SD_VOLUME_CUBIC) - r = kernel_tex_image_interp_3d_ex(id, P.x, P.y, P.z, INTERPOLATION_CUBIC); + r = kernel_tex_image_interp_3d_ex(desc.offset, P.x, P.y, P.z, INTERPOLATION_CUBIC); else - r = kernel_tex_image_interp_3d(id, P.x, P.y, P.z); + r = kernel_tex_image_interp_3d(desc.offset, P.x, P.y, P.z); #endif if(dx) *dx = 0.0f; @@ -88,22 +87,22 @@ ccl_device float volume_attribute_float(KernelGlobals *kg, const ShaderData *sd, return average(float4_to_float3(r)); } -ccl_device float3 volume_attribute_float3(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int id, float3 *dx, float3 *dy) +ccl_device float3 volume_attribute_float3(KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float3 *dx, float3 *dy) { float3 P = volume_normalized_position(kg, sd, sd->P); #ifdef __KERNEL_GPU__ # if __CUDA_ARCH__ >= 300 - CUtexObject tex = kernel_tex_fetch(__bindless_mapping, id); + CUtexObject tex = kernel_tex_fetch(__bindless_mapping, desc.offset); float4 r = kernel_tex_image_interp_3d_float4(tex, P.x, P.y, P.z); # else - float4 r = volume_image_texture_3d(id, P.x, P.y, P.z); + float4 r = volume_image_texture_3d(desc.offset, P.x, P.y, P.z); # endif #else float4 r; if(sd->flag & SD_VOLUME_CUBIC) - r = kernel_tex_image_interp_3d_ex(id, P.x, P.y, P.z, INTERPOLATION_CUBIC); + r = kernel_tex_image_interp_3d_ex(desc.offset, P.x, P.y, P.z, INTERPOLATION_CUBIC); else - r = kernel_tex_image_interp_3d(id, P.x, P.y, P.z); + r = kernel_tex_image_interp_3d(desc.offset, P.x, P.y, P.z); #endif if(dx) *dx = make_float3(0.0f, 0.0f, 0.0f); diff --git a/intern/cycles/kernel/kernel_compat_cpu.h b/intern/cycles/kernel/kernel_compat_cpu.h index c882b477c35..3775934f293 100644 --- a/intern/cycles/kernel/kernel_compat_cpu.h +++ b/intern/cycles/kernel/kernel_compat_cpu.h @@ -495,6 +495,7 @@ typedef texture<uint> texture_uint; typedef texture<int> texture_int; typedef texture<uint4> texture_uint4; typedef texture<uchar4> texture_uchar4; +typedef texture<uchar> texture_uchar; typedef texture_image<float> texture_image_float; typedef texture_image<uchar> texture_image_uchar; typedef texture_image<half> texture_image_half; diff --git a/intern/cycles/kernel/kernel_compat_cuda.h b/intern/cycles/kernel/kernel_compat_cuda.h index a039b414006..9a96cb9f438 100644 --- a/intern/cycles/kernel/kernel_compat_cuda.h +++ b/intern/cycles/kernel/kernel_compat_cuda.h @@ -31,6 +31,7 @@ #endif #include <cuda.h> +#include <cuda_fp16.h> #include <float.h> /* Qualifier wrappers for different names on different devices */ @@ -47,6 +48,7 @@ #define ccl_may_alias #define ccl_addr_space #define ccl_restrict __restrict__ +#define ccl_align(n) __align__(n) /* No assert supported for CUDA */ @@ -65,6 +67,7 @@ typedef texture<float, 1> texture_float; typedef texture<uint, 1> texture_uint; typedef texture<int, 1> texture_int; typedef texture<uint4, 1> texture_uint4; +typedef texture<uchar, 1> texture_uchar; typedef texture<uchar4, 1> texture_uchar4; typedef texture<float4, 2> texture_image_float4; typedef texture<float4, 3> texture_image3d_float4; diff --git a/intern/cycles/kernel/kernel_compat_opencl.h b/intern/cycles/kernel/kernel_compat_opencl.h index 8505cb85576..2ae89dde7c4 100644 --- a/intern/cycles/kernel/kernel_compat_opencl.h +++ b/intern/cycles/kernel/kernel_compat_opencl.h @@ -40,6 +40,7 @@ #define ccl_local __local #define ccl_private __private #define ccl_restrict restrict +#define ccl_align(n) __attribute__((aligned(n))) #ifdef __SPLIT_KERNEL__ # define ccl_addr_space __global diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h index 1f08f3459e6..903be4f09a0 100644 --- a/intern/cycles/kernel/kernel_path.h +++ b/intern/cycles/kernel/kernel_path.h @@ -435,8 +435,12 @@ ccl_device_noinline void kernel_path_ao(KernelGlobals *kg, } #ifdef __SUBSURFACE__ - -ccl_device_inline bool kernel_path_subsurface_scatter( +# ifndef __KERNEL_CUDA__ +ccl_device +# else +ccl_device_inline +# endif +bool kernel_path_subsurface_scatter( KernelGlobals *kg, ShaderData *sd, ShaderData *emission_sd, diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index 98d321c9c16..079bea30bdd 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -149,7 +149,7 @@ ccl_device_noinline void shader_setup_from_ray(KernelGlobals *kg, /* ShaderData setup from BSSRDF scatter */ #ifdef __SUBSURFACE__ -# ifndef __KERNEL_CUDS__ +# ifndef __KERNEL_CUDA__ ccl_device # else ccl_device_inline @@ -539,7 +539,7 @@ ccl_device_inline void _shader_bsdf_multi_eval_branched(KernelGlobals *kg, #endif -#ifndef __KERNEL_CUDS__ +#ifndef __KERNEL_CUDA__ ccl_device #else ccl_device_inline diff --git a/intern/cycles/kernel/kernel_subsurface.h b/intern/cycles/kernel/kernel_subsurface.h index f404666177a..e83bfc3f08a 100644 --- a/intern/cycles/kernel/kernel_subsurface.h +++ b/intern/cycles/kernel/kernel_subsurface.h @@ -85,11 +85,16 @@ ccl_device ShaderClosure *subsurface_scatter_pick_closure(KernelGlobals *kg, Sha return NULL; } -ccl_device_inline float3 subsurface_scatter_eval(ShaderData *sd, - ShaderClosure *sc, - float disk_r, - float r, - bool all) +#ifndef __KERNEL_GPU__ +ccl_device_noinline +#else +ccl_device_inline +#endif +float3 subsurface_scatter_eval(ShaderData *sd, + ShaderClosure *sc, + float disk_r, + float r, + bool all) { #ifdef BSSRDF_MULTI_EVAL /* this is the veach one-sample model with balance heuristic, some pdf @@ -235,7 +240,12 @@ ccl_device void subsurface_color_bump_blur(KernelGlobals *kg, /* Subsurface scattering step, from a point on the surface to other * nearby points on the same object. */ -ccl_device_inline int subsurface_scatter_multi_intersect( +#ifndef __KERNEL_CUDA__ +ccl_device +#else +ccl_device_inline +#endif +int subsurface_scatter_multi_intersect( KernelGlobals *kg, SubsurfaceIntersection* ss_isect, ShaderData *sd, diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/kernel_textures.h index 7d6fec02331..8d5bb75a428 100644 --- a/intern/cycles/kernel/kernel_textures.h +++ b/intern/cycles/kernel/kernel_textures.h @@ -188,6 +188,8 @@ KERNEL_TEX(uint, texture_uint, __bindless_mapping) /* packed image (opencl) */ KERNEL_TEX(uchar4, texture_uchar4, __tex_image_byte4_packed) KERNEL_TEX(float4, texture_float4, __tex_image_float4_packed) +KERNEL_TEX(uchar, texture_uchar, __tex_image_byte_packed) +KERNEL_TEX(float, texture_float, __tex_image_float_packed) KERNEL_TEX(uint4, texture_uint4, __tex_image_packed_info) #undef KERNEL_TEX diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 18b5c35c768..e29940672ca 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -19,6 +19,7 @@ #include "kernel_math.h" #include "svm/svm_types.h" +#include "util_static_assert.h" #ifndef __KERNEL_GPU__ # define __KERNEL_CPU__ @@ -34,7 +35,7 @@ CCL_NAMESPACE_BEGIN /* constants */ -#define OBJECT_SIZE 11 +#define OBJECT_SIZE 12 #define OBJECT_VECTOR_SIZE 6 #define LIGHT_SIZE 5 #define FILTER_TABLE_SIZE 1024 @@ -147,6 +148,7 @@ CCL_NAMESPACE_BEGIN #define __CAMERA_CLIPPING__ #define __INTERSECTION_REFINE__ #define __CLAMP_SAMPLE__ +#define __PATCH_EVAL__ #ifdef __KERNEL_SHADING__ # define __SVM__ @@ -196,6 +198,9 @@ CCL_NAMESPACE_BEGIN #ifdef __NO_BRANCHED_PATH__ # undef __BRANCHED_PATH__ #endif +#ifdef __NO_PATCH_EVAL__ +# undef __PATCH_EVAL__ +#endif /* Random Numbers */ @@ -624,6 +629,18 @@ typedef enum AttributeStandard { ATTR_STD_NOT_FOUND = ~0 } AttributeStandard; +typedef enum AttributeFlag { + ATTR_FINAL_SIZE = (1 << 0), + ATTR_SUBDIVIDED = (1 << 1), +} AttributeFlag; + +typedef struct AttributeDescriptor { + AttributeElement element; + NodeAttributeType type; + uint flags; /* see enum AttributeFlag */ + int offset; +} AttributeDescriptor; + /* Closure data */ #ifdef __MULTI_CLOSURE__ @@ -644,23 +661,18 @@ typedef enum AttributeStandard { * ShaderClosure has a fixed size, and any extra space must be allocated * with closure_alloc_extra(). * - * float3 is 12 bytes on CUDA and 16 bytes on CPU/OpenCL, we set the data - * size to ensure ShaderClosure is 80 bytes total everywhere. */ + * We pad the struct to 80 bytes and ensure it is aligned to 16 bytes, which + * we assume to be the maximum required alignment for any struct. */ #define SHADER_CLOSURE_BASE \ float3 weight; \ ClosureType type; \ float sample_weight \ -typedef ccl_addr_space struct ShaderClosure { +typedef ccl_addr_space struct ccl_align(16) ShaderClosure { SHADER_CLOSURE_BASE; - /* pad to 80 bytes, data types are aligned to own size */ -#ifdef __KERNEL_CUDA__ - float data[15]; -#else - float data[14]; -#endif + float data[14]; /* pad to 80 bytes */ } ShaderClosure; /* Shader Context @@ -735,7 +747,7 @@ enum ShaderDataFlag { # define SD_THREAD (get_global_id(1) * get_global_size(0) + get_global_id(0)) # if defined(__SPLIT_KERNEL_AOS__) /* ShaderData is stored as an Array-of-Structures */ -# define ccl_soa_member(type, name) type soa_##name; +# define ccl_soa_member(type, name) type soa_##name # define ccl_fetch(s, t) (s[SD_THREAD].soa_##t) # define ccl_fetch_array(s, t, index) (&s[SD_THREAD].soa_##t[index]) # else @@ -743,7 +755,7 @@ enum ShaderDataFlag { # define SD_GLOBAL_SIZE (get_global_size(0) * get_global_size(1)) # define SD_FIELD_SIZE(t) sizeof(((struct ShaderData*)0)->t) # define SD_OFFSETOF(t) ((char*)(&((struct ShaderData*)0)->t) - (char*)0) -# define ccl_soa_member(type, name) type soa_##name; +# define ccl_soa_member(type, name) type soa_##name # define ccl_fetch(s, t) (((ShaderData*)((ccl_addr_space char*)s + SD_GLOBAL_SIZE * SD_OFFSETOF(soa_##t) + SD_FIELD_SIZE(soa_##t) * SD_THREAD - SD_OFFSETOF(soa_##t)))->soa_##t) # define ccl_fetch_array(s, t, index) (&ccl_fetch(s, t)[index]) # endif @@ -979,6 +991,7 @@ typedef struct KernelCamera { int pad; } KernelCamera; +static_assert_align(KernelCamera, 16); typedef struct KernelFilm { float exposure; @@ -1033,6 +1046,7 @@ typedef struct KernelFilm { int pass_pad3; #endif } KernelFilm; +static_assert_align(KernelFilm, 16); typedef struct KernelBackground { /* only shader index */ @@ -1046,6 +1060,7 @@ typedef struct KernelBackground { float ao_distance; float ao_pad1, ao_pad2; } KernelBackground; +static_assert_align(KernelBackground, 16); typedef struct KernelIntegrator { /* emission */ @@ -1113,8 +1128,10 @@ typedef struct KernelIntegrator { float volume_step_size; int volume_samples; - int pad; + int pad1; + int pad2; } KernelIntegrator; +static_assert_align(KernelIntegrator, 16); typedef struct KernelBVH { /* root node */ @@ -1126,6 +1143,7 @@ typedef struct KernelBVH { int use_qbvh; int pad1, pad2; } KernelBVH; +static_assert_align(KernelBVH, 16); typedef enum CurveFlag { /* runtime flags */ @@ -1145,11 +1163,13 @@ typedef struct KernelCurves { float minimum_width; float maximum_width; } KernelCurves; +static_assert_align(KernelCurves, 16); typedef struct KernelTables { int beckmann_offset; int pad1, pad2, pad3; } KernelTables; +static_assert_align(KernelTables, 16); typedef struct KernelData { KernelCamera cam; @@ -1160,8 +1180,12 @@ typedef struct KernelData { KernelCurves curve; KernelTables tables; } KernelData; +static_assert_align(KernelData, 16); #ifdef __KERNEL_DEBUG__ +/* NOTE: This is a runtime-only struct, alignment is not + * really important here. + */ typedef ccl_addr_space struct DebugData { // Total number of BVH node traversal steps and primitives intersections // for the camera rays. @@ -1239,6 +1263,16 @@ enum RayState { #define REMOVE_RAY_FLAG(ray_state, ray_index, flag) (ray_state[ray_index] = (ray_state[ray_index] & (~flag))) #define IS_FLAG(ray_state, ray_index, flag) (ray_state[ray_index] & flag) +/* Patches */ + +#define PATCH_MAX_CONTROL_VERTS 16 + +/* Patch map node flags */ + +#define PATCH_MAP_NODE_IS_SET (1 << 30) +#define PATCH_MAP_NODE_IS_LEAF (1u << 31) +#define PATCH_MAP_NODE_INDEX_MASK (~(PATCH_MAP_NODE_IS_SET | PATCH_MAP_NODE_IS_LEAF)) + CCL_NAMESPACE_END #endif /* __KERNEL_TYPES_H__ */ diff --git a/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h b/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h index 47383140170..af68907a5c2 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h +++ b/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h @@ -25,12 +25,12 @@ ccl_device float4 kernel_tex_image_interp_impl(KernelGlobals *kg, int tex, float { if(tex >= TEX_START_HALF_CPU) return kg->texture_half_images[tex - TEX_START_HALF_CPU].interp(x, y); - else if(tex >= TEX_START_HALF4_CPU) - return kg->texture_half4_images[tex - TEX_START_HALF4_CPU].interp(x, y); else if(tex >= TEX_START_BYTE_CPU) return kg->texture_byte_images[tex - TEX_START_BYTE_CPU].interp(x, y); else if(tex >= TEX_START_FLOAT_CPU) return kg->texture_float_images[tex - TEX_START_FLOAT_CPU].interp(x, y); + else if(tex >= TEX_START_HALF4_CPU) + return kg->texture_half4_images[tex - TEX_START_HALF4_CPU].interp(x, y); else if(tex >= TEX_START_BYTE4_CPU) return kg->texture_byte4_images[tex - TEX_START_BYTE4_CPU].interp(x, y); else @@ -41,12 +41,12 @@ ccl_device float4 kernel_tex_image_interp_3d_impl(KernelGlobals *kg, int tex, fl { if(tex >= TEX_START_HALF_CPU) return kg->texture_half_images[tex - TEX_START_HALF_CPU].interp_3d(x, y, z); - else if(tex >= TEX_START_HALF4_CPU) - return kg->texture_half4_images[tex - TEX_START_HALF4_CPU].interp_3d(x, y, z); else if(tex >= TEX_START_BYTE_CPU) return kg->texture_byte_images[tex - TEX_START_BYTE_CPU].interp_3d(x, y, z); else if(tex >= TEX_START_FLOAT_CPU) return kg->texture_float_images[tex - TEX_START_FLOAT_CPU].interp_3d(x, y, z); + else if(tex >= TEX_START_HALF4_CPU) + return kg->texture_half4_images[tex - TEX_START_HALF4_CPU].interp_3d(x, y, z); else if(tex >= TEX_START_BYTE4_CPU) return kg->texture_byte4_images[tex - TEX_START_BYTE4_CPU].interp_3d(x, y, z); else @@ -57,13 +57,13 @@ ccl_device float4 kernel_tex_image_interp_3d_impl(KernelGlobals *kg, int tex, fl ccl_device float4 kernel_tex_image_interp_3d_ex_impl(KernelGlobals *kg, int tex, float x, float y, float z, int interpolation) { if(tex >= TEX_START_HALF_CPU) - return kg->texture_half4_images[tex - TEX_START_HALF_CPU].interp_3d_ex(x, y, z, interpolation); - else if(tex >= TEX_START_HALF4_CPU) - return kg->texture_half_images[tex - TEX_START_HALF4_CPU].interp_3d_ex(x, y, z, interpolation); + return kg->texture_half_images[tex - TEX_START_HALF_CPU].interp_3d_ex(x, y, z, interpolation); else if(tex >= TEX_START_BYTE_CPU) return kg->texture_byte_images[tex - TEX_START_BYTE_CPU].interp_3d_ex(x, y, z, interpolation); else if(tex >= TEX_START_FLOAT_CPU) return kg->texture_float_images[tex - TEX_START_FLOAT_CPU].interp_3d_ex(x, y, z, interpolation); + else if(tex >= TEX_START_HALF4_CPU) + return kg->texture_half4_images[tex - TEX_START_HALF4_CPU].interp_3d_ex(x, y, z, interpolation); else if(tex >= TEX_START_BYTE4_CPU) return kg->texture_byte4_images[tex - TEX_START_BYTE4_CPU].interp_3d_ex(x, y, z, interpolation); else diff --git a/intern/cycles/kernel/osl/osl_globals.h b/intern/cycles/kernel/osl/osl_globals.h index 916542ec628..8353c4e434b 100644 --- a/intern/cycles/kernel/osl/osl_globals.h +++ b/intern/cycles/kernel/osl/osl_globals.h @@ -59,8 +59,7 @@ struct OSLGlobals { /* attributes */ struct Attribute { TypeDesc type; - AttributeElement elem; - int offset; + AttributeDescriptor desc; ParamValue value; }; diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index caae24405f1..153ebad6cd2 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -554,13 +554,13 @@ static bool get_mesh_element_attribute(KernelGlobals *kg, const ShaderData *sd, attr.type == TypeDesc::TypeNormal || attr.type == TypeDesc::TypeColor) { float3 fval[3]; - fval[0] = primitive_attribute_float3(kg, sd, attr.elem, attr.offset, + fval[0] = primitive_attribute_float3(kg, sd, attr.desc, (derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL); return set_attribute_float3(fval, type, derivatives, val); } else if(attr.type == TypeDesc::TypeFloat) { float fval[3]; - fval[0] = primitive_attribute_float(kg, sd, attr.elem, attr.offset, + fval[0] = primitive_attribute_float(kg, sd, attr.desc, (derivatives) ? &fval[1] : NULL, (derivatives) ? &fval[2] : NULL); return set_attribute_float(fval, type, derivatives, val); } @@ -573,7 +573,7 @@ static bool get_mesh_attribute(KernelGlobals *kg, const ShaderData *sd, const OS const TypeDesc& type, bool derivatives, void *val) { if(attr.type == TypeDesc::TypeMatrix) { - Transform tfm = primitive_attribute_matrix(kg, sd, attr.offset); + Transform tfm = primitive_attribute_matrix(kg, sd, attr.desc); return set_attribute_matrix(tfm, type, val); } else { @@ -815,7 +815,7 @@ bool OSLRenderServices::get_attribute(ShaderData *sd, bool derivatives, ustring if(it != attribute_map.end()) { const OSLGlobals::Attribute& attr = it->second; - if(attr.elem != ATTR_ELEMENT_OBJECT) { + if(attr.desc.element != ATTR_ELEMENT_OBJECT) { /* triangle and vertex attributes */ if(get_mesh_element_attribute(kg, sd, attr, type, derivatives, val)) return true; diff --git a/intern/cycles/kernel/osl/osl_shader.cpp b/intern/cycles/kernel/osl/osl_shader.cpp index 784e468635c..43a9e2f13aa 100644 --- a/intern/cycles/kernel/osl/osl_shader.cpp +++ b/intern/cycles/kernel/osl/osl_shader.cpp @@ -340,7 +340,7 @@ void OSLShader::eval_displacement(KernelGlobals *kg, ShaderData *sd, ShaderConte /* Attributes */ -int OSLShader::find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id, AttributeElement *elem) +int OSLShader::find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id, AttributeDescriptor *desc) { /* for OSL, a hash map is used to lookup the attribute by name. */ int object = sd->object*ATTR_PRIM_TYPES; @@ -354,16 +354,23 @@ int OSLShader::find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id, if(it != attr_map.end()) { const OSLGlobals::Attribute &osl_attr = it->second; - *elem = osl_attr.elem; + *desc = osl_attr.desc; - if(sd->prim == PRIM_NONE && (AttributeElement)osl_attr.elem != ATTR_ELEMENT_MESH) + if(sd->prim == PRIM_NONE && (AttributeElement)osl_attr.desc.element != ATTR_ELEMENT_MESH) { + desc->offset = ATTR_STD_NOT_FOUND; return ATTR_STD_NOT_FOUND; + } /* return result */ - return (osl_attr.elem == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : osl_attr.offset; + if(osl_attr.desc.element == ATTR_ELEMENT_NONE) { + desc->offset = ATTR_STD_NOT_FOUND; + } + return desc->offset; } - else + else { + desc->offset = ATTR_STD_NOT_FOUND; return (int)ATTR_STD_NOT_FOUND; + } } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/osl_shader.h b/intern/cycles/kernel/osl/osl_shader.h index a185b8b8c05..ad06dd6929d 100644 --- a/intern/cycles/kernel/osl/osl_shader.h +++ b/intern/cycles/kernel/osl/osl_shader.h @@ -59,7 +59,7 @@ public: static void eval_displacement(KernelGlobals *kg, ShaderData *sd, ShaderContext ctx); /* attributes */ - static int find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id, AttributeElement *elem); + static int find_attribute(KernelGlobals *kg, const ShaderData *sd, uint id, AttributeDescriptor *desc); }; CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/shaders/node_rgb_curves.osl b/intern/cycles/kernel/shaders/node_rgb_curves.osl index c8e7e4f175b..984b7d47e8f 100644 --- a/intern/cycles/kernel/shaders/node_rgb_curves.osl +++ b/intern/cycles/kernel/shaders/node_rgb_curves.osl @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "stdosl.h" #include "node_ramp_util.h" shader node_rgb_curves( diff --git a/intern/cycles/kernel/shaders/node_rgb_ramp.osl b/intern/cycles/kernel/shaders/node_rgb_ramp.osl index 24b8728b999..4e7d8fdcf65 100644 --- a/intern/cycles/kernel/shaders/node_rgb_ramp.osl +++ b/intern/cycles/kernel/shaders/node_rgb_ramp.osl @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "stdosl.h" #include "node_ramp_util.h" shader node_rgb_ramp( diff --git a/intern/cycles/kernel/shaders/node_vector_curves.osl b/intern/cycles/kernel/shaders/node_vector_curves.osl index d92fa11d439..ff284c48e0a 100644 --- a/intern/cycles/kernel/shaders/node_vector_curves.osl +++ b/intern/cycles/kernel/shaders/node_vector_curves.osl @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "stdosl.h" #include "node_ramp_util.h" shader node_vector_curves( diff --git a/intern/cycles/kernel/svm/svm_attribute.h b/intern/cycles/kernel/svm/svm_attribute.h index bd6013e9205..de978a423b4 100644 --- a/intern/cycles/kernel/svm/svm_attribute.h +++ b/intern/cycles/kernel/svm/svm_attribute.h @@ -18,117 +18,101 @@ CCL_NAMESPACE_BEGIN /* Attribute Node */ -ccl_device void svm_node_attr_init(KernelGlobals *kg, ShaderData *sd, +ccl_device AttributeDescriptor svm_node_attr_init(KernelGlobals *kg, ShaderData *sd, uint4 node, NodeAttributeType *type, - NodeAttributeType *mesh_type, AttributeElement *elem, int *offset, uint *out_offset) + uint *out_offset) { *out_offset = node.z; *type = (NodeAttributeType)node.w; + + AttributeDescriptor desc; + if(ccl_fetch(sd, object) != OBJECT_NONE) { - /* find attribute by unique id */ - uint id = node.y; - uint attr_offset = ccl_fetch(sd, object)*kernel_data.bvh.attributes_map_stride; - attr_offset += attribute_primitive_type(kg, sd); - uint4 attr_map = kernel_tex_fetch(__attributes_map, attr_offset); - - while(attr_map.x != id) { - if(UNLIKELY(attr_map.x == ATTR_STD_NONE)) { - *elem = ATTR_ELEMENT_NONE; - *offset = 0; - *mesh_type = (NodeAttributeType)node.w; - return; - } - attr_offset += ATTR_PRIM_TYPES; - attr_map = kernel_tex_fetch(__attributes_map, attr_offset); + desc = find_attribute(kg, sd, node.y); + if(desc.offset == ATTR_STD_NOT_FOUND) { + desc.element = ATTR_ELEMENT_NONE; + desc.offset = 0; + desc.type = (NodeAttributeType)node.w; } - - /* return result */ - *elem = (AttributeElement)attr_map.y; - *offset = as_int(attr_map.z); - *mesh_type = (NodeAttributeType)attr_map.w; } else { /* background */ - *elem = ATTR_ELEMENT_NONE; - *offset = 0; - *mesh_type = (NodeAttributeType)node.w; + desc.element = ATTR_ELEMENT_NONE; + desc.offset = 0; + desc.type = (NodeAttributeType)node.w; } + + return desc; } ccl_device void svm_node_attr(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) { - NodeAttributeType type, mesh_type; - AttributeElement elem; + NodeAttributeType type; uint out_offset; - int offset; - - svm_node_attr_init(kg, sd, node, &type, &mesh_type, &elem, &offset, &out_offset); + AttributeDescriptor desc = svm_node_attr_init(kg, sd, node, &type, &out_offset); /* fetch and store attribute */ if(type == NODE_ATTR_FLOAT) { - if(mesh_type == NODE_ATTR_FLOAT) { - float f = primitive_attribute_float(kg, sd, elem, offset, NULL, NULL); + if(desc.type == NODE_ATTR_FLOAT) { + float f = primitive_attribute_float(kg, sd, desc, NULL, NULL); stack_store_float(stack, out_offset, f); } else { - float3 f = primitive_attribute_float3(kg, sd, elem, offset, NULL, NULL); + float3 f = primitive_attribute_float3(kg, sd, desc, NULL, NULL); stack_store_float(stack, out_offset, average(f)); } } else { - if(mesh_type == NODE_ATTR_FLOAT3) { - float3 f = primitive_attribute_float3(kg, sd, elem, offset, NULL, NULL); + if(desc.type == NODE_ATTR_FLOAT3) { + float3 f = primitive_attribute_float3(kg, sd, desc, NULL, NULL); stack_store_float3(stack, out_offset, f); } else { - float f = primitive_attribute_float(kg, sd, elem, offset, NULL, NULL); + float f = primitive_attribute_float(kg, sd, desc, NULL, NULL); stack_store_float3(stack, out_offset, make_float3(f, f, f)); } } } -#ifndef __KERNEL_CUDS__ +#ifndef __KERNEL_CUDA__ ccl_device #else ccl_device_noinline #endif void svm_node_attr_bump_dx(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) { - NodeAttributeType type, mesh_type; - AttributeElement elem; + NodeAttributeType type; uint out_offset; - int offset; - - svm_node_attr_init(kg, sd, node, &type, &mesh_type, &elem, &offset, &out_offset); + AttributeDescriptor desc = svm_node_attr_init(kg, sd, node, &type, &out_offset); /* fetch and store attribute */ if(type == NODE_ATTR_FLOAT) { - if(mesh_type == NODE_ATTR_FLOAT) { + if(desc.type == NODE_ATTR_FLOAT) { float dx; - float f = primitive_attribute_float(kg, sd, elem, offset, &dx, NULL); + float f = primitive_attribute_float(kg, sd, desc, &dx, NULL); stack_store_float(stack, out_offset, f+dx); } else { float3 dx; - float3 f = primitive_attribute_float3(kg, sd, elem, offset, &dx, NULL); + float3 f = primitive_attribute_float3(kg, sd, desc, &dx, NULL); stack_store_float(stack, out_offset, average(f+dx)); } } else { - if(mesh_type == NODE_ATTR_FLOAT3) { + if(desc.type == NODE_ATTR_FLOAT3) { float3 dx; - float3 f = primitive_attribute_float3(kg, sd, elem, offset, &dx, NULL); + float3 f = primitive_attribute_float3(kg, sd, desc, &dx, NULL); stack_store_float3(stack, out_offset, f+dx); } else { float dx; - float f = primitive_attribute_float(kg, sd, elem, offset, &dx, NULL); + float f = primitive_attribute_float(kg, sd, desc, &dx, NULL); stack_store_float3(stack, out_offset, make_float3(f+dx, f+dx, f+dx)); } } } -#ifndef __KERNEL_CUDS__ +#ifndef __KERNEL_CUDA__ ccl_device #else ccl_device_noinline @@ -138,35 +122,32 @@ void svm_node_attr_bump_dy(KernelGlobals *kg, float *stack, uint4 node) { - NodeAttributeType type, mesh_type; - AttributeElement elem; + NodeAttributeType type; uint out_offset; - int offset; - - svm_node_attr_init(kg, sd, node, &type, &mesh_type, &elem, &offset, &out_offset); + AttributeDescriptor desc = svm_node_attr_init(kg, sd, node, &type, &out_offset); /* fetch and store attribute */ if(type == NODE_ATTR_FLOAT) { - if(mesh_type == NODE_ATTR_FLOAT) { + if(desc.type == NODE_ATTR_FLOAT) { float dy; - float f = primitive_attribute_float(kg, sd, elem, offset, NULL, &dy); + float f = primitive_attribute_float(kg, sd, desc, NULL, &dy); stack_store_float(stack, out_offset, f+dy); } else { float3 dy; - float3 f = primitive_attribute_float3(kg, sd, elem, offset, NULL, &dy); + float3 f = primitive_attribute_float3(kg, sd, desc, NULL, &dy); stack_store_float(stack, out_offset, average(f+dy)); } } else { - if(mesh_type == NODE_ATTR_FLOAT3) { + if(desc.type == NODE_ATTR_FLOAT3) { float3 dy; - float3 f = primitive_attribute_float3(kg, sd, elem, offset, NULL, &dy); + float3 f = primitive_attribute_float3(kg, sd, desc, NULL, &dy); stack_store_float3(stack, out_offset, f+dy); } else { float dy; - float f = primitive_attribute_float(kg, sd, elem, offset, NULL, &dy); + float f = primitive_attribute_float(kg, sd, desc, NULL, &dy); stack_store_float3(stack, out_offset, make_float3(f+dy, f+dy, f+dy)); } } diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/svm_image.h index b6b90dfff81..5d02be1fa2f 100644 --- a/intern/cycles/kernel/svm/svm_image.h +++ b/intern/cycles/kernel/svm/svm_image.h @@ -18,7 +18,7 @@ CCL_NAMESPACE_BEGIN /* Float4 textures on various devices. */ #if defined(__KERNEL_CPU__) -# define TEX_NUM_FLOAT4_IMAGES TEX_NUM_FLOAT4_CPU +# define TEX_NUM_FLOAT4_IMAGES TEX_NUM_FLOAT4_CPU #elif defined(__KERNEL_CUDA__) # if __CUDA_ARCH__ < 300 # define TEX_NUM_FLOAT4_IMAGES TEX_NUM_FLOAT4_CUDA @@ -36,13 +36,26 @@ CCL_NAMESPACE_BEGIN ccl_device_inline float4 svm_image_texture_read(KernelGlobals *kg, int id, int offset) { - if(id >= TEX_NUM_FLOAT4_IMAGES) { + /* Float4 */ + if(id < TEX_START_BYTE4_OPENCL) { + return kernel_tex_fetch(__tex_image_float4_packed, offset); + } + /* Byte4 */ + else if(id < TEX_START_FLOAT_OPENCL) { uchar4 r = kernel_tex_fetch(__tex_image_byte4_packed, offset); float f = 1.0f/255.0f; return make_float4(r.x*f, r.y*f, r.z*f, r.w*f); } + /* Float */ + else if(id < TEX_START_BYTE_OPENCL) { + float f = kernel_tex_fetch(__tex_image_float_packed, offset); + return make_float4(f, f, f, 1.0f); + } + /* Byte */ else { - return kernel_tex_fetch(__tex_image_float4_packed, offset); + uchar r = kernel_tex_fetch(__tex_image_byte_packed, offset); + float f = r * (1.0f/255.0f); + return make_float4(f, f, f, 1.0f); } } @@ -277,8 +290,10 @@ ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y, } # else CUtexObject tex = kernel_tex_fetch(__bindless_mapping, id); - if(id < 2048) /* TODO(dingto): Make this a variable */ + /* float4, byte4 and half4 */ + if(id < TEX_START_FLOAT_CUDA_KEPLER) r = kernel_tex_image_interp_float4(tex, x, y); + /* float, byte and half */ else { float f = kernel_tex_image_interp_float(tex, x, y); r = make_float4(f, f, f, 1.0); diff --git a/intern/cycles/kernel/svm/svm_math_util.h b/intern/cycles/kernel/svm/svm_math_util.h index 3f7d18a02fe..6d13a0d8e02 100644 --- a/intern/cycles/kernel/svm/svm_math_util.h +++ b/intern/cycles/kernel/svm/svm_math_util.h @@ -32,21 +32,17 @@ ccl_device void svm_vector_math(float *Fac, float3 *Vector, NodeVectorMath type, *Fac = average_fac(*Vector); } else if(type == NODE_VECTOR_MATH_AVERAGE) { - *Fac = len(Vector1 + Vector2); - *Vector = normalize(Vector1 + Vector2); + *Vector = safe_normalize_len(Vector1 + Vector2, Fac); } else if(type == NODE_VECTOR_MATH_DOT_PRODUCT) { *Fac = dot(Vector1, Vector2); *Vector = make_float3(0.0f, 0.0f, 0.0f); } else if(type == NODE_VECTOR_MATH_CROSS_PRODUCT) { - float3 c = cross(Vector1, Vector2); - *Fac = len(c); - *Vector = normalize(c); + *Vector = safe_normalize_len(cross(Vector1, Vector2), Fac); } else if(type == NODE_VECTOR_MATH_NORMALIZE) { - *Fac = len(Vector1); - *Vector = normalize(Vector1); + *Vector = safe_normalize_len(Vector1, Fac); } else { *Fac = 0.0f; diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h index b39d6a3e009..01dede3fff5 100644 --- a/intern/cycles/kernel/svm/svm_tex_coord.h +++ b/intern/cycles/kernel/svm/svm_tex_coord.h @@ -287,23 +287,22 @@ ccl_device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *st } /* first try to get tangent attribute */ - AttributeElement attr_elem, attr_sign_elem, attr_normal_elem; - int attr_offset = find_attribute(kg, sd, node.z, &attr_elem); - int attr_sign_offset = find_attribute(kg, sd, node.w, &attr_sign_elem); - int attr_normal_offset = find_attribute(kg, sd, ATTR_STD_VERTEX_NORMAL, &attr_normal_elem); + const AttributeDescriptor attr = find_attribute(kg, sd, node.z); + const AttributeDescriptor attr_sign = find_attribute(kg, sd, node.w); + const AttributeDescriptor attr_normal = find_attribute(kg, sd, ATTR_STD_VERTEX_NORMAL); - if(attr_offset == ATTR_STD_NOT_FOUND || attr_sign_offset == ATTR_STD_NOT_FOUND || attr_normal_offset == ATTR_STD_NOT_FOUND) { + if(attr.offset == ATTR_STD_NOT_FOUND || attr_sign.offset == ATTR_STD_NOT_FOUND || attr_normal.offset == ATTR_STD_NOT_FOUND) { stack_store_float3(stack, normal_offset, make_float3(0.0f, 0.0f, 0.0f)); return; } /* get _unnormalized_ interpolated normal and tangent */ - float3 tangent = primitive_attribute_float3(kg, sd, attr_elem, attr_offset, NULL, NULL); - float sign = primitive_attribute_float(kg, sd, attr_sign_elem, attr_sign_offset, NULL, NULL); + float3 tangent = primitive_attribute_float3(kg, sd, attr, NULL, NULL); + float sign = primitive_attribute_float(kg, sd, attr_sign, NULL, NULL); float3 normal; if(ccl_fetch(sd, shader) & SHADER_SMOOTH_NORMAL) { - normal = primitive_attribute_float3(kg, sd, attr_normal_elem, attr_normal_offset, NULL, NULL); + normal = primitive_attribute_float3(kg, sd, attr_normal, NULL, NULL); } else { normal = ccl_fetch(sd, Ng); @@ -356,24 +355,22 @@ ccl_device void svm_node_tangent(KernelGlobals *kg, ShaderData *sd, float *stack if(direction_type == NODE_TANGENT_UVMAP) { /* UV map */ - AttributeElement attr_elem; - int attr_offset = find_attribute(kg, sd, node.z, &attr_elem); + const AttributeDescriptor desc = find_attribute(kg, sd, node.z); - if(attr_offset == ATTR_STD_NOT_FOUND) + if(desc.offset == ATTR_STD_NOT_FOUND) tangent = make_float3(0.0f, 0.0f, 0.0f); else - tangent = primitive_attribute_float3(kg, sd, attr_elem, attr_offset, NULL, NULL); + tangent = primitive_attribute_float3(kg, sd, desc, NULL, NULL); } else { /* radial */ - AttributeElement attr_elem; - int attr_offset = find_attribute(kg, sd, node.z, &attr_elem); + const AttributeDescriptor desc = find_attribute(kg, sd, node.z); float3 generated; - if(attr_offset == ATTR_STD_NOT_FOUND) + if(desc.offset == ATTR_STD_NOT_FOUND) generated = ccl_fetch(sd, P); else - generated = primitive_attribute_float3(kg, sd, attr_elem, attr_offset, NULL, NULL); + generated = primitive_attribute_float3(kg, sd, desc, NULL, NULL); if(axis == NODE_TANGENT_AXIS_X) tangent = make_float3(0.0f, -(generated.z - 0.5f), (generated.y - 0.5f)); diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp index e8ff81fe08e..c0d429a583c 100644 --- a/intern/cycles/render/attribute.cpp +++ b/intern/cycles/render/attribute.cpp @@ -44,6 +44,7 @@ void Attribute::set(ustring name_, TypeDesc type_, AttributeElement element_) type = type_; element = element_; std = ATTR_STD_NONE; + flags = 0; /* string and matrix not supported! */ assert(type == TypeDesc::TypeFloat || type == TypeDesc::TypeColor || @@ -61,6 +62,11 @@ void Attribute::resize(Mesh *mesh, AttributePrimitive prim, bool reserve_only) } } +void Attribute::resize(size_t num_elements) +{ + buffer.resize(num_elements * data_sizeof(), 0); +} + void Attribute::add(const float& f) { char *data = (char*)&f; @@ -130,6 +136,10 @@ size_t Attribute::data_sizeof() const size_t Attribute::element_size(Mesh *mesh, AttributePrimitive prim) const { + if(flags & ATTR_FINAL_SIZE) { + return buffer.size() / data_sizeof(); + } + size_t size; switch(element) { @@ -517,16 +527,19 @@ AttributeRequest::AttributeRequest(ustring name_) std = ATTR_STD_NONE; triangle_type = TypeDesc::TypeFloat; - triangle_element = ATTR_ELEMENT_NONE; - triangle_offset = 0; + triangle_desc.element = ATTR_ELEMENT_NONE; + triangle_desc.offset = 0; + triangle_desc.type = NODE_ATTR_FLOAT; curve_type = TypeDesc::TypeFloat; - curve_element = ATTR_ELEMENT_NONE; - curve_offset = 0; + curve_desc.element = ATTR_ELEMENT_NONE; + curve_desc.offset = 0; + curve_desc.type = NODE_ATTR_FLOAT; subd_type = TypeDesc::TypeFloat; - subd_element = ATTR_ELEMENT_NONE; - subd_offset = 0; + subd_desc.element = ATTR_ELEMENT_NONE; + subd_desc.offset = 0; + subd_desc.type = NODE_ATTR_FLOAT; } AttributeRequest::AttributeRequest(AttributeStandard std_) @@ -535,16 +548,19 @@ AttributeRequest::AttributeRequest(AttributeStandard std_) std = std_; triangle_type = TypeDesc::TypeFloat; - triangle_element = ATTR_ELEMENT_NONE; - triangle_offset = 0; + triangle_desc.element = ATTR_ELEMENT_NONE; + triangle_desc.offset = 0; + triangle_desc.type = NODE_ATTR_FLOAT; curve_type = TypeDesc::TypeFloat; - curve_element = ATTR_ELEMENT_NONE; - curve_offset = 0; + curve_desc.element = ATTR_ELEMENT_NONE; + curve_desc.offset = 0; + curve_desc.type = NODE_ATTR_FLOAT; subd_type = TypeDesc::TypeFloat; - subd_element = ATTR_ELEMENT_NONE; - subd_offset = 0; + subd_desc.element = ATTR_ELEMENT_NONE; + subd_desc.offset = 0; + subd_desc.type = NODE_ATTR_FLOAT; } /* AttributeRequestSet */ diff --git a/intern/cycles/render/attribute.h b/intern/cycles/render/attribute.h index e51bdf28d66..f4538c76369 100644 --- a/intern/cycles/render/attribute.h +++ b/intern/cycles/render/attribute.h @@ -54,11 +54,13 @@ public: TypeDesc type; vector<char> buffer; AttributeElement element; + uint flags; /* enum AttributeFlag */ Attribute() {} ~Attribute(); void set(ustring name, TypeDesc type, AttributeElement element); void resize(Mesh *mesh, AttributePrimitive prim, bool reserve_only); + void resize(size_t num_elements); size_t data_sizeof() const; size_t element_size(Mesh *mesh, AttributePrimitive prim) const; @@ -135,8 +137,7 @@ public: /* temporary variables used by MeshManager */ TypeDesc triangle_type, curve_type, subd_type; - AttributeElement triangle_element, curve_element, subd_element; - int triangle_offset, curve_offset, subd_offset; + AttributeDescriptor triangle_desc, curve_desc, subd_desc; explicit AttributeRequest(ustring name_); explicit AttributeRequest(AttributeStandard std); diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index 614620c14af..24543601ef9 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -52,15 +52,15 @@ ImageManager::ImageManager(const DeviceInfo& info) { \ tex_num_images[IMAGE_DATA_TYPE_FLOAT4] = TEX_NUM_FLOAT4_ ## ARCH; \ tex_num_images[IMAGE_DATA_TYPE_BYTE4] = TEX_NUM_BYTE4_ ## ARCH; \ + tex_num_images[IMAGE_DATA_TYPE_HALF4] = TEX_NUM_HALF4_ ## ARCH; \ tex_num_images[IMAGE_DATA_TYPE_FLOAT] = TEX_NUM_FLOAT_ ## ARCH; \ tex_num_images[IMAGE_DATA_TYPE_BYTE] = TEX_NUM_BYTE_ ## ARCH; \ - tex_num_images[IMAGE_DATA_TYPE_HALF4] = TEX_NUM_HALF4_ ## ARCH; \ tex_num_images[IMAGE_DATA_TYPE_HALF] = TEX_NUM_HALF_ ## ARCH; \ tex_start_images[IMAGE_DATA_TYPE_FLOAT4] = TEX_START_FLOAT4_ ## ARCH; \ tex_start_images[IMAGE_DATA_TYPE_BYTE4] = TEX_START_BYTE4_ ## ARCH; \ + tex_start_images[IMAGE_DATA_TYPE_HALF4] = TEX_START_HALF4_ ## ARCH; \ tex_start_images[IMAGE_DATA_TYPE_FLOAT] = TEX_START_FLOAT_ ## ARCH; \ tex_start_images[IMAGE_DATA_TYPE_BYTE] = TEX_START_BYTE_ ## ARCH; \ - tex_start_images[IMAGE_DATA_TYPE_HALF4] = TEX_START_HALF4_ ## ARCH; \ tex_start_images[IMAGE_DATA_TYPE_HALF] = TEX_START_HALF_ ## ARCH; \ } @@ -82,15 +82,15 @@ ImageManager::ImageManager(const DeviceInfo& info) /* Should not happen. */ tex_num_images[IMAGE_DATA_TYPE_FLOAT4] = 0; tex_num_images[IMAGE_DATA_TYPE_BYTE4] = 0; + tex_num_images[IMAGE_DATA_TYPE_HALF4] = 0; tex_num_images[IMAGE_DATA_TYPE_FLOAT] = 0; tex_num_images[IMAGE_DATA_TYPE_BYTE] = 0; - tex_num_images[IMAGE_DATA_TYPE_HALF4] = 0; tex_num_images[IMAGE_DATA_TYPE_HALF] = 0; tex_start_images[IMAGE_DATA_TYPE_FLOAT4] = 0; tex_start_images[IMAGE_DATA_TYPE_BYTE4] = 0; + tex_start_images[IMAGE_DATA_TYPE_HALF4] = 0; tex_start_images[IMAGE_DATA_TYPE_FLOAT] = 0; tex_start_images[IMAGE_DATA_TYPE_BYTE] = 0; - tex_start_images[IMAGE_DATA_TYPE_HALF4] = 0; tex_start_images[IMAGE_DATA_TYPE_HALF] = 0; assert(0); } @@ -216,7 +216,7 @@ ImageManager::ImageDataType ImageManager::get_image_metadata(const string& filen } /* We use a consecutive slot counting scheme on the devices, in order - * float4, byte4, float, byte. + * float4, byte4, half4, float, byte, half. * These functions convert the slot ids from ImageManager "images" ones * to device ones and vice versa. */ int ImageManager::type_index_to_flattened_slot(int slot, ImageDataType type) @@ -284,7 +284,7 @@ int ImageManager::add_image(const string& filename, if(type == IMAGE_DATA_TYPE_FLOAT || type == IMAGE_DATA_TYPE_FLOAT4) is_float = true; - /* No single channel and half textures on CUDA (Fermi) and OpenCL, use available slots */ + /* No single channel and half textures on CUDA (Fermi) and no half on OpenCL, use available slots */ if((type == IMAGE_DATA_TYPE_FLOAT || type == IMAGE_DATA_TYPE_HALF4 || type == IMAGE_DATA_TYPE_HALF) && @@ -1105,10 +1105,11 @@ void ImageManager::device_pack_images(Device *device, size_t size = 0, offset = 0; ImageDataType type; - int info_size = tex_num_images[IMAGE_DATA_TYPE_FLOAT4] + tex_num_images[IMAGE_DATA_TYPE_BYTE4]; + int info_size = tex_num_images[IMAGE_DATA_TYPE_FLOAT4] + tex_num_images[IMAGE_DATA_TYPE_BYTE4] + + tex_num_images[IMAGE_DATA_TYPE_FLOAT] + tex_num_images[IMAGE_DATA_TYPE_BYTE]; uint4 *info = dscene->tex_image_packed_info.resize(info_size); - /* Byte Textures*/ + /* Byte4 Textures*/ type = IMAGE_DATA_TYPE_BYTE4; for(size_t slot = 0; slot < images[type].size(); slot++) { @@ -1119,7 +1120,7 @@ void ImageManager::device_pack_images(Device *device, size += tex_img.size(); } - uchar4 *pixels_byte = dscene->tex_image_byte4_packed.resize(size); + uchar4 *pixels_byte4 = dscene->tex_image_byte4_packed.resize(size); for(size_t slot = 0; slot < images[type].size(); slot++) { if(!images[type][slot]) @@ -1131,11 +1132,11 @@ void ImageManager::device_pack_images(Device *device, info[type_index_to_flattened_slot(slot, type)] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options); - memcpy(pixels_byte+offset, (void*)tex_img.data_pointer, tex_img.memory_size()); + memcpy(pixels_byte4+offset, (void*)tex_img.data_pointer, tex_img.memory_size()); offset += tex_img.size(); } - /* Float Textures*/ + /* Float4 Textures*/ type = IMAGE_DATA_TYPE_FLOAT4; size = 0, offset = 0; @@ -1147,7 +1148,7 @@ void ImageManager::device_pack_images(Device *device, size += tex_img.size(); } - float4 *pixels_float = dscene->tex_image_float4_packed.resize(size); + float4 *pixels_float4 = dscene->tex_image_float4_packed.resize(size); for(size_t slot = 0; slot < images[type].size(); slot++) { if(!images[type][slot]) @@ -1160,6 +1161,63 @@ void ImageManager::device_pack_images(Device *device, uint8_t options = pack_image_options(type, slot); info[type_index_to_flattened_slot(slot, type)] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options); + memcpy(pixels_float4+offset, (void*)tex_img.data_pointer, tex_img.memory_size()); + offset += tex_img.size(); + } + + /* Byte Textures*/ + type = IMAGE_DATA_TYPE_BYTE; + size = 0, offset = 0; + + for(size_t slot = 0; slot < images[type].size(); slot++) { + if(!images[type][slot]) + continue; + + device_vector<uchar>& tex_img = dscene->tex_byte_image[slot]; + size += tex_img.size(); + } + + uchar *pixels_byte = dscene->tex_image_byte_packed.resize(size); + + for(size_t slot = 0; slot < images[type].size(); slot++) { + if(!images[type][slot]) + continue; + + device_vector<uchar>& tex_img = dscene->tex_byte_image[slot]; + + uint8_t options = pack_image_options(type, slot); + + info[type_index_to_flattened_slot(slot, type)] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options); + + memcpy(pixels_byte+offset, (void*)tex_img.data_pointer, tex_img.memory_size()); + offset += tex_img.size(); + } + + /* Float Textures*/ + type = IMAGE_DATA_TYPE_FLOAT; + size = 0, offset = 0; + + for(size_t slot = 0; slot < images[type].size(); slot++) { + if(!images[type][slot]) + continue; + + device_vector<float>& tex_img = dscene->tex_float_image[slot]; + size += tex_img.size(); + } + + float *pixels_float = dscene->tex_image_float_packed.resize(size); + + for(size_t slot = 0; slot < images[type].size(); slot++) { + if(!images[type][slot]) + continue; + + device_vector<float>& tex_img = dscene->tex_float_image[slot]; + + /* todo: support 3D textures, only CPU for now */ + + uint8_t options = pack_image_options(type, slot); + info[type_index_to_flattened_slot(slot, type)] = make_uint4(tex_img.data_width, tex_img.data_height, offset, options); + memcpy(pixels_float+offset, (void*)tex_img.data_pointer, tex_img.memory_size()); offset += tex_img.size(); } @@ -1178,6 +1236,20 @@ void ImageManager::device_pack_images(Device *device, } device->tex_alloc("__tex_image_float4_packed", dscene->tex_image_float4_packed); } + if(dscene->tex_image_byte_packed.size()) { + if(dscene->tex_image_byte_packed.device_pointer) { + thread_scoped_lock device_lock(device_mutex); + device->tex_free(dscene->tex_image_byte_packed); + } + device->tex_alloc("__tex_image_byte_packed", dscene->tex_image_byte_packed); + } + if(dscene->tex_image_float_packed.size()) { + if(dscene->tex_image_float_packed.device_pointer) { + thread_scoped_lock device_lock(device_mutex); + device->tex_free(dscene->tex_image_float_packed); + } + device->tex_alloc("__tex_image_float_packed", dscene->tex_image_float_packed); + } if(dscene->tex_image_packed_info.size()) { if(dscene->tex_image_packed_info.device_pointer) { thread_scoped_lock device_lock(device_mutex); @@ -1208,10 +1280,14 @@ void ImageManager::device_free(Device *device, DeviceScene *dscene) device->tex_free(dscene->tex_image_byte4_packed); device->tex_free(dscene->tex_image_float4_packed); + device->tex_free(dscene->tex_image_byte_packed); + device->tex_free(dscene->tex_image_float_packed); device->tex_free(dscene->tex_image_packed_info); dscene->tex_image_byte4_packed.clear(); dscene->tex_image_float4_packed.clear(); + dscene->tex_image_byte_packed.clear(); + dscene->tex_image_float_packed.clear(); dscene->tex_image_packed_info.clear(); } diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h index 07998684b23..cca71a6bb93 100644 --- a/intern/cycles/render/image.h +++ b/intern/cycles/render/image.h @@ -39,9 +39,9 @@ public: enum ImageDataType { IMAGE_DATA_TYPE_FLOAT4 = 0, IMAGE_DATA_TYPE_BYTE4 = 1, - IMAGE_DATA_TYPE_FLOAT = 2, - IMAGE_DATA_TYPE_BYTE = 3, - IMAGE_DATA_TYPE_HALF4 = 4, + IMAGE_DATA_TYPE_HALF4 = 2, + IMAGE_DATA_TYPE_FLOAT = 3, + IMAGE_DATA_TYPE_BYTE = 4, IMAGE_DATA_TYPE_HALF = 5, IMAGE_DATA_NUM_TYPES diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index 4cf0a785897..fcf4e69984d 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -30,6 +30,9 @@ #include "osl_globals.h" +#include "subd_split.h" +#include "subd_patch_table.h" + #include "util_foreach.h" #include "util_logging.h" #include "util_progress.h" @@ -112,19 +115,12 @@ float3 Mesh::SubdFace::normal(const Mesh *mesh) const return safe_normalize(cross(v1 - v0, v2 - v0)); } - /* Mesh */ NODE_DEFINE(Mesh) { NodeType* type = NodeType::add("mesh", create); - static NodeEnum displacement_method_enum; - displacement_method_enum.insert("bump", DISPLACE_BUMP); - displacement_method_enum.insert("true", DISPLACE_TRUE); - displacement_method_enum.insert("both", DISPLACE_BOTH); - SOCKET_ENUM(displacement_method, "Displacement Method", displacement_method_enum, DISPLACE_BUMP); - SOCKET_UINT(motion_steps, "Motion Steps", 3); SOCKET_BOOLEAN(use_motion_blur, "Use Motion Blur", false); @@ -177,11 +173,16 @@ Mesh::Mesh() num_ngons = 0; subdivision_type = SUBDIVISION_NONE; + subd_params = NULL; + + patch_table = NULL; } Mesh::~Mesh() { delete bvh; + delete patch_table; + delete subd_params; } void Mesh::resize_mesh(int numverts, int numtris) @@ -274,6 +275,8 @@ void Mesh::clear() num_subd_verts = 0; + subd_creases.clear(); + attributes.clear(); curve_attributes.clear(); subd_attributes.clear(); @@ -283,6 +286,9 @@ void Mesh::clear() transform_negative_scaled = false; transform_normal = transform_identity(); geometry_flags = GEOMETRY_NONE; + + delete patch_table; + patch_table = NULL; } int Mesh::split_vertex(int vertex) @@ -705,7 +711,6 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui } } - void Mesh::compute_bvh(DeviceScene *dscene, SceneParams *params, Progress *progress, @@ -779,6 +784,17 @@ bool Mesh::has_motion_blur() const curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION))); } +bool Mesh::has_true_displacement() const +{ + foreach(Shader *shader, used_shaders) { + if(shader->has_displacement && shader->displacement_method != DISPLACE_BUMP) { + return true; + } + } + + return false; +} + bool Mesh::need_build_bvh() const { return !transform_applied || has_surface_bssrdf; @@ -831,9 +847,10 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att OSLGlobals::Attribute osl_attr; osl_attr.type = attr.type(); - osl_attr.elem = ATTR_ELEMENT_OBJECT; + osl_attr.desc.element = ATTR_ELEMENT_OBJECT; osl_attr.value = attr; - osl_attr.offset = 0; + osl_attr.desc.offset = 0; + osl_attr.desc.flags = 0; og->attribute_map[i*ATTR_PRIM_TYPES + ATTR_PRIM_TRIANGLE][attr.name()] = osl_attr; og->attribute_map[i*ATTR_PRIM_TYPES + ATTR_PRIM_CURVE][attr.name()] = osl_attr; @@ -853,9 +870,8 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att foreach(AttributeRequest& req, attributes.requests) { OSLGlobals::Attribute osl_attr; - if(req.triangle_element != ATTR_ELEMENT_NONE) { - osl_attr.elem = req.triangle_element; - osl_attr.offset = req.triangle_offset; + if(req.triangle_desc.element != ATTR_ELEMENT_NONE) { + osl_attr.desc = req.triangle_desc; if(req.triangle_type == TypeDesc::TypeFloat) osl_attr.type = TypeDesc::TypeFloat; @@ -875,9 +891,8 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att } } - if(req.curve_element != ATTR_ELEMENT_NONE) { - osl_attr.elem = req.curve_element; - osl_attr.offset = req.curve_offset; + if(req.curve_desc.element != ATTR_ELEMENT_NONE) { + osl_attr.desc = req.curve_desc; if(req.curve_type == TypeDesc::TypeFloat) osl_attr.type = TypeDesc::TypeFloat; @@ -897,9 +912,8 @@ void MeshManager::update_osl_attributes(Device *device, Scene *scene, vector<Att } } - if(req.subd_element != ATTR_ELEMENT_NONE) { - osl_attr.elem = req.subd_element; - osl_attr.offset = req.subd_offset; + if(req.subd_desc.element != ATTR_ELEMENT_NONE) { + osl_attr.desc = req.subd_desc; if(req.subd_type == TypeDesc::TypeFloat) osl_attr.type = TypeDesc::TypeFloat; @@ -971,8 +985,8 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce if(mesh->num_triangles()) { attr_map[index].x = id; - attr_map[index].y = req.triangle_element; - attr_map[index].z = as_uint(req.triangle_offset); + attr_map[index].y = req.triangle_desc.element; + attr_map[index].z = as_uint(req.triangle_desc.offset); if(req.triangle_type == TypeDesc::TypeFloat) attr_map[index].w = NODE_ATTR_FLOAT; @@ -980,14 +994,16 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce attr_map[index].w = NODE_ATTR_MATRIX; else attr_map[index].w = NODE_ATTR_FLOAT3; + + attr_map[index].w |= req.triangle_desc.flags << 8; } index++; if(mesh->num_curves()) { attr_map[index].x = id; - attr_map[index].y = req.curve_element; - attr_map[index].z = as_uint(req.curve_offset); + attr_map[index].y = req.curve_desc.element; + attr_map[index].z = as_uint(req.curve_desc.offset); if(req.curve_type == TypeDesc::TypeFloat) attr_map[index].w = NODE_ATTR_FLOAT; @@ -995,14 +1011,16 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce attr_map[index].w = NODE_ATTR_MATRIX; else attr_map[index].w = NODE_ATTR_FLOAT3; + + attr_map[index].w |= req.curve_desc.flags << 8; } index++; if(mesh->subd_faces.size()) { attr_map[index].x = id; - attr_map[index].y = req.subd_element; - attr_map[index].z = as_uint(req.subd_offset); + attr_map[index].y = req.subd_desc.element; + attr_map[index].z = as_uint(req.subd_desc.offset); if(req.subd_type == TypeDesc::TypeFloat) attr_map[index].w = NODE_ATTR_FLOAT; @@ -1010,6 +1028,8 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce attr_map[index].w = NODE_ATTR_MATRIX; else attr_map[index].w = NODE_ATTR_FLOAT3; + + attr_map[index].w |= req.subd_desc.flags << 8; } index++; @@ -1069,17 +1089,20 @@ static void update_attribute_element_offset(Mesh *mesh, Attribute *mattr, AttributePrimitive prim, TypeDesc& type, - int& offset, - AttributeElement& element) + AttributeDescriptor& desc) { if(mattr) { /* store element and type */ - element = mattr->element; + desc.element = mattr->element; + desc.flags = mattr->flags; type = mattr->type; /* store attribute data in arrays */ size_t size = mattr->element_size(mesh, prim); + AttributeElement& element = desc.element; + int& offset = desc.offset; + if(mattr->element == ATTR_ELEMENT_VOXEL) { /* store slot in offset value */ VoxelAttribute *voxel_data = mattr->data_voxel(); @@ -1128,7 +1151,11 @@ static void update_attribute_element_offset(Mesh *mesh, /* mesh vertex/curve index is global, not per object, so we sneak * a correction for that in here */ - if(element == ATTR_ELEMENT_VERTEX) + if(mesh->subdivision_type == Mesh::SUBDIVISION_CATMULL_CLARK && desc.flags & ATTR_SUBDIVIDED) { + /* indices for subdivided attributes are retrieved + * from patch table so no need for correction here*/ + } + else if(element == ATTR_ELEMENT_VERTEX) offset -= mesh->vert_offset; else if(element == ATTR_ELEMENT_VERTEX_MOTION) offset -= mesh->vert_offset; @@ -1153,8 +1180,8 @@ static void update_attribute_element_offset(Mesh *mesh, } else { /* attribute not found */ - element = ATTR_ELEMENT_NONE; - offset = 0; + desc.element = ATTR_ELEMENT_NONE; + desc.offset = 0; } } @@ -1243,8 +1270,7 @@ void MeshManager::device_update_attributes(Device *device, DeviceScene *dscene, triangle_mattr, ATTR_PRIM_TRIANGLE, req.triangle_type, - req.triangle_offset, - req.triangle_element); + req.triangle_desc); update_attribute_element_offset(mesh, attr_float, attr_float_offset, @@ -1253,8 +1279,7 @@ void MeshManager::device_update_attributes(Device *device, DeviceScene *dscene, curve_mattr, ATTR_PRIM_CURVE, req.curve_type, - req.curve_offset, - req.curve_element); + req.curve_desc); update_attribute_element_offset(mesh, attr_float, attr_float_offset, @@ -1263,8 +1288,7 @@ void MeshManager::device_update_attributes(Device *device, DeviceScene *dscene, subd_mattr, ATTR_PRIM_SUBD, req.subd_type, - req.subd_offset, - req.subd_element); + req.subd_desc); if(progress.get_cancel()) return; } @@ -1327,6 +1351,12 @@ void MeshManager::mesh_calc_offset(Scene *scene) if(mesh->subd_faces.size()) { Mesh::SubdFace& last = mesh->subd_faces[mesh->subd_faces.size()-1]; patch_size += (last.ptex_offset + last.num_ptex_faces()) * 8; + + /* patch tables are stored in same array so include them in patch_size */ + if(mesh->patch_table) { + mesh->patch_table_offset = patch_size; + patch_size += mesh->patch_table->total_size(); + } } face_size += mesh->subd_faces.size(); corner_size += mesh->subd_face_corners.size(); @@ -1358,6 +1388,12 @@ void MeshManager::device_update_mesh(Device *device, if(mesh->subd_faces.size()) { Mesh::SubdFace& last = mesh->subd_faces[mesh->subd_faces.size()-1]; patch_size += (last.ptex_offset + last.num_ptex_faces()) * 8; + + /* patch tables are stored in same array so include them in patch_size */ + if(mesh->patch_table) { + mesh->patch_table_offset = patch_size; + patch_size += mesh->patch_table->total_size(); + } } } @@ -1440,6 +1476,11 @@ void MeshManager::device_update_mesh(Device *device, foreach(Mesh *mesh, scene->meshes) { mesh->pack_patches(&patch_data[mesh->patch_offset], mesh->vert_offset, mesh->face_offset, mesh->corner_offset); + + if(mesh->patch_table) { + mesh->patch_table->copy_adjusting_offsets(&patch_data[mesh->patch_table_offset], mesh->patch_table_offset); + } + if(progress.get_cancel()) return; } @@ -1621,12 +1662,48 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen } } + /* Tessellate meshes that are using subdivision */ + size_t total_tess_needed = 0; + foreach(Mesh *mesh, scene->meshes) { + if(mesh->need_update && + mesh->subdivision_type != Mesh::SUBDIVISION_NONE && + mesh->num_subd_verts == 0 && + mesh->subd_params) + { + total_tess_needed++; + } + } + + size_t i = 0; + foreach(Mesh *mesh, scene->meshes) { + if(mesh->need_update && + mesh->subdivision_type != Mesh::SUBDIVISION_NONE && + mesh->num_subd_verts == 0 && + mesh->subd_params) + { + string msg = "Tessellating "; + if(mesh->name == "") + msg += string_printf("%u/%u", (uint)(i+1), (uint)total_tess_needed); + else + msg += string_printf("%s %u/%u", mesh->name.c_str(), (uint)(i+1), (uint)total_tess_needed); + + progress.set_status("Updating Mesh", msg); + + DiagSplit dsplit(*mesh->subd_params); + mesh->tessellate(&dsplit); + + i++; + + if(progress.get_cancel()) return; + } + } + /* Update images needed for true displacement. */ bool true_displacement_used = false; bool old_need_object_flags_update = false; foreach(Mesh *mesh, scene->meshes) { if(mesh->need_update && - mesh->displacement_method != Mesh::DISPLACE_BUMP) + mesh->has_true_displacement()) { true_displacement_used = true; break; @@ -1652,6 +1729,10 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen } if(progress.get_cancel()) return; + /* after mesh data has been copied to device memory we need to update + * offsets for patch tables as this can't be known before hand */ + scene->object_manager->device_update_patch_map_offsets(device, dscene, scene); + device_update_attributes(device, dscene, scene, progress); if(progress.get_cancel()) return; @@ -1677,7 +1758,7 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen } /* Update bvh. */ - size_t i = 0, num_bvh = 0; + size_t num_bvh = 0; foreach(Mesh *mesh, scene->meshes) { if(mesh->need_update && mesh->need_build_bvh()) { num_bvh++; @@ -1686,6 +1767,7 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen TaskPool pool; + i = 0; foreach(Mesh *mesh, scene->meshes) { if(mesh->need_update) { pool.push(function_bind(&Mesh::compute_bvh, diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index c9ae9aab888..a77e296ea4a 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -39,7 +39,9 @@ class Progress; class Scene; class SceneParams; class AttributeRequest; +struct SubdParams; class DiagSplit; +struct PackedPatchTable; /* Mesh */ @@ -110,13 +112,9 @@ public: int num_ptex_faces() const { return num_corners == 4 ? 1 : num_corners; } }; - /* Displacement */ - enum DisplacementMethod { - DISPLACE_BUMP = 0, - DISPLACE_TRUE = 1, - DISPLACE_BOTH = 2, - - DISPLACE_NUM_METHODS, + struct SubdEdgeCrease { + int v[2]; + float crease; }; enum SubdivisionType { @@ -157,6 +155,10 @@ public: array<int> subd_face_corners; int num_ngons; + array<SubdEdgeCrease> subd_creases; + + SubdParams *subd_params; + vector<Shader*> used_shaders; AttributeSet attributes; AttributeSet curve_attributes; @@ -166,7 +168,8 @@ public: bool transform_applied; bool transform_negative_scaled; Transform transform_normal; - DisplacementMethod displacement_method; + + PackedPatchTable *patch_table; uint motion_steps; bool use_motion_blur; @@ -184,6 +187,7 @@ public: size_t curvekey_offset; size_t patch_offset; + size_t patch_table_offset; size_t face_offset; size_t corner_offset; @@ -234,6 +238,7 @@ public: void tag_update(Scene *scene, bool rebuild); bool has_motion_blur() const; + bool has_true_displacement() const; /* Check whether the mesh should have own BVH built separately. Briefly, * own BVH is needed for mesh, if: diff --git a/intern/cycles/render/mesh_displace.cpp b/intern/cycles/render/mesh_displace.cpp index 95f46ff02a2..ef9cfedd412 100644 --- a/intern/cycles/render/mesh_displace.cpp +++ b/intern/cycles/render/mesh_displace.cpp @@ -26,19 +26,27 @@ CCL_NAMESPACE_BEGIN +static float3 compute_face_normal(const Mesh::Triangle& t, float3 *verts) +{ + float3 v0 = verts[t.v[0]]; + float3 v1 = verts[t.v[1]]; + float3 v2 = verts[t.v[2]]; + + float3 norm = cross(v1 - v0, v2 - v0); + float normlen = len(norm); + + if(normlen == 0.0f) + return make_float3(1.0f, 0.0f, 0.0f); + + return norm / normlen; +} + bool MeshManager::displace(Device *device, DeviceScene *dscene, Scene *scene, Mesh *mesh, Progress& progress) { /* verify if we have a displacement shader */ - bool has_displacement = false; - - if(mesh->displacement_method != Mesh::DISPLACE_BUMP) { - foreach(Shader *shader, mesh->used_shaders) - if(shader->has_displacement) - has_displacement = true; - } - - if(!has_displacement) + if(!mesh->has_true_displacement()) { return false; + } string msg = string_printf("Computing Displacement %s", mesh->name.c_str()); progress.set_status("Updating Mesh", msg); @@ -67,8 +75,9 @@ bool MeshManager::displace(Device *device, DeviceScene *dscene, Scene *scene, Me Shader *shader = (shader_index < mesh->used_shaders.size()) ? mesh->used_shaders[shader_index] : scene->default_surface; - if(!shader->has_displacement) + if(!shader->has_displacement || shader->displacement_method == DISPLACE_BUMP) { continue; + } for(int j = 0; j < 3; j++) { if(done[t.v[j]]) @@ -153,8 +162,9 @@ bool MeshManager::displace(Device *device, DeviceScene *dscene, Scene *scene, Me Shader *shader = (shader_index < mesh->used_shaders.size()) ? mesh->used_shaders[shader_index] : scene->default_surface; - if(!shader->has_displacement) + if(!shader->has_displacement || shader->displacement_method == DISPLACE_BUMP) { continue; + } for(int j = 0; j < 3; j++) { if(!done[t.v[j]]) { @@ -178,9 +188,131 @@ bool MeshManager::displace(Device *device, DeviceScene *dscene, Scene *scene, Me mesh->attributes.remove(ATTR_STD_FACE_NORMAL); mesh->add_face_normals(); - if(mesh->displacement_method == Mesh::DISPLACE_TRUE) { - mesh->attributes.remove(ATTR_STD_VERTEX_NORMAL); - mesh->add_vertex_normals(); + bool need_recompute_vertex_normals = false; + + foreach(Shader *shader, mesh->used_shaders) { + if(shader->has_displacement && shader->displacement_method == DISPLACE_TRUE) { + need_recompute_vertex_normals = true; + break; + } + } + + if(need_recompute_vertex_normals) { + bool flip = mesh->transform_negative_scaled; + vector<bool> tri_has_true_disp(num_triangles, false); + + for(size_t i = 0; i < num_triangles; i++) { + int shader_index = mesh->shader[i]; + Shader *shader = (shader_index < mesh->used_shaders.size()) ? + mesh->used_shaders[shader_index] : scene->default_surface; + + tri_has_true_disp[i] = shader->has_displacement && shader->displacement_method == DISPLACE_TRUE; + } + + /* static vertex normals */ + + /* get attributes */ + Attribute *attr_fN = mesh->attributes.find(ATTR_STD_FACE_NORMAL); + Attribute *attr_vN = mesh->attributes.find(ATTR_STD_VERTEX_NORMAL); + + float3 *fN = attr_fN->data_float3(); + float3 *vN = attr_vN->data_float3(); + + /* compute vertex normals */ + + /* zero vertex normals on triangles with true displacement */ + for(size_t i = 0; i < num_triangles; i++) { + if(tri_has_true_disp[i]) { + for(size_t j = 0; j < 3; j++) { + vN[mesh->get_triangle(i).v[j]] = make_float3(0.0f, 0.0f, 0.0f); + } + } + } + + /* add face normals to vertex normals */ + for(size_t i = 0; i < num_triangles; i++) { + if(tri_has_true_disp[i]) { + for(size_t j = 0; j < 3; j++) { + vN[mesh->get_triangle(i).v[j]] += fN[i]; + } + } + } + + /* normalize vertex normals */ + done.clear(); + done.resize(num_verts, false); + + for(size_t i = 0; i < num_triangles; i++) { + if(tri_has_true_disp[i]) { + for(size_t j = 0; j < 3; j++) { + int vert = mesh->get_triangle(i).v[j]; + + if(done[vert]) { + continue; + } + + vN[vert] = normalize(vN[vert]); + if(flip) + vN[vert] = -vN[vert]; + + done[vert] = true; + } + } + } + + /* motion vertex normals */ + Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + Attribute *attr_mN = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_NORMAL); + + if(mesh->has_motion_blur() && attr_mP && attr_mN) { + for(int step = 0; step < mesh->motion_steps - 1; step++) { + float3 *mP = attr_mP->data_float3() + step*mesh->verts.size(); + float3 *mN = attr_mN->data_float3() + step*mesh->verts.size(); + + /* compute */ + + /* zero vertex normals on triangles with true displacement */ + for(size_t i = 0; i < num_triangles; i++) { + if(tri_has_true_disp[i]) { + for(size_t j = 0; j < 3; j++) { + mN[mesh->get_triangle(i).v[j]] = make_float3(0.0f, 0.0f, 0.0f); + } + } + } + + /* add face normals to vertex normals */ + for(size_t i = 0; i < num_triangles; i++) { + if(tri_has_true_disp[i]) { + for(size_t j = 0; j < 3; j++) { + float3 fN = compute_face_normal(mesh->get_triangle(i), mP); + mN[mesh->get_triangle(i).v[j]] += fN; + } + } + } + + /* normalize vertex normals */ + done.clear(); + done.resize(num_verts, false); + + for(size_t i = 0; i < num_triangles; i++) { + if(tri_has_true_disp[i]) { + for(size_t j = 0; j < 3; j++) { + int vert = mesh->get_triangle(i).v[j]; + + if(done[vert]) { + continue; + } + + mN[vert] = normalize(mN[vert]); + if(flip) + mN[vert] = -mN[vert]; + + done[vert] = true; + } + } + } + } + } } return true; diff --git a/intern/cycles/render/mesh_subdivision.cpp b/intern/cycles/render/mesh_subdivision.cpp index fe8e41e8d35..efb40efbb79 100644 --- a/intern/cycles/render/mesh_subdivision.cpp +++ b/intern/cycles/render/mesh_subdivision.cpp @@ -19,13 +19,302 @@ #include "subd_split.h" #include "subd_patch.h" +#include "subd_patch_table.h" #include "util_foreach.h" CCL_NAMESPACE_BEGIN +#ifdef WITH_OPENSUBDIV + +CCL_NAMESPACE_END + +#include <opensubdiv/far/topologyRefinerFactory.h> +#include <opensubdiv/far/primvarRefiner.h> +#include <opensubdiv/far/patchTableFactory.h> +#include <opensubdiv/far/patchMap.h> + +/* specializations of TopologyRefinerFactory for ccl::Mesh */ + +namespace OpenSubdiv { +namespace OPENSUBDIV_VERSION { +namespace Far { + template<> + bool TopologyRefinerFactory<ccl::Mesh>::resizeComponentTopology(TopologyRefiner& refiner, ccl::Mesh const& mesh) + { + setNumBaseVertices(refiner, mesh.verts.size()); + setNumBaseFaces(refiner, mesh.subd_faces.size()); + + ccl::Mesh::SubdFace* face = &mesh.subd_faces[0]; + + for(int i = 0; i < mesh.subd_faces.size(); i++, face++) { + setNumBaseFaceVertices(refiner, i, face->num_corners); + } + + return true; + } + + template<> + bool TopologyRefinerFactory<ccl::Mesh>::assignComponentTopology(TopologyRefiner& refiner, ccl::Mesh const& mesh) + { + ccl::Mesh::SubdFace* face = &mesh.subd_faces[0]; + + for(int i = 0; i < mesh.subd_faces.size(); i++, face++) { + IndexArray face_verts = getBaseFaceVertices(refiner, i); + + int* corner = &mesh.subd_face_corners[face->start_corner]; + + for(int j = 0; j < face->num_corners; j++, corner++) { + face_verts[j] = *corner; + } + } + + return true; + } + + template<> + bool TopologyRefinerFactory<ccl::Mesh>::assignComponentTags(TopologyRefiner& refiner, ccl::Mesh const& mesh) + { + const ccl::Mesh::SubdEdgeCrease* crease = mesh.subd_creases.data(); + + for(int i = 0; i < mesh.subd_creases.size(); i++, crease++) { + Index edge = findBaseEdge(refiner, crease->v[0], crease->v[1]); + + if(edge != INDEX_INVALID) { + setBaseEdgeSharpness(refiner, edge, crease->crease * 10.0f); + } + } + + for(int i = 0; i < mesh.verts.size(); i++) { + ConstIndexArray vert_edges = getBaseVertexEdges(refiner, i); + + if(vert_edges.size() == 2) { + float sharpness = refiner.getLevel(0).getEdgeSharpness(vert_edges[0]); + sharpness = std::min(sharpness, refiner.getLevel(0).getEdgeSharpness(vert_edges[1])); + + setBaseVertexSharpness(refiner, i, sharpness); + } + } + + return true; + } + + template<> + bool TopologyRefinerFactory<ccl::Mesh>::assignFaceVaryingTopology(TopologyRefiner& /*refiner*/, ccl::Mesh const& /*mesh*/) + { + return true; + } + + template<> + void TopologyRefinerFactory<ccl::Mesh>::reportInvalidTopology(TopologyError /*err_code*/, + char const */*msg*/, ccl::Mesh const& /*mesh*/) + { + } +} /* namespace Far */ +} /* namespace OPENSUBDIV_VERSION */ +} /* namespace OpenSubdiv */ + +CCL_NAMESPACE_BEGIN + +using namespace OpenSubdiv; + +/* struct that implements OpenSubdiv's vertex interface */ + +template<typename T> +struct OsdValue { + T value; + + OsdValue() {} + + void Clear(void* = 0) { + memset(&value, 0, sizeof(T)); + } + + void AddWithWeight(OsdValue<T> const& src, float weight) { + value += src.value * weight; + } +}; + +template<> +void OsdValue<uchar4>::AddWithWeight(OsdValue<uchar4> const& src, float weight) +{ + for(int i = 0; i < 4; i++) { + value[i] += (uchar)(src.value[i] * weight); + } +} + +/* class for holding OpenSubdiv data used during tessellation */ + +class OsdData { + Mesh* mesh; + vector<OsdValue<float3> > verts; + Far::TopologyRefiner* refiner; + Far::PatchTable* patch_table; + Far::PatchMap* patch_map; + +public: + OsdData() : mesh(NULL), refiner(NULL), patch_table(NULL), patch_map(NULL) {} + + ~OsdData() + { + delete refiner; + delete patch_table; + delete patch_map; + } + + void build_from_mesh(Mesh* mesh_) + { + mesh = mesh_; + + /* type and options */ + Sdc::SchemeType type = Sdc::SCHEME_CATMARK; + + Sdc::Options options; + options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_ONLY); + + /* create refiner */ + refiner = Far::TopologyRefinerFactory<Mesh>::Create(*mesh, + Far::TopologyRefinerFactory<Mesh>::Options(type, options)); + + /* adaptive refinement */ + int max_isolation = 10; + refiner->RefineAdaptive(Far::TopologyRefiner::AdaptiveOptions(max_isolation)); + + /* create patch table */ + Far::PatchTableFactory::Options patch_options; + patch_options.endCapType = Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS; + + patch_table = Far::PatchTableFactory::Create(*refiner, patch_options); + + /* interpolate verts */ + int num_refiner_verts = refiner->GetNumVerticesTotal(); + int num_local_points = patch_table->GetNumLocalPoints(); + + verts.resize(num_refiner_verts + num_local_points); + for(int i = 0; i < mesh->verts.size(); i++) { + verts[i].value = mesh->verts[i]; + } + + OsdValue<float3>* src = &verts[0]; + for(int i = 0; i < refiner->GetMaxLevel(); i++) { + OsdValue<float3>* dest = src + refiner->GetLevel(i).GetNumVertices(); + Far::PrimvarRefiner(*refiner).Interpolate(i+1, src, dest); + src = dest; + } + + patch_table->ComputeLocalPointValues(&verts[0], &verts[num_refiner_verts]); + + /* create patch map */ + patch_map = new Far::PatchMap(*patch_table); + } + + void subdivide_attribute(Attribute& attr) + { + Far::PrimvarRefiner primvar_refiner(*refiner); + + if(attr.element == ATTR_ELEMENT_VERTEX) { + int num_refiner_verts = refiner->GetNumVerticesTotal(); + int num_local_points = patch_table->GetNumLocalPoints(); + + attr.resize(num_refiner_verts + num_local_points); + attr.flags |= ATTR_FINAL_SIZE; + + char* src = &attr.buffer[0]; + + for(int i = 0; i < refiner->GetMaxLevel(); i++) { + char* dest = src + refiner->GetLevel(i).GetNumVertices() * attr.data_sizeof(); + + if(attr.same_storage(attr.type, TypeDesc::TypeFloat)) { + primvar_refiner.Interpolate(i+1, (OsdValue<float>*)src, (OsdValue<float>*&)dest); + } + else { + primvar_refiner.Interpolate(i+1, (OsdValue<float4>*)src, (OsdValue<float4>*&)dest); + } + + src = dest; + } + + if(attr.same_storage(attr.type, TypeDesc::TypeFloat)) { + patch_table->ComputeLocalPointValues((OsdValue<float>*)&attr.buffer[0], + (OsdValue<float>*)&attr.buffer[num_refiner_verts * attr.data_sizeof()]); + } + else { + patch_table->ComputeLocalPointValues((OsdValue<float4>*)&attr.buffer[0], + (OsdValue<float4>*)&attr.buffer[num_refiner_verts * attr.data_sizeof()]); + } + } + else if(attr.element == ATTR_ELEMENT_CORNER || attr.element == ATTR_ELEMENT_CORNER_BYTE) { + // TODO(mai): fvar interpolation + } + } + + friend struct OsdPatch; + friend class Mesh; +}; + +/* ccl::Patch implementation that uses OpenSubdiv for eval */ + +struct OsdPatch : Patch { + OsdData* osd_data; + + OsdPatch(OsdData* data) : osd_data(data) {} + + void eval(float3 *P, float3 *dPdu, float3 *dPdv, float3 *N, float u, float v) + { + const Far::PatchTable::PatchHandle* handle = osd_data->patch_map->FindPatch(patch_index, u, v); + assert(handle); + + float p_weights[20], du_weights[20], dv_weights[20]; + osd_data->patch_table->EvaluateBasis(*handle, u, v, p_weights, du_weights, dv_weights); + + Far::ConstIndexArray cv = osd_data->patch_table->GetPatchVertices(*handle); + + float3 du, dv; + if(P) *P = make_float3(0.0f, 0.0f, 0.0f); + du = make_float3(0.0f, 0.0f, 0.0f); + dv = make_float3(0.0f, 0.0f, 0.0f); + + for(int i = 0; i < cv.size(); i++) { + float3 p = osd_data->verts[cv[i]].value; + + if(P) *P += p * p_weights[i]; + du += p * du_weights[i]; + dv += p * dv_weights[i]; + } + + if(dPdu) *dPdu = du; + if(dPdv) *dPdv = dv; + if(N) *N = normalize(cross(du, dv)); + } + + BoundBox bound() { return BoundBox::empty; } +}; + +#endif + void Mesh::tessellate(DiagSplit *split) { +#ifdef WITH_OPENSUBDIV + OsdData osd_data; + bool need_packed_patch_table = false; + + if(subdivision_type == SUBDIVISION_CATMULL_CLARK) { + osd_data.build_from_mesh(this); + } + else +#endif + { + /* force linear subdivision if OpenSubdiv is unavailable to avoid + * falling into catmull-clark code paths by accident + */ + subdivision_type = SUBDIVISION_LINEAR; + + /* force disable attribute subdivision for same reason as above */ + foreach(Attribute& attr, subd_attributes.attributes) { + attr.flags &= ~ATTR_SUBDIVIDED; + } + } + int num_faces = subd_faces.size(); Attribute *attr_vN = subd_attributes.find(ATTR_STD_VERTEX_NORMAL); @@ -36,113 +325,158 @@ void Mesh::tessellate(DiagSplit *split) if(face.is_quad()) { /* quad */ - LinearQuadPatch patch; - float3 *hull = patch.hull; - float3 *normals = patch.normals; + QuadDice::SubPatch subpatch; - patch.patch_index = face.ptex_offset; - patch.shader = face.shader; + LinearQuadPatch quad_patch; +#ifdef WITH_OPENSUBDIV + OsdPatch osd_patch(&osd_data); - for(int i = 0; i < 4; i++) { - hull[i] = verts[subd_face_corners[face.start_corner+i]]; + if(subdivision_type == SUBDIVISION_CATMULL_CLARK) { + osd_patch.patch_index = face.ptex_offset; + + subpatch.patch = &osd_patch; } + else +#endif + { + float3 *hull = quad_patch.hull; + float3 *normals = quad_patch.normals; + + quad_patch.patch_index = face.ptex_offset; - if(face.smooth) { for(int i = 0; i < 4; i++) { - normals[i] = vN[subd_face_corners[face.start_corner+i]]; + hull[i] = verts[subd_face_corners[face.start_corner+i]]; } - } - else { - float3 N = face.normal(this); - for(int i = 0; i < 4; i++) { - normals[i] = N; + + if(face.smooth) { + for(int i = 0; i < 4; i++) { + normals[i] = vN[subd_face_corners[face.start_corner+i]]; + } + } + else { + float3 N = face.normal(this); + for(int i = 0; i < 4; i++) { + normals[i] = N; + } } + + swap(hull[2], hull[3]); + swap(normals[2], normals[3]); + + subpatch.patch = &quad_patch; } - swap(hull[2], hull[3]); - swap(normals[2], normals[3]); + subpatch.patch->shader = face.shader; /* Quad faces need to be split at least once to line up with split ngons, we do this * here in this manner because if we do it later edge factors may end up slightly off. */ - QuadDice::SubPatch subpatch; - subpatch.patch = &patch; - subpatch.P00 = make_float2(0.0f, 0.0f); subpatch.P10 = make_float2(0.5f, 0.0f); subpatch.P01 = make_float2(0.0f, 0.5f); subpatch.P11 = make_float2(0.5f, 0.5f); - split->split_quad(&patch, &subpatch); + split->split_quad(subpatch.patch, &subpatch); subpatch.P00 = make_float2(0.5f, 0.0f); subpatch.P10 = make_float2(1.0f, 0.0f); subpatch.P01 = make_float2(0.5f, 0.5f); subpatch.P11 = make_float2(1.0f, 0.5f); - split->split_quad(&patch, &subpatch); + split->split_quad(subpatch.patch, &subpatch); subpatch.P00 = make_float2(0.0f, 0.5f); subpatch.P10 = make_float2(0.5f, 0.5f); subpatch.P01 = make_float2(0.0f, 1.0f); subpatch.P11 = make_float2(0.5f, 1.0f); - split->split_quad(&patch, &subpatch); + split->split_quad(subpatch.patch, &subpatch); subpatch.P00 = make_float2(0.5f, 0.5f); subpatch.P10 = make_float2(1.0f, 0.5f); subpatch.P01 = make_float2(0.5f, 1.0f); subpatch.P11 = make_float2(1.0f, 1.0f); - split->split_quad(&patch, &subpatch); + split->split_quad(subpatch.patch, &subpatch); } else { /* ngon */ - float3 center_vert = make_float3(0.0f, 0.0f, 0.0f); - float3 center_normal = make_float3(0.0f, 0.0f, 0.0f); +#ifdef WITH_OPENSUBDIV + if(subdivision_type == SUBDIVISION_CATMULL_CLARK) { + OsdPatch patch(&osd_data); + + patch.shader = face.shader; - float inv_num_corners = 1.0f/float(face.num_corners); - for(int corner = 0; corner < face.num_corners; corner++) { - center_vert += verts[subd_face_corners[face.start_corner + corner]] * inv_num_corners; - center_normal += vN[subd_face_corners[face.start_corner + corner]] * inv_num_corners; + for(int corner = 0; corner < face.num_corners; corner++) { + patch.patch_index = face.ptex_offset + corner; + + split->split_quad(&patch); + } } + else +#endif + { + float3 center_vert = make_float3(0.0f, 0.0f, 0.0f); + float3 center_normal = make_float3(0.0f, 0.0f, 0.0f); + + float inv_num_corners = 1.0f/float(face.num_corners); + for(int corner = 0; corner < face.num_corners; corner++) { + center_vert += verts[subd_face_corners[face.start_corner + corner]] * inv_num_corners; + center_normal += vN[subd_face_corners[face.start_corner + corner]] * inv_num_corners; + } - for(int corner = 0; corner < face.num_corners; corner++) { - LinearQuadPatch patch; - float3 *hull = patch.hull; - float3 *normals = patch.normals; + for(int corner = 0; corner < face.num_corners; corner++) { + LinearQuadPatch patch; + float3 *hull = patch.hull; + float3 *normals = patch.normals; - patch.patch_index = face.ptex_offset + corner; + patch.patch_index = face.ptex_offset + corner; - patch.shader = face.shader; + patch.shader = face.shader; - hull[0] = verts[subd_face_corners[face.start_corner + mod(corner + 0, face.num_corners)]]; - hull[1] = verts[subd_face_corners[face.start_corner + mod(corner + 1, face.num_corners)]]; - hull[2] = verts[subd_face_corners[face.start_corner + mod(corner - 1, face.num_corners)]]; - hull[3] = center_vert; + hull[0] = verts[subd_face_corners[face.start_corner + mod(corner + 0, face.num_corners)]]; + hull[1] = verts[subd_face_corners[face.start_corner + mod(corner + 1, face.num_corners)]]; + hull[2] = verts[subd_face_corners[face.start_corner + mod(corner - 1, face.num_corners)]]; + hull[3] = center_vert; - hull[1] = (hull[1] + hull[0]) * 0.5; - hull[2] = (hull[2] + hull[0]) * 0.5; + hull[1] = (hull[1] + hull[0]) * 0.5; + hull[2] = (hull[2] + hull[0]) * 0.5; - if(face.smooth) { - normals[0] = vN[subd_face_corners[face.start_corner + mod(corner + 0, face.num_corners)]]; - normals[1] = vN[subd_face_corners[face.start_corner + mod(corner + 1, face.num_corners)]]; - normals[2] = vN[subd_face_corners[face.start_corner + mod(corner - 1, face.num_corners)]]; - normals[3] = center_normal; + if(face.smooth) { + normals[0] = vN[subd_face_corners[face.start_corner + mod(corner + 0, face.num_corners)]]; + normals[1] = vN[subd_face_corners[face.start_corner + mod(corner + 1, face.num_corners)]]; + normals[2] = vN[subd_face_corners[face.start_corner + mod(corner - 1, face.num_corners)]]; + normals[3] = center_normal; - normals[1] = (normals[1] + normals[0]) * 0.5; - normals[2] = (normals[2] + normals[0]) * 0.5; - } - else { - float3 N = face.normal(this); - for(int i = 0; i < 4; i++) { - normals[i] = N; + normals[1] = (normals[1] + normals[0]) * 0.5; + normals[2] = (normals[2] + normals[0]) * 0.5; + } + else { + float3 N = face.normal(this); + for(int i = 0; i < 4; i++) { + normals[i] = N; + } } - } - split->split_quad(&patch); + split->split_quad(&patch); + } } } } /* interpolate center points for attributes */ foreach(Attribute& attr, subd_attributes.attributes) { +#ifdef WITH_OPENSUBDIV + if(subdivision_type == SUBDIVISION_CATMULL_CLARK && attr.flags & ATTR_SUBDIVIDED) { + if(attr.element == ATTR_ELEMENT_CORNER || attr.element == ATTR_ELEMENT_CORNER_BYTE) { + /* keep subdivision for corner attributes disabled for now */ + attr.flags &= ~ATTR_SUBDIVIDED; + } + else { + osd_data.subdivide_attribute(attr); + + need_packed_patch_table = true; + continue; + } + } +#endif + char* data = attr.data(); size_t stride = attr.data_sizeof(); int ngons = 0; @@ -218,6 +552,15 @@ void Mesh::tessellate(DiagSplit *split) default: break; } } + +#ifdef WITH_OPENSUBDIV + /* pack patch tables */ + if(need_packed_patch_table) { + delete patch_table; + patch_table = new PackedPatchTable; + patch_table->pack(osd_data.patch_table); + } +#endif } CCL_NAMESPACE_END diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index 662d87e8b6b..62076f3a865 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -29,6 +29,8 @@ #include "util_progress.h" #include "util_vector.h" +#include "subd_patch_table.h" + CCL_NAMESPACE_BEGIN /* Object */ @@ -55,9 +57,9 @@ Object::Object() particle_system = NULL; particle_index = 0; bounds = BoundBox::empty; - motion.pre = transform_identity(); - motion.mid = transform_identity(); - motion.post = transform_identity(); + motion.pre = transform_empty(); + motion.mid = transform_empty(); + motion.post = transform_empty(); use_motion = false; } @@ -70,19 +72,28 @@ void Object::compute_bounds(bool motion_blur) BoundBox mbounds = mesh->bounds; if(motion_blur && use_motion) { - DecompMotionTransform decomp; - transform_motion_decompose(&decomp, &motion, &tfm); + if(motion.pre == transform_empty() || + motion.post == transform_empty()) { + /* Hide objects that have no valid previous or next transform, for + * example particle that stop existing. TODO: add support for this + * case in the kernel so we don't get render artifacts. */ + bounds = BoundBox::empty; + } + else { + DecompMotionTransform decomp; + transform_motion_decompose(&decomp, &motion, &tfm); - bounds = BoundBox::empty; + bounds = BoundBox::empty; - /* todo: this is really terrible. according to pbrt there is a better - * way to find this iteratively, but did not find implementation yet - * or try to implement myself */ - for(float t = 0.0f; t < 1.0f; t += (1.0f/128.0f)) { - Transform ttfm; + /* todo: this is really terrible. according to pbrt there is a better + * way to find this iteratively, but did not find implementation yet + * or try to implement myself */ + for(float t = 0.0f; t < 1.0f; t += (1.0f/128.0f)) { + Transform ttfm; - transform_motion_interpolate(&ttfm, &decomp, t); - bounds.grow(mbounds.transformed(&ttfm)); + transform_motion_interpolate(&ttfm, &decomp, t); + bounds.grow(mbounds.transformed(&ttfm)); + } } } else { @@ -228,7 +239,7 @@ vector<float> Object::motion_times() bool Object::is_traceable() { /* Mesh itself can be empty,can skip all such objects. */ - if (bounds.size() == make_float3(0.0f, 0.0f, 0.0f)) { + if (!bounds.valid() || bounds.size() == make_float3(0.0f, 0.0f, 0.0f)) { return false; } /* TODO(sergey): Check for mesh vertices/curves. visibility flags. */ @@ -337,6 +348,15 @@ void ObjectManager::device_update_object_transform(UpdateObejctTransformState *s Transform mtfm_pre = ob->motion.pre; Transform mtfm_post = ob->motion.post; + /* In case of missing motion information for previous/next frame, + * assume there is no motion. */ + if(!ob->use_motion || mtfm_pre == transform_empty()) { + mtfm_pre = ob->tfm; + } + if(!ob->use_motion || mtfm_post == transform_empty()) { + mtfm_post = ob->tfm; + } + if(!mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION)) { mtfm_pre = mtfm_pre * itfm; mtfm_post = mtfm_post * itfm; @@ -589,6 +609,40 @@ void ObjectManager::device_update_flags(Device *device, device->tex_alloc("__object_flag", dscene->object_flag); } +void ObjectManager::device_update_patch_map_offsets(Device *device, DeviceScene *dscene, Scene *scene) +{ + if (scene->objects.size() == 0) + return; + + uint4* objects = (uint4*)dscene->objects.get_data(); + + bool update = false; + + int object_index = 0; + foreach(Object *object, scene->objects) { + int offset = object_index*OBJECT_SIZE + 11; + + Mesh* mesh = object->mesh; + + if(mesh->patch_table) { + uint patch_map_offset = 2*(mesh->patch_table_offset + mesh->patch_table->total_size() - + mesh->patch_table->num_nodes * PATCH_NODE_SIZE) - mesh->patch_offset; + + if(objects[offset].x != patch_map_offset) { + objects[offset].x = patch_map_offset; + update = true; + } + } + + object_index++; + } + + if(update) { + device->tex_free(dscene->objects); + device->tex_alloc("__objects", dscene->objects); + } +} + void ObjectManager::device_free(Device *device, DeviceScene *dscene) { device->tex_free(dscene->objects); @@ -638,7 +692,7 @@ void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, u * Could be solved by moving reference counter to Mesh. */ if((mesh_users[object->mesh] == 1 && !object->mesh->has_surface_bssrdf) && - object->mesh->displacement_method == Mesh::DISPLACE_BUMP) + !object->mesh->has_true_displacement()) { if(!(motion_blur && object->use_motion)) { if(!object->mesh->transform_applied) { diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h index 7ab73f3c91a..2e5837f672f 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/render/object.h @@ -97,6 +97,8 @@ public: Scene *scene, Progress& progress, bool bounds_valid = true); + void device_update_patch_map_offsets(Device *device, DeviceScene *dscene, Scene *scene); + void device_free(Device *device, DeviceScene *dscene); void tag_update(Scene *scene); diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index 676afad997e..1a6ae5f9277 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -549,7 +549,7 @@ string OSLCompiler::id(ShaderNode *node) { /* assign layer unique name based on pointer address + bump mode */ stringstream stream; - stream << "node_" << node->name << "_" << node; + stream << "node_" << node->type->name << "_" << node; return stream.str(); } diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 9e72f197cce..8fec171b6fb 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -123,6 +123,8 @@ public: /* opencl images */ device_vector<uchar4> tex_image_byte4_packed; device_vector<float4> tex_image_float4_packed; + device_vector<uchar> tex_image_byte_packed; + device_vector<float> tex_image_float_packed; device_vector<uint4> tex_image_packed_info; KernelData data; diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 1cd76ff2b39..9d8c9fed7af 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -635,6 +635,11 @@ DeviceRequestedFeatures Session::get_requested_device_features() } requested_features.use_object_motion |= object->use_motion | mesh->use_motion_blur; requested_features.use_camera_motion |= mesh->use_motion_blur; +#ifdef WITH_OPENSUBDIV + if(mesh->subdivision_type != Mesh::SUBDIVISION_NONE) { + requested_features.use_patch_evaluation = true; + } +#endif } BakeManager *bake_manager = scene->bake_manager; diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index 4cdb878df45..d000cca5a45 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -150,6 +150,12 @@ NODE_DEFINE(Shader) volume_interpolation_method_enum.insert("cubic", VOLUME_INTERPOLATION_CUBIC); SOCKET_ENUM(volume_interpolation_method, "Volume Interpolation Method", volume_interpolation_method_enum, VOLUME_INTERPOLATION_LINEAR); + static NodeEnum displacement_method_enum; + displacement_method_enum.insert("bump", DISPLACE_BUMP); + displacement_method_enum.insert("true", DISPLACE_TRUE); + displacement_method_enum.insert("both", DISPLACE_BOTH); + SOCKET_ENUM(displacement_method, "Displacement Method", displacement_method_enum, DISPLACE_BUMP); + return type; } @@ -173,6 +179,8 @@ Shader::Shader() has_object_dependency = false; has_integrator_dependency = false; + displacement_method = DISPLACE_BUMP; + id = -1; used = false; @@ -310,7 +318,7 @@ int ShaderManager::get_shader_id(Shader *shader, Mesh *mesh, bool smooth) int id = shader->id*2; /* index depends bump since this setting is not in the shader */ - if(mesh && mesh->displacement_method != Mesh::DISPLACE_TRUE) + if(mesh && shader->displacement_method != DISPLACE_TRUE) id += 1; /* smooth flag */ if(smooth) diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h index dc57ed4e4eb..060ad7056bc 100644 --- a/intern/cycles/render/shader.h +++ b/intern/cycles/render/shader.h @@ -66,6 +66,14 @@ enum VolumeInterpolation { VOLUME_NUM_INTERPOLATION, }; +enum DisplacementMethod { + DISPLACE_BUMP = 0, + DISPLACE_TRUE = 1, + DISPLACE_BOTH = 2, + + DISPLACE_NUM_METHODS, +}; + /* Shader describing the appearance of a Mesh, Light or Background. * * While there is only a single shader graph, it has three outputs: surface, @@ -110,6 +118,9 @@ public: bool has_object_dependency; bool has_integrator_dependency; + /* displacement */ + DisplacementMethod displacement_method; + /* requested mesh attributes */ AttributeRequestSet attributes; diff --git a/intern/cycles/subd/CMakeLists.txt b/intern/cycles/subd/CMakeLists.txt index db497013693..dafb807bdf3 100644 --- a/intern/cycles/subd/CMakeLists.txt +++ b/intern/cycles/subd/CMakeLists.txt @@ -16,18 +16,16 @@ set(SRC subd_dice.cpp subd_patch.cpp subd_split.cpp + subd_patch_table.cpp ) set(SRC_HEADERS subd_dice.h subd_patch.h + subd_patch_table.h subd_split.h ) -if(WITH_CYCLES_OPENSUBDIV) - add_definitions(-DWITH_OPENSUBDIV) -endif() - include_directories(${INC}) include_directories(SYSTEM ${INC_SYS}) diff --git a/intern/cycles/subd/subd_patch_table.cpp b/intern/cycles/subd/subd_patch_table.cpp new file mode 100644 index 00000000000..68ec1b2c6a6 --- /dev/null +++ b/intern/cycles/subd/subd_patch_table.cpp @@ -0,0 +1,297 @@ +/* + * Based on code from OpenSubdiv released under this license: + * + * Copyright 2014 DreamWorks Animation LLC. + * + * Licensed under the Apache License, Version 2.0 (the "Apache License") + * with the following modification; you may not use this file except in + * compliance with the Apache License and the following modification to it: + * Section 6. Trademarks. is deleted and replaced with: + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor + * and its affiliates, except as required to comply with Section 4(c) of + * the License and to reproduce the content of the NOTICE file. + * + * You may obtain a copy of the Apache License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Apache License with the above modification is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the Apache License for the specific + * language governing permissions and limitations under the Apache License. + * + */ + +#include "subd_patch_table.h" +#include "kernel_types.h" + +#include "util_math.h" + +#ifdef WITH_OPENSUBDIV +#include <opensubdiv/far/patchTable.h> +#endif + +CCL_NAMESPACE_BEGIN + +#ifdef WITH_OPENSUBDIV + +using namespace OpenSubdiv; + +/* functions for building patch maps */ + +struct PatchMapQuadNode { + /* sets all the children to point to the patch of index */ + void set_child(int index) + { + for (int i = 0; i < 4; i++) { + children[i] = index | PATCH_MAP_NODE_IS_SET | PATCH_MAP_NODE_IS_LEAF; + } + } + + /* sets the child in quadrant to point to the node or patch of the given index */ + void set_child(unsigned char quadrant, int index, bool is_leaf=true) + { + assert(quadrant < 4); + children[quadrant] = index | PATCH_MAP_NODE_IS_SET | (is_leaf ? PATCH_MAP_NODE_IS_LEAF : 0); + } + + uint children[4]; +}; + +template<class T> +static int resolve_quadrant(T& median, T& u, T& v) +{ + int quadrant = -1; + + if(u < median) { + if(v < median) { + quadrant = 0; + } + else { + quadrant = 1; + v -= median; + } + } + else { + if(v < median) { + quadrant = 3; + } + else { + quadrant = 2; + v -= median; + } + u -= median; + } + + return quadrant; +} + +static void build_patch_map(PackedPatchTable& table, OpenSubdiv::Far::PatchTable* patch_table, int offset) +{ + int num_faces = 0; + + for(int array = 0; array < table.num_arrays; array++) { + Far::ConstPatchParamArray params = patch_table->GetPatchParams(array); + + for(int j = 0; j < patch_table->GetNumPatches(array); j++) { + num_faces = max(num_faces, (int)params[j].GetFaceId()); + } + } + num_faces++; + + vector<PatchMapQuadNode> quadtree; + quadtree.reserve(num_faces + table.num_patches); + quadtree.resize(num_faces); + + /* adjust offsets to make indices relative to the table */ + int handle_index = -(table.num_patches * PATCH_HANDLE_SIZE); + offset += table.total_size(); + + /* populate the quadtree from the FarPatchArrays sub-patches */ + for(int array = 0; array < table.num_arrays; array++) { + Far::ConstPatchParamArray params = patch_table->GetPatchParams(array); + + for(int i = 0; i < patch_table->GetNumPatches(array); i++, handle_index += PATCH_HANDLE_SIZE) { + const Far::PatchParam& param = params[i]; + unsigned short depth = param.GetDepth(); + + PatchMapQuadNode* node = &quadtree[params[i].GetFaceId()]; + + if(depth == (param.NonQuadRoot() ? 1 : 0)) { + /* special case : regular BSpline face w/ no sub-patches */ + node->set_child(handle_index + offset); + continue; + } + + int u = param.GetU(); + int v = param.GetV(); + int pdepth = param.NonQuadRoot() ? depth-2 : depth-1; + int half = 1 << pdepth; + + for(int j = 0; j < depth; j++) { + int delta = half >> 1; + + int quadrant = resolve_quadrant(half, u, v); + assert(quadrant >= 0); + + half = delta; + + if(j == pdepth) { + /* we have reached the depth of the sub-patch : add a leaf */ + assert(!(node->children[quadrant] & PATCH_MAP_NODE_IS_SET)); + node->set_child(quadrant, handle_index + offset, true); + break; + } + else { + /* travel down the child node of the corresponding quadrant */ + if(!(node->children[quadrant] & PATCH_MAP_NODE_IS_SET)) { + /* create a new branch in the quadrant */ + quadtree.push_back(PatchMapQuadNode()); + + int idx = (int)quadtree.size() - 1; + node->set_child(quadrant, idx*4 + offset, false); + + node = &quadtree[idx]; + } + else { + /* travel down an existing branch */ + uint idx = node->children[quadrant] & PATCH_MAP_NODE_INDEX_MASK; + node = &(quadtree[(idx - offset)/4]); + } + } + } + } + } + + /* copy into table */ + assert(table.table.size() == table.total_size()); + uint map_offset = table.total_size(); + + table.num_nodes = quadtree.size() * 4; + table.table.resize(table.total_size()); + + uint* data = &table.table[map_offset]; + + for(int i = 0; i < quadtree.size(); i++) { + for(int j = 0; j < 4; j++) { + assert(quadtree[i].children[j] & PATCH_MAP_NODE_IS_SET); + *(data++) = quadtree[i].children[j]; + } + } +} + +#endif + +/* packed patch table functions */ + +size_t PackedPatchTable::total_size() +{ + return num_arrays * PATCH_ARRAY_SIZE + + num_indices + + num_patches * (PATCH_PARAM_SIZE + PATCH_HANDLE_SIZE) + + num_nodes * PATCH_NODE_SIZE; +} + +void PackedPatchTable::pack(Far::PatchTable* patch_table, int offset) +{ + num_arrays = 0; + num_patches = 0; + num_indices = 0; + num_nodes = 0; + +#ifdef WITH_OPENSUBDIV + num_arrays = patch_table->GetNumPatchArrays(); + + for(int i = 0; i < num_arrays; i++) { + int patches = patch_table->GetNumPatches(i); + int num_control = patch_table->GetPatchArrayDescriptor(i).GetNumControlVertices(); + + num_patches += patches; + num_indices += patches * num_control; + } + + table.resize(total_size()); + uint* data = &table[0]; + + uint* array = data; + uint* index = array + num_arrays * PATCH_ARRAY_SIZE; + uint* param = index + num_indices; + uint* handle = param + num_patches * PATCH_PARAM_SIZE; + + uint current_param = 0; + + for(int i = 0; i < num_arrays; i++) { + *(array++) = patch_table->GetPatchArrayDescriptor(i).GetType(); + *(array++) = patch_table->GetNumPatches(i); + *(array++) = (index - data) + offset; + *(array++) = (param - data) + offset; + + Far::ConstIndexArray indices = patch_table->GetPatchArrayVertices(i); + + for(int j = 0; j < indices.size(); j++) { + *(index++) = indices[j]; + } + + const Far::PatchParamTable& param_table = patch_table->GetPatchParamTable(); + + int num_control = patch_table->GetPatchArrayDescriptor(i).GetNumControlVertices(); + int patches = patch_table->GetNumPatches(i); + + for(int j = 0; j < patches; j++, current_param++) { + *(param++) = param_table[current_param].field0; + *(param++) = param_table[current_param].field1; + + *(handle++) = (array - data) - PATCH_ARRAY_SIZE + offset; + *(handle++) = (param - data) - PATCH_PARAM_SIZE + offset; + *(handle++) = j * num_control; + } + } + + build_patch_map(*this, patch_table, offset); +#else + (void)patch_table; + (void)offset; +#endif +} + +void PackedPatchTable::copy_adjusting_offsets(uint* dest, int doffset) +{ + uint* src = &table[0]; + + /* arrays */ + for(int i = 0; i < num_arrays; i++) { + *(dest++) = *(src++); + *(dest++) = *(src++); + *(dest++) = *(src++) + doffset; + *(dest++) = *(src++) + doffset; + } + + /* indices */ + for(int i = 0; i < num_indices; i++) { + *(dest++) = *(src++); + } + + /* params */ + for(int i = 0; i < num_patches; i++) { + *(dest++) = *(src++); + *(dest++) = *(src++); + } + + /* handles */ + for(int i = 0; i < num_patches; i++) { + *(dest++) = *(src++) + doffset; + *(dest++) = *(src++) + doffset; + *(dest++) = *(src++); + } + + /* nodes */ + for(int i = 0; i < num_nodes; i++) { + *(dest++) = *(src++) + doffset; + } +} + +CCL_NAMESPACE_END + diff --git a/intern/cycles/subd/subd_patch_table.h b/intern/cycles/subd/subd_patch_table.h new file mode 100644 index 00000000000..c8c7ecf9e47 --- /dev/null +++ b/intern/cycles/subd/subd_patch_table.h @@ -0,0 +1,63 @@ +/* + * Copyright 2011-2016 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __SUBD_PATCH_TABLE_H__ +#define __SUBD_PATCH_TABLE_H__ + +#include "util_types.h" +#include "util_vector.h" + +#ifdef WITH_OPENSUBDIV +#ifdef _MSC_VER +# include "iso646.h" +#endif + +#include <opensubdiv/far/patchTable.h> +#endif + +CCL_NAMESPACE_BEGIN + +#ifdef WITH_OPENSUBDIV +using namespace OpenSubdiv; +#else +/* forward declare for when OpenSubdiv is unavailable */ +namespace Far { struct PatchTable; } +#endif + +#define PATCH_ARRAY_SIZE 4 +#define PATCH_PARAM_SIZE 2 +#define PATCH_HANDLE_SIZE 3 +#define PATCH_NODE_SIZE 1 + +struct PackedPatchTable { + vector<uint> table; + + size_t num_arrays; + size_t num_indices; + size_t num_patches; + size_t num_nodes; + + /* calculated size from num_* members */ + size_t total_size(); + + void pack(Far::PatchTable* patch_table, int offset = 0); + void copy_adjusting_offsets(uint* dest, int doffset); +}; + +CCL_NAMESPACE_END + +#endif /* __SUBD_PATCH_TABLE_H__ */ + diff --git a/intern/cycles/test/CMakeLists.txt b/intern/cycles/test/CMakeLists.txt index 80fe893826a..9af777fb9dd 100644 --- a/intern/cycles/test/CMakeLists.txt +++ b/intern/cycles/test/CMakeLists.txt @@ -26,6 +26,7 @@ set(ALL_CYCLES_LIBRARIES cycles_device cycles_bvh cycles_graph + cycles_subd cycles_util ${OPENIMAGEIO_LIBRARIES} ) @@ -41,6 +42,16 @@ if(WITH_IMAGE_OPENJPEG AND NOT WITH_SYSTEM_OPENJPEG) extern_openjpeg ) endif() +if(WITH_CYCLES_OPENSUBDIV) + add_definitions(-DWITH_OPENSUBDIV) + include_directories( + SYSTEM + ${OPENSUBDIV_INCLUDE_DIR} + ) + list(APPEND ALL_CYCLES_LIBRARIES + ${OPENSUBDIV_LIBRARIES} + ) +endif() list(APPEND ALL_CYCLES_LIBRARIES ${BOOST_LIBRARIES} ) diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt index e6140b3ed09..f5674bdc15c 100644 --- a/intern/cycles/util/CMakeLists.txt +++ b/intern/cycles/util/CMakeLists.txt @@ -25,10 +25,6 @@ set(SRC util_windows.cpp ) -if(NOT CYCLES_STANDALONE_REPOSITORY) - add_definitions(-DWITH_GLEW_MX) -endif() - if(WITH_CYCLES_STANDALONE AND WITH_CYCLES_STANDALONE_GUI) list(APPEND SRC util_view.cpp @@ -71,6 +67,7 @@ set(SRC_HEADERS util_ssef.h util_ssei.h util_stack_allocator.h + util_static_assert.h util_stats.h util_string.h util_system.h diff --git a/intern/cycles/util/util_debug.h b/intern/cycles/util/util_debug.h index 1787ff648ee..73fd228b5d9 100644 --- a/intern/cycles/util/util_debug.h +++ b/intern/cycles/util/util_debug.h @@ -20,6 +20,8 @@ #include <cassert> #include <iostream> +#include "util_static_assert.h" + CCL_NAMESPACE_BEGIN /* Global storage for all sort of flags used to fine-tune behavior of particular diff --git a/intern/cycles/util/util_half.h b/intern/cycles/util/util_half.h index ae85ab3a915..5db3384cda4 100644 --- a/intern/cycles/util/util_half.h +++ b/intern/cycles/util/util_half.h @@ -33,17 +33,21 @@ CCL_NAMESPACE_BEGIN #else +/* CUDA has its own half data type, no need to define then */ +#ifndef __KERNEL_CUDA__ typedef unsigned short half; +#endif + struct half4 { half x, y, z, w; }; #ifdef __KERNEL_CUDA__ ccl_device_inline void float4_store_half(half *h, float4 f, float scale) { - h[0] = __float2half_rn(f.x * scale); - h[1] = __float2half_rn(f.y * scale); - h[2] = __float2half_rn(f.z * scale); - h[3] = __float2half_rn(f.w * scale); + h[0] = __float2half(f.x * scale); + h[1] = __float2half(f.y * scale); + h[2] = __float2half(f.z * scale); + h[3] = __float2half(f.w * scale); } #else diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h index 13aba0646d2..89a882d9b9d 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/util_math.h @@ -572,6 +572,12 @@ ccl_device_inline float3 safe_normalize(const float3 a) return (t != 0.0f)? a/t: a; } +ccl_device_inline float3 safe_normalize_len(const float3 a, float *t) +{ + *t = len(a); + return (*t != 0.0f)? a/(*t): a; +} + #ifndef __KERNEL_OPENCL__ ccl_device_inline bool operator==(const float3 a, const float3 b) diff --git a/intern/cycles/util/util_static_assert.h b/intern/cycles/util/util_static_assert.h new file mode 100644 index 00000000000..1b945705145 --- /dev/null +++ b/intern/cycles/util/util_static_assert.h @@ -0,0 +1,64 @@ +/* + * Copyright 2011-2016 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __UTIL_STATIC_ASSERT_H__ +#define __UTIL_STATIC_ASSERT_H__ + +CCL_NAMESPACE_BEGIN + +/* TODO(sergey): In theory CUDA might work with own static assert + * implementation since it's just pure C++. + */ +#ifndef __KERNEL_GPU__ +# if (__cplusplus > 199711L) || (defined(_MSC_VER) && _MSC_VER >= 1800) +/* C++11 has built-in static_assert() */ +# else /* C++11 or MSVC2015 */ +template <bool Test> class StaticAssertFailure; +template <> class StaticAssertFailure<true> {}; +# define _static_assert_private_glue_impl(A, B) A ## B +# define _static_assert_glue(A, B) _static_assert_private_glue_impl(A, B) +# ifdef __COUNTER__ +# define static_assert(condition, message) \ + enum {_static_assert_glue(q_static_assert_result, __COUNTER__) = sizeof(StaticAssertFailure<!!(condition)>)} // NOLINT +# else /* __COUNTER__ */ +# define static_assert(condition, message) \ + enum {_static_assert_glue(q_static_assert_result, __LINE__) = sizeof(StaticAssertFailure<!!(condition)>)} // NOLINT +# endif /* __COUNTER__ */ +# endif /* C++11 or MSVC2015 */ +#else /* __KERNEL_GPU__ */ +# define static_assert(statement, message) +#endif /* __KERNEL_GPU__ */ + +/* TODO(sergey): For until C++11 is a bare minimum for us, + * we do a bit of a trickery to show meaningful message so + * it's more or less clear what's wrong when building without + * C++11. + * + * The thing here is: our non-C++11 implementation doesn't + * have a way to print any message after preprocessor + * substitution so we rely on the message which is passed to + * static_assert() since that's the only message visible when + * compilation fails. + * + * After C++11 bump it should be possible to glue structure + * name to the error message, + */ +# define static_assert_align(st, align) \ + static_assert((sizeof(st) % (align) == 0), "Structure must be strictly aligned") // NOLINT + +CCL_NAMESPACE_END + +#endif /* __UTIL_STATIC_ASSERT_H__ */ diff --git a/intern/cycles/util/util_texture.h b/intern/cycles/util/util_texture.h index 2ef47283029..aff928ea2ee 100644 --- a/intern/cycles/util/util_texture.h +++ b/intern/cycles/util/util_texture.h @@ -24,58 +24,58 @@ CCL_NAMESPACE_BEGIN /* CPU */ #define TEX_NUM_FLOAT4_CPU 1024 #define TEX_NUM_BYTE4_CPU 1024 +#define TEX_NUM_HALF4_CPU 1024 #define TEX_NUM_FLOAT_CPU 1024 #define TEX_NUM_BYTE_CPU 1024 -#define TEX_NUM_HALF4_CPU 1024 #define TEX_NUM_HALF_CPU 1024 #define TEX_START_FLOAT4_CPU 0 #define TEX_START_BYTE4_CPU TEX_NUM_FLOAT4_CPU -#define TEX_START_FLOAT_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU) -#define TEX_START_BYTE_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU + TEX_NUM_FLOAT_CPU) -#define TEX_START_HALF4_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU + TEX_NUM_FLOAT_CPU + TEX_NUM_BYTE_CPU) -#define TEX_START_HALF_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU + TEX_NUM_FLOAT_CPU + TEX_NUM_BYTE_CPU + TEX_NUM_HALF4_CPU) +#define TEX_START_HALF4_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU) +#define TEX_START_FLOAT_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU + TEX_NUM_HALF4_CPU) +#define TEX_START_BYTE_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU + TEX_NUM_HALF4_CPU + TEX_NUM_FLOAT_CPU) +#define TEX_START_HALF_CPU (TEX_NUM_FLOAT4_CPU + TEX_NUM_BYTE4_CPU + TEX_NUM_HALF4_CPU + TEX_NUM_FLOAT_CPU + TEX_NUM_BYTE_CPU) /* CUDA (Geforce 4xx and 5xx) */ #define TEX_NUM_FLOAT4_CUDA 5 -#define TEX_NUM_BYTE4_CUDA 88 +#define TEX_NUM_BYTE4_CUDA 85 +#define TEX_NUM_HALF4_CUDA 0 #define TEX_NUM_FLOAT_CUDA 0 #define TEX_NUM_BYTE_CUDA 0 -#define TEX_NUM_HALF4_CUDA 0 #define TEX_NUM_HALF_CUDA 0 #define TEX_START_FLOAT4_CUDA 0 #define TEX_START_BYTE4_CUDA TEX_NUM_FLOAT4_CUDA -#define TEX_START_FLOAT_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA) -#define TEX_START_BYTE_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA + TEX_NUM_FLOAT_CUDA) -#define TEX_START_HALF4_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA + TEX_NUM_FLOAT_CUDA + TEX_NUM_BYTE_CUDA) -#define TEX_START_HALF_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA + TEX_NUM_FLOAT_CUDA + TEX_NUM_BYTE_CUDA + TEX_NUM_HALF4_CUDA) +#define TEX_START_HALF4_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA) +#define TEX_START_FLOAT_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA + TEX_NUM_HALF4_CUDA) +#define TEX_START_BYTE_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA + TEX_NUM_HALF4_CUDA + TEX_NUM_FLOAT_CUDA) +#define TEX_START_HALF_CUDA (TEX_NUM_FLOAT4_CUDA + TEX_NUM_BYTE4_CUDA + TEX_NUM_HALF4_CUDA + TEX_NUM_FLOAT_CUDA + TEX_NUM_BYTE_CUDA) /* CUDA (Kepler, Geforce 6xx and above) */ #define TEX_NUM_FLOAT4_CUDA_KEPLER 1024 #define TEX_NUM_BYTE4_CUDA_KEPLER 1024 +#define TEX_NUM_HALF4_CUDA_KEPLER 1024 #define TEX_NUM_FLOAT_CUDA_KEPLER 1024 #define TEX_NUM_BYTE_CUDA_KEPLER 1024 -#define TEX_NUM_HALF4_CUDA_KEPLER 0 -#define TEX_NUM_HALF_CUDA_KEPLER 0 +#define TEX_NUM_HALF_CUDA_KEPLER 1024 #define TEX_START_FLOAT4_CUDA_KEPLER 0 #define TEX_START_BYTE4_CUDA_KEPLER TEX_NUM_FLOAT4_CUDA_KEPLER -#define TEX_START_FLOAT_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER) -#define TEX_START_BYTE_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER + TEX_NUM_FLOAT_CUDA_KEPLER) -#define TEX_START_HALF4_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER + TEX_NUM_FLOAT_CUDA_KEPLER + TEX_NUM_BYTE_CUDA_KEPLER) -#define TEX_START_HALF_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER + TEX_NUM_FLOAT_CUDA_KEPLER + TEX_NUM_BYTE_CUDA_KEPLER + TEX_NUM_HALF4_CUDA_KEPLER) +#define TEX_START_HALF4_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER) +#define TEX_START_FLOAT_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER + TEX_NUM_HALF4_CUDA_KEPLER) +#define TEX_START_BYTE_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER + TEX_NUM_HALF4_CUDA_KEPLER + TEX_NUM_FLOAT_CUDA_KEPLER) +#define TEX_START_HALF_CUDA_KEPLER (TEX_NUM_FLOAT4_CUDA_KEPLER + TEX_NUM_BYTE4_CUDA_KEPLER + TEX_NUM_HALF4_CUDA_KEPLER + TEX_NUM_FLOAT_CUDA_KEPLER + TEX_NUM_BYTE_CUDA_KEPLER) /* OpenCL */ #define TEX_NUM_FLOAT4_OPENCL 1024 #define TEX_NUM_BYTE4_OPENCL 1024 -#define TEX_NUM_FLOAT_OPENCL 0 -#define TEX_NUM_BYTE_OPENCL 0 #define TEX_NUM_HALF4_OPENCL 0 +#define TEX_NUM_FLOAT_OPENCL 1024 +#define TEX_NUM_BYTE_OPENCL 1024 #define TEX_NUM_HALF_OPENCL 0 #define TEX_START_FLOAT4_OPENCL 0 #define TEX_START_BYTE4_OPENCL TEX_NUM_FLOAT4_OPENCL -#define TEX_START_FLOAT_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL) -#define TEX_START_BYTE_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL + TEX_NUM_FLOAT_OPENCL) -#define TEX_START_HALF4_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL + TEX_NUM_FLOAT_OPENCL + TEX_NUM_BYTE_OPENCL) -#define TEX_START_HALF_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL + TEX_NUM_FLOAT_OPENCL + TEX_NUM_BYTE_OPENCL + TEX_NUM_HALF4_OPENCL) +#define TEX_START_HALF4_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL) +#define TEX_START_FLOAT_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL + TEX_NUM_HALF4_OPENCL) +#define TEX_START_BYTE_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL + TEX_NUM_HALF4_OPENCL + TEX_NUM_FLOAT_OPENCL) +#define TEX_START_HALF_OPENCL (TEX_NUM_FLOAT4_OPENCL + TEX_NUM_BYTE4_OPENCL + TEX_NUM_HALF4_OPENCL + TEX_NUM_FLOAT_OPENCL + TEX_NUM_BYTE_OPENCL) /* Color to use when textures are not found. */ diff --git a/intern/cycles/util/util_transform.h b/intern/cycles/util/util_transform.h index 6fed18a3db8..bfc8f55feed 100644 --- a/intern/cycles/util/util_transform.h +++ b/intern/cycles/util/util_transform.h @@ -323,6 +323,15 @@ ccl_device_inline Transform transform_clear_scale(const Transform& tfm) return ntfm; } +ccl_device_inline Transform transform_empty() +{ + return make_transform( + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0); +} + #endif /* Motion Transform */ diff --git a/intern/cycles/util/util_vector.h b/intern/cycles/util/util_vector.h index 6f8c3f6f3de..546b17570bb 100644 --- a/intern/cycles/util/util_vector.h +++ b/intern/cycles/util/util_vector.h @@ -222,6 +222,11 @@ public: return datasize_; } + T* data() + { + return data_; + } + const T* data() const { return data_; diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index 6a27d7aadf9..2aa950f8278 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -649,7 +649,7 @@ GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType ty WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, 3, 2, #endif - GHOST_OPENGL_WGL_CONTEXT_FLAGS, + (m_debug_context ? WGL_CONTEXT_DEBUG_BIT_ARB : 0), GHOST_OPENGL_WGL_RESET_NOTIFICATION_STRATEGY); #else # error diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg index 40b974661fa..030953cf614 100644 --- a/release/datafiles/blender_icons.svg +++ b/release/datafiles/blender_icons.svg @@ -30957,6 +30957,93 @@ y1="-16" x2="-93.75" y2="-16.264704" /> + <filter + inkscape:label="Opacity" + style="color-interpolation-filters:sRGB" + id="filter17385"> + <feColorMatrix + values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 5 -1 " + result="colormatrix" + id="feColorMatrix17387" /> + <feComposite + k4="0" + k3="0" + k1="0" + in2="colormatrix" + operator="arithmetic" + k2="0.24" + result="composite" + id="feComposite17389" /> + </filter> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient18134" + id="radialGradient37501-3-6-2" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.08933014,-0.7764284,0.7350832,-0.08334857,57.410559,233.30156)" + cx="135.83771" + cy="117.97826" + fx="135.83771" + fy="117.97826" + r="8" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient18134" + id="radialGradient37501-3-6" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(-0.08933014,-0.7764284,0.7350832,-0.08334857,57.410559,233.30156)" + cx="135.83771" + cy="117.97826" + fx="135.83771" + fy="117.97826" + r="8" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient18134" + id="linearGradient17463" + gradientUnits="userSpaceOnUse" + x1="121.19734" + y1="105.94044" + x2="148.06364" + y2="137.6748" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient16595" + id="linearGradient17281" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(144,188)" + x1="209" + y1="238" + x2="226.625" + y2="251.71078" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient18134" + id="linearGradient31399" + gradientUnits="userSpaceOnUse" + x1="62.793919" + y1="133.73566" + x2="64.109718" + y2="135.18265" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient319" + id="linearGradient31452" + gradientUnits="userSpaceOnUse" + x1="121.19734" + y1="105.94044" + x2="148.06364" + y2="137.6748" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient1610-36-6-5" + id="linearGradient31454" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(144,188)" + x1="209" + y1="238" + x2="226.625" + y2="251.71078" /> </defs> <sodipodi:namedview id="base" @@ -30968,16 +31055,16 @@ objecttolerance="10000" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="40.768576" - inkscape:cx="236.56738" - inkscape:cy="163.14077" + inkscape:zoom="10.192144" + inkscape:cx="406.73801" + inkscape:cy="204.25086" inkscape:document-units="px" inkscape:current-layer="g24024-1" showgrid="true" - inkscape:window-width="1920" - inkscape:window-height="982" - inkscape:window-x="0" - inkscape:window-y="28" + inkscape:window-width="1680" + inkscape:window-height="987" + inkscape:window-x="-8" + inkscape:window-y="-8" inkscape:snap-nodes="false" inkscape:snap-bbox="true" showguides="true" @@ -91590,8 +91677,7 @@ cx="304.0946" cy="242.89087" rx="0.59120387" - ry="0.61330098" - d="m 304.68581,242.89087 c 0,0.33872 -0.26469,0.6133 -0.59121,0.6133 -0.32651,0 -0.5912,-0.27458 -0.5912,-0.6133 0,-0.33872 0.26469,-0.6133 0.5912,-0.6133 0.32652,0 0.59121,0.27458 0.59121,0.6133 z" /> + ry="0.61330098" /> <ellipse sodipodi:ry="0.61330098" sodipodi:rx="0.59120387" @@ -91602,8 +91688,7 @@ cx="315.98523" cy="242.95337" rx="0.59120387" - ry="0.61330098" - d="m 316.57643,242.95337 c 0,0.33872 -0.26469,0.6133 -0.5912,0.6133 -0.32651,0 -0.5912,-0.27458 -0.5912,-0.6133 0,-0.33872 0.26469,-0.6133 0.5912,-0.6133 0.32651,0 0.5912,0.27458 0.5912,0.6133 z" /> + ry="0.61330098" /> </g> <g clip-path="url(#clipPath15455-9)" @@ -91819,8 +91904,7 @@ cy="242.89087" cx="304.0946" id="ellipse15366" - style="opacity:0.51799999;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" - d="m 304.68581,242.89087 c 0,0.33872 -0.26469,0.6133 -0.59121,0.6133 -0.32651,0 -0.5912,-0.27458 -0.5912,-0.6133 0,-0.33872 0.26469,-0.6133 0.5912,-0.6133 0.32652,0 0.59121,0.27458 0.59121,0.6133 z" /> + style="opacity:0.51799999;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> <ellipse sodipodi:ry="0.61330098" sodipodi:rx="0.59120387" @@ -91831,8 +91915,7 @@ cy="242.95337" cx="315.98523" id="ellipse15368" - style="opacity:0.51799999;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" - d="m 316.57643,242.95337 c 0,0.33872 -0.26469,0.6133 -0.5912,0.6133 -0.32651,0 -0.5912,-0.27458 -0.5912,-0.6133 0,-0.33872 0.26469,-0.6133 0.5912,-0.6133 0.32651,0 0.5912,0.27458 0.5912,0.6133 z" /> + style="opacity:0.51799999;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> </g> <g transform="translate(189.00803,-79.37555)" @@ -91942,6 +92025,382 @@ style="color:#000000;fill:url(#radialGradient14216);fill-opacity:1;stroke:none;stroke-width:1.20000005;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:new" /> </g> </g> + <g + style="display:inline;enable-background:new" + id="g17465" + transform="translate(62.844714,-106.93345)"> + <g + transform="matrix(0.7,0,0,0.7,146.7,264.8)" + id="ICON_COLOR-1-9" + style="display:inline;enable-background:new"> + <rect + y="238" + x="341" + height="16" + width="16" + id="rect36341-2-0" + style="display:inline;overflow:visible;visibility:visible;opacity:0;fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.79999995;marker:none;enable-background:accumulate" /> + <g + id="g36343-7-5" + transform="translate(0,-12)"> + <g + transform="matrix(1.1658027,0,0,1.1657997,198.71028,-2.0560643)" + id="g36345-0-4"> + <path + transform="matrix(0.6969448,0,0,0.6969467,36.918512,140.83126)" + sodipodi:type="arc" + style="fill:#ff6600;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path36349-4-2" + sodipodi:cx="132" + sodipodi:cy="118" + sodipodi:rx="8" + sodipodi:ry="8" + d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + sodipodi:start="4.712389" + sodipodi:end="5.7595865" + inkscape:transform-center-x="-2.8145849" + inkscape:transform-center-y="-3.2499984" /> + <path + inkscape:transform-center-y="1.6729808e-005" + inkscape:transform-center-x="-3.2630798" + sodipodi:end="5.7595865" + sodipodi:start="4.712389" + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z" + sodipodi:ry="8" + sodipodi:rx="8" + sodipodi:cy="118" + sodipodi:cx="132" + id="path36351-3-6" + style="fill:#ad2f94;fill-opacity:1;fill-rule:nonzero;stroke:none" + sodipodi:type="arc" + transform="matrix(0.3484724,0.6035735,-0.603572,0.3484734,154.13836,102.27942)" /> + <path + transform="matrix(-0.3484724,0.6035735,-0.603572,-0.3484733,246.13507,184.51913)" + sodipodi:type="arc" + style="fill:#0060f0;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path36353-9-8" + sodipodi:cx="132" + sodipodi:cy="118" + sodipodi:rx="8" + sodipodi:ry="8" + d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + sodipodi:start="4.712389" + sodipodi:end="5.7595865" + inkscape:transform-center-x="-2.8145756" + inkscape:transform-center-y="3.2500173" /> + <path + inkscape:transform-center-y="3.249994" + inkscape:transform-center-x="2.8145978" + sodipodi:end="5.7595865" + sodipodi:start="4.712389" + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z" + sodipodi:ry="8" + sodipodi:rx="8" + sodipodi:cy="118" + sodipodi:cx="132" + id="path36355-6-4" + style="fill:#00d4aa;fill-opacity:1;fill-rule:nonzero;stroke:none" + sodipodi:type="arc" + transform="matrix(-0.6969448,2.2484149e-8,-4.6257528e-8,-0.6969467,220.91956,305.31067)" /> + <path + transform="matrix(-0.3484724,-0.6035734,0.603572,-0.3484734,103.69972,343.86251)" + sodipodi:type="arc" + style="fill:#ccff00;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path36357-5-2" + sodipodi:cx="132" + sodipodi:cy="118" + sodipodi:rx="8" + sodipodi:ry="8" + d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + sodipodi:start="4.712389" + sodipodi:end="5.7595865" + inkscape:transform-center-x="3.2630773" /> + <path + inkscape:transform-center-x="2.8145777" + sodipodi:end="5.7595865" + sodipodi:start="4.712389" + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z" + sodipodi:ry="8" + sodipodi:rx="8" + sodipodi:cy="118" + sodipodi:cx="132" + id="path36359-0-5" + style="fill:#ffbf0e;fill-opacity:1;fill-rule:nonzero;stroke:none" + sodipodi:type="arc" + transform="matrix(0.3484724,-0.6035734,0.603572,0.3484733,11.703006,261.6228)" + inkscape:transform-center-y="-3.2500006" /> + </g> + <circle + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + id="path36361-8-8" + style="fill:none;stroke:#000000;stroke-width:0.98948926;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + transform="matrix(0.8124999,0,0,0.8045157,241.75,163.13011)" + cx="132" + cy="118" + r="8" /> + <circle + style="display:inline;opacity:0.3;fill:url(#radialGradient37501-3-6);fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path36363-2-9" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + transform="matrix(-0.7451143,-0.08386971,0.08492794,-0.7396793,437.33358,356.39712)" + cx="132" + cy="118" + r="8" /> + <circle + transform="matrix(0.6860851,0,0,0.6874876,258.44808,176.87656)" + style="fill:none;stroke:url(#linearGradient17463);stroke-width:1.45605874;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path36365-2-9" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + cx="132" + cy="118" + r="8" /> + </g> + </g> + <g + inkscape:transform-center-y="0.19622957" + inkscape:transform-center-x="-1.373607" + id="ICON_RESTRICT_SELECT_OFF-9" + style="display:inline;enable-background:new" + transform="matrix(0.45975513,-0.19653299,0.19653299,0.45975513,138.04837,311.70175)"> + <path + style="display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient17281);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001;marker:none;enable-background:accumulate" + d="m 367.75,440.75 1.75,-1.5 2.5,5.25 1.75,-1 -2.25,-5 2.5,0 -6.25,-6.25 z" + id="path45378-1-5-6-6" + sodipodi:nodetypes="cccccccc" + inkscape:connector-curvature="0" /> + <rect + style="display:inline;overflow:visible;visibility:visible;opacity:0;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate" + id="rect45374-0-5-6-1" + width="16" + height="16" + x="362" + y="430" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 367.5,431.5 7,7.25 -3,0 2.5,4.75 -1.75,1 -2.5,-5 -2.25,2.25 z" + id="path17835-7-5" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccccccc" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 368.34375,433.75 0,5.75" + id="path17845-9-6" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + </g> + </g> + <g + style="display:inline;filter:url(#filter17385);enable-background:new" + id="g17465-0" + transform="translate(41.727744,-106.93345)"> + <g + transform="matrix(0.7,0,0,0.7,146.7,264.8)" + id="ICON_COLOR-1-9-4" + style="display:inline;enable-background:new"> + <rect + y="238" + x="341" + height="16" + width="16" + id="rect36341-2-0-1" + style="display:inline;overflow:visible;visibility:visible;opacity:0;fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.79999995;marker:none;enable-background:accumulate" /> + <g + id="g36343-7-5-5" + transform="translate(0,-12)"> + <g + transform="matrix(1.1658027,0,0,1.1657997,198.71028,-2.0560643)" + id="g36345-0-4-4"> + <path + transform="matrix(0.6969448,0,0,0.6969467,36.918512,140.83126)" + sodipodi:type="arc" + style="fill:#ff6600;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path36349-4-2-6" + sodipodi:cx="132" + sodipodi:cy="118" + sodipodi:rx="8" + sodipodi:ry="8" + d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + sodipodi:start="4.712389" + sodipodi:end="5.7595865" + inkscape:transform-center-x="-2.8145849" + inkscape:transform-center-y="-3.2499984" /> + <path + inkscape:transform-center-y="1.6729808e-005" + inkscape:transform-center-x="-3.2630798" + sodipodi:end="5.7595865" + sodipodi:start="4.712389" + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z" + sodipodi:ry="8" + sodipodi:rx="8" + sodipodi:cy="118" + sodipodi:cx="132" + id="path36351-3-6-5" + style="fill:#ad2f94;fill-opacity:1;fill-rule:nonzero;stroke:none" + sodipodi:type="arc" + transform="matrix(0.3484724,0.6035735,-0.603572,0.3484734,154.13836,102.27942)" /> + <path + transform="matrix(-0.3484724,0.6035735,-0.603572,-0.3484733,246.13507,184.51913)" + sodipodi:type="arc" + style="fill:#0060f0;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path36353-9-8-2" + sodipodi:cx="132" + sodipodi:cy="118" + sodipodi:rx="8" + sodipodi:ry="8" + d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + sodipodi:start="4.712389" + sodipodi:end="5.7595865" + inkscape:transform-center-x="-2.8145756" + inkscape:transform-center-y="3.2500173" /> + <path + inkscape:transform-center-y="3.249994" + inkscape:transform-center-x="2.8145978" + sodipodi:end="5.7595865" + sodipodi:start="4.712389" + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z" + sodipodi:ry="8" + sodipodi:rx="8" + sodipodi:cy="118" + sodipodi:cx="132" + id="path36355-6-4-4" + style="fill:#00d4aa;fill-opacity:1;fill-rule:nonzero;stroke:none" + sodipodi:type="arc" + transform="matrix(-0.6969448,2.2484149e-8,-4.6257528e-8,-0.6969467,220.91956,305.31067)" /> + <path + transform="matrix(-0.3484724,-0.6035734,0.603572,-0.3484734,103.69972,343.86251)" + sodipodi:type="arc" + style="fill:#ccff00;fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path36357-5-2-5" + sodipodi:cx="132" + sodipodi:cy="118" + sodipodi:rx="8" + sodipodi:ry="8" + d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + sodipodi:start="4.712389" + sodipodi:end="5.7595865" + inkscape:transform-center-x="3.2630773" /> + <path + inkscape:transform-center-x="2.8145777" + sodipodi:end="5.7595865" + sodipodi:start="4.712389" + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + d="m 132,110 a 8,8 0 0 1 6.9282,4 L 132,118 Z" + sodipodi:ry="8" + sodipodi:rx="8" + sodipodi:cy="118" + sodipodi:cx="132" + id="path36359-0-5-9" + style="fill:#ffbf0e;fill-opacity:1;fill-rule:nonzero;stroke:none" + sodipodi:type="arc" + transform="matrix(0.3484724,-0.6035734,0.603572,0.3484733,11.703006,261.6228)" + inkscape:transform-center-y="-3.2500006" /> + </g> + <circle + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + id="path36361-8-8-0" + style="fill:none;stroke:#000000;stroke-width:0.98948926;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + transform="matrix(0.8124999,0,0,0.8045157,241.75,163.13011)" + cx="132" + cy="118" + r="8" /> + <circle + style="display:inline;opacity:0.3;fill:url(#radialGradient37501-3-6-2);fill-opacity:1;fill-rule:nonzero;stroke:none" + id="path36363-2-9-7" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + transform="matrix(-0.7451143,-0.08386971,0.08492794,-0.7396793,437.33358,356.39712)" + cx="132" + cy="118" + r="8" /> + <circle + transform="matrix(0.6860851,0,0,0.6874876,258.44808,176.87656)" + style="fill:none;stroke:url(#linearGradient31452);stroke-width:1.45605874;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path36365-2-9-7" + inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\Kopia blender\.blender\icons\blender's iconset.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + cx="132" + cy="118" + r="8" /> + </g> + </g> + <g + inkscape:transform-center-y="0.19622957" + inkscape:transform-center-x="-1.373607" + id="ICON_RESTRICT_SELECT_OFF-9-8" + style="display:inline;enable-background:new" + transform="matrix(0.45975513,-0.19653299,0.19653299,0.45975513,138.04837,311.70175)"> + <path + style="display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient31454);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001;marker:none;enable-background:accumulate" + d="m 367.75,440.75 1.75,-1.5 2.5,5.25 1.75,-1 -2.25,-5 2.5,0 -6.25,-6.25 z" + id="path45378-1-5-6-6-9" + sodipodi:nodetypes="cccccccc" + inkscape:connector-curvature="0" /> + <rect + style="display:inline;overflow:visible;visibility:visible;opacity:0;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate" + id="rect45374-0-5-6-1-0" + width="16" + height="16" + x="362" + y="430" /> + <path + style="fill:none;stroke:#000000;stroke-width:0.89999998;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 367.5,431.5 7,7.25 -3,0 2.5,4.75 -1.75,1 -2.5,-5 -2.25,2.25 z" + id="path17835-7-5-7" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccccccc" /> + <path + style="fill:none;stroke:#ffffff;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 368.34375,433.75 0,5.75" + id="path17845-9-6-9" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + </g> + </g> </g> </g> <g diff --git a/release/datafiles/blender_icons16/icon16_restrict_color_off.dat b/release/datafiles/blender_icons16/icon16_restrict_color_off.dat Binary files differnew file mode 100644 index 00000000000..d313539e3c5 --- /dev/null +++ b/release/datafiles/blender_icons16/icon16_restrict_color_off.dat diff --git a/release/datafiles/blender_icons16/icon16_restrict_color_on.dat b/release/datafiles/blender_icons16/icon16_restrict_color_on.dat Binary files differnew file mode 100644 index 00000000000..bb7782bb42a --- /dev/null +++ b/release/datafiles/blender_icons16/icon16_restrict_color_on.dat diff --git a/release/datafiles/blender_icons32/icon32_restrict_color_off.dat b/release/datafiles/blender_icons32/icon32_restrict_color_off.dat Binary files differnew file mode 100644 index 00000000000..bbe5f61935d --- /dev/null +++ b/release/datafiles/blender_icons32/icon32_restrict_color_off.dat diff --git a/release/datafiles/blender_icons32/icon32_restrict_color_on.dat b/release/datafiles/blender_icons32/icon32_restrict_color_on.dat Binary files differnew file mode 100644 index 00000000000..46ba9e31407 --- /dev/null +++ b/release/datafiles/blender_icons32/icon32_restrict_color_on.dat diff --git a/release/scripts/freestyle/modules/parameter_editor.py b/release/scripts/freestyle/modules/parameter_editor.py index 082ce139a59..93305cb7c5a 100644 --- a/release/scripts/freestyle/modules/parameter_editor.py +++ b/release/scripts/freestyle/modules/parameter_editor.py @@ -914,14 +914,25 @@ class QuantitativeInvisibilityRangeUP1D(UnaryPredicate1D): return self.qi_start <= qi <= self.qi_end +def getQualifiedObjectName(ob): + if ob.library is not None: + return ob.library.filepath + '/' + ob.name + return ob.name + + class ObjectNamesUP1D(UnaryPredicate1D): def __init__(self, names, negative): UnaryPredicate1D.__init__(self) self.names = names self.negative = negative + def getViewShapeName(self, vs): + if vs.library_path is not None: + return vs.library_path + '/' + vs.name + return vs.name + def __call__(self, viewEdge): - found = viewEdge.viewshape.name in self.names + found = self.getViewShapeName(viewEdge.viewshape) in self.names if self.negative: return not found return found @@ -1256,7 +1267,7 @@ def process(layer_name, lineset_name): # prepare selection criteria by group of objects if lineset.select_by_group: if lineset.group is not None: - names = {ob.name: True for ob in lineset.group.objects} + names = {getQualifiedObjectName(ob): True for ob in lineset.group.objects} upred = ObjectNamesUP1D(names, lineset.group_negation == 'EXCLUSIVE') selection_criteria.append(upred) # prepare selection criteria by image border diff --git a/release/scripts/modules/sys_info.py b/release/scripts/modules/sys_info.py index b225325ba27..30b9cdfaf37 100644 --- a/release/scripts/modules/sys_info.py +++ b/release/scripts/modules/sys_info.py @@ -158,6 +158,13 @@ def write_sysinfo(filepath): else: output.write("Blender was built without OpenVDB support\n") + alembic = bpy.app.alembic + output.write("Alembic: ") + if alembic.supported: + output.write("%s\n" % alembic.version_string) + else: + output.write("Blender was built without Alembic support\n") + if not bpy.app.build_options.sdl: output.write("SDL: Blender was built without SDL support\n") diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index 4ca2f773dcc..cb5f1595ff3 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -880,6 +880,19 @@ class ConstraintButtonsPanel: layout.operator("clip.constraint_to_fcurve") + def TRANSFORM_CACHE(self, context, layout, con): + layout.label(text="Cache File Properties:") + box = layout.box() + box.template_cache_file(con, "cache_file") + + cache_file = con.cache_file + + layout.label(text="Constraint Properties:") + box = layout.box() + + if cache_file is not None: + box.prop_search(con, "object_path", cache_file, "object_paths") + def SCRIPT(self, context, layout, con): layout.label("Blender 2.6 doesn't support python constraints yet") diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index 6f53ae6e118..50221d57b49 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -151,14 +151,15 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col = split.column() col.label(text="Operation:") col.prop(md, "operation", text="") - row = layout.row() - row.label("Solver:") - row.prop(md, "solver", expand=True) col = split.column() col.label(text="Object:") col.prop(md, "object", text="") + split = layout.split() + split.column().label(text="Solver:") + split.column().prop(md, "solver", text="") + if md.solver == 'BMESH': layout.prop(md, "double_threshold") @@ -180,6 +181,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): layout.prop(md, "cache_format") layout.prop(md, "filepath") + if md.cache_format == 'ABC': + layout.prop(md, "sub_object") + layout.label(text="Evaluation:") layout.prop(md, "factor", slider=True) layout.prop(md, "deform_mode") @@ -214,6 +218,22 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): row = split.row() row.prop(md, "flip_axis") + def MESH_SEQUENCE_CACHE(self, layout, ob, md): + layout.label(text="Cache File Properties:") + box = layout.box() + box.template_cache_file(md, "cache_file") + + cache_file = md.cache_file + + layout.label(text="Modifier Properties:") + box = layout.box() + + if cache_file is not None: + box.prop_search(md, "object_path", cache_file, "object_paths") + + if ob.type == 'MESH': + box.row().prop(md, "read_data") + def CAST(self, layout, ob, md): split = layout.split(percentage=0.25) diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index baa70ed08f5..90cf410036a 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -80,6 +80,7 @@ class GreasePencilDrawingToolsPanel: sub = col.column(align=True) sub.prop(context.tool_settings, "use_gpencil_additive_drawing", text="Additive Drawing") sub.prop(context.tool_settings, "use_gpencil_continuous_drawing", text="Continuous Drawing") + sub.prop(context.tool_settings, "use_gpencil_draw_onback", text="Draw on Back") col.separator() col.separator() @@ -151,18 +152,19 @@ class GreasePencilStrokeEditPanel: col.operator("gpencil.select_linked") col.operator("gpencil.select_more") col.operator("gpencil.select_less") - - layout.separator() + col.operator("gpencil.palettecolor_select") layout.label(text="Edit:") row = layout.row(align=True) row.operator("gpencil.copy", text="Copy") - row.operator("gpencil.paste", text="Paste") + row.operator("gpencil.paste", text="Paste").type = 'COPY' + row.operator("gpencil.paste", text="Paste & Merge").type = 'MERGE' col = layout.column(align=True) - col.operator("gpencil.delete", text="Delete") + col.operator("gpencil.delete") col.operator("gpencil.duplicate_move", text="Duplicate") - col.operator("transform.mirror", text="Mirror") + if is_3d_view: + col.operator("gpencil.stroke_cyclical_set", text="Toggle Cyclic").type = 'TOGGLE' layout.separator() @@ -176,9 +178,92 @@ class GreasePencilStrokeEditPanel: col = layout.column(align=True) col.operator("transform.bend", text="Bend") + col.operator("transform.mirror", text="Mirror") col.operator("transform.shear", text="Shear") col.operator("transform.tosphere", text="To Sphere") + layout.separator() + col = layout.column(align=True) + col.operator_menu_enum("gpencil.stroke_arrange", text="Arrange Strokes...", property="direction") + col.operator("gpencil.stroke_change_color", text="Move to Color") + + layout.separator() + col = layout.column(align=True) + col.operator("gpencil.stroke_join", text="Join").type = 'JOIN' + col.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY' + col.operator("gpencil.stroke_flip", text="Flip Direction") + + gpd = context.gpencil_data + if gpd: + col.prop(gpd, "show_stroke_direction", text="Show Directions") + + +class GreasePencilBrushPanel: + # subclass must set + # bl_space_type = 'IMAGE_EDITOR' + bl_label = "Drawing Brushes" + bl_category = "Grease Pencil" + bl_region_type = 'TOOLS' + + @staticmethod + def draw(self, context): + layout = self.layout + + row = layout.row() + col = row.column() + ts = context.scene.tool_settings + if len(ts.gpencil_brushes) >= 2: + brows = 3 + else: + brows = 2 + col.template_list("GPENCIL_UL_brush", "", ts, "gpencil_brushes", ts.gpencil_brushes, "active_index", rows=brows) + + col = row.column() + + sub = col.column(align=True) + sub.operator("gpencil.brush_add", icon='ZOOMIN', text="") + sub.operator("gpencil.brush_remove", icon='ZOOMOUT', text="") + sub.menu("GPENCIL_MT_brush_specials", icon='DOWNARROW_HLT', text="") + brush = context.active_gpencil_brush + if brush: + if len(ts.gpencil_brushes) > 1: + col.separator() + sub = col.column(align=True) + sub.operator("gpencil.brush_move", icon='TRIA_UP', text="").type = 'UP' + sub.operator("gpencil.brush_move", icon='TRIA_DOWN', text="").type = 'DOWN' + + # Brush details + if brush is not None: + row = layout.row() + row.prop(brush, "line_width") + row = layout.row(align=True) + row.prop(brush, "use_random_pressure", text='', icon='RNDCURVE') + row.prop(brush, "pen_sensitivity_factor", slider=True) + row.prop(brush, "use_pressure", text='', icon='STYLUS_PRESSURE') + row = layout.row(align=True) + row.prop(brush, "use_random_strength", text='', icon='RNDCURVE') + row.prop(brush, "strength", slider=True) + row.prop(brush, "use_strength_pressure", text='', icon='STYLUS_PRESSURE') + row = layout.row(align=True) + row.prop(brush, "random_press", slider=True) + + row = layout.row(align=True) + row.prop(brush, "jitter", slider=True) + row.prop(brush, "use_jitter_pressure", text='', icon='STYLUS_PRESSURE') + row = layout.row() + row.prop(brush, "angle", slider=True) + row.prop(brush, "angle_factor", text="Factor", slider=True) + + box = layout.box() + col = box.column(align=True) + col.label(text="Stroke Quality:") + col.prop(brush, "pen_smooth_factor") + col.prop(brush, "pen_smooth_steps") + col.separator() + row = col.row(align=False) + row.prop(brush, "pen_subdivision_steps") + row.prop(brush, "random_subdiv", text='Randomness', slider=True) + class GreasePencilStrokeSculptPanel: # subclass must set @@ -203,7 +288,7 @@ class GreasePencilStrokeSculptPanel: tool = settings.tool brush = settings.brush - layout.column().prop(settings, "tool", expand=True) + layout.column().prop(settings, "tool") col = layout.column() col.prop(brush, "size", slider=True) @@ -211,6 +296,11 @@ class GreasePencilStrokeSculptPanel: row.prop(brush, "strength", slider=True) row.prop(brush, "use_pressure_strength", text="") col.prop(brush, "use_falloff") + if tool in {'SMOOTH', 'RANDOMIZE'}: + row = layout.row(align=True) + row.prop(settings, "affect_position", text="Position", icon='MESH_DATA', toggle=True) + row.prop(settings, "affect_strength", text="Strength", icon='COLOR', toggle=True) + row.prop(settings, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True) layout.separator() @@ -220,18 +310,54 @@ class GreasePencilStrokeSculptPanel: row = layout.row(align=True) row.prop_enum(brush, "direction", 'ADD', text="Pinch") row.prop_enum(brush, "direction", 'SUBTRACT', text="Inflate") - elif tool == 'TWIST': + elif settings.tool == 'TWIST': row = layout.row(align=True) row.prop_enum(brush, "direction", 'SUBTRACT', text="CW") row.prop_enum(brush, "direction", 'ADD', text="CCW") - layout.separator() - layout.prop(settings, "use_select_mask") + row = layout.row(align=True) + row.prop(settings, "use_select_mask") + row = layout.row(align=True) + row.prop(settings, "selection_alpha", slider=True) if tool == 'SMOOTH': layout.prop(brush, "affect_pressure") +class GreasePencilBrushCurvesPanel: + # subclass must set + # bl_space_type = 'IMAGE_EDITOR' + bl_label = "Brush Curves" + bl_category = "Grease Pencil" + bl_region_type = 'TOOLS' + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + if context.active_gpencil_brush is None: + return False + + brush = context.active_gpencil_brush + return bool(brush) + + @staticmethod + def draw(self, context): + layout = self.layout + brush = context.active_gpencil_brush + # Brush + layout.label("Sensitivity") + box = layout.box() + box.template_curve_mapping(brush, "curve_sensitivity", brush=True) + + layout.label("Strength") + box = layout.box() + box.template_curve_mapping(brush, "curve_strength", brush=True) + + layout.label("Jitter") + box = layout.box() + box.template_curve_mapping(brush, "curve_jitter", brush=True) + + ############################### class GPENCIL_PIE_tool_palette(Menu): @@ -282,6 +408,7 @@ class GPENCIL_PIE_tool_palette(Menu): col.operator("gpencil.select_all", text="Select All", icon='PARTICLE_POINT') col.operator("gpencil.select_all", text="Select Inverse", icon='BLANK1') col.operator("gpencil.select_linked", text="Select Linked", icon='LINKED') + col.operator("gpencil.palettecolor_select", text="Select Color", icon='COLOR') # NE - Select (Modal) col = pie.column() @@ -315,24 +442,47 @@ class GPENCIL_PIE_settings_palette(Menu): pie = layout.menu_pie() # gpd = context.gpencil_data gpl = context.active_gpencil_layer + palcolor = context.active_gpencil_palettecolor + brush = context.active_gpencil_brush # W - Stroke draw settings col = pie.column(align=True) - col.label(text="Stroke") - col.prop(gpl, "color", text="") - col.prop(gpl, "alpha", text="", slider=True) + if palcolor is not None: + col.label(text="Stroke") + col.prop(palcolor, "color", text="") + col.prop(palcolor, "alpha", text="", slider=True) # E - Fill draw settings col = pie.column(align=True) - col.label(text="Fill") - col.prop(gpl, "fill_color", text="") - col.prop(gpl, "fill_alpha", text="", slider=True) + if palcolor is not None: + col.label(text="Fill") + col.prop(palcolor, "fill_color", text="") + col.prop(palcolor, "fill_alpha", text="", slider=True) - # S - Layer settings + # S Brush settings col = pie.column() - col.prop(gpl, "line_width", slider=True) - # col.prop(gpl, "use_volumetric_strokes") - col.prop(gpl, "use_onion_skinning") + col.label("Active Brush: ") + + row = col.row() + row.operator_context = 'EXEC_REGION_WIN' + row.operator_menu_enum("gpencil.brush_change", "brush", text="", icon='BRUSH_DATA') + row.prop(brush, "name", text="") + + col.prop(brush, "line_width", slider=True) + row = col.row(align=True) + row.prop(brush, "use_random_pressure", text='', icon='RNDCURVE') + row.prop(brush, "pen_sensitivity_factor", slider=True) + row.prop(brush, "use_pressure", text='', icon='STYLUS_PRESSURE') + row = col.row(align=True) + row.prop(brush, "use_random_strength", text='', icon='RNDCURVE') + row.prop(brush, "strength", slider=True) + row.prop(brush, "use_strength_pressure", text='', icon='STYLUS_PRESSURE') + row = col.row(align=True) + row.prop(brush, "jitter", slider=True) + row.prop(brush, "use_jitter_pressure", text='', icon='STYLUS_PRESSURE') + row = col.row() + row.prop(brush, "angle", slider=True) + row.prop(brush, "angle_factor", text="Factor", slider=True) # N - Active Layer col = pie.column() @@ -347,6 +497,35 @@ class GPENCIL_PIE_settings_palette(Menu): row = col.row() row.prop(gpl, "lock") row.prop(gpl, "hide") + col.prop(gpl, "use_onion_skinning") + + # NW - Move stroke Down + col = pie.column(align=True) + col.label("Arrange Strokes") + col.operator("gpencil.stroke_arrange", text="Send to Back").direction = 'BOTTOM' + col.operator("gpencil.stroke_arrange", text="Send Backward").direction = 'DOWN' + + # NE - Move stroke Up + col = pie.column(align=True) + col.label("Arrange Strokes") + col.operator("gpencil.stroke_arrange", text="Bring to Front").direction = 'TOP' + col.operator("gpencil.stroke_arrange", text="Bring Forward").direction = 'UP' + + # SW - Move stroke to color + col = pie.column(align=True) + col.operator("gpencil.stroke_change_color", text="Move to Color") + + # SE - Join strokes + col = pie.column(align=True) + col.label("Join Strokes") + row = col.row() + row.operator("gpencil.stroke_join", text="Join").type = 'JOIN' + row.operator("gpencil.stroke_join", text="Join & Copy").type = 'JOINCOPY' + col.operator("gpencil.stroke_flip", text="Flip direction") + + gpd = context.gpencil_data + if gpd: + col.prop(gpd, "show_stroke_direction", text="Show drawing direction") class GPENCIL_PIE_tools_more(Menu): @@ -411,6 +590,11 @@ class GPENCIL_PIE_sculpt(Menu): row.prop(brush, "strength", slider=True) # row.prop(brush, "use_pressure_strength", text="", icon_only=True) col.prop(brush, "use_falloff") + if settings.tool in {'SMOOTH', 'RANDOMIZE'}: + row = col.row(align=True) + row.prop(settings, "affect_position", text="Position", icon='MESH_DATA', toggle=True) + row.prop(settings, "affect_strength", text="Strength", icon='COLOR', toggle=True) + row.prop(settings, "affect_thickness", text="Thickness", icon='LINE_DATA', toggle=True) # S - Change Brush Type Shortcuts row = pie.row() @@ -422,6 +606,7 @@ class GPENCIL_PIE_sculpt(Menu): row = pie.row() row.prop_enum(settings, "tool", value='SMOOTH') row.prop_enum(settings, "tool", value='THICKNESS') + row.prop_enum(settings, "tool", value='STRENGTH') row.prop_enum(settings, "tool", value='RANDOMIZE') @@ -448,6 +633,48 @@ class GPENCIL_MT_snap(Menu): ############################### +class GPENCIL_UL_brush(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + # assert(isinstance(item, bpy.types.GPencilBrush) + brush = item + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + row = layout.row(align=True) + row.prop(brush, "name", text="", emboss=False, icon='BRUSH_DATA') + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + +class GPENCIL_UL_palettecolor(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + # assert(isinstance(item, bpy.types.PaletteColor) + palcolor = item + + if self.layout_type in {'DEFAULT', 'COMPACT'}: + if palcolor.lock: + layout.active = False + + split = layout.split(percentage=0.25) + row = split.row(align=True) + row.prop(palcolor, "color", text="", emboss=palcolor.is_stroke_visible) + row.prop(palcolor, "fill_color", text="", emboss=palcolor.is_fill_visible) + split.prop(palcolor, "name", text="", emboss=False) + + row = layout.row(align=True) + row.prop(palcolor, "lock", text="", emboss=False) + row.prop(palcolor, "hide", text="", emboss=False) + if palcolor.ghost is True: + icon = 'GHOST_DISABLED' + else: + icon = 'GHOST_ENABLED' + row.prop(palcolor, "ghost", text="", icon=icon, emboss=False) + + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.label(text="", icon_value=icon) + + class GPENCIL_UL_layer(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): # assert(isinstance(item, bpy.types.GPencilLayer) @@ -457,15 +684,19 @@ class GPENCIL_UL_layer(UIList): if gpl.lock: layout.active = False - split = layout.split(percentage=0.25) - row = split.row(align=True) - row.prop(gpl, "color", text="", emboss=gpl.is_stroke_visible) - row.prop(gpl, "fill_color", text="", emboss=gpl.is_fill_visible) - split.prop(gpl, "info", text="", emboss=False) + row = layout.row(align=True) + if gpl.is_parented: + icon = 'BONE_DATA' + else: + icon = 'BLANK1' + + row.label(text="", icon=icon) + row.prop(gpl, "info", text="", emboss=False) row = layout.row(align=True) row.prop(gpl, "lock", text="", emboss=False) row.prop(gpl, "hide", text="", emboss=False) + row.prop(gpl, "unlock_color", text="", emboss=False) elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="", icon_value=icon) @@ -489,11 +720,40 @@ class GPENCIL_MT_layer_specials(Menu): layout.operator("gpencil.lock_all", icon='LOCKED', text="Lock All") layout.operator("gpencil.unlock_all", icon='UNLOCKED', text="UnLock All") + layout.separator() + + layout.operator("gpencil.layer_merge", icon='NLA', text="Merge Down") + + +class GPENCIL_MT_brush_specials(Menu): + bl_label = "Layer" + + def draw(self, context): + layout = self.layout + layout.operator("gpencil.brush_copy", icon='PASTEDOWN', text="Copy current drawing brush") + layout.operator("gpencil.brush_presets_create", icon='HELP', text="Create a set of predefined brushes") + + +class GPENCIL_MT_palettecolor_specials(Menu): + bl_label = "Layer" + + def draw(self, context): + layout = self.layout + + layout.operator("gpencil.palettecolor_reveal", icon='RESTRICT_VIEW_OFF', text="Show All") + layout.operator("gpencil.palettecolor_hide", icon='RESTRICT_VIEW_ON', text="Hide Others").unselected = True + + layout.separator() + + layout.operator("gpencil.palettecolor_lock_all", icon='LOCKED', text="Lock All") + layout.operator("gpencil.palettecolor_unlock_all", icon='UNLOCKED', text="UnLock All") + layout.operator("gpencil.palettecolor_copy", icon='PASTEDOWN', text="Copy Color") + class GreasePencilDataPanel: # subclass must set # bl_space_type = 'IMAGE_EDITOR' - bl_label = "Grease Pencil" + bl_label = "Grease Pencil Layers" bl_region_type = 'UI' @staticmethod @@ -553,44 +813,49 @@ class GreasePencilDataPanel: col.separator() sub = col.column(align=True) - sub.operator("gpencil.layer_isolate", icon='SOLO_OFF', text="").affect_visibility = False + sub.operator("gpencil.layer_isolate", icon='LOCKED', text="").affect_visibility = False sub.operator("gpencil.layer_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True if gpl: - self.draw_layer(layout, gpl) + self.draw_layer(context, layout, gpl) - def draw_layer(self, layout, gpl): - # layer settings + def draw_layer(self, context, layout, gpl): + row = layout.row(align=True) + row.prop(gpl, "opacity", text="Opacity", slider=True) + + # Layer options split = layout.split(percentage=0.5) split.active = not gpl.lock + split.prop(gpl, "show_x_ray") + split.prop(gpl, "show_points") - # Column 1 - Stroke - col = split.column(align=True) - col.label(text="Stroke:") - col.prop(gpl, "color", text="") - col.prop(gpl, "alpha", slider=True) - - # Column 2 - Fill - col = split.column(align=True) - col.label(text="Fill:") - col.prop(gpl, "fill_color", text="") - col.prop(gpl, "fill_alpha", text="Opacity", slider=True) - - # Options - col = layout.column(align=True) - col.active = not gpl.lock - col.prop(gpl, "line_width", slider=True) - + # Offsets + Parenting (where available) split = layout.split(percentage=0.5) split.active = not gpl.lock - col = split.column(align=True) - col.prop(gpl, "use_volumetric_strokes") - col.prop(gpl, "show_points", text="Points") + # Offsets - Color Tint + col = split.column() + subcol = col.column(align=True) + subcol.label("Tint") + subcol.prop(gpl, "tint_color", text="") + subcol.prop(gpl, "tint_factor", text="Factor", slider=True) - col = split.column(align=True) - col.prop(gpl, "use_hq_fill") - col.prop(gpl, "show_x_ray") + # Offsets - Thickness + row = col.row(align=True) + row.prop(gpl, "line_change", text="Thickness Change", slider=True) + row.operator("gpencil.stroke_apply_thickness", icon='STYLUS_PRESSURE', text="") + + # Parenting + if context.space_data.type == 'VIEW_3D': + col = split.column(align=True) + col.label(text="Parent:") + col.prop(gpl, "parent", text="") + + sub = col.column() + sub.prop(gpl, "parent_type", text="") + parent = gpl.parent + if parent and gpl.parent_type == 'BONE' and parent.type == 'ARMATURE': + sub.prop_search(gpl, "parent_bone", parent.data, "bones", text="") layout.separator() @@ -633,14 +898,103 @@ class GreasePencilDataPanel: row.prop(gpl, "after_color", text="") sub.prop(gpl, "ghost_after_range", text="After") - # Smooth and subdivide new strokes - layout.separator() - col = layout.column(align=True) - col.label(text="New Stroke Quality:") - col.prop(gpl, "pen_smooth_factor") - col.prop(gpl, "pen_smooth_steps") - col.separator() - col.prop(gpl, "pen_subdivision_steps") + +class GreasePencilPaletteColorPanel: + # subclass must set + bl_label = "Grease Pencil Colors" + bl_region_type = 'UI' + + @classmethod + def poll(cls, context): + if context.gpencil_data is None: + return False + + gpd = context.gpencil_data + return bool(gpd.layers.active) + + @staticmethod + def draw(self, context): + layout = self.layout + palette = context.active_gpencil_palette + + if palette: + row = layout.row(align=True) + row.operator_context = 'EXEC_REGION_WIN' + row.operator_menu_enum("gpencil.palette_change", "palette", text="", icon='COLOR') + row.prop(palette, "name", text="") + row.operator("gpencil.palette_add", icon='ZOOMIN', text="") + row.operator("gpencil.palette_remove", icon='X', text="") + + # Palette colors + row = layout.row() + col = row.column() + if len(palette.colors) >= 2: + color_rows = 5 + else: + color_rows = 2 + col.template_list("GPENCIL_UL_palettecolor", "", palette, "colors", palette.colors, "active_index", + rows=color_rows) + + col = row.column() + + sub = col.column(align=True) + sub.operator("gpencil.palettecolor_add", icon='ZOOMIN', text="") + sub.operator("gpencil.palettecolor_remove", icon='ZOOMOUT', text="") + + palcol = context.active_gpencil_palettecolor + if palcol: + sub.menu("GPENCIL_MT_palettecolor_specials", icon='DOWNARROW_HLT', text="") + + if len(palette.colors) > 1: + col.separator() + + sub = col.column(align=True) + sub.operator("gpencil.palettecolor_move", icon='TRIA_UP', text="").direction = 'UP' + sub.operator("gpencil.palettecolor_move", icon='TRIA_DOWN', text="").direction = 'DOWN' + + col.separator() + sub = col.column(align=True) + sub.operator("gpencil.palettecolor_isolate", icon='LOCKED', text="").affect_visibility = False + sub.operator("gpencil.palettecolor_isolate", icon='RESTRICT_VIEW_OFF', text="").affect_visibility = True + sub.operator("gpencil.stroke_lock_color", icon='BORDER_RECT', text="") + sub.operator("gpencil.palette_lock_layer", icon='COLOR', text="") + + pcolor = palette.colors.active + if pcolor: + self.draw_palettecolors(layout, pcolor) + + # ---------------------------------------------- + # Draw palette colors + # ---------------------------------------------- + def draw_palettecolors(self, layout, pcolor): + # color settings + split = layout.split(percentage=0.5) + split.active = not pcolor.lock + + # Column 1 - Stroke + col = split.column(align=True) + col.active = not pcolor.lock + col.label(text="Stroke:") + col.prop(pcolor, "color", text="") + col.prop(pcolor, "alpha", slider=True) + + # Column 2 - Fill + col = split.column(align=True) + col.active = not pcolor.lock + col.label(text="Fill:") + col.prop(pcolor, "fill_color", text="") + col.prop(pcolor, "fill_alpha", text="Opacity", slider=True) + + # Options + split = layout.split(percentage=0.5) + split.active = not pcolor.lock + + col = split.column(align=True) + col.active = not pcolor.lock + col.prop(pcolor, "use_volumetric_strokes") + col = split.column(align=True) + col.active = not pcolor.lock + col.prop(pcolor, "use_hq_fill") class GreasePencilToolsPanel: diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py index c2580d4ac71..89ea9dff69b 100644 --- a/release/scripts/startup/bl_ui/properties_particle.py +++ b/release/scripts/startup/bl_ui/properties_particle.py @@ -716,6 +716,8 @@ class PARTICLE_PT_physics(ParticleButtonsPanel, Panel): col.prop(boids, "land_personal_space") col.prop(boids, "land_stick_force") + layout.prop(part, "collision_group") + split = layout.split() col = split.column(align=True) diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index 58bb956f653..a9db1266bc6 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -25,8 +25,10 @@ from bl_ui.properties_grease_pencil_common import ( GreasePencilDrawingToolsPanel, GreasePencilStrokeEditPanel, GreasePencilStrokeSculptPanel, - GreasePencilDataPanel - ) + GreasePencilBrushPanel, + GreasePencilBrushCurvesPanel, + GreasePencilDataPanel, + GreasePencilPaletteColorPanel) class CLIP_UL_tracking_objects(UIList): @@ -619,6 +621,7 @@ class CLIP_PT_track(CLIP_PT_tracking_panel, Panel): text="", toggle=True, icon='IMAGE_ALPHA') layout.prop(act_track, "weight") + layout.prop(act_track, "weight_stab") if act_track.has_bundle: label_text = "Average Error: %.4f" % (act_track.average_error) @@ -905,44 +908,81 @@ class CLIP_PT_stabilization(CLIP_PT_reconstruction_panel, Panel): self.layout.prop(stab, "use_2d_stabilization", text="") def draw(self, context): - layout = self.layout - tracking = context.space_data.clip.tracking stab = tracking.stabilization + layout = self.layout layout.active = stab.use_2d_stabilization - row = layout.row() - row.template_list("UI_UL_list", "stabilization_tracks", stab, "tracks", - stab, "active_track_index", rows=2) + layout.prop(stab, "anchor_frame") - sub = row.column(align=True) + layout.prop(stab, "use_stabilize_rotation") - sub.operator("clip.stabilize_2d_add", icon='ZOOMIN', text="") - sub.operator("clip.stabilize_2d_remove", icon='ZOOMOUT', text="") + box = layout.box() + row = box.row(align=True) + row.prop(stab, "show_tracks_expanded", text="", emboss=False) - sub.menu('CLIP_MT_stabilize_2d_specials', text="", - icon='DOWNARROW_HLT') + if not stab.show_tracks_expanded: + row.label(text="Tracks For Stabilization") + else: + row.label(text="Tracks For Location") + row = box.row() + row.template_list("UI_UL_list", "stabilization_tracks", stab, "tracks", + stab, "active_track_index", rows=2) - layout.prop(stab, "influence_location") + sub = row.column(align=True) - layout.prop(stab, "use_autoscale") - col = layout.column() - col.active = stab.use_autoscale - col.prop(stab, "scale_max") - col.prop(stab, "influence_scale") + sub.operator("clip.stabilize_2d_add", icon='ZOOMIN', text="") + sub.operator("clip.stabilize_2d_remove", icon='ZOOMOUT', text="") - layout.prop(stab, "use_stabilize_rotation") - col = layout.column() - col.active = stab.use_stabilize_rotation + sub.menu('CLIP_MT_stabilize_2d_specials', text="", + icon='DOWNARROW_HLT') - row = col.row(align=True) - row.prop_search(stab, "rotation_track", tracking, "tracks", text="") - row.operator("clip.stabilize_2d_set_rotation", text="", icon='ZOOMIN') + row = box.row() + row.label(text="Tracks For Rotation / Scale") + row = box.row() + row.active = stab.use_stabilize_rotation + row.template_list("UI_UL_list", "stabilization_rotation_tracks", stab, "rotation_tracks", + stab, "active_rotation_track_index", rows=2) + + sub = row.column(align=True) + + sub.operator("clip.stabilize_2d_rotation_add", icon='ZOOMIN', text="") + sub.operator("clip.stabilize_2d_rotation_remove", icon='ZOOMOUT', text="") + + sub.menu('CLIP_MT_stabilize_2d_rotation_specials', text="", + icon='DOWNARROW_HLT') + + row = layout.row() + row.active = stab.use_stabilize_rotation + row.prop(stab, "use_stabilize_scale") + if stab.use_autoscale: + row = layout.row(align=True) + row.prop(stab, "use_autoscale") + row.prop(stab, "scale_max", text="Max") + else: + layout.prop(stab, "use_autoscale") + + layout.separator() + layout.label(text="Expected Position") + layout.prop(stab, "target_pos", text="") + layout.prop(stab, "target_rot") + if stab.use_autoscale: + layout.label(text="Auto Scale Factor: %5.3f" % (1.0 / stab.target_zoom)) + else: + layout.prop(stab, "target_zoom") + layout.separator() + row = layout.row() + row.active = 0 < len(stab.tracks.values()) + row.prop(stab, "influence_location") + + col = layout.column() + col.active = stab.use_stabilize_rotation and 0 < len(stab.rotation_tracks.values()) row = col.row() - row.active = stab.rotation_track is not None row.prop(stab, "influence_rotation") + row = col.row() + row.prop(stab, "influence_scale") layout.prop(stab, "filter_type") @@ -1126,6 +1166,16 @@ class CLIP_PT_grease_pencil(GreasePencilDataPanel, CLIP_PT_clip_view_panel, Pane # But, this should only be visible in "clip" view +# Grease Pencil palette colors +class CLIP_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, CLIP_PT_clip_view_panel, Panel): + bl_space_type = 'CLIP_EDITOR' + bl_region_type = 'UI' + bl_options = {'DEFAULT_CLOSED'} + + # NOTE: this is just a wrapper around the generic GP Panel + # But, this should only be visible in "clip" view + + # Grease Pencil drawing tools class CLIP_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel): bl_space_type = 'CLIP_EDITOR' @@ -1141,6 +1191,15 @@ class CLIP_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel): bl_space_type = 'CLIP_EDITOR' +# Grease Pencil drawing brushes +class CLIP_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel): + bl_space_type = 'CLIP_EDITOR' + + +# Grease Pencil drawing curves +class CLIP_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel): + bl_space_type = 'CLIP_EDITOR' + class CLIP_MT_view(Menu): bl_label = "View" @@ -1413,7 +1472,7 @@ class CLIP_MT_track_color_specials(Menu): class CLIP_MT_stabilize_2d_specials(Menu): - bl_label = "Track Color Specials" + bl_label = "Translation Track Specials" def draw(self, context): layout = self.layout @@ -1421,5 +1480,14 @@ class CLIP_MT_stabilize_2d_specials(Menu): layout.operator("clip.stabilize_2d_select") +class CLIP_MT_stabilize_2d_rotation_specials(Menu): + bl_label = "Rotation Track Specials" + + def draw(self, context): + layout = self.layout + + layout.operator("clip.stabilize_2d_rotation_select") + + if __name__ == "__main__": # only for live edit. bpy.utils.register_module(__name__) diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 9f719bc793e..bf6df05c2b2 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -29,7 +29,10 @@ from bl_ui.properties_grease_pencil_common import ( GreasePencilDrawingToolsPanel, GreasePencilStrokeEditPanel, GreasePencilStrokeSculptPanel, + GreasePencilBrushPanel, + GreasePencilBrushCurvesPanel, GreasePencilDataPanel, + GreasePencilPaletteColorPanel ) from bpy.app.translations import pgettext_iface as iface_ @@ -1187,6 +1190,14 @@ class IMAGE_PT_grease_pencil(GreasePencilDataPanel, Panel): # NOTE: this is just a wrapper around the generic GP Panel +# Grease Pencil palette colors +class IMAGE_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, Panel): + bl_space_type = 'IMAGE_EDITOR' + bl_region_type = 'UI' + + # NOTE: this is just a wrapper around the generic GP Panel + + # Grease Pencil drawing tools class IMAGE_PT_tools_grease_pencil_draw(GreasePencilDrawingToolsPanel, Panel): bl_space_type = 'IMAGE_EDITOR' @@ -1202,5 +1213,15 @@ class IMAGE_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel): bl_space_type = 'IMAGE_EDITOR' +# Grease Pencil drawing brushes +class IMAGE_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel): + bl_space_type = 'IMAGE_EDITOR' + + +# Grease Pencil drawing curves +class IMAGE_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel): + bl_space_type = 'IMAGE_EDITOR' + + if __name__ == "__main__": # only for live edit. bpy.utils.register_module(__name__) diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py index 97ef37fa5e0..780dc4cf982 100644 --- a/release/scripts/startup/bl_ui/space_info.py +++ b/release/scripts/startup/bl_ui/space_info.py @@ -158,6 +158,8 @@ class INFO_MT_file_import(Menu): def draw(self, context): if bpy.app.build_options.collada: self.layout.operator("wm.collada_import", text="Collada (Default) (.dae)") + if bpy.app.build_options.alembic: + self.layout.operator("wm.alembic_import", text="Alembic (.abc)") class INFO_MT_file_export(Menu): @@ -167,6 +169,8 @@ class INFO_MT_file_export(Menu): def draw(self, context): if bpy.app.build_options.collada: self.layout.operator("wm.collada_export", text="Collada (Default) (.dae)") + if bpy.app.build_options.alembic: + self.layout.operator("wm.alembic_export", text="Alembic (.abc)") class INFO_MT_file_external_data(Menu): diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index ee342265f3d..8821fa0ca58 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -25,8 +25,11 @@ from bl_ui.properties_grease_pencil_common import ( GreasePencilDrawingToolsPanel, GreasePencilStrokeEditPanel, GreasePencilStrokeSculptPanel, + GreasePencilBrushPanel, + GreasePencilBrushCurvesPanel, GreasePencilDataPanel, - GreasePencilToolsPanel, + GreasePencilPaletteColorPanel, + GreasePencilToolsPanel ) @@ -464,6 +467,19 @@ class NODE_PT_grease_pencil(GreasePencilDataPanel, Panel): return snode is not None and snode.node_tree is not None +# Grease Pencil palette colors +class NODE_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, Panel): + bl_space_type = 'NODE_EDITOR' + bl_region_type = 'UI' + + # NOTE: this is just a wrapper around the generic GP Panel + + @classmethod + def poll(cls, context): + snode = context.space_data + return snode is not None and snode.node_tree is not None + + class NODE_PT_grease_pencil_tools(GreasePencilToolsPanel, Panel): bl_space_type = 'NODE_EDITOR' bl_region_type = 'UI' @@ -494,6 +510,16 @@ class NODE_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel): bl_space_type = 'NODE_EDITOR' bl_region_type = 'TOOLS' +# Grease Pencil drawing brushes +class NODE_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel): + bl_space_type = 'NODE_EDITOR' + bl_region_type = 'TOOLS' + +# Grease Pencil drawing curves +class NODE_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel): + bl_space_type = 'NODE_EDITOR' + bl_region_type = 'TOOLS' + # ----------------------------- diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 4d1b9104344..26136a8e024 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -20,7 +20,11 @@ import bpy from bpy.types import Header, Menu, Panel from rna_prop_ui import PropertyPanel -from bl_ui.properties_grease_pencil_common import GreasePencilDataPanel, GreasePencilToolsPanel +from bl_ui.properties_grease_pencil_common import ( + GreasePencilDataPanel, + GreasePencilPaletteColorPanel, + GreasePencilToolsPanel, + ) from bpy.app.translations import pgettext_iface as iface_ @@ -1186,6 +1190,14 @@ class SEQUENCER_PT_grease_pencil(GreasePencilDataPanel, SequencerButtonsPanel_Ou # But, it should only show up when there are images in the preview region +class SEQUENCER_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, SequencerButtonsPanel_Output, Panel): + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'UI' + + # NOTE: this is just a wrapper around the generic GP Panel + # But, it should only show up when there are images in the preview region + + class SEQUENCER_PT_grease_pencil_tools(GreasePencilToolsPanel, SequencerButtonsPanel_Output, Panel): bl_space_type = 'SEQUENCE_EDITOR' bl_region_type = 'UI' diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 681fa8e39aa..5f16957b435 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -19,7 +19,10 @@ # <pep8 compliant> import bpy from bpy.types import Header, Menu, Panel -from bl_ui.properties_grease_pencil_common import GreasePencilDataPanel +from bl_ui.properties_grease_pencil_common import ( + GreasePencilDataPanel, + GreasePencilPaletteColorPanel, + ) from bl_ui.properties_paint_common import UnifiedPaintPanel from bpy.app.translations import contexts as i18n_contexts @@ -139,7 +142,9 @@ class VIEW3D_HT_header(Header): # XXX: icon layout.prop(context.gpencil_data, "use_onion_skinning", text="Onion Skins", icon='PARTICLE_PATH') - layout.prop(context.tool_settings.gpencil_sculpt, "use_select_mask") + row = layout.row(align=True) + row.prop(context.tool_settings.gpencil_sculpt, "use_select_mask") + row.prop(context.tool_settings.gpencil_sculpt, "selection_alpha", slider=True) class VIEW3D_MT_editor_menus(Menu): @@ -3079,6 +3084,13 @@ class VIEW3D_PT_grease_pencil(GreasePencilDataPanel, Panel): # NOTE: this is just a wrapper around the generic GP Panel +class VIEW3D_PT_grease_pencil_palettecolor(GreasePencilPaletteColorPanel, Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + + # NOTE: this is just a wrapper around the generic GP Panel + + class VIEW3D_PT_view3d_properties(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index e9f4a45c2c3..8019c8d2f34 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -22,7 +22,9 @@ from bpy.types import Menu, Panel, UIList from bl_ui.properties_grease_pencil_common import ( GreasePencilDrawingToolsPanel, GreasePencilStrokeEditPanel, - GreasePencilStrokeSculptPanel + GreasePencilStrokeSculptPanel, + GreasePencilBrushPanel, + GreasePencilBrushCurvesPanel ) from bl_ui.properties_paint_common import ( UnifiedPaintPanel, @@ -1965,6 +1967,15 @@ class VIEW3D_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel) bl_space_type = 'VIEW_3D' +# Grease Pencil drawing brushes +class VIEW3D_PT_tools_grease_pencil_brush(GreasePencilBrushPanel, Panel): + bl_space_type = 'VIEW_3D' + +# Grease Pencil drawingcurves +class VIEW3D_PT_tools_grease_pencil_brushcurves(GreasePencilBrushCurvesPanel, Panel): + bl_space_type = 'VIEW_3D' + + # Note: moved here so that it's always in last position in 'Tools' panels! class VIEW3D_PT_tools_history(View3DPanel, Panel): bl_category = "Tools" diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index e36f9e2b43e..6f2b78e0845 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -31,6 +31,7 @@ set(SRC_DNA_INC ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_armature_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_boid_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_brush_types.h + ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_cachefile_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_camera_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_cloth_types.h ${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_color_types.h @@ -153,3 +154,6 @@ if(WITH_FREESTYLE) add_subdirectory(freestyle) endif() +if(WITH_ALEMBIC) + add_subdirectory(alembic) +endif() diff --git a/source/blender/alembic/ABC_alembic.h b/source/blender/alembic/ABC_alembic.h new file mode 100644 index 00000000000..cf121f8488c --- /dev/null +++ b/source/blender/alembic/ABC_alembic.h @@ -0,0 +1,110 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_ALEMBIC_H__ +#define __ABC_ALEMBIC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct bContext; +struct DerivedMesh; +struct ListBase; +struct Object; +struct Scene; + +typedef struct AbcArchiveHandle AbcArchiveHandle; + +enum { + ABC_ARCHIVE_OGAWA = 0, + ABC_ARCHIVE_HDF5 = 1, +}; + +int ABC_get_version(void); + +struct AlembicExportParams { + double frame_start; + double frame_end; + + double frame_step_xform; + double frame_step_shape; + + double shutter_open; + double shutter_close; + + /* bools */ + unsigned int selected_only : 1; + unsigned int uvs : 1; + unsigned int normals : 1; + unsigned int vcolors : 1; + unsigned int apply_subdiv : 1; + unsigned int flatten_hierarchy : 1; + unsigned int visible_layers_only : 1; + unsigned int renderable_only : 1; + unsigned int face_sets : 1; + unsigned int use_subdiv_schema : 1; + unsigned int packuv : 1; + + unsigned int compression_type : 1; + float global_scale; +}; + +void ABC_export( + struct Scene *scene, + struct bContext *C, + const char *filepath, + const struct AlembicExportParams *params); + +void ABC_import(struct bContext *C, + const char *filepath, + float scale, + bool is_sequence, + bool set_frame_range, + int sequence_len, + int offset, + bool validate_meshes); + +AbcArchiveHandle *ABC_create_handle(const char *filename, struct ListBase *object_paths); + +void ABC_free_handle(AbcArchiveHandle *handle); + +void ABC_get_transform(AbcArchiveHandle *handle, + struct Object *ob, + const char *object_path, + float r_mat[4][4], + float time, + float scale); + +struct DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle, + struct Object *ob, + struct DerivedMesh *dm, + const char *object_path, + const float time, + const char **err_str, + int flags); + +#ifdef __cplusplus +} +#endif + +#endif /* __ABC_ALEMBIC_H__ */ diff --git a/source/blender/alembic/CMakeLists.txt b/source/blender/alembic/CMakeLists.txt new file mode 100644 index 00000000000..42bd6a9c340 --- /dev/null +++ b/source/blender/alembic/CMakeLists.txt @@ -0,0 +1,81 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2006, Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): Kevin Dietrich. +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + ../blenkernel + ../blenlib + ../blenloader + ../editors/include + ../makesdna + ../makesrna + ../windowmanager + ../../../intern/guardedalloc +) + +set(INC_SYS + ${ALEMBIC_INCLUDE_DIRS} + ${HDF5_INCLUDE_DIRS} + ${OPENEXR_INCLUDE_DIRS} +) +if(APPLE OR WIN32) + list(APPEND INC_SYS + ${BOOST_INCLUDE_DIR} + ) +endif() + +set(SRC + intern/abc_camera.cc + intern/abc_customdata.cc + intern/abc_curves.cc + intern/abc_exporter.cc + intern/abc_hair.cc + intern/abc_mesh.cc + intern/abc_nurbs.cc + intern/abc_object.cc + intern/abc_points.cc + intern/abc_transform.cc + intern/abc_util.cc + intern/alembic_capi.cc + + ABC_alembic.h + intern/abc_camera.h + intern/abc_customdata.h + intern/abc_curves.h + intern/abc_exporter.h + intern/abc_hair.h + intern/abc_mesh.h + intern/abc_nurbs.h + intern/abc_object.h + intern/abc_points.h + intern/abc_transform.h + intern/abc_util.h +) + +if(WITH_ALEMBIC_HDF5) + add_definitions(-DWITH_ALEMBIC_HDF5) +endif() + +blender_add_lib(bf_alembic "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/alembic/intern/abc_camera.cc b/source/blender/alembic/intern/abc_camera.cc new file mode 100644 index 00000000000..38a6d3d33cf --- /dev/null +++ b/source/blender/alembic/intern/abc_camera.cc @@ -0,0 +1,162 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_camera.h" + +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_camera_types.h" +#include "DNA_object_types.h" + +#include "BKE_camera.h" +#include "BKE_object.h" + +#include "BLI_math.h" +#include "BLI_string.h" +} + +using Alembic::AbcGeom::ICamera; +using Alembic::AbcGeom::ICompoundProperty; +using Alembic::AbcGeom::IFloatProperty; +using Alembic::AbcGeom::ISampleSelector; + +using Alembic::AbcGeom::OCamera; +using Alembic::AbcGeom::OFloatProperty; + +using Alembic::AbcGeom::CameraSample; +using Alembic::AbcGeom::kWrapExisting; + +/* ************************************************************************** */ + +AbcCameraWriter::AbcCameraWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(scene, ob, time_sampling, settings, parent) +{ + OCamera camera(parent->alembicXform(), m_name, m_time_sampling); + m_camera_schema = camera.getSchema(); + + m_custom_data_container = m_camera_schema.getUserProperties(); + m_stereo_distance = OFloatProperty(m_custom_data_container, "stereoDistance", m_time_sampling); + m_eye_separation = OFloatProperty(m_custom_data_container, "eyeSeparation", m_time_sampling); +} + +void AbcCameraWriter::do_write() +{ + Camera *cam = static_cast<Camera *>(m_object->data); + + m_stereo_distance.set(cam->stereo.convergence_distance); + m_eye_separation.set(cam->stereo.interocular_distance); + + const double apperture_x = cam->sensor_x / 10.0; + const double apperture_y = cam->sensor_y / 10.0; + const double film_aspect = apperture_x / apperture_y; + + m_camera_sample.setFocalLength(cam->lens); + m_camera_sample.setHorizontalAperture(apperture_x); + m_camera_sample.setVerticalAperture(apperture_y); + m_camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx); + m_camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect); + m_camera_sample.setNearClippingPlane(cam->clipsta); + m_camera_sample.setFarClippingPlane(cam->clipend); + + if (cam->dof_ob) { + Imath::V3f v(m_object->loc[0] - cam->dof_ob->loc[0], + m_object->loc[1] - cam->dof_ob->loc[1], + m_object->loc[2] - cam->dof_ob->loc[2]); + m_camera_sample.setFocusDistance(v.length()); + } + else { + m_camera_sample.setFocusDistance(cam->gpu_dof.focus_distance); + } + + /* Blender camera does not have an fstop param, so try to find a custom prop + * instead. */ + m_camera_sample.setFStop(cam->gpu_dof.fstop); + + m_camera_sample.setLensSqueezeRatio(1.0); + m_camera_schema.set(m_camera_sample); +} + +/* ************************************************************************** */ + +AbcCameraReader::AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + ICamera abc_cam(m_iobject, kWrapExisting); + m_schema = abc_cam.getSchema(); + + get_min_max_time(m_schema, m_min_time, m_max_time); +} + +bool AbcCameraReader::valid() const +{ + return m_schema.valid(); +} + +void AbcCameraReader::readObjectData(Main *bmain, float time) +{ + Camera *bcam = static_cast<Camera *>(BKE_camera_add(bmain, "abc_camera")); + + ISampleSelector sample_sel(time); + CameraSample cam_sample; + m_schema.get(cam_sample, sample_sel); + + ICompoundProperty customDataContainer = m_schema.getUserProperties(); + + if (customDataContainer.valid() && + customDataContainer.getPropertyHeader("stereoDistance") && + customDataContainer.getPropertyHeader("eyeSeparation")) + { + IFloatProperty convergence_plane(customDataContainer, "stereoDistance"); + IFloatProperty eye_separation(customDataContainer, "eyeSeparation"); + + bcam->stereo.interocular_distance = eye_separation.getValue(sample_sel); + bcam->stereo.convergence_distance = convergence_plane.getValue(sample_sel); + } + + const float lens = cam_sample.getFocalLength(); + const float apperture_x = cam_sample.getHorizontalAperture(); + const float apperture_y = cam_sample.getVerticalAperture(); + const float h_film_offset = cam_sample.getHorizontalFilmOffset(); + const float v_film_offset = cam_sample.getVerticalFilmOffset(); + const float film_aspect = apperture_x / apperture_y; + + bcam->lens = lens; + bcam->sensor_x = apperture_x * 10; + bcam->sensor_y = apperture_y * 10; + bcam->shiftx = h_film_offset / apperture_x; + bcam->shifty = v_film_offset / apperture_y / film_aspect; + bcam->clipsta = max_ff(0.1f, cam_sample.getNearClippingPlane()); + bcam->clipend = cam_sample.getFarClippingPlane(); + bcam->gpu_dof.focus_distance = cam_sample.getFocusDistance(); + bcam->gpu_dof.fstop = cam_sample.getFStop(); + + BLI_strncpy(bcam->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1); + + m_object = BKE_object_add_only_object(bmain, OB_CAMERA, m_object_name.c_str()); + m_object->data = bcam; +} diff --git a/source/blender/alembic/intern/abc_camera.h b/source/blender/alembic/intern/abc_camera.h new file mode 100644 index 00000000000..fafb4d3eb39 --- /dev/null +++ b/source/blender/alembic/intern/abc_camera.h @@ -0,0 +1,61 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_CAMERA_H__ +#define __ABC_CAMERA_H__ + +#include "abc_object.h" + +/* ************************************************************************** */ + +class AbcCameraWriter : public AbcObjectWriter { + Alembic::AbcGeom::OCameraSchema m_camera_schema; + Alembic::AbcGeom::CameraSample m_camera_sample; + Alembic::AbcGeom::OCompoundProperty m_custom_data_container; + Alembic::AbcGeom::OFloatProperty m_stereo_distance; + Alembic::AbcGeom::OFloatProperty m_eye_separation; + +public: + AbcCameraWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + +private: + virtual void do_write(); +}; + +/* ************************************************************************** */ + +class AbcCameraReader : public AbcObjectReader { + Alembic::AbcGeom::ICameraSchema m_schema; + +public: + AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + + void readObjectData(Main *bmain, float time); +}; + +#endif /* __ABC_CAMERA_H__ */
\ No newline at end of file diff --git a/source/blender/alembic/intern/abc_curves.cc b/source/blender/alembic/intern/abc_curves.cc new file mode 100644 index 00000000000..4e1e4e7e490 --- /dev/null +++ b/source/blender/alembic/intern/abc_curves.cc @@ -0,0 +1,355 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#include "abc_curves.h" + +#include <cstdio> + +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "MEM_guardedalloc.h" + +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "BLI_listbase.h" + +#include "BKE_curve.h" +#include "BKE_object.h" + +#include "ED_curve.h" +} + +using Alembic::Abc::IInt32ArrayProperty; +using Alembic::Abc::Int32ArraySamplePtr; +using Alembic::Abc::FloatArraySamplePtr; +using Alembic::Abc::P3fArraySamplePtr; +using Alembic::Abc::UcharArraySamplePtr; + +using Alembic::AbcGeom::ICurves; +using Alembic::AbcGeom::ICurvesSchema; +using Alembic::AbcGeom::IFloatGeomParam; +using Alembic::AbcGeom::ISampleSelector; +using Alembic::AbcGeom::kWrapExisting; +using Alembic::AbcGeom::CurvePeriodicity; + +using Alembic::AbcGeom::OCurves; +using Alembic::AbcGeom::OCurvesSchema; +using Alembic::AbcGeom::ON3fGeomParam; +using Alembic::AbcGeom::OV2fGeomParam; + +/* ************************************************************************** */ + +AbcCurveWriter::AbcCurveWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(scene, ob, time_sampling, settings, parent) +{ + OCurves curves(parent->alembicXform(), m_name, m_time_sampling); + m_schema = curves.getSchema(); +} + +void AbcCurveWriter::do_write() +{ + Curve *curve = static_cast<Curve *>(m_object->data); + + std::vector<Imath::V3f> verts; + std::vector<int32_t> vert_counts; + std::vector<float> widths; + std::vector<float> weights; + std::vector<float> knots; + std::vector<uint8_t> orders; + Imath::V3f temp_vert; + + Alembic::AbcGeom::BasisType curve_basis; + Alembic::AbcGeom::CurveType curve_type; + Alembic::AbcGeom::CurvePeriodicity periodicity; + + Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first); + for (; nurbs; nurbs = nurbs->next) { + if (nurbs->bp) { + curve_basis = Alembic::AbcGeom::kNoBasis; + curve_type = Alembic::AbcGeom::kLinear; + + const int totpoint = nurbs->pntsu * nurbs->pntsv; + + const BPoint *point = nurbs->bp; + + for (int i = 0; i < totpoint; ++i, ++point) { + copy_zup_yup(temp_vert.getValue(), point->vec); + verts.push_back(temp_vert); + weights.push_back(point->vec[3]); + widths.push_back(point->radius); + } + } + else if (nurbs->bezt) { + curve_basis = Alembic::AbcGeom::kBezierBasis; + curve_type = Alembic::AbcGeom::kCubic; + + const int totpoint = nurbs->pntsu; + + const BezTriple *bezier = nurbs->bezt; + + /* TODO(kevin): store info about handles, Alembic doesn't have this. */ + for (int i = 0; i < totpoint; ++i, ++bezier) { + copy_zup_yup(temp_vert.getValue(), bezier->vec[1]); + verts.push_back(temp_vert); + widths.push_back(bezier->radius); + } + } + + if ((nurbs->flagu & CU_NURB_ENDPOINT) != 0) { + periodicity = Alembic::AbcGeom::kNonPeriodic; + } + else if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) { + periodicity = Alembic::AbcGeom::kPeriodic; + + /* Duplicate the start points to indicate that the curve is actually + * cyclic since other software need those. + */ + + for (int i = 0; i < nurbs->orderu; ++i) { + verts.push_back(verts[i]); + } + } + + if (nurbs->knotsu != NULL) { + const size_t num_knots = KNOTSU(nurbs); + + /* Add an extra knot at the beggining and end of the array since most apps + * require/expect them. */ + knots.resize(num_knots + 2); + + for (int i = 0; i < num_knots; ++i) { + knots[i + 1] = nurbs->knotsu[i]; + } + + if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) { + knots[0] = nurbs->knotsu[0]; + knots[num_knots - 1] = nurbs->knotsu[num_knots - 1]; + } + else { + knots[0] = (2.0f * nurbs->knotsu[0] - nurbs->knotsu[1]); + knots[num_knots - 1] = (2.0f * nurbs->knotsu[num_knots - 1] - nurbs->knotsu[num_knots - 2]); + } + } + + orders.push_back(nurbs->orderu + 1); + vert_counts.push_back(verts.size()); + } + + Alembic::AbcGeom::OFloatGeomParam::Sample width_sample; + width_sample.setVals(widths); + + m_sample = OCurvesSchema::Sample(verts, + vert_counts, + curve_type, + periodicity, + width_sample, + OV2fGeomParam::Sample(), /* UVs */ + ON3fGeomParam::Sample(), /* normals */ + curve_basis, + weights, + orders, + knots); + + m_sample.setSelfBounds(bounds()); + m_schema.set(m_sample); +} + +/* ************************************************************************** */ + +AbcCurveReader::AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + ICurves abc_curves(object, kWrapExisting); + m_curves_schema = abc_curves.getSchema(); + + get_min_max_time(m_curves_schema, m_min_time, m_max_time); +} + +bool AbcCurveReader::valid() const +{ + return m_curves_schema.valid(); +} + +void AbcCurveReader::readObjectData(Main *bmain, float time) +{ + Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVE); + + cu->flag |= CU_DEFORM_FILL | CU_3D; + cu->actvert = CU_ACT_NONE; + + m_object = BKE_object_add_only_object(bmain, OB_CURVE, m_object_name.c_str()); + m_object->data = cu; + + read_curve_sample(cu, m_curves_schema, time); + + if (has_animations(m_curves_schema, m_settings)) { + addCacheModifier(); + } +} + +/* ************************************************************************** */ + +void read_curve_sample(Curve *cu, const ICurvesSchema &schema, const float time) +{ + const ISampleSelector sample_sel(time); + ICurvesSchema::Sample smp = schema.getValue(sample_sel); + const Int32ArraySamplePtr num_vertices = smp.getCurvesNumVertices(); + const P3fArraySamplePtr positions = smp.getPositions(); + const FloatArraySamplePtr weights = smp.getPositionWeights(); + const FloatArraySamplePtr knots = smp.getKnots(); + const CurvePeriodicity periodicity = smp.getWrap(); + const UcharArraySamplePtr orders = smp.getOrders(); + + const IFloatGeomParam widths_param = schema.getWidthsParam(); + FloatArraySamplePtr radiuses; + + if (widths_param.valid()) { + IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(sample_sel); + radiuses = wsample.getVals(); + } + + int knot_offset = 0; + + size_t idx = 0; + for (size_t i = 0; i < num_vertices->size(); ++i) { + const int num_verts = (*num_vertices)[i]; + + Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), "abc_getnurb")); + nu->resolu = cu->resolu; + nu->resolv = cu->resolv; + nu->pntsu = num_verts; + nu->pntsv = 1; + nu->flag |= CU_SMOOTH; + + nu->orderu = num_verts; + + if (smp.getType() == Alembic::AbcGeom::kCubic) { + nu->orderu = 3; + } + else if (orders && orders->size() > i) { + nu->orderu = static_cast<short>((*orders)[i] - 1); + } + + if (periodicity == Alembic::AbcGeom::kNonPeriodic) { + nu->flagu |= CU_NURB_ENDPOINT; + } + else if (periodicity == Alembic::AbcGeom::kPeriodic) { + nu->flagu |= CU_NURB_CYCLIC; + + /* Check the number of points which overlap, we don't have + * overlapping points in Blender, but other software do use them to + * indicate that a curve is actually cyclic. Usually the number of + * overlapping points is equal to the order/degree of the curve. + */ + + const int start = idx; + const int end = idx + num_verts; + int overlap = 0; + + for (int j = start, k = end - nu->orderu; j < nu->orderu; ++j, ++k) { + const Imath::V3f &p1 = (*positions)[j]; + const Imath::V3f &p2 = (*positions)[k]; + + if (p1 != p2) { + break; + } + + ++overlap; + } + + /* TODO: Special case, need to figure out how it coincides with knots. */ + if (overlap == 0 && num_verts > 2 && (*positions)[start] == (*positions)[end - 1]) { + overlap = 1; + } + + /* There is no real cycles. */ + if (overlap == 0) { + nu->flagu &= ~CU_NURB_CYCLIC; + nu->flagu |= CU_NURB_ENDPOINT; + } + + nu->pntsu -= overlap; + } + + const bool do_weights = (weights != NULL) && (weights->size() > 1); + float weight = 1.0f; + + const bool do_radius = (radiuses != NULL) && (radiuses->size() > 1); + float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : 1.0f; + + nu->type = CU_NURBS; + + nu->bp = static_cast<BPoint *>(MEM_callocN(sizeof(BPoint) * nu->pntsu, "abc_getnurb")); + BPoint *bp = nu->bp; + + for (int j = 0; j < nu->pntsu; ++j, ++bp, ++idx) { + const Imath::V3f &pos = (*positions)[idx]; + + if (do_radius) { + radius = (*radiuses)[idx]; + } + + if (do_weights) { + weight = (*weights)[idx]; + } + + copy_yup_zup(bp->vec, pos.getValue()); + bp->vec[3] = weight; + bp->f1 = SELECT; + bp->radius = radius; + bp->weight = 1.0f; + } + + if (knots && knots->size() != 0) { + nu->knotsu = static_cast<float *>(MEM_callocN(KNOTSU(nu) * sizeof(float), "abc_setsplineknotsu")); + + /* TODO: second check is temporary, for until the check for cycles is rock solid. */ + if (periodicity == Alembic::AbcGeom::kPeriodic && (KNOTSU(nu) == knots->size() - 2)) { + /* Skip first and last knots. */ + for (size_t i = 1; i < knots->size() - 1; ++i) { + nu->knotsu[i - 1] = (*knots)[knot_offset + i]; + } + } + else { + /* TODO: figure out how to use the knots array from other + * software in this case. */ + BKE_nurb_knot_calc_u(nu); + } + + knot_offset += knots->size(); + } + else { + BKE_nurb_knot_calc_u(nu); + } + + BLI_addtail(BKE_curve_nurbs_get(cu), nu); + } +} diff --git a/source/blender/alembic/intern/abc_curves.h b/source/blender/alembic/intern/abc_curves.h new file mode 100644 index 00000000000..ee47f1931ea --- /dev/null +++ b/source/blender/alembic/intern/abc_curves.h @@ -0,0 +1,65 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#ifndef __ABC_CURVES_H__ +#define __ABC_CURVES_H__ + +#include "abc_object.h" + +struct Curve; + +/* ************************************************************************** */ + +class AbcCurveWriter : public AbcObjectWriter { + Alembic::AbcGeom::OCurvesSchema m_schema; + Alembic::AbcGeom::OCurvesSchema::Sample m_sample; + +public: + AbcCurveWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + void do_write(); +}; + +/* ************************************************************************** */ + +class AbcCurveReader : public AbcObjectReader { + Alembic::AbcGeom::ICurvesSchema m_curves_schema; + +public: + AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + + void readObjectData(Main *bmain, float time); +}; + +/* ************************************************************************** */ + +void read_curve_sample(Curve *cu, const Alembic::AbcGeom::ICurvesSchema &schema, const float time); + +#endif /* __ABC_CURVES_H__ */
\ No newline at end of file diff --git a/source/blender/alembic/intern/abc_customdata.cc b/source/blender/alembic/intern/abc_customdata.cc new file mode 100644 index 00000000000..ebf1b2ba96e --- /dev/null +++ b/source/blender/alembic/intern/abc_customdata.cc @@ -0,0 +1,379 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#include "abc_customdata.h" + +#include <Alembic/AbcGeom/All.h> +#include <algorithm> + +extern "C" { +#include "DNA_customdata_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +} + +/* NOTE: for now only UVs and Vertex Colors are supported for streaming. + * Although Alembic only allows for a single UV layer per {I|O}Schema, and does + * not have a vertex color concept, there is a convention between DCCs to write + * such data in a way that lets other DCC know what they are for. See comments + * in the write code for the conventions. */ + +using Alembic::AbcGeom::kVertexScope; +using Alembic::AbcGeom::kFacevaryingScope; + +using Alembic::Abc::C4fArraySample; +using Alembic::Abc::UInt32ArraySample; +using Alembic::Abc::V2fArraySample; + +using Alembic::AbcGeom::OV2fGeomParam; +using Alembic::AbcGeom::OC4fGeomParam; + +static void get_uvs(const CDStreamConfig &config, + std::vector<Imath::V2f> &uvs, + std::vector<uint32_t> &uvidx, + void *cd_data) +{ + MLoopUV *mloopuv_array = static_cast<MLoopUV *>(cd_data); + + if (!mloopuv_array) { + return; + } + + const int num_poly = config.totpoly; + MPoly *polygons = config.mpoly; + + if (!config.pack_uvs) { + int cnt = 0; + uvidx.resize(config.totloop); + uvs.resize(config.totloop); + + for (int i = 0; i < num_poly; ++i) { + MPoly ¤t_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 ¤t_poly = polygons[i]; + MLoopUV *loopuvpoly = mloopuv_array + current_poly.loopstart + current_poly.totloop; + + for (int j = 0; j < current_poly.totloop; ++j) { + loopuvpoly--; + Imath::V2f uv(loopuvpoly->uv[0], loopuvpoly->uv[1]); + + std::vector<Imath::V2f>::iterator it = std::find(uvs.begin(), uvs.end(), uv); + + if (it == uvs.end()) { + uvidx.push_back(uvs.size()); + uvs.push_back(uv); + } + else { + uvidx.push_back(std::distance(uvs.begin(), it)); + } + } + } + } +} + +const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data) +{ + const int active_uvlayer = CustomData_get_active_layer(data, CD_MLOOPUV); + + if (active_uvlayer < 0) { + return ""; + } + + void *cd_data = CustomData_get_layer_n(data, CD_MLOOPUV, active_uvlayer); + + get_uvs(config, sample.uvs, sample.indices, cd_data); + + return CustomData_get_layer_name(data, CD_MLOOPUV, active_uvlayer); +} + +/* Convention to write UVs: + * - V2fGeomParam on the arbGeomParam + * - set scope as face varying + * - (optional due to its behaviour) tag as UV using Alembic::AbcGeom::SetIsUV + */ +static void write_uv(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name) +{ + std::vector<uint32_t> indices; + std::vector<Imath::V2f> uvs; + + get_uvs(config, uvs, indices, data); + + if (indices.empty() || uvs.empty()) { + return; + } + + OV2fGeomParam param(prop, name, true, kFacevaryingScope, 1); + + OV2fGeomParam::Sample sample( + V2fArraySample(&uvs.front(), uvs.size()), + UInt32ArraySample(&indices.front(), indices.size()), + kFacevaryingScope); + + param.set(sample); +} + +/* Convention to write Vertex Colors: + * - C3fGeomParam/C4fGeomParam on the arbGeomParam + * - set scope as vertex varying + */ +static void write_mcol(const OCompoundProperty &prop, const CDStreamConfig &config, void *data, const char *name) +{ + const float cscale = 1.0f / 255.0f; + MPoly *polys = config.mpoly; + MLoop *mloops = config.mloop; + MCol *cfaces = static_cast<MCol *>(data); + + std::vector<Imath::C4f> buffer(config.totvert); + + Imath::C4f col; + + for (int i = 0; i < config.totpoly; ++i) { + MPoly *p = &polys[i]; + MCol *cface = &cfaces[p->loopstart + p->totloop]; + MLoop *mloop = &mloops[p->loopstart + p->totloop]; + + for (int j = 0; j < p->totloop; ++j) { + cface--; + mloop--; + + col[0] = cface->a * cscale; + col[1] = cface->r * cscale; + col[2] = cface->g * cscale; + col[3] = cface->b * cscale; + + buffer[mloop->v] = col; + } + } + + OC4fGeomParam param(prop, name, true, kFacevaryingScope, 1); + + OC4fGeomParam::Sample sample( + C4fArraySample(&buffer.front(), buffer.size()), + kVertexScope); + + param.set(sample); +} + +void write_custom_data(const OCompoundProperty &prop, const CDStreamConfig &config, CustomData *data, int data_type) +{ + CustomDataType cd_data_type = static_cast<CustomDataType>(data_type); + + if (!CustomData_has_layer(data, cd_data_type)) { + return; + } + + const int active_layer = CustomData_get_active_layer(data, cd_data_type); + const int tot_layers = CustomData_number_of_layers(data, cd_data_type); + + for (int i = 0; i < tot_layers; ++i) { + void *cd_data = CustomData_get_layer_n(data, cd_data_type, i); + const char *name = CustomData_get_layer_name(data, cd_data_type, i); + + if (cd_data_type == CD_MLOOPUV) { + /* Already exported. */ + if (i == active_layer) { + continue; + } + + write_uv(prop, config, cd_data, name); + } + else if (cd_data_type == CD_MLOOPCOL) { + write_mcol(prop, config, cd_data, name); + } + } +} + +/* ************************************************************************** */ + +using Alembic::Abc::C3fArraySamplePtr; +using Alembic::Abc::C4fArraySamplePtr; +using Alembic::Abc::PropertyHeader; + +using Alembic::AbcGeom::IC3fGeomParam; +using Alembic::AbcGeom::IC4fGeomParam; +using Alembic::AbcGeom::IV2fGeomParam; + +static void read_mcols(const CDStreamConfig &config, void *data, + const C3fArraySamplePtr &c3f_ptr, const C4fArraySamplePtr &c4f_ptr) +{ + MCol *cfaces = static_cast<MCol *>(data); + MPoly *polys = config.mpoly; + MLoop *mloops = config.mloop; + + if (c3f_ptr) { + for (int i = 0; i < config.totpoly; ++i) { + MPoly *p = &polys[i]; + MCol *cface = &cfaces[p->loopstart + p->totloop]; + MLoop *mloop = &mloops[p->loopstart + p->totloop]; + + for (int j = 0; j < p->totloop; ++j) { + cface--; + mloop--; + const Imath::C3f &color = (*c3f_ptr)[mloop->v]; + cface->a = FTOCHAR(color[0]); + cface->r = FTOCHAR(color[1]); + cface->g = FTOCHAR(color[2]); + cface->b = 255; + } + } + } + else if (c4f_ptr) { + for (int i = 0; i < config.totpoly; ++i) { + MPoly *p = &polys[i]; + MCol *cface = &cfaces[p->loopstart + p->totloop]; + MLoop *mloop = &mloops[p->loopstart + p->totloop]; + + for (int j = 0; j < p->totloop; ++j) { + cface--; + mloop--; + const Imath::C4f &color = (*c4f_ptr)[mloop->v]; + cface->a = FTOCHAR(color[0]); + cface->r = FTOCHAR(color[1]); + cface->g = FTOCHAR(color[2]); + cface->b = FTOCHAR(color[3]); + } + } + } +} + +static void read_uvs(const CDStreamConfig &config, void *data, + const Alembic::AbcGeom::V2fArraySamplePtr &uvs, + const Alembic::AbcGeom::UInt32ArraySamplePtr &indices) +{ + MPoly *mpolys = config.mpoly; + MLoopUV *mloopuvs = static_cast<MLoopUV *>(data); + + unsigned int uv_index, loop_index; + + for (int i = 0; i < config.totpoly; ++i) { + MPoly &poly = mpolys[i]; + + for (int f = 0; f < poly.totloop; ++f) { + loop_index = poly.loopstart + f; + uv_index = (*indices)[loop_index]; + const Imath::V2f &uv = (*uvs)[uv_index]; + + MLoopUV &loopuv = mloopuvs[loop_index]; + loopuv.uv[0] = uv[0]; + loopuv.uv[1] = uv[1]; + } + } +} + +static void read_custom_data_ex(const ICompoundProperty &prop, + const PropertyHeader &prop_header, + const CDStreamConfig &config, + const Alembic::Abc::ISampleSelector &iss, + int data_type) +{ + if (data_type == CD_MLOOPCOL) { + C3fArraySamplePtr c3f_ptr = C3fArraySamplePtr(); + C4fArraySamplePtr c4f_ptr = C4fArraySamplePtr(); + + if (IC3fGeomParam::matches(prop_header)) { + IC3fGeomParam color_param(prop, prop_header.getName()); + IC3fGeomParam::Sample sample; + color_param.getIndexed(sample, iss); + + c3f_ptr = sample.getVals(); + } + else if (IC4fGeomParam::matches(prop_header)) { + IC4fGeomParam color_param(prop, prop_header.getName()); + IC4fGeomParam::Sample sample; + color_param.getIndexed(sample, iss); + + c4f_ptr = sample.getVals(); + } + + void *cd_data = config.add_customdata_cb(config.user_data, + prop_header.getName().c_str(), + data_type); + + read_mcols(config, cd_data, c3f_ptr, c4f_ptr); + } + else if (data_type == CD_MLOOPUV) { + IV2fGeomParam uv_param(prop, prop_header.getName()); + IV2fGeomParam::Sample sample; + uv_param.getIndexed(sample, iss); + + if (uv_param.getScope() != kFacevaryingScope) { + return; + } + + void *cd_data = config.add_customdata_cb(config.user_data, + prop_header.getName().c_str(), + data_type); + + read_uvs(config, cd_data, sample.getVals(), sample.getIndices()); + } +} + +void read_custom_data(const ICompoundProperty &prop, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss) +{ + if (!prop.valid()) { + return; + } + + int num_uvs = 0; + int num_colors = 0; + + const size_t num_props = prop.getNumProperties(); + + for (size_t i = 0; i < num_props; ++i) { + const Alembic::Abc::PropertyHeader &prop_header = prop.getPropertyHeader(i); + + /* Read UVs according to convention. */ + if (IV2fGeomParam::matches(prop_header) && Alembic::AbcGeom::isUV(prop_header)) { + if (++num_uvs > MAX_MTFACE) { + continue; + } + + read_custom_data_ex(prop, prop_header, config, iss, CD_MLOOPUV); + continue; + } + + /* Read vertex colors according to convention. */ + if (IC3fGeomParam::matches(prop_header) || IC4fGeomParam::matches(prop_header)) { + if (++num_colors > MAX_MCOL) { + continue; + } + + read_custom_data_ex(prop, prop_header, config, iss, CD_MLOOPCOL); + continue; + } + } +} diff --git a/source/blender/alembic/intern/abc_customdata.h b/source/blender/alembic/intern/abc_customdata.h new file mode 100644 index 00000000000..3b16c0d9796 --- /dev/null +++ b/source/blender/alembic/intern/abc_customdata.h @@ -0,0 +1,93 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#ifndef __ABC_CUSTOMDATA_H__ +#define __ABC_CUSTOMDATA_H__ + +#include <Alembic/Abc/All.h> + +struct CustomData; +struct MLoop; +struct MLoopUV; +struct MPoly; +struct MVert; + +using Alembic::Abc::ICompoundProperty; +using Alembic::Abc::OCompoundProperty; + +struct UVSample { + std::vector<Imath::V2f> uvs; + std::vector<uint32_t> indices; +}; + +struct CDStreamConfig { + MLoop *mloop; + int totloop; + + MPoly *mpoly; + int totpoly; + + MVert *mvert; + int totvert; + + MLoopUV *mloopuv; + + CustomData *loopdata; + + bool pack_uvs; + + /* TODO(kevin): might need a better way to handle adding and/or updating + * custom datas such that it updates the custom data holder and its pointers + * properly. */ + void *user_data; + void *(*add_customdata_cb)(void *user_data, const char *name, int data_type); + + CDStreamConfig() + : mloop(NULL) + , totloop(0) + , mpoly(NULL) + , totpoly(0) + , totvert(0) + , pack_uvs(false) + , user_data(NULL) + , add_customdata_cb(NULL) + {} +}; + +/* Get the UVs for the main UV property on a OSchema. + * Returns the name of the UV layer. + * + * For now the active layer is used, maybe needs a better way to choose this. */ +const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data); + +void write_custom_data(const OCompoundProperty &prop, + const CDStreamConfig &config, + CustomData *data, + int data_type); + +void read_custom_data(const ICompoundProperty &prop, + const CDStreamConfig &config, + const Alembic::Abc::ISampleSelector &iss); + +#endif /* __ABC_CUSTOMDATA_H__ */ diff --git a/source/blender/alembic/intern/abc_exporter.cc b/source/blender/alembic/intern/abc_exporter.cc new file mode 100644 index 00000000000..127e8853789 --- /dev/null +++ b/source/blender/alembic/intern/abc_exporter.cc @@ -0,0 +1,600 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_exporter.h" + +#include <cmath> + +#ifdef WITH_ALEMBIC_HDF5 +# include <Alembic/AbcCoreHDF5/All.h> +#endif + +#include <Alembic/AbcCoreOgawa/All.h> + +#include "abc_camera.h" +#include "abc_curves.h" +#include "abc_hair.h" +#include "abc_mesh.h" +#include "abc_nurbs.h" +#include "abc_points.h" +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_camera_types.h" +#include "DNA_curve_types.h" +#include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" /* for FILE_MAX */ + +#include "BLI_string.h" + +#ifdef WIN32 +/* needed for MSCV because of snprintf from BLI_string */ +# include "BLI_winstuff.h" +#endif + +#include "BKE_anim.h" +#include "BKE_global.h" +#include "BKE_idprop.h" +#include "BKE_main.h" +#include "BKE_modifier.h" +#include "BKE_particle.h" +#include "BKE_scene.h" +} + +using Alembic::Abc::TimeSamplingPtr; +using Alembic::Abc::OBox3dProperty; + +/* ************************************************************************** */ + +ExportSettings::ExportSettings() + : scene(NULL) + , selected_only(false) + , visible_layers_only(false) + , renderable_only(false) + , frame_start(1) + , frame_end(1) + , frame_step_xform(1) + , frame_step_shape(1) + , shutter_open(0.0) + , shutter_close(1.0) + , global_scale(1.0f) + , flatten_hierarchy(false) + , export_normals(false) + , export_uvs(false) + , export_vcols(false) + , export_face_sets(false) + , export_vweigths(false) + , apply_subdiv(false) + , use_subdiv_schema(false) + , export_child_hairs(true) + , export_ogawa(true) + , pack_uv(false) + , do_convert_axis(false) +{} + +static bool object_is_smoke_sim(Object *ob) +{ + ModifierData *md = modifiers_findByType(ob, eModifierType_Smoke); + + if (md) { + SmokeModifierData *smd = reinterpret_cast<SmokeModifierData *>(md); + return (smd->type == MOD_SMOKE_TYPE_DOMAIN); + } + + return false; +} + +static bool object_is_shape(Object *ob) +{ + switch (ob->type) { + case OB_MESH: + if (object_is_smoke_sim(ob)) { + return false; + } + + return true; + case OB_CURVE: + case OB_SURF: + case OB_CAMERA: + return true; + default: + return false; + } +} + +static bool export_object(const ExportSettings * const settings, Object *ob) +{ + if (settings->selected_only && !parent_selected(ob)) { + return false; + } + + if (settings->visible_layers_only && !(settings->scene->lay & ob->lay)) { + return false; + } + + if (settings->renderable_only && (ob->restrictflag & OB_RESTRICT_RENDER)) { + return false; + } + + return true; +} + +/* ************************************************************************** */ + +AbcExporter::AbcExporter(Scene *scene, const char *filename, ExportSettings &settings) + : m_settings(settings) + , m_filename(filename) + , m_trans_sampling_index(0) + , m_shape_sampling_index(0) + , m_scene(scene) +{} + +AbcExporter::~AbcExporter() +{ + std::map<std::string, AbcTransformWriter*>::iterator it, e; + for (it = m_xforms.begin(), e = m_xforms.end(); it != e; ++it) { + delete it->second; + } + + for (int i = 0, e = m_shapes.size(); i != e; ++i) { + delete m_shapes[i]; + } +} + +void AbcExporter::getShutterSamples(double step, bool time_relative, + std::vector<double> &samples) +{ + samples.clear(); + + const double time_factor = time_relative ? m_scene->r.frs_sec : 1.0; + const double shutter_open = m_settings.shutter_open; + const double shutter_close = m_settings.shutter_close; + + /* sample all frame */ + if (shutter_open == 0.0 && shutter_close == 1.0) { + for (double t = 0; t < 1.0; t += step) { + samples.push_back(t / time_factor); + } + } + else { + /* sample between shutter open & close */ + const int nsamples = std::max((1.0 / step) - 1.0, 1.0); + const double time_inc = (shutter_close - shutter_open) / nsamples; + + for (double t = shutter_open; t <= shutter_close; t += time_inc) { + samples.push_back(t / time_factor); + } + } +} + +Alembic::Abc::TimeSamplingPtr AbcExporter::createTimeSampling(double step) +{ + TimeSamplingPtr time_sampling; + std::vector<double> samples; + + if (m_settings.frame_start == m_settings.frame_end) { + time_sampling.reset(new Alembic::Abc::TimeSampling()); + return time_sampling; + } + + getShutterSamples(step, true, samples); + + Alembic::Abc::TimeSamplingType ts(static_cast<uint32_t>(samples.size()), 1.0 / m_scene->r.frs_sec); + time_sampling.reset(new Alembic::Abc::TimeSampling(ts, samples)); + + return time_sampling; +} + +void AbcExporter::getFrameSet(double step, std::set<double> &frames) +{ + frames.clear(); + + std::vector<double> shutter_samples; + + getShutterSamples(step, false, shutter_samples); + + for (int frame = m_settings.frame_start; frame <= m_settings.frame_end; ++frame) { + for (int j = 0, e = shutter_samples.size(); j < e; ++j) { + frames.insert(frame + shutter_samples[j]); + } + } +} + +void AbcExporter::operator()(Main *bmain, float &progress, bool &was_canceled) +{ + std::string scene_name; + + if (bmain->name[0] != '\0') { + char scene_file_name[FILE_MAX]; + BLI_strncpy(scene_file_name, bmain->name, FILE_MAX); + scene_name = scene_file_name; + } + else { + scene_name = "untitled"; + } + + Scene *scene = m_scene; + const int fps = FPS; + char buf[16]; + snprintf(buf, 15, "%d", fps); + const std::string str_fps = buf; + + Alembic::AbcCoreAbstract::MetaData md; + md.set("FramesPerTimeUnit", str_fps); + + Alembic::Abc::Argument arg(md); + +#ifdef WITH_ALEMBIC_HDF5 + if (!m_settings.export_ogawa) { + m_archive = Alembic::Abc::CreateArchiveWithInfo(Alembic::AbcCoreHDF5::WriteArchive(), + m_filename, + "Blender", + scene_name, + Alembic::Abc::ErrorHandler::kThrowPolicy, + arg); + } + else +#endif + { + m_archive = Alembic::Abc::CreateArchiveWithInfo(Alembic::AbcCoreOgawa::WriteArchive(), + m_filename, + "Blender", + scene_name, + Alembic::Abc::ErrorHandler::kThrowPolicy, + arg); + } + + /* Create time samplings for transforms and shapes. */ + + TimeSamplingPtr trans_time = createTimeSampling(m_settings.frame_step_xform); + + m_trans_sampling_index = m_archive.addTimeSampling(*trans_time); + + TimeSamplingPtr shape_time; + + if ((m_settings.frame_step_shape == m_settings.frame_step_xform) || + (m_settings.frame_start == m_settings.frame_end)) + { + shape_time = trans_time; + m_shape_sampling_index = m_trans_sampling_index; + } + else { + shape_time = createTimeSampling(m_settings.frame_step_shape); + m_shape_sampling_index = m_archive.addTimeSampling(*shape_time); + } + + OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds(m_archive, m_trans_sampling_index); + + if (m_settings.flatten_hierarchy) { + createTransformWritersFlat(); + } + else { + createTransformWritersHierarchy(bmain->eval_ctx); + } + + createShapeWriters(bmain->eval_ctx); + + /* Make a list of frames to export. */ + + std::set<double> xform_frames; + getFrameSet(m_settings.frame_step_xform, xform_frames); + + std::set<double> shape_frames; + getFrameSet(m_settings.frame_step_shape, shape_frames); + + /* Merge all frames needed. */ + + std::set<double> frames(xform_frames); + frames.insert(shape_frames.begin(), shape_frames.end()); + + /* Export all frames. */ + + std::set<double>::const_iterator begin = frames.begin(); + std::set<double>::const_iterator end = frames.end(); + + const float size = static_cast<float>(frames.size()); + size_t i = 0; + + for (; begin != end; ++begin) { + progress = (++i / size); + + if (G.is_break) { + was_canceled = true; + break; + } + + double f = *begin; + setCurrentFrame(bmain, f); + + if (shape_frames.count(f) != 0) { + for (int i = 0, e = m_shapes.size(); i != e; ++i) { + m_shapes[i]->write(); + } + } + + if (xform_frames.count(f) == 0) { + continue; + } + + std::map<std::string, AbcTransformWriter *>::iterator xit, xe; + for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) { + xit->second->write(); + } + + /* Save the archive 's bounding box. */ + Imath::Box3d bounds; + + for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) { + Imath::Box3d box = xit->second->bounds(); + bounds.extendBy(box); + } + + archive_bounds_prop.set(bounds); + } +} + +void AbcExporter::createTransformWritersHierarchy(EvaluationContext *eval_ctx) +{ + Base *base = static_cast<Base *>(m_scene->base.first); + + while (base) { + Object *ob = base->object; + + if (export_object(&m_settings, ob)) { + switch(ob->type) { + case OB_LAMP: + case OB_LATTICE: + case OB_MBALL: + case OB_SPEAKER: + /* We do not export transforms for objects of these classes. */ + break; + + default: + exploreTransform(eval_ctx, ob, ob->parent, NULL); + } + } + + base = base->next; + } +} + +void AbcExporter::createTransformWritersFlat() +{ + Base *base = static_cast<Base *>(m_scene->base.first); + + while (base) { + Object *ob = base->object; + + if (export_object(&m_settings, ob) && object_is_shape(ob)) { + std::string name = get_id_name(ob); + m_xforms[name] = new AbcTransformWriter(ob, m_archive.getTop(), 0, m_trans_sampling_index, m_settings); + } + + base = base->next; + } +} + +void AbcExporter::exploreTransform(EvaluationContext *eval_ctx, Object *ob, Object *parent, Object *dupliObParent) +{ + createTransformWriter(ob, parent, dupliObParent); + + ListBase *lb = object_duplilist(eval_ctx, m_scene, ob); + + if (lb) { + DupliObject *link = static_cast<DupliObject *>(lb->first); + Object *dupli_ob = NULL; + Object *dupli_parent = NULL; + + while (link) { + if (link->type == OB_DUPLIGROUP) { + dupli_ob = link->ob; + dupli_parent = (dupli_ob->parent) ? dupli_ob->parent : ob; + + exploreTransform(eval_ctx, dupli_ob, dupli_parent, ob); + } + + link = link->next; + } + } + + free_object_duplilist(lb); +} + +void AbcExporter::createTransformWriter(Object *ob, Object *parent, Object *dupliObParent) +{ + const std::string name = get_object_dag_path_name(ob, dupliObParent); + + /* check if we have already created a transform writer for this object */ + if (m_xforms.find(name) != m_xforms.end()){ + std::cerr << "xform " << name << " already exists\n"; + return; + } + + AbcTransformWriter *parent_xform = NULL; + + if (parent) { + const std::string parentname = get_object_dag_path_name(parent, dupliObParent); + parent_xform = getXForm(parentname); + + if (!parent_xform) { + if (parent->parent) { + createTransformWriter(parent, parent->parent, dupliObParent); + } + else { + createTransformWriter(parent, dupliObParent, dupliObParent); + } + + parent_xform = getXForm(parentname); + } + } + + if (parent_xform) { + m_xforms[name] = new AbcTransformWriter(ob, parent_xform->alembicXform(), parent_xform, m_trans_sampling_index, m_settings); + m_xforms[name]->setParent(parent); + } + else { + m_xforms[name] = new AbcTransformWriter(ob, m_archive.getTop(), NULL, m_trans_sampling_index, m_settings); + } +} + +void AbcExporter::createShapeWriters(EvaluationContext *eval_ctx) +{ + Base *base = static_cast<Base *>(m_scene->base.first); + + while (base) { + Object *ob = base->object; + exploreObject(eval_ctx, ob, NULL); + + base = base->next; + } +} + +void AbcExporter::exploreObject(EvaluationContext *eval_ctx, Object *ob, Object *dupliObParent) +{ + ListBase *lb = object_duplilist(eval_ctx, m_scene, ob); + + createShapeWriter(ob, dupliObParent); + + if (lb) { + DupliObject *dupliob = static_cast<DupliObject *>(lb->first); + + while (dupliob) { + if (dupliob->type == OB_DUPLIGROUP) { + exploreObject(eval_ctx, dupliob->ob, ob); + } + + dupliob = dupliob->next; + } + } + + free_object_duplilist(lb); +} + +void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent) +{ + if (!object_is_shape(ob)) { + return; + } + + if (!export_object(&m_settings, ob)) { + return; + } + + std::string name; + + if (m_settings.flatten_hierarchy) { + name = get_id_name(ob); + } + else { + name = get_object_dag_path_name(ob, dupliObParent); + } + + AbcTransformWriter *xform = getXForm(name); + + if (!xform) { + std::cerr << __func__ << ": xform " << name << " is NULL\n"; + return; + } + + ParticleSystem *psys = static_cast<ParticleSystem *>(ob->particlesystem.first); + + for (; psys; psys = psys->next) { + if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) { + continue; + } + + if (psys->part->type == PART_HAIR) { + m_settings.export_child_hairs = true; + m_shapes.push_back(new AbcHairWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys)); + } + else if (psys->part->type == PART_EMITTER) { + m_shapes.push_back(new AbcPointsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings, psys)); + } + } + + switch(ob->type) { + case OB_MESH: + { + Mesh *me = static_cast<Mesh *>(ob->data); + + if (!me || me->totvert == 0) { + return; + } + + m_shapes.push_back(new AbcMeshWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings)); + break; + } + case OB_SURF: + { + Curve *cu = static_cast<Curve *>(ob->data); + + if (!cu) { + return; + } + + m_shapes.push_back(new AbcNurbsWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings)); + break; + } + case OB_CURVE: + { + Curve *cu = static_cast<Curve *>(ob->data); + + if (!cu) { + return; + } + + m_shapes.push_back(new AbcCurveWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings)); + break; + } + case OB_CAMERA: + { + Camera *cam = static_cast<Camera *>(ob->data); + + if (cam->type == CAM_PERSP) { + m_shapes.push_back(new AbcCameraWriter(m_scene, ob, xform, m_shape_sampling_index, m_settings)); + } + + break; + } + } +} + +AbcTransformWriter *AbcExporter::getXForm(const std::string &name) +{ + std::map<std::string, AbcTransformWriter *>::iterator it = m_xforms.find(name); + + if (it == m_xforms.end()) { + return NULL; + } + + return it->second; +} + +void AbcExporter::setCurrentFrame(Main *bmain, double t) +{ + m_scene->r.cfra = std::floor(t); + m_scene->r.subframe = t - m_scene->r.cfra; + BKE_scene_update_for_newframe(bmain->eval_ctx, bmain, m_scene, m_scene->lay); +} diff --git a/source/blender/alembic/intern/abc_exporter.h b/source/blender/alembic/intern/abc_exporter.h new file mode 100644 index 00000000000..070eb9ea81a --- /dev/null +++ b/source/blender/alembic/intern/abc_exporter.h @@ -0,0 +1,112 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_EXPORTER_H__ +#define __ABC_EXPORTER_H__ + +#include <Alembic/Abc/All.h> +#include <map> +#include <set> +#include <vector> + +class AbcObjectWriter; +class AbcTransformWriter; + +struct EvaluationContext; +struct Main; +struct Object; +struct Scene; + +struct ExportSettings { + ExportSettings(); + + Scene *scene; + + bool selected_only; + bool visible_layers_only; + bool renderable_only; + + double frame_start, frame_end; + double frame_step_xform; + double frame_step_shape; + double shutter_open; + double shutter_close; + float global_scale; + + bool flatten_hierarchy; + + bool export_normals; + bool export_uvs; + bool export_vcols; + bool export_face_sets; + bool export_vweigths; + + bool apply_subdiv; + bool use_subdiv_schema; + bool export_child_hairs; + bool export_ogawa; + bool pack_uv; + + bool do_convert_axis; + float convert_matrix[3][3]; +}; + +class AbcExporter { + ExportSettings &m_settings; + + const char *m_filename; + + Alembic::Abc::OArchive m_archive; + unsigned int m_trans_sampling_index, m_shape_sampling_index; + + Scene *m_scene; + + std::map<std::string, AbcTransformWriter *> m_xforms; + std::vector<AbcObjectWriter *> m_shapes; + +public: + AbcExporter(Scene *scene, const char *filename, ExportSettings &settings); + ~AbcExporter(); + + void operator()(Main *bmain, float &progress, bool &was_canceled); + +private: + void getShutterSamples(double step, bool time_relative, std::vector<double> &samples); + + Alembic::Abc::TimeSamplingPtr createTimeSampling(double step); + + void getFrameSet(double step, std::set<double> &frames); + + void createTransformWritersHierarchy(EvaluationContext *eval_ctx); + void createTransformWritersFlat(); + void createTransformWriter(Object *ob, Object *parent, Object *dupliObParent); + void exploreTransform(EvaluationContext *eval_ctx, Object *ob, Object *parent, Object *dupliObParent = NULL); + void exploreObject(EvaluationContext *eval_ctx, Object *ob, Object *dupliObParent); + void createShapeWriters(EvaluationContext *eval_ctx); + void createShapeWriter(Object *ob, Object *dupliObParent); + + AbcTransformWriter *getXForm(const std::string &name); + + void setCurrentFrame(Main *bmain, double t); +}; + +#endif /* __ABC_EXPORTER_H__ */ diff --git a/source/blender/alembic/intern/abc_hair.cc b/source/blender/alembic/intern/abc_hair.cc new file mode 100644 index 00000000000..45bf9b7ab8a --- /dev/null +++ b/source/blender/alembic/intern/abc_hair.cc @@ -0,0 +1,290 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_hair.h" + +#include <cstdio> + +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "MEM_guardedalloc.h" + +#include "DNA_modifier_types.h" + +#include "BLI_listbase.h" +#include "BLI_math_geom.h" + +#include "BKE_DerivedMesh.h" +#include "BKE_object.h" +#include "BKE_particle.h" +} + +using Alembic::Abc::P3fArraySamplePtr; + +using Alembic::AbcGeom::OCurves; +using Alembic::AbcGeom::OCurvesSchema; +using Alembic::AbcGeom::ON3fGeomParam; +using Alembic::AbcGeom::OV2fGeomParam; + +/* ************************************************************************** */ + +AbcHairWriter::AbcHairWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys) + : AbcObjectWriter(scene, ob, time_sampling, settings, parent) +{ + m_psys = psys; + + OCurves curves(parent->alembicXform(), m_name, m_time_sampling); + m_schema = curves.getSchema(); +} + +void AbcHairWriter::do_write() +{ + if (!m_psys) { + return; + } + + ParticleSystemModifierData *psmd = psys_get_modifier(m_object, m_psys); + + if (!psmd->dm_final) { + return; + } + + DerivedMesh *dm = mesh_create_derived_view(m_scene, m_object, CD_MASK_MESH); + DM_ensure_tessface(dm); + DM_update_tessface_data(dm); + + std::vector<Imath::V3f> verts; + std::vector<int32_t> hvertices; + std::vector<Imath::V2f> uv_values; + std::vector<Imath::V3f> norm_values; + + if (m_psys->pathcache) { + ParticleSettings *part = m_psys->part; + + write_hair_sample(dm, part, verts, norm_values, uv_values, hvertices); + + if (m_settings.export_child_hairs && m_psys->childcache) { + write_hair_child_sample(dm, part, verts, norm_values, uv_values, hvertices); + } + } + + dm->release(dm); + + Alembic::Abc::P3fArraySample iPos(verts); + m_sample = OCurvesSchema::Sample(iPos, hvertices); + m_sample.setBasis(Alembic::AbcGeom::kNoBasis); + m_sample.setType(Alembic::AbcGeom::kLinear); + m_sample.setWrap(Alembic::AbcGeom::kNonPeriodic); + + if (!uv_values.empty()) { + OV2fGeomParam::Sample uv_smp; + uv_smp.setVals(uv_values); + m_sample.setUVs(uv_smp); + } + + if (!norm_values.empty()) { + ON3fGeomParam::Sample norm_smp; + norm_smp.setVals(norm_values); + m_sample.setNormals(norm_smp); + } + + m_sample.setSelfBounds(bounds()); + m_schema.set(m_sample); +} + +void AbcHairWriter::write_hair_sample(DerivedMesh *dm, + ParticleSettings *part, + std::vector<Imath::V3f> &verts, + std::vector<Imath::V3f> &norm_values, + std::vector<Imath::V2f> &uv_values, + std::vector<int32_t> &hvertices) +{ + /* Get untransformed vertices, there's a xform under the hair. */ + float inv_mat[4][4]; + invert_m4_m4_safe(inv_mat, m_object->obmat); + + MTFace *mtface = static_cast<MTFace *>(CustomData_get_layer(&dm->faceData, CD_MTFACE)); + MFace *mface = dm->getTessFaceArray(dm); + MVert *mverts = dm->getVertArray(dm); + + if (!mtface || !mface) { + std::fprintf(stderr, "Warning, no UV set found for underlying geometry.\n"); + } + + ParticleData * pa = m_psys->particles; + int k; + + ParticleCacheKey **cache = m_psys->pathcache; + ParticleCacheKey *path; + float normal[3]; + Imath::V3f tmp_nor; + + for (int p = 0; p < m_psys->totpart; ++p, ++pa) { + /* underlying info for faces-only emission */ + path = cache[p]; + + if (part->from == PART_FROM_FACE && mtface) { + const int num = pa->num_dmcache >= 0 ? pa->num_dmcache : pa->num; + + if (num < dm->getNumTessFaces(dm)) { + MFace *face = static_cast<MFace *>(dm->getTessFaceData(dm, num, CD_MFACE)); + MTFace *tface = mtface + num; + + if (mface) { + float r_uv[2], mapfw[4], vec[3]; + + psys_interpolate_uvs(tface, face->v4, pa->fuv, r_uv); + uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1])); + + psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, normal, NULL, NULL, NULL, NULL); + + copy_zup_yup(tmp_nor.getValue(), normal); + norm_values.push_back(tmp_nor); + } + } + else { + std::fprintf(stderr, "Particle to faces overflow (%d/%d)\n", num, dm->getNumTessFaces(dm)); + } + } + else if (part->from == PART_FROM_VERT && mtface) { + /* vertex id */ + const int num = (pa->num_dmcache >= 0) ? pa->num_dmcache : pa->num; + + /* iterate over all faces to find a corresponding underlying UV */ + for (int n = 0; n < dm->getNumTessFaces(dm); ++n) { + MFace *face = static_cast<MFace *>(dm->getTessFaceData(dm, n, CD_MFACE)); + MTFace *tface = mtface + n; + unsigned int vtx[4]; + vtx[0] = face->v1; + vtx[1] = face->v2; + vtx[2] = face->v3; + vtx[3] = face->v4; + bool found = false; + + for (int o = 0; o < 4; ++o) { + if (o > 2 && vtx[o] == 0) { + break; + } + + if (vtx[o] == num) { + uv_values.push_back(Imath::V2f(tface->uv[o][0], tface->uv[o][1])); + + MVert *mv = mverts + vtx[o]; + + normal_short_to_float_v3(normal, mv->no); + copy_zup_yup(tmp_nor.getValue(), normal); + norm_values.push_back(tmp_nor); + found = true; + break; + } + } + + if (found) { + break; + } + } + } + + int steps = path->segments + 1; + hvertices.push_back(steps); + + for (k = 0; k < steps; ++k) { + float vert[3]; + copy_v3_v3(vert, path->co); + mul_m4_v3(inv_mat, vert); + + /* Convert Z-up to Y-up. */ + verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1])); + + ++path; + } + } +} + +void AbcHairWriter::write_hair_child_sample(DerivedMesh *dm, + ParticleSettings *part, + std::vector<Imath::V3f> &verts, + std::vector<Imath::V3f> &norm_values, + std::vector<Imath::V2f> &uv_values, + std::vector<int32_t> &hvertices) +{ + /* Get untransformed vertices, there's a xform under the hair. */ + float inv_mat[4][4]; + invert_m4_m4_safe(inv_mat, m_object->obmat); + + MTFace *mtface = static_cast<MTFace *>(CustomData_get_layer(&dm->faceData, CD_MTFACE)); + MFace *mface = dm->getTessFaceArray(dm); + MVert *mverts = dm->getVertArray(dm); + + if (!mtface || !mface) { + std::fprintf(stderr, "Warning, no UV set found for underlying geometry.\n"); + } + + ParticleCacheKey **cache = m_psys->childcache; + ParticleCacheKey *path; + + ChildParticle *pc = m_psys->child; + + for (int p = 0; p < m_psys->totchild; ++p, ++pc) { + path = cache[p]; + + if (part->from == PART_FROM_FACE) { + const int num = pc->num; + + MFace *face = static_cast<MFace *>(dm->getTessFaceData(dm, num, CD_MFACE)); + MTFace *tface = mtface + num; + + if (mface && mtface) { + float r_uv[2], tmpnor[3], mapfw[4], vec[3]; + + psys_interpolate_uvs(tface, face->v4, pc->fuv, r_uv); + uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1])); + + psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, tmpnor, NULL, NULL, NULL, NULL); + + /* Convert Z-up to Y-up. */ + norm_values.push_back(Imath::V3f(tmpnor[0], tmpnor[2], -tmpnor[1])); + } + } + + int steps = path->segments + 1; + hvertices.push_back(steps); + + for (int k = 0; k < steps; ++k) { + float vert[3]; + copy_v3_v3(vert, path->co); + mul_m4_v3(inv_mat, vert); + + /* Convert Z-up to Y-up. */ + verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1])); + + ++path; + } + } +} diff --git a/source/blender/alembic/intern/abc_hair.h b/source/blender/alembic/intern/abc_hair.h new file mode 100644 index 00000000000..d132b60be12 --- /dev/null +++ b/source/blender/alembic/intern/abc_hair.h @@ -0,0 +1,66 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_HAIR_H__ +#define __ABC_HAIR_H__ + +#include "abc_object.h" + +struct DerivedMesh; +struct ParticleSettings; +struct ParticleSystem; + +/* ************************************************************************** */ + +class AbcHairWriter : public AbcObjectWriter { + ParticleSystem *m_psys; + + Alembic::AbcGeom::OCurvesSchema m_schema; + Alembic::AbcGeom::OCurvesSchema::Sample m_sample; + +public: + AbcHairWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys); + +private: + virtual void do_write(); + + void write_hair_sample(DerivedMesh *dm, + ParticleSettings *part, + std::vector<Imath::V3f> &verts, + std::vector<Imath::V3f> &norm_values, + std::vector<Imath::V2f> &uv_values, + std::vector<int32_t> &hvertices); + + void write_hair_child_sample(DerivedMesh *dm, + ParticleSettings *part, + std::vector<Imath::V3f> &verts, + std::vector<Imath::V3f> &norm_values, + std::vector<Imath::V2f> &uv_values, + std::vector<int32_t> &hvertices); +}; + +#endif /* __ABC_HAIR_H__ */ diff --git a/source/blender/alembic/intern/abc_mesh.cc b/source/blender/alembic/intern/abc_mesh.cc new file mode 100644 index 00000000000..f1c7b6b3aa3 --- /dev/null +++ b/source/blender/alembic/intern/abc_mesh.cc @@ -0,0 +1,1213 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_mesh.h" + +#include <algorithm> + +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_fluidsim.h" +#include "DNA_object_types.h" + +#include "BLI_math_geom.h" +#include "BLI_string.h" + +#include "BKE_depsgraph.h" +#include "BKE_DerivedMesh.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_object.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_mesh.h" +} + +using Alembic::Abc::FloatArraySample; +using Alembic::Abc::ICompoundProperty; +using Alembic::Abc::Int32ArraySample; +using Alembic::Abc::Int32ArraySamplePtr; +using Alembic::Abc::P3fArraySamplePtr; +using Alembic::Abc::V2fArraySample; +using Alembic::Abc::V3fArraySample; +using Alembic::Abc::C4fArraySample; + +using Alembic::AbcGeom::IFaceSet; +using Alembic::AbcGeom::IFaceSetSchema; +using Alembic::AbcGeom::IObject; +using Alembic::AbcGeom::IPolyMesh; +using Alembic::AbcGeom::IPolyMeshSchema; +using Alembic::AbcGeom::ISampleSelector; +using Alembic::AbcGeom::ISubD; +using Alembic::AbcGeom::ISubDSchema; +using Alembic::AbcGeom::IV2fGeomParam; + +using Alembic::AbcGeom::OArrayProperty; +using Alembic::AbcGeom::OBoolProperty; +using Alembic::AbcGeom::OC3fArrayProperty; +using Alembic::AbcGeom::OC3fGeomParam; +using Alembic::AbcGeom::OC4fGeomParam; +using Alembic::AbcGeom::OCompoundProperty; +using Alembic::AbcGeom::OFaceSet; +using Alembic::AbcGeom::OFaceSetSchema; +using Alembic::AbcGeom::OFloatGeomParam; +using Alembic::AbcGeom::OInt32GeomParam; +using Alembic::AbcGeom::ON3fArrayProperty; +using Alembic::AbcGeom::ON3fGeomParam; +using Alembic::AbcGeom::OPolyMesh; +using Alembic::AbcGeom::OPolyMeshSchema; +using Alembic::AbcGeom::OSubD; +using Alembic::AbcGeom::OSubDSchema; +using Alembic::AbcGeom::OV2fGeomParam; +using Alembic::AbcGeom::OV3fGeomParam; + +using Alembic::AbcGeom::kFacevaryingScope; +using Alembic::AbcGeom::kVaryingScope; +using Alembic::AbcGeom::kVertexScope; +using Alembic::AbcGeom::kWrapExisting; +using Alembic::AbcGeom::UInt32ArraySample; +using Alembic::AbcGeom::N3fArraySamplePtr; +using Alembic::AbcGeom::IN3fGeomParam; + +/* ************************************************************************** */ + +/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */ + +static void get_vertices(DerivedMesh *dm, std::vector<Imath::V3f> &points) +{ + points.clear(); + points.resize(dm->getNumVerts(dm)); + + MVert *verts = dm->getVertArray(dm); + + for (int i = 0, e = dm->getNumVerts(dm); i < e; ++i) { + copy_zup_yup(points[i].getValue(), verts[i].co); + } +} + +static void get_topology(DerivedMesh *dm, + std::vector<int32_t> &poly_verts, + std::vector<int32_t> &loop_counts, + bool &smooth_normal) +{ + const int num_poly = dm->getNumPolys(dm); + const int num_loops = dm->getNumLoops(dm); + MLoop *mloop = dm->getLoopArray(dm); + MPoly *mpoly = dm->getPolyArray(dm); + + poly_verts.clear(); + loop_counts.clear(); + poly_verts.reserve(num_loops); + loop_counts.reserve(num_poly); + + /* NOTE: data needs to be written in the reverse order. */ + for (int i = 0; i < num_poly; ++i) { + MPoly &poly = mpoly[i]; + loop_counts.push_back(poly.totloop); + + smooth_normal |= ((poly.flag & ME_SMOOTH) != 0); + + MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1); + + for (int j = 0; j < poly.totloop; ++j, --loop) { + poly_verts.push_back(loop->v); + } + } +} + +static void get_material_indices(DerivedMesh *dm, std::vector<int32_t> &indices) +{ + indices.clear(); + indices.reserve(dm->getNumTessFaces(dm)); + + MPoly *mpolys = dm->getPolyArray(dm); + + for (int i = 1, e = dm->getNumPolys(dm); i < e; ++i) { + MPoly *mpoly = &mpolys[i]; + indices.push_back(mpoly->mat_nr); + } +} + +static void get_creases(DerivedMesh *dm, + std::vector<int32_t> &indices, + std::vector<int32_t> &lengths, + std::vector<float> &sharpnesses) +{ + const float factor = 1.0f / 255.0f; + + indices.clear(); + lengths.clear(); + sharpnesses.clear(); + + MEdge *edge = dm->getEdgeArray(dm); + + for (int i = 0, e = dm->getNumEdges(dm); i < e; ++i) { + const float sharpness = static_cast<float>(edge[i].crease) * factor; + + if (sharpness != 0.0f) { + indices.push_back(edge[i].v1); + indices.push_back(edge[i].v2); + sharpnesses.push_back(sharpness); + } + } + + lengths.resize(sharpnesses.size(), 2); +} + +static void get_vertex_normals(DerivedMesh *dm, std::vector<Imath::V3f> &normals) +{ + normals.clear(); + normals.resize(dm->getNumVerts(dm)); + + MVert *verts = dm->getVertArray(dm); + float no[3]; + + for (int i = 0, e = dm->getNumVerts(dm); i < e; ++i) { + normal_short_to_float_v3(no, verts[i].no); + copy_zup_yup(normals[i].getValue(), no); + } +} + +static void get_loop_normals(DerivedMesh *dm, std::vector<Imath::V3f> &normals) +{ + MPoly *mpoly = dm->getPolyArray(dm); + MPoly *mp = mpoly; + + MLoop *mloop = dm->getLoopArray(dm); + MLoop *ml = mloop; + + MVert *verts = dm->getVertArray(dm); + + const float (*lnors)[3] = static_cast<float(*)[3]>(dm->getLoopDataArray(dm, CD_NORMAL)); + + normals.clear(); + normals.resize(dm->getNumLoops(dm)); + + unsigned loop_index = 0; + + /* NOTE: data needs to be written in the reverse order. */ + + if (lnors) { + for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i, ++mp) { + ml = mloop + mp->loopstart + (mp->totloop - 1); + + for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) { + const int index = ml->v; + copy_zup_yup(normals[loop_index].getValue(), lnors[index]); + } + } + } + else { + float no[3]; + + for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i, ++mp) { + ml = mloop + mp->loopstart + (mp->totloop - 1); + + /* Flat shaded, use common normal for all verts. */ + if ((mp->flag & ME_SMOOTH) == 0) { + BKE_mesh_calc_poly_normal(mp, ml - (mp->totloop - 1), verts, no); + + for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) { + copy_zup_yup(normals[loop_index].getValue(), no); + } + } + else { + /* Smooth shaded, use individual vert normals. */ + for (int j = 0; j < mp->totloop; --ml, ++j, ++loop_index) { + normal_short_to_float_v3(no, verts[ml->v].no); + copy_zup_yup(normals[loop_index].getValue(), no); + } + } + } + } +} + +/* *************** Modifiers *************** */ + +/* check if the mesh is a subsurf, ignoring disabled modifiers and + * displace if it's after subsurf. */ +static ModifierData *get_subsurf_modifier(Scene *scene, Object *ob) +{ + ModifierData *md = static_cast<ModifierData *>(ob->modifiers.last); + + for (; md; md = md->prev) { + if (!modifier_isEnabled(scene, md, eModifierMode_Render)) { + continue; + } + + if (md->type == eModifierType_Subsurf) { + SubsurfModifierData *smd = reinterpret_cast<SubsurfModifierData*>(md); + + if (smd->subdivType == ME_CC_SUBSURF) { + return md; + } + } + + /* mesh is not a subsurf. break */ + if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) { + return NULL; + } + } + + return NULL; +} + +static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob) +{ + ModifierData *md = modifiers_findByType(ob, eModifierType_Fluidsim); + + if (md && (modifier_isEnabled(scene, md, eModifierMode_Render))) { + FluidsimModifierData *fsmd = reinterpret_cast<FluidsimModifierData *>(md); + + if (fsmd->fss && fsmd->fss->type == OB_FLUIDSIM_DOMAIN) { + return md; + } + } + + return NULL; +} + +/* ************************************************************************** */ + +AbcMeshWriter::AbcMeshWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(scene, ob, time_sampling, settings, parent) +{ + m_is_animated = isAnimated(); + m_subsurf_mod = NULL; + m_has_per_face_materials = false; + m_is_subd = false; + + /* If the object is static, use the default static time sampling. */ + if (!m_is_animated) { + time_sampling = 0; + } + + if (!m_settings.apply_subdiv) { + m_subsurf_mod = get_subsurf_modifier(m_scene, m_object); + m_is_subd = (m_subsurf_mod != NULL); + } + + m_is_liquid = (get_liquid_sim_modifier(m_scene, m_object) != NULL); + + while (parent->alembicXform().getChildHeader(m_name)) { + m_name.append("_"); + } + + if (m_settings.use_subdiv_schema && m_is_subd) { + OSubD subd(parent->alembicXform(), m_name, m_time_sampling); + m_subdiv_schema = subd.getSchema(); + } + else { + OPolyMesh mesh(parent->alembicXform(), m_name, m_time_sampling); + m_mesh_schema = mesh.getSchema(); + + OCompoundProperty typeContainer = m_mesh_schema.getUserProperties(); + OBoolProperty type(typeContainer, "meshtype"); + type.set(m_is_subd); + } +} + +AbcMeshWriter::~AbcMeshWriter() +{ + if (m_subsurf_mod) { + m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary; + } +} + +bool AbcMeshWriter::isAnimated() const +{ + /* Check if object has shape keys. */ + Mesh *me = static_cast<Mesh *>(m_object->data); + + if (me->key) { + return true; + } + + /* Test modifiers. */ + ModifierData *md = static_cast<ModifierData *>(m_object->modifiers.first); + + while (md) { + if (md->type != eModifierType_Subsurf) { + return true; + } + + md = md->next; + } + + return false; +} + +void AbcMeshWriter::do_write() +{ + /* We have already stored a sample for this object. */ + if (!m_first_frame && !m_is_animated) + return; + + DerivedMesh *dm = getFinalMesh(); + + try { + if (m_settings.use_subdiv_schema && m_subdiv_schema.valid()) { + writeSubD(dm); + } + else { + writeMesh(dm); + } + + freeMesh(dm); + } + catch (...) { + freeMesh(dm); + throw; + } +} + +void AbcMeshWriter::writeMesh(DerivedMesh *dm) +{ + std::vector<Imath::V3f> points, normals; + std::vector<int32_t> poly_verts, loop_counts; + + bool smooth_normal = false; + + get_vertices(dm, points); + get_topology(dm, poly_verts, loop_counts, smooth_normal); + + if (m_first_frame) { + writeCommonData(dm, m_mesh_schema); + } + + m_mesh_sample = OPolyMeshSchema::Sample(V3fArraySample(points), + Int32ArraySample(poly_verts), + Int32ArraySample(loop_counts)); + + UVSample sample; + if (m_settings.export_uvs) { + const char *name = get_uv_sample(sample, m_custom_data_config, &dm->loopData); + + if (!sample.indices.empty() && !sample.uvs.empty()) { + OV2fGeomParam::Sample uv_sample; + uv_sample.setVals(V2fArraySample(sample.uvs)); + uv_sample.setIndices(UInt32ArraySample(sample.indices)); + uv_sample.setScope(kFacevaryingScope); + + m_mesh_schema.setUVSourceName(name); + m_mesh_sample.setUVs(uv_sample); + } + + write_custom_data(m_mesh_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPUV); + } + + if (m_settings.export_normals) { + if (smooth_normal) { + get_loop_normals(dm, normals); + } + else { + get_vertex_normals(dm, normals); + } + + ON3fGeomParam::Sample normals_sample; + if (!normals.empty()) { + normals_sample.setScope((smooth_normal) ? kFacevaryingScope : kVertexScope); + normals_sample.setVals(V3fArraySample(normals)); + } + + m_mesh_sample.setNormals(normals_sample); + } + + if (m_is_liquid) { + std::vector<Imath::V3f> velocities; + getVelocities(dm, velocities); + + m_mesh_sample.setVelocities(V3fArraySample(velocities)); + } + + m_mesh_sample.setSelfBounds(bounds()); + + m_mesh_schema.set(m_mesh_sample); + + writeArbGeoParams(dm); +} + +void AbcMeshWriter::writeSubD(DerivedMesh *dm) +{ + std::vector<float> crease_sharpness; + std::vector<Imath::V3f> points; + std::vector<int32_t> poly_verts, loop_counts; + std::vector<int32_t> crease_indices, crease_lengths; + + bool smooth_normal = false; + + get_vertices(dm, points); + get_topology(dm, poly_verts, loop_counts, smooth_normal); + get_creases(dm, crease_indices, crease_lengths, crease_sharpness); + + if (m_first_frame) { + /* create materials' face_sets */ + writeCommonData(dm, m_subdiv_schema); + } + + m_subdiv_sample = OSubDSchema::Sample(V3fArraySample(points), + Int32ArraySample(poly_verts), + Int32ArraySample(loop_counts)); + + UVSample sample; + if (m_settings.export_uvs) { + const char *name = get_uv_sample(sample, m_custom_data_config, &dm->loopData); + + if (!sample.indices.empty() && !sample.uvs.empty()) { + OV2fGeomParam::Sample uv_sample; + uv_sample.setVals(V2fArraySample(sample.uvs)); + uv_sample.setIndices(UInt32ArraySample(sample.indices)); + uv_sample.setScope(kFacevaryingScope); + + m_subdiv_schema.setUVSourceName(name); + m_subdiv_sample.setUVs(uv_sample); + } + + write_custom_data(m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPUV); + } + + if (!crease_indices.empty()) { + m_subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices)); + m_subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths)); + m_subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness)); + } + + m_subdiv_sample.setSelfBounds(bounds()); + m_subdiv_schema.set(m_subdiv_sample); + + writeArbGeoParams(dm); +} + +template <typename Schema> +void AbcMeshWriter::writeCommonData(DerivedMesh *dm, Schema &schema) +{ + std::map< std::string, std::vector<int32_t> > geo_groups; + getGeoGroups(dm, geo_groups); + + std::map< std::string, std::vector<int32_t> >::iterator it; + for (it = geo_groups.begin(); it != geo_groups.end(); ++it) { + OFaceSet face_set = schema.createFaceSet(it->first); + OFaceSetSchema::Sample samp; + samp.setFaces(Int32ArraySample(it->second)); + face_set.getSchema().set(samp); + } +} + +DerivedMesh *AbcMeshWriter::getFinalMesh() +{ + /* We don't want subdivided mesh data */ + if (m_subsurf_mod) { + m_subsurf_mod->mode |= eModifierMode_DisableTemporary; + } + + DerivedMesh *dm = mesh_create_derived_render(m_scene, m_object, CD_MASK_MESH); + + if (m_subsurf_mod) { + m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary; + } + + m_custom_data_config.pack_uvs = m_settings.pack_uv; + m_custom_data_config.mpoly = dm->getPolyArray(dm); + m_custom_data_config.mloop = dm->getLoopArray(dm); + m_custom_data_config.totpoly = dm->getNumPolys(dm); + m_custom_data_config.totloop = dm->getNumLoops(dm); + m_custom_data_config.totvert = dm->getNumVerts(dm); + + return dm; +} + +void AbcMeshWriter::freeMesh(DerivedMesh *dm) +{ + dm->release(dm); +} + +void AbcMeshWriter::writeArbGeoParams(DerivedMesh *dm) +{ + if (m_is_liquid) { + /* We don't need anything more for liquid meshes. */ + return; + } + + if (m_settings.export_vcols) { + if (m_subdiv_schema.valid()) { + write_custom_data(m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPCOL); + } + else { + write_custom_data(m_mesh_schema.getArbGeomParams(), m_custom_data_config, &dm->loopData, CD_MLOOPCOL); + } + } + + if (m_first_frame && m_has_per_face_materials) { + std::vector<int32_t> material_indices; + + if (m_settings.export_face_sets) { + get_material_indices(dm, material_indices); + + OFaceSetSchema::Sample samp; + samp.setFaces(Int32ArraySample(material_indices)); + m_face_set.getSchema().set(samp); + } + } +} + +void AbcMeshWriter::getVelocities(DerivedMesh *dm, std::vector<Imath::V3f> &vels) +{ + const int totverts = dm->getNumVerts(dm); + + vels.clear(); + vels.resize(totverts); + + ModifierData *md = get_liquid_sim_modifier(m_scene, m_object); + FluidsimModifierData *fmd = reinterpret_cast<FluidsimModifierData *>(md); + FluidsimSettings *fss = fmd->fss; + + if (fss->meshVelocities) { + float *mesh_vels = reinterpret_cast<float *>(fss->meshVelocities); + + for (int i = 0; i < totverts; ++i) { + copy_zup_yup(vels[i].getValue(), mesh_vels); + mesh_vels += 3; + } + } + else { + std::fill(vels.begin(), vels.end(), Imath::V3f(0.0f)); + } +} + +void AbcMeshWriter::getGeoGroups( + DerivedMesh *dm, + std::map<std::string, std::vector<int32_t> > &geo_groups) +{ + const int num_poly = dm->getNumPolys(dm); + MPoly *polygons = dm->getPolyArray(dm); + + for (int i = 0; i < num_poly; ++i) { + MPoly ¤t_poly = polygons[i]; + short mnr = current_poly.mat_nr; + + Material *mat = give_current_material(m_object, mnr + 1); + + if (!mat) { + continue; + } + + std::string name = get_id_name(&mat->id); + + if (geo_groups.find(name) == geo_groups.end()) { + std::vector<int32_t> faceArray; + geo_groups[name] = faceArray; + } + + geo_groups[name].push_back(i); + } + + if (geo_groups.size() == 0) { + Material *mat = give_current_material(m_object, 1); + + std::string name = (mat) ? get_id_name(&mat->id) : "default"; + + std::vector<int32_t> faceArray; + + for (int i = 0, e = dm->getNumTessFaces(dm); i < e; ++i) { + faceArray.push_back(i); + } + + geo_groups[name] = faceArray; + } +} + +/* ************************************************************************** */ + +/* Some helpers for mesh generation */ +namespace utils { + +void mesh_add_verts(Mesh *mesh, size_t len) +{ + if (len == 0) { + return; + } + + const int totvert = mesh->totvert + len; + CustomData vdata; + CustomData_copy(&mesh->vdata, &vdata, CD_MASK_MESH, CD_DEFAULT, totvert); + CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert); + + if (!CustomData_has_layer(&vdata, CD_MVERT)) { + CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, NULL, totvert); + } + + CustomData_free(&mesh->vdata, mesh->totvert); + mesh->vdata = vdata; + BKE_mesh_update_customdata_pointers(mesh, false); + + mesh->totvert = totvert; +} + +static void mesh_add_mloops(Mesh *mesh, size_t len) +{ + if (len == 0) { + return; + } + + /* new face count */ + const int totloops = mesh->totloop + len; + + CustomData ldata; + CustomData_copy(&mesh->ldata, &ldata, CD_MASK_MESH, CD_DEFAULT, totloops); + CustomData_copy_data(&mesh->ldata, &ldata, 0, 0, mesh->totloop); + + if (!CustomData_has_layer(&ldata, CD_MLOOP)) { + CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, NULL, totloops); + } + + CustomData_free(&mesh->ldata, mesh->totloop); + mesh->ldata = ldata; + BKE_mesh_update_customdata_pointers(mesh, false); + + mesh->totloop = totloops; +} + +static void mesh_add_mpolygons(Mesh *mesh, size_t len) +{ + if (len == 0) { + return; + } + + const int totpolys = mesh->totpoly + len; + + CustomData pdata; + CustomData_copy(&mesh->pdata, &pdata, CD_MASK_MESH, CD_DEFAULT, totpolys); + CustomData_copy_data(&mesh->pdata, &pdata, 0, 0, mesh->totpoly); + + if (!CustomData_has_layer(&pdata, CD_MPOLY)) { + CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, NULL, totpolys); + } + + CustomData_free(&mesh->pdata, mesh->totpoly); + mesh->pdata = pdata; + BKE_mesh_update_customdata_pointers(mesh, false); + + mesh->totpoly = totpolys; +} + +static void build_mat_map(const Main *bmain, std::map<std::string, Material *> &mat_map) +{ + Material *material = static_cast<Material *>(bmain->mat.first); + + for (; material; material = static_cast<Material *>(material->id.next)) { + mat_map[material->id.name + 2] = material; + } +} + +static void assign_materials(Main *bmain, Object *ob, const std::map<std::string, int> &mat_index_map) +{ + bool can_assign = true; + std::map<std::string, int>::const_iterator it = mat_index_map.begin(); + + int matcount = 0; + for (; it != mat_index_map.end(); ++it, ++matcount) { + if (!BKE_object_material_slot_add(ob)) { + can_assign = false; + break; + } + } + + /* TODO(kevin): use global map? */ + std::map<std::string, Material *> mat_map; + build_mat_map(bmain, mat_map); + + std::map<std::string, Material *>::iterator mat_iter; + + if (can_assign) { + it = mat_index_map.begin(); + + for (; it != mat_index_map.end(); ++it) { + std::string mat_name = it->first; + mat_iter = mat_map.find(mat_name.c_str()); + + Material *assigned_name; + + if (mat_iter == mat_map.end()) { + assigned_name = BKE_material_add(bmain, mat_name.c_str()); + mat_map[mat_name] = assigned_name; + } + else { + assigned_name = mat_iter->second; + } + + assign_material(ob, assigned_name, it->second, BKE_MAT_ASSIGN_OBJECT); + } + } +} + +} /* namespace utils */ + +/* ************************************************************************** */ + +using Alembic::AbcGeom::UInt32ArraySamplePtr; +using Alembic::AbcGeom::V2fArraySamplePtr; + +struct AbcMeshData { + Int32ArraySamplePtr face_indices; + Int32ArraySamplePtr face_counts; + + P3fArraySamplePtr positions; + + N3fArraySamplePtr vertex_normals; + N3fArraySamplePtr face_normals; + + V2fArraySamplePtr uvs; + UInt32ArraySamplePtr uvs_indices; +}; + +static void *add_customdata_cb(void *user_data, const char *name, int data_type) +{ + Mesh *mesh = static_cast<Mesh *>(user_data); + CustomDataType cd_data_type = static_cast<CustomDataType>(data_type); + void *cd_ptr = NULL; + + int index = -1; + if (cd_data_type == CD_MLOOPUV) { + index = ED_mesh_uv_texture_add(mesh, name, true); + cd_ptr = CustomData_get_layer(&mesh->ldata, cd_data_type); + } + else if (cd_data_type == CD_MLOOPCOL) { + index = ED_mesh_color_add(mesh, name, true); + cd_ptr = CustomData_get_layer(&mesh->ldata, cd_data_type); + } + + if (index == -1) { + return NULL; + } + + return cd_ptr; +} + +CDStreamConfig create_config(Mesh *mesh) +{ + CDStreamConfig config; + + config.mvert = mesh->mvert; + config.mpoly = mesh->mpoly; + config.mloop = mesh->mloop; + config.totpoly = mesh->totpoly; + config.totloop = mesh->totloop; + config.user_data = mesh; + config.loopdata = &mesh->ldata; + config.add_customdata_cb = add_customdata_cb; + + return config; +} + +static void read_mverts(CDStreamConfig &config, const AbcMeshData &mesh_data) +{ + MVert *mverts = config.mvert; + const P3fArraySamplePtr &positions = mesh_data.positions; + const N3fArraySamplePtr &normals = mesh_data.vertex_normals; + + read_mverts(mverts, positions, normals); +} + +void read_mverts(MVert *mverts, const P3fArraySamplePtr &positions, const N3fArraySamplePtr &normals) +{ + for (int i = 0; i < positions->size(); ++i) { + MVert &mvert = mverts[i]; + Imath::V3f pos_in = (*positions)[i]; + + copy_yup_zup(mvert.co, pos_in.getValue()); + + mvert.bweight = 0; + + if (normals) { + Imath::V3f nor_in = (*normals)[i]; + + short no[3]; + normal_float_to_short_v3(no, nor_in.getValue()); + + copy_yup_zup(mvert.no, no); + } + } +} + +static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data) +{ + MPoly *mpolys = config.mpoly; + MLoop *mloops = config.mloop; + MLoopUV *mloopuvs = config.mloopuv; + + const Int32ArraySamplePtr &face_indices = mesh_data.face_indices; + const Int32ArraySamplePtr &face_counts = mesh_data.face_counts; + const V2fArraySamplePtr &uvs = mesh_data.uvs; + const UInt32ArraySamplePtr &uvs_indices = mesh_data.uvs_indices; + const N3fArraySamplePtr &normals = mesh_data.face_normals; + + const bool do_uvs = (mloopuvs && uvs && uvs_indices) && (uvs_indices->size() == face_indices->size()); + unsigned int loop_index = 0; + unsigned int rev_loop_index = 0; + unsigned int uv_index = 0; + + for (int i = 0; i < face_counts->size(); ++i) { + const int face_size = (*face_counts)[i]; + + MPoly &poly = mpolys[i]; + poly.loopstart = loop_index; + poly.totloop = face_size; + + if (normals != NULL) { + poly.flag |= ME_SMOOTH; + } + + /* NOTE: Alembic data is stored in the reverse order. */ + rev_loop_index = loop_index + (face_size - 1); + + for (int f = 0; f < face_size; ++f, ++loop_index, --rev_loop_index) { + MLoop &loop = mloops[rev_loop_index]; + loop.v = (*face_indices)[loop_index]; + + if (do_uvs) { + MLoopUV &loopuv = mloopuvs[rev_loop_index]; + + uv_index = (*uvs_indices)[loop_index]; + loopuv.uv[0] = (*uvs)[uv_index][0]; + loopuv.uv[1] = (*uvs)[uv_index][1]; + } + } + } +} + +ABC_INLINE void read_uvs_params(CDStreamConfig &config, + AbcMeshData &abc_data, + const IV2fGeomParam &uv, + const ISampleSelector &selector) +{ + if (!uv.valid()) { + return; + } + + IV2fGeomParam::Sample uvsamp; + uv.getIndexed(uvsamp, selector); + + abc_data.uvs = uvsamp.getVals(); + abc_data.uvs_indices = uvsamp.getIndices(); + + if (abc_data.uvs_indices->size() == config.totloop) { + std::string name = Alembic::Abc::GetSourceName(uv.getMetaData()); + + /* According to the convention, primary UVs should have had their name + * set using Alembic::Abc::SetSourceName, but you can't expect everyone + * to follow it! :) */ + if (name.empty()) { + name = uv.getName(); + } + + void *cd_ptr = config.add_customdata_cb(config.user_data, name.c_str(), CD_MLOOPUV); + config.mloopuv = static_cast<MLoopUV *>(cd_ptr); + } +} + +/* TODO(kevin): normals from Alembic files are not read in anymore, this is due + * to the fact that there are many issues that are not so easy to solve, mainly + * regarding the way normals are handled in Blender (MPoly.flag vs loop normals). + */ +ABC_INLINE void read_normals_params(AbcMeshData &abc_data, + const IN3fGeomParam &normals, + const ISampleSelector &selector) +{ + if (!normals.valid()) { + return; + } + + IN3fGeomParam::Sample normsamp = normals.getExpandedValue(selector); + + if (normals.getScope() == kFacevaryingScope) { + abc_data.face_normals = normsamp.getVals(); + } + else if ((normals.getScope() == kVertexScope) || (normals.getScope() == kVaryingScope)) { + abc_data.vertex_normals = N3fArraySamplePtr(); + } +} + +/* ************************************************************************** */ + +AbcMeshReader::AbcMeshReader(const IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + m_settings->read_flag |= MOD_MESHSEQ_READ_ALL; + + IPolyMesh ipoly_mesh(m_iobject, kWrapExisting); + m_schema = ipoly_mesh.getSchema(); + get_min_max_time(m_schema, m_min_time, m_max_time); +} + +bool AbcMeshReader::valid() const +{ + return m_schema.valid(); +} + +void AbcMeshReader::readObjectData(Main *bmain, float time) +{ + Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); + + m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); + m_object->data = mesh; + + const ISampleSelector sample_sel(time); + const IPolyMeshSchema::Sample sample = m_schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); + const Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); + + utils::mesh_add_verts(mesh, positions->size()); + utils::mesh_add_mpolygons(mesh, face_counts->size()); + utils::mesh_add_mloops(mesh, face_indices->size()); + + m_mesh_data = create_config(mesh); + + bool has_smooth_normals = false; + read_mesh_sample(m_settings, m_schema, sample_sel, m_mesh_data, has_smooth_normals); + + BKE_mesh_calc_normals(mesh); + BKE_mesh_calc_edges(mesh, false, false); + + if (m_settings->validate_meshes) { + BKE_mesh_validate(mesh, false, false); + } + + readFaceSetsSample(bmain, mesh, 0, sample_sel); + + if (has_animations(m_schema, m_settings)) { + addCacheModifier(); + } +} + +void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, size_t poly_start, + const ISampleSelector &sample_sel) +{ + std::vector<std::string> face_sets; + m_schema.getFaceSetNames(face_sets); + + if (face_sets.empty()) { + return; + } + + std::map<std::string, int> mat_map; + int current_mat = 0; + + for (int i = 0; i < face_sets.size(); ++i) { + const std::string &grp_name = face_sets[i]; + + if (mat_map.find(grp_name) == mat_map.end()) { + mat_map[grp_name] = 1 + current_mat++; + } + + const int assigned_mat = mat_map[grp_name]; + + const IFaceSet faceset = m_schema.getFaceSet(grp_name); + + if (!faceset.valid()) { + continue; + } + + const IFaceSetSchema face_schem = faceset.getSchema(); + const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel); + const Int32ArraySamplePtr group_faces = face_sample.getFaces(); + const size_t num_group_faces = group_faces->size(); + + for (size_t l = 0; l < num_group_faces; l++) { + size_t pos = (*group_faces)[l] + poly_start; + + if (pos >= mesh->totpoly) { + std::cerr << "Faceset overflow on " << faceset.getName() << '\n'; + break; + } + + MPoly &poly = mesh->mpoly[pos]; + poly.mat_nr = assigned_mat - 1; + } + } + + utils::assign_materials(bmain, m_object, mat_map); +} + +void read_mesh_sample(ImportSettings *settings, + const IPolyMeshSchema &schema, + const ISampleSelector &selector, + CDStreamConfig &config, + bool &do_normals) +{ + const IPolyMeshSchema::Sample sample = schema.getValue(selector); + + AbcMeshData abc_mesh_data; + abc_mesh_data.face_counts = sample.getFaceCounts(); + abc_mesh_data.face_indices = sample.getFaceIndices(); + abc_mesh_data.positions = sample.getPositions(); + + read_normals_params(abc_mesh_data, schema.getNormalsParam(), selector); + + do_normals = (abc_mesh_data.face_normals != NULL); + + if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) { + read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) { + read_mverts(config, abc_mesh_data); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) { + read_mpolys(config, abc_mesh_data); + } + + if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) { + read_custom_data(schema.getArbGeomParams(), config, selector); + } + + /* TODO: face sets */ +} + +/* ************************************************************************** */ + +ABC_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2) +{ + for (int i = 0, e = totedge; i < e; ++i) { + MEdge &edge = edges[i]; + + if (edge.v1 == v1 && edge.v2 == v2) { + return &edge; + } + } + + return NULL; +} + +AbcSubDReader::AbcSubDReader(const IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + m_settings->read_flag |= MOD_MESHSEQ_READ_ALL; + + ISubD isubd_mesh(m_iobject, kWrapExisting); + m_schema = isubd_mesh.getSchema(); + get_min_max_time(m_schema, m_min_time, m_max_time); +} + +bool AbcSubDReader::valid() const +{ + return m_schema.valid(); +} + +void AbcSubDReader::readObjectData(Main *bmain, float time) +{ + Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); + + m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); + m_object->data = mesh; + + const ISampleSelector sample_sel(time); + const ISubDSchema::Sample sample = m_schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); + const Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); + + utils::mesh_add_verts(mesh, positions->size()); + utils::mesh_add_mpolygons(mesh, face_counts->size()); + utils::mesh_add_mloops(mesh, face_indices->size()); + + m_mesh_data = create_config(mesh); + + read_subd_sample(m_settings, m_schema, sample_sel, m_mesh_data); + + Int32ArraySamplePtr indices = sample.getCreaseIndices(); + Alembic::Abc::FloatArraySamplePtr sharpnesses = sample.getCreaseSharpnesses(); + + MEdge *edges = mesh->medge; + + if (indices && sharpnesses) { + for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, ++s) { + MEdge *edge = find_edge(edges, mesh->totedge, (*indices)[i], (*indices)[i + 1]); + + if (edge) { + edge->crease = FTOCHAR((*sharpnesses)[s]); + } + } + + mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE; + } + + BKE_mesh_calc_normals(mesh); + BKE_mesh_calc_edges(mesh, false, false); + + if (m_settings->validate_meshes) { + BKE_mesh_validate(mesh, false, false); + } + + if (has_animations(m_schema, m_settings)) { + addCacheModifier(); + } +} + +void read_subd_sample(ImportSettings *settings, + const ISubDSchema &schema, + const ISampleSelector &selector, + CDStreamConfig &config) +{ + const ISubDSchema::Sample sample = schema.getValue(selector); + + AbcMeshData abc_mesh_data; + abc_mesh_data.face_counts = sample.getFaceCounts(); + abc_mesh_data.face_indices = sample.getFaceIndices(); + abc_mesh_data.vertex_normals = N3fArraySamplePtr(); + abc_mesh_data.face_normals = N3fArraySamplePtr(); + abc_mesh_data.positions = sample.getPositions(); + + if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) { + read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) { + read_mverts(config, abc_mesh_data); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) { + read_mpolys(config, abc_mesh_data); + } + + if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) { + read_custom_data(schema.getArbGeomParams(), config, selector); + } + + /* TODO: face sets */ +} diff --git a/source/blender/alembic/intern/abc_mesh.h b/source/blender/alembic/intern/abc_mesh.h new file mode 100644 index 00000000000..9dc222e5206 --- /dev/null +++ b/source/blender/alembic/intern/abc_mesh.h @@ -0,0 +1,152 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_MESH_H__ +#define __ABC_MESH_H__ + +#include "abc_customdata.h" +#include "abc_object.h" + +struct DerivedMesh; +struct Mesh; +struct ModifierData; + +/* ************************************************************************** */ + +class AbcMeshWriter : public AbcObjectWriter { + Alembic::AbcGeom::OPolyMeshSchema m_mesh_schema; + Alembic::AbcGeom::OPolyMeshSchema::Sample m_mesh_sample; + + Alembic::AbcGeom::OSubDSchema m_subdiv_schema; + Alembic::AbcGeom::OSubDSchema::Sample m_subdiv_sample; + + bool m_has_per_face_materials; + Alembic::AbcGeom::OFaceSet m_face_set; + Alembic::Abc::OArrayProperty m_mat_indices; + + bool m_is_animated; + ModifierData *m_subsurf_mod; + + CDStreamConfig m_custom_data_config; + + bool m_is_liquid; + bool m_is_subd; + +public: + AbcMeshWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + ~AbcMeshWriter(); + +private: + virtual void do_write(); + + bool isAnimated() const; + + void writeMesh(DerivedMesh *dm); + void writeSubD(DerivedMesh *dm); + + void getMeshInfo(DerivedMesh *dm, std::vector<float> &points, + std::vector<int32_t> &facePoints, + std::vector<int32_t> &faceCounts, + std::vector<int32_t> &creaseIndices, + std::vector<int32_t> &creaseLengths, + std::vector<float> &creaseSharpness); + + DerivedMesh *getFinalMesh(); + void freeMesh(DerivedMesh *dm); + + void getMaterialIndices(DerivedMesh *dm, std::vector<int32_t> &indices); + + void writeArbGeoParams(DerivedMesh *dm); + void getGeoGroups(DerivedMesh *dm, std::map<std::string, std::vector<int32_t> > &geoGroups); + + /* fluid surfaces support */ + void getVelocities(DerivedMesh *dm, std::vector<Imath::V3f> &vels); + + template <typename Schema> + void writeCommonData(DerivedMesh *dm, Schema &schema); +}; + +/* ************************************************************************** */ + +class AbcMeshReader : public AbcObjectReader { + Alembic::AbcGeom::IPolyMeshSchema m_schema; + + CDStreamConfig m_mesh_data; + +public: + AbcMeshReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + + void readObjectData(Main *bmain, float time); + +private: + void readFaceSetsSample(Main *bmain, Mesh *mesh, size_t poly_start, + const Alembic::AbcGeom::ISampleSelector &sample_sel); +}; + +void read_mesh_sample(ImportSettings *settings, + const Alembic::AbcGeom::IPolyMeshSchema &schema, + const Alembic::AbcGeom::ISampleSelector &selector, + CDStreamConfig &config, + bool &do_normals); + +/* ************************************************************************** */ + +class AbcSubDReader : public AbcObjectReader { + Alembic::AbcGeom::ISubDSchema m_schema; + + CDStreamConfig m_mesh_data; + +public: + AbcSubDReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + + void readObjectData(Main *bmain, float time); +}; + +void read_subd_sample(ImportSettings *settings, + const Alembic::AbcGeom::ISubDSchema &schema, + const Alembic::AbcGeom::ISampleSelector &selector, + CDStreamConfig &config); + +/* ************************************************************************** */ + +namespace utils { + +void mesh_add_verts(struct Mesh *mesh, size_t len); + +} + +void read_mverts(MVert *mverts, + const Alembic::AbcGeom::P3fArraySamplePtr &positions, + const Alembic::AbcGeom::N3fArraySamplePtr &normals); + +CDStreamConfig create_config(Mesh *mesh); + +#endif /* __ABC_MESH_H__ */ diff --git a/source/blender/alembic/intern/abc_nurbs.cc b/source/blender/alembic/intern/abc_nurbs.cc new file mode 100644 index 00000000000..a3c18ad6301 --- /dev/null +++ b/source/blender/alembic/intern/abc_nurbs.cc @@ -0,0 +1,367 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_nurbs.h" + +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "MEM_guardedalloc.h" + +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" + +#include "BKE_curve.h" +#include "BKE_object.h" +} + +using Alembic::AbcGeom::bool_t; +using Alembic::AbcGeom::FloatArraySample; +using Alembic::AbcGeom::FloatArraySamplePtr; +using Alembic::AbcGeom::MetaData; +using Alembic::AbcGeom::P3fArraySamplePtr; +using Alembic::AbcGeom::kWrapExisting; + +using Alembic::AbcGeom::IBoolProperty; +using Alembic::AbcGeom::ICompoundProperty; +using Alembic::AbcGeom::INuPatch; +using Alembic::AbcGeom::INuPatchSchema; +using Alembic::AbcGeom::IObject; +using Alembic::AbcGeom::ISampleSelector; + +using Alembic::AbcGeom::OBoolProperty; +using Alembic::AbcGeom::OCompoundProperty; +using Alembic::AbcGeom::ONuPatch; +using Alembic::AbcGeom::ONuPatchSchema; + +/* ************************************************************************** */ + +AbcNurbsWriter::AbcNurbsWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(scene, ob, time_sampling, settings, parent) +{ + m_is_animated = isAnimated(); + + /* if the object is static, use the default static time sampling */ + if (!m_is_animated) { + m_time_sampling = 0; + } + + Curve *curve = static_cast<Curve *>(m_object->data); + size_t numNurbs = BLI_listbase_count(&curve->nurb); + + for (size_t i = 0; i < numNurbs; ++i) { + std::stringstream str; + str << m_name << '_' << i; + + while (parent->alembicXform().getChildHeader(str.str())) { + str << "_"; + } + + ONuPatch nurbs(parent->alembicXform(), str.str().c_str(), m_time_sampling); + m_nurbs_schema.push_back(nurbs.getSchema()); + } +} + +bool AbcNurbsWriter::isAnimated() const +{ + /* check if object has shape keys */ + Curve *cu = static_cast<Curve *>(m_object->data); + return (cu->key != NULL); +} + +static void get_knots(std::vector<float> &knots, const int num_knots, float *nu_knots) +{ + if (num_knots <= 1) { + return; + } + + /* Add an extra knot at the beggining and end of the array since most apps + * require/expect them. */ + knots.reserve(num_knots + 2); + + knots.push_back(0.0f); + + for (int i = 0; i < num_knots; ++i) { + knots.push_back(nu_knots[i]); + } + + knots[0] = 2.0f * knots[1] - knots[2]; + knots.push_back(2.0f * knots[num_knots] - knots[num_knots - 1]); +} + +void AbcNurbsWriter::do_write() +{ + /* we have already stored a sample for this object. */ + if (!m_first_frame && !m_is_animated) { + return; + } + + if (!ELEM(m_object->type, OB_SURF, OB_CURVE)) { + return; + } + + Curve *curve = static_cast<Curve *>(m_object->data); + ListBase *nulb; + + if (m_object->curve_cache->deformed_nurbs.first != NULL) { + nulb = &m_object->curve_cache->deformed_nurbs; + } + else { + nulb = BKE_curve_nurbs_get(curve); + } + + size_t count = 0; + for (Nurb *nu = static_cast<Nurb *>(nulb->first); nu; nu = nu->next, ++count) { + std::vector<float> knotsU; + get_knots(knotsU, KNOTSU(nu), nu->knotsu); + + std::vector<float> knotsV; + get_knots(knotsV, KNOTSV(nu), nu->knotsv); + + const int size = nu->pntsu * nu->pntsv; + std::vector<Imath::V3f> positions(size); + std::vector<float> weights(size); + + const BPoint *bp = nu->bp; + + for (int i = 0; i < size; ++i, ++bp) { + copy_zup_yup(positions[i].getValue(), bp->vec); + weights[i] = bp->vec[3]; + } + + ONuPatchSchema::Sample sample; + sample.setUOrder(nu->orderu + 1); + sample.setVOrder(nu->orderv + 1); + sample.setPositions(positions); + sample.setPositionWeights(weights); + sample.setUKnot(FloatArraySample(knotsU)); + sample.setVKnot(FloatArraySample(knotsV)); + sample.setNu(nu->pntsu); + sample.setNv(nu->pntsv); + + /* TODO(kevin): to accomodate other software we should duplicate control + * points to indicate that a NURBS is cyclic. */ + OCompoundProperty user_props = m_nurbs_schema[count].getUserProperties(); + + if ((nu->flagu & CU_NURB_ENDPOINT) != 0) { + OBoolProperty prop(user_props, "endpoint_u"); + prop.set(true); + } + + if ((nu->flagv & CU_NURB_ENDPOINT) != 0) { + OBoolProperty prop(user_props, "endpoint_v"); + prop.set(true); + } + + if ((nu->flagu & CU_NURB_CYCLIC) != 0) { + OBoolProperty prop(user_props, "cyclic_u"); + prop.set(true); + } + + if ((nu->flagv & CU_NURB_CYCLIC) != 0) { + OBoolProperty prop(user_props, "cyclic_v"); + prop.set(true); + } + + m_nurbs_schema[count].set(sample); + } +} + +/* ************************************************************************** */ + +AbcNurbsReader::AbcNurbsReader(const IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + getNurbsPatches(m_iobject); + get_min_max_time(m_schemas[0].first, m_min_time, m_max_time); +} + +bool AbcNurbsReader::valid() const +{ + if (m_schemas.empty()) { + return false; + } + + std::vector< std::pair<INuPatchSchema, IObject> >::const_iterator it; + for (it = m_schemas.begin(); it != m_schemas.end(); ++it) { + const INuPatchSchema &schema = it->first; + + if (!schema.valid()) { + return false; + } + } + + return true; +} + +static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots) +{ + if (!knots || knots->size() == 0) { + return false; + } + + /* Skip first and last knots, as they are used for padding. */ + const size_t num_knots = knots->size() - 2; + nu_knots = static_cast<float *>(MEM_callocN(num_knots * sizeof(float), "abc_setsplineknotsu")); + + for (size_t i = 0; i < num_knots; ++i) { + nu_knots[i] = (*knots)[i + 1]; + } + + return true; +} + +void AbcNurbsReader::readObjectData(Main *bmain, float time) +{ + Curve *cu = static_cast<Curve *>(BKE_curve_add(bmain, "abc_curve", OB_SURF)); + cu->actvert = CU_ACT_NONE; + + std::vector< std::pair<INuPatchSchema, IObject> >::iterator it; + + for (it = m_schemas.begin(); it != m_schemas.end(); ++it) { + Nurb *nu = static_cast<Nurb *>(MEM_callocN(sizeof(Nurb), "abc_getnurb")); + nu->flag = CU_SMOOTH; + nu->type = CU_NURBS; + nu->resolu = cu->resolu; + nu->resolv = cu->resolv; + + const ISampleSelector sample_sel(time); + const INuPatchSchema &schema = it->first; + const INuPatchSchema::Sample smp = schema.getValue(sample_sel); + + nu->orderu = smp.getUOrder() - 1; + nu->orderv = smp.getVOrder() - 1; + nu->pntsu = smp.getNumU(); + nu->pntsv = smp.getNumV(); + + /* Read positions and weights. */ + + const P3fArraySamplePtr positions = smp.getPositions(); + const FloatArraySamplePtr weights = smp.getPositionWeights(); + + const size_t num_points = positions->size(); + + nu->bp = static_cast<BPoint *>(MEM_callocN(num_points * sizeof(BPoint), "abc_setsplinetype")); + + BPoint *bp = nu->bp; + float posw_in = 1.0f; + + for (int i = 0; i < num_points; ++i, ++bp) { + const Imath::V3f &pos_in = (*positions)[i]; + + if (weights) { + posw_in = (*weights)[i]; + } + + copy_yup_zup(bp->vec, pos_in.getValue()); + bp->vec[3] = posw_in; + bp->f1 = SELECT; + bp->radius = 1.0f; + bp->weight = 1.0f; + } + + /* Read knots. */ + + if (!set_knots(smp.getUKnot(), nu->knotsu)) { + BKE_nurb_knot_calc_u(nu); + } + + if (!set_knots(smp.getVKnot(), nu->knotsv)) { + BKE_nurb_knot_calc_v(nu); + } + + /* Read flags. */ + + ICompoundProperty user_props = schema.getUserProperties(); + + if (has_property(user_props, "enpoint_u")) { + nu->flagu |= CU_NURB_ENDPOINT; + } + + if (has_property(user_props, "enpoint_v")) { + nu->flagv |= CU_NURB_ENDPOINT; + } + + if (has_property(user_props, "cyclic_u")) { + nu->flagu |= CU_NURB_CYCLIC; + } + + if (has_property(user_props, "cyclic_v")) { + nu->flagv |= CU_NURB_CYCLIC; + } + + BLI_addtail(BKE_curve_nurbs_get(cu), nu); + } + + BLI_strncpy(cu->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1); + + m_object = BKE_object_add_only_object(bmain, OB_SURF, m_object_name.c_str()); + m_object->data = cu; +} + +void AbcNurbsReader::getNurbsPatches(const IObject &obj) +{ + if (!obj.valid()) { + return; + } + + const int num_children = obj.getNumChildren(); + + if (num_children == 0) { + INuPatch abc_nurb(obj, kWrapExisting); + INuPatchSchema schem = abc_nurb.getSchema(); + m_schemas.push_back(std::pair<INuPatchSchema, IObject>(schem, obj)); + return; + } + + for (int i = 0; i < num_children; ++i) { + bool ok = true; + IObject child(obj, obj.getChildHeader(i).getName()); + + if (!m_name.empty() && child.valid() && !begins_with(child.getFullName(), m_name)) { + ok = false; + } + + if (!child.valid()) { + continue; + } + + const MetaData &md = child.getMetaData(); + + if (INuPatch::matches(md) && ok) { + INuPatch abc_nurb(child, kWrapExisting); + INuPatchSchema schem = abc_nurb.getSchema(); + m_schemas.push_back(std::pair<INuPatchSchema, IObject>(schem, child)); + } + + getNurbsPatches(child); + } +} diff --git a/source/blender/alembic/intern/abc_nurbs.h b/source/blender/alembic/intern/abc_nurbs.h new file mode 100644 index 00000000000..1b2e7a8391f --- /dev/null +++ b/source/blender/alembic/intern/abc_nurbs.h @@ -0,0 +1,63 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_NURBS_H__ +#define __ABC_NURBS_H__ + +#include "abc_object.h" + +/* ************************************************************************** */ + +class AbcNurbsWriter : public AbcObjectWriter { + std::vector<Alembic::AbcGeom::ONuPatchSchema> m_nurbs_schema; + bool m_is_animated; + +public: + AbcNurbsWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + +private: + virtual void do_write(); + + bool isAnimated() const; +}; + +/* ************************************************************************** */ + +class AbcNurbsReader : public AbcObjectReader { + std::vector< std::pair<Alembic::AbcGeom::INuPatchSchema, Alembic::Abc::IObject> > m_schemas; + +public: + AbcNurbsReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + + void readObjectData(Main *bmain, float time); + +private: + void getNurbsPatches(const Alembic::Abc::IObject &obj); +}; + +#endif /* __ABC_NURBS_H__ */ diff --git a/source/blender/alembic/intern/abc_object.cc b/source/blender/alembic/intern/abc_object.cc new file mode 100644 index 00000000000..5b7b85f10ea --- /dev/null +++ b/source/blender/alembic/intern/abc_object.cc @@ -0,0 +1,238 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_object.h" + +#include "abc_util.h" + +extern "C" { +#include "DNA_cachefile_types.h" +#include "DNA_constraint_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" /* for FILE_MAX */ + +#include "BKE_constraint.h" +#include "BKE_depsgraph.h" +#include "BKE_idprop.h" +#include "BKE_library.h" +#include "BKE_modifier.h" +#include "BKE_object.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" +} + +using Alembic::AbcGeom::IObject; +using Alembic::AbcGeom::IXform; +using Alembic::AbcGeom::IXformSchema; + +using Alembic::AbcGeom::OCompoundProperty; +using Alembic::AbcGeom::ODoubleArrayProperty; +using Alembic::AbcGeom::ODoubleProperty; +using Alembic::AbcGeom::OFloatArrayProperty; +using Alembic::AbcGeom::OFloatProperty; +using Alembic::AbcGeom::OInt32ArrayProperty; +using Alembic::AbcGeom::OInt32Property; +using Alembic::AbcGeom::OStringArrayProperty; +using Alembic::AbcGeom::OStringProperty; + +/* ************************************************************************** */ + +AbcObjectWriter::AbcObjectWriter(Scene *scene, + Object *ob, + uint32_t time_sampling, + ExportSettings &settings, + AbcObjectWriter *parent) + : m_object(ob) + , m_settings(settings) + , m_scene(scene) + , m_time_sampling(time_sampling) + , m_first_frame(true) +{ + m_name = get_id_name(m_object) + "Shape"; + + if (parent) { + parent->addChild(this); + } +} + +AbcObjectWriter::~AbcObjectWriter() +{} + +void AbcObjectWriter::addChild(AbcObjectWriter *child) +{ + m_children.push_back(child); +} + +Imath::Box3d AbcObjectWriter::bounds() +{ + BoundBox *bb = BKE_object_boundbox_get(this->m_object); + + if (!bb) { + if (this->m_object->type != OB_CAMERA) { + std::cerr << "Boundbox is null!\n"; + } + + return Imath::Box3d(); + } + + /* Convert Z-up to Y-up. */ + this->m_bounds.min.x = bb->vec[0][0]; + this->m_bounds.min.y = bb->vec[0][2]; + this->m_bounds.min.z = -bb->vec[0][1]; + + this->m_bounds.max.x = bb->vec[6][0]; + this->m_bounds.max.y = bb->vec[6][2]; + this->m_bounds.max.z = -bb->vec[6][1]; + + return this->m_bounds; +} + +void AbcObjectWriter::write() +{ + do_write(); + m_first_frame = false; +} + +/* ************************************************************************** */ + +AbcObjectReader::AbcObjectReader(const IObject &object, ImportSettings &settings) + : m_name("") + , m_object_name("") + , m_data_name("") + , m_object(NULL) + , m_iobject(object) + , m_settings(&settings) + , m_min_time(std::numeric_limits<chrono_t>::max()) + , m_max_time(std::numeric_limits<chrono_t>::min()) +{ + m_name = object.getFullName(); + std::vector<std::string> parts; + split(m_name, '/', parts); + + if (parts.size() >= 2) { + m_object_name = parts[parts.size() - 2]; + m_data_name = parts[parts.size() - 1]; + } + else { + m_object_name = m_data_name = parts[parts.size() - 1]; + } +} + +AbcObjectReader::~AbcObjectReader() +{} + +const IObject &AbcObjectReader::iobject() const +{ + return m_iobject; +} + +Object *AbcObjectReader::object() const +{ + return m_object; +} + +void AbcObjectReader::readObjectMatrix(const float time) +{ + IXform ixform; + bool has_alembic_parent = false; + + /* Check that we have an empty object (locator, bone head/tail...). */ + if (IXform::matches(m_iobject.getMetaData())) { + ixform = IXform(m_iobject, Alembic::AbcGeom::kWrapExisting); + + /* See comment below. */ + has_alembic_parent = m_iobject.getParent().getParent().valid(); + } + /* Check that we have an object with actual data. */ + else if (IXform::matches(m_iobject.getParent().getMetaData())) { + ixform = IXform(m_iobject.getParent(), Alembic::AbcGeom::kWrapExisting); + + /* This is a bit hackish, but we need to make sure that extra + * transformations added to the matrix (rotation/scale) are only applied + * to root objects. The way objects and their hierarchy are created will + * need to be revisited at some point but for now this seems to do the + * trick. + * + * Explanation of the trick: + * The first getParent() will return this object's transformation matrix. + * The second getParent() will get the parent of the transform, but this + * might be the archive root ('/') which is valid, so we go passed it to + * make sure that there is no parent. + */ + has_alembic_parent = m_iobject.getParent().getParent().getParent().valid(); + } + /* Should not happen. */ + else { + return; + } + + const IXformSchema &schema(ixform.getSchema()); + + if (!schema.valid()) { + return; + } + + Alembic::AbcGeom::ISampleSelector sample_sel(time); + Alembic::AbcGeom::XformSample xs; + schema.get(xs, sample_sel); + + create_input_transform(sample_sel, ixform, m_object, m_object->obmat, m_settings->scale, has_alembic_parent); + + invert_m4_m4(m_object->imat, m_object->obmat); + + BKE_object_apply_mat4(m_object, m_object->obmat, false, false); + + if (!schema.isConstant()) { + bConstraint *con = BKE_constraint_add_for_object(m_object, NULL, CONSTRAINT_TYPE_TRANSFORM_CACHE); + bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data); + BLI_strncpy(data->object_path, m_iobject.getFullName().c_str(), FILE_MAX); + + data->cache_file = m_settings->cache_file; + id_us_plus(&data->cache_file->id); + } +} + +void AbcObjectReader::addCacheModifier() const +{ + ModifierData *md = modifier_new(eModifierType_MeshSequenceCache); + BLI_addtail(&m_object->modifiers, md); + + MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md); + + mcmd->cache_file = m_settings->cache_file; + id_us_plus(&mcmd->cache_file->id); + + BLI_strncpy(mcmd->object_path, m_iobject.getFullName().c_str(), FILE_MAX); +} + +chrono_t AbcObjectReader::minTime() const +{ + return m_min_time; +} + +chrono_t AbcObjectReader::maxTime() const +{ + return m_max_time; +} diff --git a/source/blender/alembic/intern/abc_object.h b/source/blender/alembic/intern/abc_object.h new file mode 100644 index 00000000000..2e885f296b1 --- /dev/null +++ b/source/blender/alembic/intern/abc_object.h @@ -0,0 +1,169 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_OBJECT_H__ +#define __ABC_OBJECT_H__ + +#include <Alembic/Abc/All.h> +#include <Alembic/AbcGeom/All.h> + +#include "abc_exporter.h" + +extern "C" { +#include "DNA_ID.h" +} + +class AbcTransformWriter; + +struct Main; +struct Object; + +/* ************************************************************************** */ + +class AbcObjectWriter { +protected: + Object *m_object; + ExportSettings &m_settings; + + Scene *m_scene; + uint32_t m_time_sampling; + + Imath::Box3d m_bounds; + std::vector<AbcObjectWriter *> m_children; + + std::vector< std::pair<std::string, IDProperty *> > m_props; + + bool m_first_frame; + std::string m_name; + +public: + AbcObjectWriter(Scene *scene, + Object *ob, + uint32_t time_sampling, + ExportSettings &settings, + AbcObjectWriter *parent = NULL); + + virtual ~AbcObjectWriter(); + + void addChild(AbcObjectWriter *child); + + virtual Imath::Box3d bounds(); + + void write(); + +private: + virtual void do_write() = 0; +}; + +/* ************************************************************************** */ + +class CacheFile; + +struct ImportSettings { + bool do_convert_mat; + float conversion_mat[4][4]; + + int from_up; + int from_forward; + float scale; + bool is_sequence; + bool set_frame_range; + + /* Length and frame offset of file sequences. */ + int sequence_len; + int offset; + + /* From MeshSeqCacheModifierData.read_flag */ + int read_flag; + + bool validate_meshes; + + CacheFile *cache_file; + + ImportSettings() + : do_convert_mat(false) + , from_up(0) + , from_forward(0) + , scale(1.0f) + , is_sequence(false) + , set_frame_range(false) + , sequence_len(1) + , offset(0) + , read_flag(0) + , validate_meshes(false) + , cache_file(NULL) + {} +}; + +template <typename Schema> +static bool has_animations(Schema &schema, ImportSettings *settings) +{ + if (settings->is_sequence) { + return true; + } + + if (!schema.isConstant()) { + return true; + } + + return false; +} + +/* ************************************************************************** */ + +using Alembic::AbcCoreAbstract::chrono_t; + +class AbcObjectReader { +protected: + std::string m_name; + std::string m_object_name; + std::string m_data_name; + Object *m_object; + Alembic::Abc::IObject m_iobject; + + ImportSettings *m_settings; + + chrono_t m_min_time; + chrono_t m_max_time; + +public: + explicit AbcObjectReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + virtual ~AbcObjectReader(); + + const Alembic::Abc::IObject &iobject() const; + + Object *object() const; + + virtual bool valid() const = 0; + + virtual void readObjectData(Main *bmain, float time) = 0; + + void readObjectMatrix(const float time); + + void addCacheModifier() const; + + chrono_t minTime() const; + chrono_t maxTime() const; +}; + +#endif /* __ABC_OBJECT_H__ */ diff --git a/source/blender/alembic/intern/abc_points.cc b/source/blender/alembic/intern/abc_points.cc new file mode 100644 index 00000000000..fa5b71ac094 --- /dev/null +++ b/source/blender/alembic/intern/abc_points.cc @@ -0,0 +1,198 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#include "abc_points.h" + +#include "abc_mesh.h" +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_mesh_types.h" + +#include "BKE_lattice.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +#include "BKE_particle.h" +#include "BKE_scene.h" + +#include "BLI_math.h" +} + +using Alembic::AbcGeom::kVertexScope; +using Alembic::AbcGeom::kWrapExisting; +using Alembic::AbcGeom::P3fArraySamplePtr; +using Alembic::AbcGeom::N3fArraySamplePtr; + +using Alembic::AbcGeom::ICompoundProperty; +using Alembic::AbcGeom::IN3fArrayProperty; +using Alembic::AbcGeom::IPoints; +using Alembic::AbcGeom::IPointsSchema; +using Alembic::AbcGeom::ISampleSelector; + +using Alembic::AbcGeom::OPoints; +using Alembic::AbcGeom::OPointsSchema; + +/* ************************************************************************** */ + +AbcPointsWriter::AbcPointsWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys) + : AbcObjectWriter(scene, ob, time_sampling, settings, parent) +{ + m_psys = psys; + + OPoints points(parent->alembicXform(), m_name, m_time_sampling); + m_schema = points.getSchema(); +} + +void AbcPointsWriter::do_write() +{ + if (!m_psys) { + return; + } + + std::vector<Imath::V3f> points; + std::vector<Imath::V3f> velocities; + std::vector<float> widths; + std::vector<uint64_t> ids; + + ParticleKey state; + + ParticleSimulationData sim; + sim.scene = m_scene; + sim.ob = m_object; + sim.psys = m_psys; + + m_psys->lattice_deform_data = psys_create_lattice_deform_data(&sim); + + uint64_t index = 0; + for (int p = 0; p < m_psys->totpart; p++) { + float pos[3], vel[3]; + + if (m_psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) { + continue; + } + + state.time = BKE_scene_frame_get(m_scene); + + if (psys_get_particle_state(&sim, p, &state, 0) == 0) { + continue; + } + + /* location */ + mul_v3_m4v3(pos, m_object->imat, state.co); + + /* velocity */ + sub_v3_v3v3(vel, state.co, m_psys->particles[p].prev_state.co); + + /* Convert Z-up to Y-up. */ + points.push_back(Imath::V3f(pos[0], pos[2], -pos[1])); + velocities.push_back(Imath::V3f(vel[0], vel[2], -vel[1])); + widths.push_back(m_psys->particles[p].size); + ids.push_back(index++); + } + + if (m_psys->lattice_deform_data) { + end_latt_deform(m_psys->lattice_deform_data); + m_psys->lattice_deform_data = NULL; + } + + Alembic::Abc::P3fArraySample psample(points); + Alembic::Abc::UInt64ArraySample idsample(ids); + Alembic::Abc::V3fArraySample vsample(velocities); + Alembic::Abc::FloatArraySample wsample_array(widths); + Alembic::AbcGeom::OFloatGeomParam::Sample wsample(wsample_array, kVertexScope); + + m_sample = OPointsSchema::Sample(psample, idsample, vsample, wsample); + m_sample.setSelfBounds(bounds()); + + m_schema.set(m_sample); +} + +/* ************************************************************************** */ + +AbcPointsReader::AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + IPoints ipoints(m_iobject, kWrapExisting); + m_schema = ipoints.getSchema(); + get_min_max_time(m_schema, m_min_time, m_max_time); +} + +bool AbcPointsReader::valid() const +{ + return m_schema.valid(); +} + +void AbcPointsReader::readObjectData(Main *bmain, float time) +{ + Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); + + const ISampleSelector sample_sel(time); + m_sample = m_schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = m_sample.getPositions(); + utils::mesh_add_verts(mesh, positions->size()); + + CDStreamConfig config = create_config(mesh); + read_points_sample(m_schema, sample_sel, config, time); + + if (m_settings->validate_meshes) { + BKE_mesh_validate(mesh, false, false); + } + + m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); + m_object->data = mesh; + + if (has_animations(m_schema, m_settings)) { + addCacheModifier(); + } +} + +void read_points_sample(const IPointsSchema &schema, + const ISampleSelector &selector, + CDStreamConfig &config, + float time) +{ + Alembic::AbcGeom::IPointsSchema::Sample sample = schema.getValue(selector); + + const P3fArraySamplePtr &positions = sample.getPositions(); + + ICompoundProperty prop = schema.getArbGeomParams(); + N3fArraySamplePtr vnormals; + + if (has_property(prop, "N")) { + const IN3fArrayProperty &normals_prop = IN3fArrayProperty(prop, "N", time); + + if (normals_prop) { + vnormals = normals_prop.getValue(selector); + } + } + + read_mverts(config.mvert, positions, vnormals); +} diff --git a/source/blender/alembic/intern/abc_points.h b/source/blender/alembic/intern/abc_points.h new file mode 100644 index 00000000000..51f3103bd8b --- /dev/null +++ b/source/blender/alembic/intern/abc_points.h @@ -0,0 +1,70 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#ifndef __ABC_POINTS_H__ +#define __ABC_POINTS_H__ + +#include "abc_object.h" +#include "abc_customdata.h" + +class ParticleSystem; + +/* ************************************************************************** */ + +class AbcPointsWriter : public AbcObjectWriter { + Alembic::AbcGeom::OPointsSchema m_schema; + Alembic::AbcGeom::OPointsSchema::Sample m_sample; + ParticleSystem *m_psys; + +public: + AbcPointsWriter(Scene *scene, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys); + + void do_write(); +}; + +/* ************************************************************************** */ + +class AbcPointsReader : public AbcObjectReader { + Alembic::AbcGeom::IPointsSchema m_schema; + Alembic::AbcGeom::IPointsSchema::Sample m_sample; + +public: + AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + + void readObjectData(Main *bmain, float time); +}; + +void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema, + const Alembic::AbcGeom::ISampleSelector &selector, + CDStreamConfig &config, + float time); + +#endif /* __ABC_POINTS_H__ */ diff --git a/source/blender/alembic/intern/abc_transform.cc b/source/blender/alembic/intern/abc_transform.cc new file mode 100644 index 00000000000..3326ae0ed84 --- /dev/null +++ b/source/blender/alembic/intern/abc_transform.cc @@ -0,0 +1,152 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_transform.h" + +#include <OpenEXR/ImathBoxAlgo.h> + +#include "abc_util.h" + +extern "C" { +#include "DNA_object_types.h" + +#include "BLI_math.h" + +#include "BKE_object.h" +} + +using Alembic::AbcGeom::OObject; +using Alembic::AbcGeom::OXform; + +/* ************************************************************************** */ + +static bool has_parent_camera(Object *ob) +{ + if (!ob->parent) { + return false; + } + + Object *parent = ob->parent; + + if (parent->type == OB_CAMERA) { + return true; + } + + return has_parent_camera(parent); +} + +/* ************************************************************************** */ + +AbcTransformWriter::AbcTransformWriter(Object *ob, + const OObject &abc_parent, + AbcTransformWriter *parent, + unsigned int time_sampling, + ExportSettings &settings) + : AbcObjectWriter(NULL, ob, time_sampling, settings, parent) +{ + m_is_animated = hasAnimation(m_object); + m_parent = NULL; + + if (!m_is_animated) { + time_sampling = 0; + } + + m_xform = OXform(abc_parent, get_id_name(m_object), time_sampling); + m_schema = m_xform.getSchema(); +} + +void AbcTransformWriter::do_write() +{ + if (m_first_frame) { + m_visibility = Alembic::AbcGeom::CreateVisibilityProperty(m_xform, m_xform.getSchema().getTimeSampling()); + } + + m_visibility.set(!(m_object->restrictflag & OB_RESTRICT_VIEW)); + + if (!m_first_frame && !m_is_animated) { + return; + } + + float mat[4][4]; + create_transform_matrix(m_object, mat); + + /* Only apply rotation to root camera, parenting will propagate it. */ + if (m_object->type == OB_CAMERA && !has_parent_camera(m_object)) { + float rot_mat[4][4]; + unit_m4(rot_mat); + rotate_m4(rot_mat, 'X', -M_PI_2); + mul_m4_m4m4(mat, mat, rot_mat); + } + + if (!m_object->parent) { + /* Only apply scaling to root objects, parenting will propagate it. */ + float scale_mat[4][4]; + scale_m4_fl(scale_mat, m_settings.global_scale); + mul_m4_m4m4(mat, mat, scale_mat); + mul_v3_fl(mat[3], m_settings.global_scale); + } + + m_matrix = convert_matrix(mat); + + m_sample.setMatrix(m_matrix); + m_schema.set(m_sample); +} + +Imath::Box3d AbcTransformWriter::bounds() +{ + Imath::Box3d bounds; + + for (int i = 0; i < m_children.size(); ++i) { + Imath::Box3d box(m_children[i]->bounds()); + bounds.extendBy(box); + } + + return Imath::transform(bounds, m_matrix); +} + +bool AbcTransformWriter::hasAnimation(Object */*ob*/) const +{ + /* TODO(kevin): implement this. */ + return true; +} + +/* ************************************************************************** */ + +AbcEmptyReader::AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + Alembic::AbcGeom::IXform xform(object, Alembic::AbcGeom::kWrapExisting); + m_schema = xform.getSchema(); + + get_min_max_time(m_schema, m_min_time, m_max_time); +} + +bool AbcEmptyReader::valid() const +{ + return m_schema.valid(); +} + +void AbcEmptyReader::readObjectData(Main *bmain, float /*time*/) +{ + m_object = BKE_object_add_only_object(bmain, OB_EMPTY, m_object_name.c_str()); + m_object->data = NULL; +} diff --git a/source/blender/alembic/intern/abc_transform.h b/source/blender/alembic/intern/abc_transform.h new file mode 100644 index 00000000000..6a3aae216f2 --- /dev/null +++ b/source/blender/alembic/intern/abc_transform.h @@ -0,0 +1,73 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_TRANSFORM_H__ +#define __ABC_TRANSFORM_H__ + +#include "abc_object.h" + +#include <Alembic/AbcGeom/All.h> + +/* ************************************************************************** */ + +class AbcTransformWriter : public AbcObjectWriter { + Alembic::AbcGeom::OXform m_xform; + Alembic::AbcGeom::OXformSchema m_schema; + Alembic::AbcGeom::XformSample m_sample; + Alembic::AbcGeom::OVisibilityProperty m_visibility; + Alembic::Abc::M44d m_matrix; + + bool m_is_animated; + Object *m_parent; + bool m_visible; + +public: + AbcTransformWriter(Object *ob, + const Alembic::AbcGeom::OObject &abc_parent, + AbcTransformWriter *parent, + unsigned int time_sampling, + ExportSettings &settings); + + Alembic::AbcGeom::OXform &alembicXform() { return m_xform;} + virtual Imath::Box3d bounds(); + void setParent(Object *p) { m_parent = p; } + +private: + virtual void do_write(); + + bool hasAnimation(Object *ob) const; +}; + +/* ************************************************************************** */ + +class AbcEmptyReader : public AbcObjectReader { + Alembic::AbcGeom::IXformSchema m_schema; + +public: + AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + + void readObjectData(Main *bmain, float time); +}; + +#endif /* __ABC_TRANSFORM_H__ */ diff --git a/source/blender/alembic/intern/abc_util.cc b/source/blender/alembic/intern/abc_util.cc new file mode 100644 index 00000000000..fbab0bcdf34 --- /dev/null +++ b/source/blender/alembic/intern/abc_util.cc @@ -0,0 +1,437 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "abc_util.h" + +#include <algorithm> + +extern "C" { +#include "DNA_object_types.h" + +#include "BLI_math.h" +} + +std::string get_id_name(Object *ob) +{ + if (!ob) { + return ""; + } + + return get_id_name(&ob->id); +} + +std::string get_id_name(ID *id) +{ + std::string name(id->name + 2); + std::replace(name.begin(), name.end(), ' ', '_'); + std::replace(name.begin(), name.end(), '.', '_'); + std::replace(name.begin(), name.end(), ':', '_'); + + return name; +} + +std::string get_object_dag_path_name(Object *ob, Object *dupli_parent) +{ + std::string name = get_id_name(ob); + + Object *p = ob->parent; + + while (p) { + name = get_id_name(p) + "/" + name; + p = p->parent; + } + + if (dupli_parent && (ob != dupli_parent)) { + name = get_id_name(dupli_parent) + "/" + name; + } + + return name; +} + +bool object_selected(Object *ob) +{ + return ob->flag & SELECT; +} + +bool parent_selected(Object *ob) +{ + if (object_selected(ob)) { + return true; + } + + bool do_export = false; + + Object *parent = ob->parent; + + while (parent != NULL) { + if (object_selected(parent)) { + do_export = true; + break; + } + + parent = parent->parent; + } + + return do_export; +} + +Imath::M44d convert_matrix(float mat[4][4]) +{ + Imath::M44d m; + + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + m[i][j] = mat[i][j]; + } + } + + return m; +} + +void split(const std::string &s, const char delim, std::vector<std::string> &tokens) +{ + tokens.clear(); + + std::stringstream ss(s); + std::string item; + + while (std::getline(ss, item, delim)) { + if (!item.empty()) { + tokens.push_back(item); + } + } +} + +/* Create a rotation matrix for each axis from euler angles. + * Euler angles are swaped to change coordinate system. */ +static void create_rotation_matrix( + float rot_x_mat[3][3], float rot_y_mat[3][3], + float rot_z_mat[3][3], const float euler[3], const bool to_yup) +{ + const float rx = euler[0]; + const float ry = (to_yup) ? euler[2] : -euler[2]; + const float rz = (to_yup) ? -euler[1] : euler[1]; + + unit_m3(rot_x_mat); + unit_m3(rot_y_mat); + unit_m3(rot_z_mat); + + rot_x_mat[1][1] = cos(rx); + rot_x_mat[2][1] = -sin(rx); + rot_x_mat[1][2] = sin(rx); + rot_x_mat[2][2] = cos(rx); + + rot_y_mat[2][2] = cos(ry); + rot_y_mat[0][2] = -sin(ry); + rot_y_mat[2][0] = sin(ry); + rot_y_mat[0][0] = cos(ry); + + rot_z_mat[0][0] = cos(rz); + rot_z_mat[1][0] = -sin(rz); + rot_z_mat[0][1] = sin(rz); + rot_z_mat[1][1] = cos(rz); +} + +/* Recompute transform matrix of object in new coordinate system + * (from Y-Up to Z-Up). */ +void create_transform_matrix(float r_mat[4][4]) +{ + float rot_mat[3][3], rot[3][3], scale_mat[4][4], invmat[4][4], transform_mat[4][4]; + float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3]; + float loc[3], scale[3], euler[3]; + + zero_v3(loc); + zero_v3(scale); + zero_v3(euler); + unit_m3(rot); + unit_m3(rot_mat); + unit_m4(scale_mat); + unit_m4(transform_mat); + unit_m4(invmat); + + /* Compute rotation matrix. */ + + /* Extract location, rotation, and scale from matrix. */ + mat4_to_loc_rot_size(loc, rot, scale, r_mat); + + /* Get euler angles from rotation matrix. */ + mat3_to_eulO(euler, ROT_MODE_XYZ, rot); + + /* Create X, Y, Z rotation matrices from euler angles. */ + create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, false); + + /* Concatenate rotation matrices. */ + mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); + + /* Add rotation matrix to transformation matrix. */ + copy_m4_m3(transform_mat, rot_mat); + + /* Add translation to transformation matrix. */ + copy_yup_zup(transform_mat[3], loc); + + /* Create scale matrix. */ + scale_mat[0][0] = scale[0]; + scale_mat[1][1] = scale[2]; + scale_mat[2][2] = scale[1]; + + /* Add scale to transformation matrix. */ + mul_m4_m4m4(transform_mat, transform_mat, scale_mat); + + copy_m4_m4(r_mat, transform_mat); +} + +void create_input_transform(const Alembic::AbcGeom::ISampleSelector &sample_sel, + const Alembic::AbcGeom::IXform &ixform, Object *ob, + float r_mat[4][4], float scale, bool has_alembic_parent) +{ + + const Alembic::AbcGeom::IXformSchema &ixform_schema = ixform.getSchema(); + Alembic::AbcGeom::XformSample xs; + ixform_schema.get(xs, sample_sel); + const Imath::M44d &xform = xs.getMatrix(); + + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + r_mat[i][j] = xform[i][j]; + } + } + + if (ob->type == OB_CAMERA) { + float cam_to_yup[4][4]; + unit_m4(cam_to_yup); + rotate_m4(cam_to_yup, 'X', M_PI_2); + mul_m4_m4m4(r_mat, r_mat, cam_to_yup); + } + + create_transform_matrix(r_mat); + + if (ob->parent) { + mul_m4_m4m4(r_mat, ob->parent->obmat, r_mat); + } + /* TODO(kevin) */ + else if (!has_alembic_parent) { + /* Only apply scaling to root objects, parenting will propagate it. */ + float scale_mat[4][4]; + scale_m4_fl(scale_mat, scale); + mul_m4_m4m4(r_mat, r_mat, scale_mat); + mul_v3_fl(r_mat[3], scale); + } +} + +/* Recompute transform matrix of object in new coordinate system (from Z-Up to Y-Up). */ +void create_transform_matrix(Object *obj, float transform_mat[4][4]) +{ + float rot_mat[3][3], rot[3][3], scale_mat[4][4], invmat[4][4], mat[4][4]; + float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3]; + float loc[3], scale[3], euler[3]; + + zero_v3(loc); + zero_v3(scale); + zero_v3(euler); + unit_m3(rot); + unit_m3(rot_mat); + unit_m4(scale_mat); + unit_m4(transform_mat); + unit_m4(invmat); + unit_m4(mat); + + /* get local matrix. */ + if (obj->parent) { + invert_m4_m4(invmat, obj->parent->obmat); + mul_m4_m4m4(mat, invmat, obj->obmat); + } + else { + copy_m4_m4(mat, obj->obmat); + } + + /* Compute rotation matrix. */ + switch (obj->rotmode) { + case ROT_MODE_AXISANGLE: + { + /* Get euler angles from axis angle rotation. */ + axis_angle_to_eulO(euler, ROT_MODE_XYZ, obj->rotAxis, obj->rotAngle); + + /* Create X, Y, Z rotation matrices from euler angles. */ + create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true); + + /* Concatenate rotation matrices. */ + mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); + + /* Extract location and scale from matrix. */ + mat4_to_loc_rot_size(loc, rot, scale, mat); + + break; + } + case ROT_MODE_QUAT: + { + float q[4]; + copy_v4_v4(q, obj->quat); + + /* Swap axis. */ + q[2] = obj->quat[3]; + q[3] = -obj->quat[2]; + + /* Compute rotation matrix from quaternion. */ + quat_to_mat3(rot_mat, q); + + /* Extract location and scale from matrix. */ + mat4_to_loc_rot_size(loc, rot, scale, mat); + + break; + } + case ROT_MODE_XYZ: + { + /* Extract location, rotation, and scale form matrix. */ + mat4_to_loc_rot_size(loc, rot, scale, mat); + + /* Get euler angles from rotation matrix. */ + mat3_to_eulO(euler, ROT_MODE_XYZ, rot); + + /* Create X, Y, Z rotation matrices from euler angles. */ + create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true); + + /* Concatenate rotation matrices. */ + mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); + + break; + } + case ROT_MODE_XZY: + { + /* Extract location, rotation, and scale form matrix. */ + mat4_to_loc_rot_size(loc, rot, scale, mat); + + /* Get euler angles from rotation matrix. */ + mat3_to_eulO(euler, ROT_MODE_XZY, rot); + + /* Create X, Y, Z rotation matrices from euler angles. */ + create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true); + + /* Concatenate rotation matrices. */ + mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); + + break; + } + case ROT_MODE_YXZ: + { + /* Extract location, rotation, and scale form matrix. */ + mat4_to_loc_rot_size(loc, rot, scale, mat); + + /* Get euler angles from rotation matrix. */ + mat3_to_eulO(euler, ROT_MODE_YXZ, rot); + + /* Create X, Y, Z rotation matrices from euler angles. */ + create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true); + + /* Concatenate rotation matrices. */ + mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); + + break; + } + case ROT_MODE_YZX: + { + /* Extract location, rotation, and scale form matrix. */ + mat4_to_loc_rot_size(loc, rot, scale, mat); + + /* Get euler angles from rotation matrix. */ + mat3_to_eulO(euler, ROT_MODE_YZX, rot); + + /* Create X, Y, Z rotation matrices from euler angles. */ + create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true); + + /* Concatenate rotation matrices. */ + mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); + + break; + } + case ROT_MODE_ZXY: + { + /* Extract location, rotation, and scale form matrix. */ + mat4_to_loc_rot_size(loc, rot, scale, mat); + + /* Get euler angles from rotation matrix. */ + mat3_to_eulO(euler, ROT_MODE_ZXY, rot); + + /* Create X, Y, Z rotation matrices from euler angles. */ + create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true); + + /* Concatenate rotation matrices. */ + mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); + + break; + } + case ROT_MODE_ZYX: + { + /* Extract location, rotation, and scale form matrix. */ + mat4_to_loc_rot_size(loc, rot, scale, mat); + + /* Get euler angles from rotation matrix. */ + mat3_to_eulO(euler, ROT_MODE_ZYX, rot); + + /* Create X, Y, Z rotation matrices from euler angles. */ + create_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, true); + + /* Concatenate rotation matrices. */ + mul_m3_m3m3(rot_mat, rot_mat, rot_x_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_z_mat); + mul_m3_m3m3(rot_mat, rot_mat, rot_y_mat); + + break; + } + } + + /* Add rotation matrix to transformation matrix. */ + copy_m4_m3(transform_mat, rot_mat); + + /* Add translation to transformation matrix. */ + copy_zup_yup(transform_mat[3], loc); + + /* Create scale matrix. */ + scale_mat[0][0] = scale[0]; + scale_mat[1][1] = scale[2]; + scale_mat[2][2] = scale[1]; + + /* Add scale to transformation matrix. */ + mul_m4_m4m4(transform_mat, transform_mat, scale_mat); +} + +bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name) +{ + if (!prop.valid()) { + return false; + } + + return prop.getPropertyHeader(name) != NULL; +} diff --git a/source/blender/alembic/intern/abc_util.h b/source/blender/alembic/intern/abc_util.h new file mode 100644 index 00000000000..688a25d85f6 --- /dev/null +++ b/source/blender/alembic/intern/abc_util.h @@ -0,0 +1,125 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __ABC_UTIL_H__ +#define __ABC_UTIL_H__ + +#include <Alembic/Abc/All.h> +#include <Alembic/AbcGeom/All.h> + +#ifdef _MSC_VER +# define ABC_INLINE static __forceinline +#else +# define ABC_INLINE static inline +#endif + +using Alembic::Abc::chrono_t; + +class ImportSettings; + +struct ID; +struct Object; + +std::string get_id_name(ID *id); +std::string get_id_name(Object *ob); +std::string get_object_dag_path_name(Object *ob, Object *dupli_parent); + +bool object_selected(Object *ob); +bool parent_selected(Object *ob); + +Imath::M44d convert_matrix(float mat[4][4]); +void create_transform_matrix(float r_mat[4][4]); +void create_transform_matrix(Object *obj, float transform_mat[4][4]); + +void split(const std::string &s, const char delim, std::vector<std::string> &tokens); + +template<class TContainer> +bool begins_with(const TContainer &input, const TContainer &match) +{ + return input.size() >= match.size() + && std::equal(match.begin(), match.end(), input.begin()); +} + +void create_input_transform(const Alembic::AbcGeom::ISampleSelector &sample_sel, + const Alembic::AbcGeom::IXform &ixform, Object *ob, + float r_mat[4][4], float scale, bool has_alembic_parent = false); + +template <typename Schema> +void get_min_max_time(const Schema &schema, chrono_t &min, chrono_t &max) +{ + const Alembic::Abc::TimeSamplingPtr &time_samp = schema.getTimeSampling(); + + if (!schema.isConstant()) { + const size_t num_samps = schema.getNumSamples(); + + if (num_samps > 0) { + const chrono_t min_time = time_samp->getSampleTime(0); + min = std::min(min, min_time); + + const chrono_t max_time = time_samp->getSampleTime(num_samps - 1); + max = std::max(max, max_time); + } + } +} + +bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name); + +/* ************************** */ + +/* TODO(kevin): for now keeping these transformations hardcoded to make sure + * everything works properly, and also because Alembic is almost exclusively + * used in Y-up software, but eventually they'll be set by the user in the UI + * like other importers/exporters do, to support other axis. */ + +/* Copy from Y-up to Z-up. */ + +ABC_INLINE void copy_yup_zup(float zup[3], const float yup[3]) +{ + zup[0] = yup[0]; + zup[1] = -yup[2]; + zup[2] = yup[1]; +} + +ABC_INLINE void copy_yup_zup(short zup[3], const short yup[3]) +{ + zup[0] = yup[0]; + zup[1] = -yup[2]; + zup[2] = yup[1]; +} + +/* Copy from Z-up to Y-up. */ + +ABC_INLINE void copy_zup_yup(float yup[3], const float zup[3]) +{ + yup[0] = zup[0]; + yup[1] = zup[2]; + yup[2] = -zup[1]; +} + +ABC_INLINE void copy_zup_yup(short yup[3], const short zup[3]) +{ + yup[0] = zup[0]; + yup[1] = zup[2]; + yup[2] = -zup[1]; +} + +#endif /* __ABC_UTIL_H__ */ diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc new file mode 100644 index 00000000000..0e96ac22e11 --- /dev/null +++ b/source/blender/alembic/intern/alembic_capi.cc @@ -0,0 +1,1136 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Esteban Tovagliari, Cedric Paille, Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "../ABC_alembic.h" + +#ifdef WITH_ALEMBIC_HDF5 +# include <Alembic/AbcCoreHDF5/All.h> +#endif + +#include <Alembic/AbcCoreOgawa/All.h> +#include <Alembic/AbcMaterial/IMaterial.h> + +#include "abc_camera.h" +#include "abc_curves.h" +#include "abc_hair.h" +#include "abc_mesh.h" +#include "abc_nurbs.h" +#include "abc_points.h" +#include "abc_transform.h" +#include "abc_util.h" + +extern "C" { +#include "MEM_guardedalloc.h" + +#include "DNA_cachefile_types.h" +#include "DNA_curve_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_cachefile.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_scene.h" + +/* SpaceType struct has a member called 'new' which obviously conflicts with C++ + * so temporarily redefining the new keyword to make it compile. */ +#define new extern_new +#include "BKE_screen.h" +#undef new + +#include "BLI_fileops.h" +#include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "WM_api.h" +#include "WM_types.h" +} + +using Alembic::Abc::Int32ArraySamplePtr; +using Alembic::Abc::ObjectHeader; + +using Alembic::AbcGeom::ErrorHandler; +using Alembic::AbcGeom::Exception; +using Alembic::AbcGeom::MetaData; +using Alembic::AbcGeom::P3fArraySamplePtr; +using Alembic::AbcGeom::kWrapExisting; + +using Alembic::AbcGeom::IArchive; +using Alembic::AbcGeom::ICamera; +using Alembic::AbcGeom::ICurves; +using Alembic::AbcGeom::ICurvesSchema; +using Alembic::AbcGeom::IFaceSet; +using Alembic::AbcGeom::ILight; +using Alembic::AbcGeom::INuPatch; +using Alembic::AbcGeom::IObject; +using Alembic::AbcGeom::IPoints; +using Alembic::AbcGeom::IPointsSchema; +using Alembic::AbcGeom::IPolyMesh; +using Alembic::AbcGeom::IPolyMeshSchema; +using Alembic::AbcGeom::ISampleSelector; +using Alembic::AbcGeom::ISubD; +using Alembic::AbcGeom::IV2fGeomParam; +using Alembic::AbcGeom::IXform; +using Alembic::AbcGeom::IXformSchema; +using Alembic::AbcGeom::N3fArraySamplePtr; +using Alembic::AbcGeom::XformSample; +using Alembic::AbcGeom::ICompoundProperty; +using Alembic::AbcGeom::IN3fArrayProperty; +using Alembic::AbcGeom::IN3fGeomParam; +using Alembic::AbcGeom::V3fArraySamplePtr; + +using Alembic::AbcMaterial::IMaterial; + +struct AbcArchiveHandle { + int unused; +}; + +ABC_INLINE IArchive *archive_from_handle(AbcArchiveHandle *handle) +{ + return reinterpret_cast<IArchive *>(handle); +} + +ABC_INLINE AbcArchiveHandle *handle_from_archive(IArchive *archive) +{ + return reinterpret_cast<AbcArchiveHandle *>(archive); +} + +static IArchive *open_archive(const std::string &filename) +{ + Alembic::AbcCoreAbstract::ReadArraySampleCachePtr cache_ptr; + IArchive *archive; + + try { + archive = new IArchive(Alembic::AbcCoreOgawa::ReadArchive(), + filename.c_str(), ErrorHandler::kThrowPolicy, + cache_ptr); + } + catch (const Exception &e) { + std::cerr << e.what() << '\n'; + +#ifdef WITH_ALEMBIC_HDF5 + try { + archive = new IArchive(Alembic::AbcCoreHDF5::ReadArchive(), + filename.c_str(), ErrorHandler::kThrowPolicy, + cache_ptr); + } + catch (const Exception &) { + std::cerr << e.what() << '\n'; + return NULL; + } +#else + return NULL; +#endif + } + + return archive; +} + +//#define USE_NURBS + +/* NOTE: this function is similar to visit_objects below, need to keep them in + * sync. */ +static void gather_objects_paths(const IObject &object, ListBase *object_paths) +{ + if (!object.valid()) { + return; + } + + for (int i = 0; i < object.getNumChildren(); ++i) { + IObject child = object.getChild(i); + + if (!child.valid()) { + continue; + } + + bool get_path = false; + + const MetaData &md = child.getMetaData(); + + if (IXform::matches(md)) { + /* Check whether or not this object is a Maya locator, which is + * similar to empties used as parent object in Blender. */ + if (has_property(child.getProperties(), "locator")) { + get_path = true; + } + else { + /* Avoid creating an empty object if the child of this transform + * is not a transform (that is an empty). */ + if (child.getNumChildren() == 1) { + if (IXform::matches(child.getChild(0).getMetaData())) { + get_path = true; + } +#if 0 + else { + std::cerr << "Skipping " << child.getFullName() << '\n'; + } +#endif + } + else { + get_path = true; + } + } + } + else if (IPolyMesh::matches(md)) { + get_path = true; + } + else if (ISubD::matches(md)) { + get_path = true; + } + else if (INuPatch::matches(md)) { +#ifdef USE_NURBS + get_path = true; +#endif + } + else if (ICamera::matches(md)) { + get_path = true; + } + else if (IPoints::matches(md)) { + get_path = true; + } + else if (IMaterial::matches(md)) { + /* Pass for now. */ + } + else if (ILight::matches(md)) { + /* Pass for now. */ + } + else if (IFaceSet::matches(md)) { + /* Pass, those are handled in the mesh reader. */ + } + else if (ICurves::matches(md)) { + get_path = true; + } + else { + assert(false); + } + + if (get_path) { + AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>( + MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath")); + + BLI_strncpy(abc_path->path, child.getFullName().c_str(), PATH_MAX); + + BLI_addtail(object_paths, abc_path); + } + + gather_objects_paths(child, object_paths); + } +} + +AbcArchiveHandle *ABC_create_handle(const char *filename, ListBase *object_paths) +{ + IArchive *archive = open_archive(filename); + + if (!archive) { + return NULL; + } + + if (object_paths) { + gather_objects_paths(archive->getTop(), object_paths); + } + + return handle_from_archive(archive); +} + +void ABC_free_handle(AbcArchiveHandle *handle) +{ + delete archive_from_handle(handle); +} + +int ABC_get_version() +{ + return ALEMBIC_LIBRARY_VERSION; +} + +static void find_iobject(const IObject &object, IObject &ret, + const std::string &path) +{ + if (!object.valid()) { + return; + } + + std::vector<std::string> tokens; + split(path, '/', tokens); + + IObject tmp = object; + + std::vector<std::string>::iterator iter; + for (iter = tokens.begin(); iter != tokens.end(); ++iter) { + IObject child = tmp.getChild(*iter); + tmp = child; + } + + ret = tmp; +} + +struct ExportJobData { + Scene *scene; + Main *bmain; + + char filename[1024]; + ExportSettings settings; + + short *stop; + short *do_update; + float *progress; + + bool was_canceled; +}; + +static void export_startjob(void *customdata, short *stop, short *do_update, float *progress) +{ + ExportJobData *data = static_cast<ExportJobData *>(customdata); + + data->stop = stop; + data->do_update = do_update; + data->progress = progress; + + /* XXX annoying hack: needed to prevent data corruption when changing + * scene frame in separate threads + */ + G.is_rendering = true; + BKE_spacedata_draw_locks(true); + + G.is_break = false; + + try { + Scene *scene = data->scene; + AbcExporter exporter(scene, data->filename, data->settings); + + const int orig_frame = CFRA; + + data->was_canceled = false; + exporter(data->bmain, *data->progress, data->was_canceled); + + if (CFRA != orig_frame) { + CFRA = orig_frame; + + BKE_scene_update_for_newframe(data->bmain->eval_ctx, data->bmain, + scene, scene->lay); + } + } + catch (const std::exception &e) { + std::cerr << "Abc Export error: " << e.what() << '\n'; + } + catch (...) { + std::cerr << "Abc Export error\n"; + } +} + +static void export_endjob(void *customdata) +{ + ExportJobData *data = static_cast<ExportJobData *>(customdata); + + if (data->was_canceled && BLI_exists(data->filename)) { + BLI_delete(data->filename, false, false); + } + + G.is_rendering = false; + BKE_spacedata_draw_locks(false); +} + +void ABC_export( + Scene *scene, + bContext *C, + const char *filepath, + const struct AlembicExportParams *params) +{ + ExportJobData *job = static_cast<ExportJobData *>(MEM_mallocN(sizeof(ExportJobData), "ExportJobData")); + job->scene = scene; + job->bmain = CTX_data_main(C); + BLI_strncpy(job->filename, filepath, 1024); + + job->settings.scene = job->scene; + job->settings.frame_start = params->frame_start; + job->settings.frame_end = params->frame_end; + job->settings.frame_step_xform = params->frame_step_xform; + job->settings.frame_step_shape = params->frame_step_shape; + job->settings.shutter_open = params->shutter_open; + job->settings.shutter_close = params->shutter_close; + job->settings.selected_only = params->selected_only; + job->settings.export_face_sets = params->face_sets; + job->settings.export_normals = params->normals; + job->settings.export_uvs = params->uvs; + job->settings.export_vcols = params->vcolors; + job->settings.apply_subdiv = params->apply_subdiv; + job->settings.flatten_hierarchy = params->flatten_hierarchy; + job->settings.visible_layers_only = params->visible_layers_only; + job->settings.renderable_only = params->renderable_only; + job->settings.use_subdiv_schema = params->use_subdiv_schema; + job->settings.export_ogawa = (params->compression_type == ABC_ARCHIVE_OGAWA); + job->settings.pack_uv = params->packuv; + job->settings.global_scale = params->global_scale; + + if (job->settings.frame_start > job->settings.frame_end) { + std::swap(job->settings.frame_start, job->settings.frame_end); + } + + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + job->scene, + "Alembic Export", + WM_JOB_PROGRESS, + WM_JOB_TYPE_ALEMBIC); + + /* setup job */ + WM_jobs_customdata_set(wm_job, job, MEM_freeN); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); + WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob); + + WM_jobs_start(CTX_wm_manager(C), wm_job); +} + +/* ********************** Import file ********************** */ + +static void visit_object(const IObject &object, + std::vector<AbcObjectReader *> &readers, + GHash *parent_map, + ImportSettings &settings) +{ + if (!object.valid()) { + return; + } + + for (int i = 0; i < object.getNumChildren(); ++i) { + IObject child = object.getChild(i); + + if (!child.valid()) { + continue; + } + + AbcObjectReader *reader = NULL; + + const MetaData &md = child.getMetaData(); + + if (IXform::matches(md)) { + bool create_xform = false; + + /* Check whether or not this object is a Maya locator, which is + * similar to empties used as parent object in Blender. */ + if (has_property(child.getProperties(), "locator")) { + create_xform = true; + } + else { + /* Avoid creating an empty object if the child of this transform + * is not a transform (that is an empty). */ + if (child.getNumChildren() == 1) { + if (IXform::matches(child.getChild(0).getMetaData())) { + create_xform = true; + } +#if 0 + else { + std::cerr << "Skipping " << child.getFullName() << '\n'; + } +#endif + } + else { + create_xform = true; + } + } + + if (create_xform) { + reader = new AbcEmptyReader(child, settings); + } + } + else if (IPolyMesh::matches(md)) { + reader = new AbcMeshReader(child, settings); + } + else if (ISubD::matches(md)) { + reader = new AbcSubDReader(child, settings); + } + else if (INuPatch::matches(md)) { +#ifdef USE_NURBS + /* TODO(kevin): importing cyclic NURBS from other software crashes + * at the moment. This is due to the fact that NURBS in other + * software have duplicated points which causes buffer overflows in + * Blender. Need to figure out exactly how these points are + * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV). + * Until this is fixed, disabling NURBS reading. */ + reader = new AbcNurbsReader(child, settings); +#endif + } + else if (ICamera::matches(md)) { + reader = new AbcCameraReader(child, settings); + } + else if (IPoints::matches(md)) { + reader = new AbcPointsReader(child, settings); + } + else if (IMaterial::matches(md)) { + /* Pass for now. */ + } + else if (ILight::matches(md)) { + /* Pass for now. */ + } + else if (IFaceSet::matches(md)) { + /* Pass, those are handled in the mesh reader. */ + } + else if (ICurves::matches(md)) { + reader = new AbcCurveReader(child, settings); + } + else { + assert(false); + } + + if (reader) { + readers.push_back(reader); + + AlembicObjectPath *abc_path = static_cast<AlembicObjectPath *>( + MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath")); + + BLI_strncpy(abc_path->path, child.getFullName().c_str(), PATH_MAX); + + BLI_addtail(&settings.cache_file->object_paths, abc_path); + + /* Cast to `void *` explicitly to avoid compiler errors because it + * is a `const char *` which the compiler cast to `const void *` + * instead of the expected `void *`. */ + BLI_ghash_insert(parent_map, (void *)child.getFullName().c_str(), reader); + } + + visit_object(child, readers, parent_map, settings); + } +} + +enum { + ABC_NO_ERROR = 0, + ABC_ARCHIVE_FAIL, +}; + +struct ImportJobData { + Main *bmain; + Scene *scene; + + char filename[1024]; + ImportSettings settings; + + GHash *parent_map; + std::vector<AbcObjectReader *> readers; + + short *stop; + short *do_update; + float *progress; + + char error_code; + bool was_cancelled; +}; + +ABC_INLINE bool is_mesh_and_strands(const IObject &object) +{ + if (object.getNumChildren() != 2) { + return false; + } + + bool has_mesh = false; + bool has_curve = false; + + for (int i = 0; i < object.getNumChildren(); ++i) { + const IObject &child = object.getChild(i); + + if (!child.valid()) { + continue; + } + + const MetaData &md = child.getMetaData(); + + if (IPolyMesh::matches(md)) { + has_mesh = true; + } + else if (ISubD::matches(md)) { + has_mesh = true; + } + else if (ICurves::matches(md)) { + has_curve = true; + } + } + + return has_mesh && has_curve; +} + +static void import_startjob(void *user_data, short *stop, short *do_update, float *progress) +{ + ImportJobData *data = static_cast<ImportJobData *>(user_data); + + data->stop = stop; + data->do_update = do_update; + data->progress = progress; + + IArchive *archive = open_archive(data->filename); + + if (!archive || !archive->valid()) { + data->error_code = ABC_ARCHIVE_FAIL; + return; + } + + CacheFile *cache_file = static_cast<CacheFile *>(BKE_cachefile_add(data->bmain, BLI_path_basename(data->filename))); + + /* Decrement the ID ref-count because it is going to be incremented for each + * modifier and constraint that it will be attached to, so since currently + * it is not used by anyone, its use count will off by one. */ + id_us_min(&cache_file->id); + + cache_file->is_sequence = data->settings.is_sequence; + cache_file->scale = data->settings.scale; + cache_file->handle = handle_from_archive(archive); + BLI_strncpy(cache_file->filepath, data->filename, 1024); + + data->settings.cache_file = cache_file; + + *data->do_update = true; + *data->progress = 0.05f; + + data->parent_map = BLI_ghash_str_new("alembic parent ghash"); + + /* Parse Alembic Archive. */ + + visit_object(archive->getTop(), data->readers, data->parent_map, data->settings); + + if (G.is_break) { + data->was_cancelled = true; + return; + } + + *data->do_update = true; + *data->progress = 0.1f; + + /* Create objects and set scene frame range. */ + + const float size = static_cast<float>(data->readers.size()); + size_t i = 0; + + chrono_t min_time = std::numeric_limits<chrono_t>::max(); + chrono_t max_time = std::numeric_limits<chrono_t>::min(); + + std::vector<AbcObjectReader *>::iterator iter; + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + AbcObjectReader *reader = *iter; + + if (reader->valid()) { + reader->readObjectData(data->bmain, 0.0f); + reader->readObjectMatrix(0.0f); + + min_time = std::min(min_time, reader->minTime()); + max_time = std::max(max_time, reader->maxTime()); + } + + *data->progress = 0.1f + 0.6f * (++i / size); + *data->do_update = true; + + if (G.is_break) { + data->was_cancelled = true; + return; + } + } + + if (data->settings.set_frame_range) { + Scene *scene = data->scene; + + if (data->settings.is_sequence) { + SFRA = data->settings.offset; + EFRA = SFRA + (data->settings.sequence_len - 1); + CFRA = SFRA; + } + else if (min_time < max_time) { + SFRA = min_time * FPS; + EFRA = max_time * FPS; + CFRA = SFRA; + } + } + + /* Setup parentship. */ + + i = 0; + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + const AbcObjectReader *reader = *iter; + const AbcObjectReader *parent_reader = NULL; + const IObject &iobject = reader->iobject(); + + IObject parent = iobject.getParent(); + + if (!IXform::matches(iobject.getHeader())) { + /* In the case of an non XForm node, the parent is the transform + * matrix of the data itself, so we get the its grand parent. + */ + + /* Special case with object only containing a mesh and some strands, + * we want both objects to be parented to the same object. */ + if (!is_mesh_and_strands(parent)) { + parent = parent.getParent(); + } + } + + parent_reader = reinterpret_cast<AbcObjectReader *>( + BLI_ghash_lookup(data->parent_map, parent.getFullName().c_str())); + + if (parent_reader) { + Object *parent = parent_reader->object(); + + if (parent != NULL && reader->object() != parent) { + Object *ob = reader->object(); + ob->parent = parent; + } + } + + *data->progress = 0.7f + 0.3f * (++i / size); + *data->do_update = true; + + if (G.is_break) { + data->was_cancelled = true; + return; + } + } +} + +static void import_endjob(void *user_data) +{ + ImportJobData *data = static_cast<ImportJobData *>(user_data); + + std::vector<AbcObjectReader *>::iterator iter; + + /* Delete objects on cancelation. */ + if (data->was_cancelled) { + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + Object *ob = (*iter)->object(); + + if (ob->data) { + BKE_libblock_free_us(data->bmain, ob->data); + ob->data = NULL; + } + + BKE_libblock_free_us(data->bmain, ob); + } + } + else { + /* Add object to scene. */ + BKE_scene_base_deselect_all(data->scene); + + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + Object *ob = (*iter)->object(); + ob->lay = data->scene->lay; + + BKE_scene_base_add(data->scene, ob); + + DAG_id_tag_update_ex(data->bmain, &ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); + } + + DAG_relations_tag_update(data->bmain); + } + + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + delete *iter; + } + + if (data->parent_map) { + BLI_ghash_free(data->parent_map, NULL, NULL); + } + + switch (data->error_code) { + default: + case ABC_NO_ERROR: + break; + case ABC_ARCHIVE_FAIL: + WM_report(RPT_ERROR, "Could not open Alembic archive for reading! See console for detail."); + break; + } + + WM_main_add_notifier(NC_SCENE | ND_FRAME, data->scene); +} + +static void import_freejob(void *user_data) +{ + ImportJobData *data = static_cast<ImportJobData *>(user_data); + delete data; +} + +void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence, bool set_frame_range, int sequence_len, int offset, bool validate_meshes) +{ + /* Using new here since MEM_* funcs do not call ctor to properly initialize + * data. */ + ImportJobData *job = new ImportJobData(); + job->bmain = CTX_data_main(C); + job->scene = CTX_data_scene(C); + BLI_strncpy(job->filename, filepath, 1024); + + job->settings.scale = scale; + job->settings.is_sequence = is_sequence; + job->settings.set_frame_range = set_frame_range; + job->settings.sequence_len = sequence_len; + job->settings.offset = offset; + job->settings.validate_meshes = validate_meshes; + job->parent_map = NULL; + job->error_code = ABC_NO_ERROR; + job->was_cancelled = false; + + G.is_break = false; + + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + job->scene, + "Alembic Import", + WM_JOB_PROGRESS, + WM_JOB_TYPE_ALEMBIC); + + /* setup job */ + WM_jobs_customdata_set(wm_job, job, import_freejob); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); + WM_jobs_callbacks(wm_job, import_startjob, NULL, NULL, import_endjob); + + WM_jobs_start(CTX_wm_manager(C), wm_job); +} + +/* ******************************* */ + +void ABC_get_transform(AbcArchiveHandle *handle, Object *ob, const char *object_path, float r_mat[4][4], float time, float scale) +{ + IArchive *archive = archive_from_handle(handle); + + if (!archive || !archive->valid()) { + return; + } + + IObject tmp; + find_iobject(archive->getTop(), tmp, object_path); + + IXform ixform; + + if (IXform::matches(tmp.getHeader())) { + ixform = IXform(tmp, kWrapExisting); + } + else { + ixform = IXform(tmp.getParent(), kWrapExisting); + } + + IXformSchema schema = ixform.getSchema(); + + if (!schema.valid()) { + return; + } + + ISampleSelector sample_sel(time); + + create_input_transform(sample_sel, ixform, ob, r_mat, scale); +} + +/* ***************************************** */ + +static bool check_smooth_poly_flag(DerivedMesh *dm) +{ + MPoly *mpolys = dm->getPolyArray(dm); + + for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) { + MPoly &poly = mpolys[i]; + + if ((poly.flag & ME_SMOOTH) != 0) { + return true; + } + } + + return false; +} + +static void set_smooth_poly_flag(DerivedMesh *dm) +{ + MPoly *mpolys = dm->getPolyArray(dm); + + for (int i = 0, e = dm->getNumPolys(dm); i < e; ++i) { + MPoly &poly = mpolys[i]; + poly.flag |= ME_SMOOTH; + } +} + +static void *add_customdata_cb(void *user_data, const char *name, int data_type) +{ + DerivedMesh *dm = static_cast<DerivedMesh *>(user_data); + CustomDataType cd_data_type = static_cast<CustomDataType>(data_type); + void *cd_ptr = NULL; + + if (ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) { + cd_ptr = CustomData_get_layer_named(dm->getLoopDataLayout(dm), cd_data_type, name); + + if (cd_ptr == NULL) { + cd_ptr = CustomData_add_layer_named(dm->getLoopDataLayout(dm), + cd_data_type, + CD_DEFAULT, + NULL, + dm->getNumLoops(dm), + name); + } + } + + return cd_ptr; +} + +ABC_INLINE CDStreamConfig get_config(DerivedMesh *dm) +{ + CDStreamConfig config; + + config.user_data = dm; + config.mvert = dm->getVertArray(dm); + config.mloop = dm->getLoopArray(dm); + config.mpoly = dm->getPolyArray(dm); + config.totloop = dm->getNumLoops(dm); + config.totpoly = dm->getNumPolys(dm); + config.loopdata = dm->getLoopDataLayout(dm); + config.add_customdata_cb = add_customdata_cb; + + return config; +} + +static DerivedMesh *read_mesh_sample(DerivedMesh *dm, const IObject &iobject, const float time, int read_flag) +{ + IPolyMesh mesh(iobject, kWrapExisting); + IPolyMeshSchema schema = mesh.getSchema(); + ISampleSelector sample_sel(time); + const IPolyMeshSchema::Sample sample = schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); + const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); + + DerivedMesh *new_dm = NULL; + + /* Only read point data when streaming meshes, unless we need to create new ones. */ + ImportSettings settings; + settings.read_flag |= read_flag; + + if (dm->getNumVerts(dm) != positions->size()) { + new_dm = CDDM_from_template(dm, + positions->size(), + 0, + 0, + face_indices->size(), + face_counts->size()); + + settings.read_flag |= MOD_MESHSEQ_READ_ALL; + } + + CDStreamConfig config = get_config(new_dm ? new_dm : dm); + + bool has_loop_normals = false; + read_mesh_sample(&settings, schema, sample_sel, config, has_loop_normals); + + if (new_dm) { + /* Check if we had ME_SMOOTH flag set to restore it. */ + if (!has_loop_normals && check_smooth_poly_flag(dm)) { + set_smooth_poly_flag(new_dm); + } + + CDDM_calc_normals(new_dm); + CDDM_calc_edges(new_dm); + + return new_dm; + } + + return dm; +} + +using Alembic::AbcGeom::ISubDSchema; + +static DerivedMesh *read_subd_sample(DerivedMesh *dm, const IObject &iobject, const float time, int read_flag) +{ + ISubD mesh(iobject, kWrapExisting); + ISubDSchema schema = mesh.getSchema(); + ISampleSelector sample_sel(time); + const ISubDSchema::Sample sample = schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); + const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); + + DerivedMesh *new_dm = NULL; + + ImportSettings settings; + settings.read_flag |= read_flag; + + if (dm->getNumVerts(dm) != positions->size()) { + new_dm = CDDM_from_template(dm, + positions->size(), + 0, + 0, + face_indices->size(), + face_counts->size()); + + settings.read_flag |= MOD_MESHSEQ_READ_ALL; + } + + /* Only read point data when streaming meshes, unless we need to create new ones. */ + CDStreamConfig config = get_config(new_dm ? new_dm : dm); + read_subd_sample(&settings, schema, sample_sel, config); + + if (new_dm) { + /* Check if we had ME_SMOOTH flag set to restore it. */ + if (check_smooth_poly_flag(dm)) { + set_smooth_poly_flag(new_dm); + } + + CDDM_calc_normals(new_dm); + CDDM_calc_edges(new_dm); + + return new_dm; + } + + return dm; +} + +static DerivedMesh *read_points_sample(DerivedMesh *dm, const IObject &iobject, const float time) +{ + IPoints points(iobject, kWrapExisting); + IPointsSchema schema = points.getSchema(); + ISampleSelector sample_sel(time); + const IPointsSchema::Sample sample = schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + + DerivedMesh *new_dm = NULL; + + if (dm->getNumVerts(dm) != positions->size()) { + new_dm = CDDM_new(positions->size(), 0, 0, 0, 0); + } + + CDStreamConfig config = get_config(new_dm ? new_dm : dm); + read_points_sample(schema, sample_sel, config, time); + + return new_dm ? new_dm : dm; +} + +/* NOTE: Alembic only stores data about control points, but the DerivedMesh + * passed from the cache modifier contains the displist, which has more data + * than the control points, so to avoid corrupting the displist we modify the + * object directly and create a new DerivedMesh from that. Also we might need to + * create new or delete existing NURBS in the curve. + */ +static DerivedMesh *read_curves_sample(Object *ob, const IObject &iobject, const float time) +{ + ICurves points(iobject, kWrapExisting); + ICurvesSchema schema = points.getSchema(); + ISampleSelector sample_sel(time); + const ICurvesSchema::Sample sample = schema.getValue(sample_sel); + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Int32ArraySamplePtr num_vertices = sample.getCurvesNumVertices(); + + int vertex_idx = 0; + int curve_idx = 0; + Curve *curve = static_cast<Curve *>(ob->data); + + const int curve_count = BLI_listbase_count(&curve->nurb); + + if (curve_count != num_vertices->size()) { + BKE_nurbList_free(&curve->nurb); + read_curve_sample(curve, schema, time); + } + else { + Nurb *nurbs = static_cast<Nurb *>(curve->nurb.first); + for (; nurbs; nurbs = nurbs->next, ++curve_idx) { + const int totpoint = (*num_vertices)[curve_idx]; + + if (nurbs->bp) { + BPoint *point = nurbs->bp; + + for (int i = 0; i < totpoint; ++i, ++point, ++vertex_idx) { + const Imath::V3f &pos = (*positions)[vertex_idx]; + copy_yup_zup(point->vec, pos.getValue()); + } + } + else if (nurbs->bezt) { + BezTriple *bezier = nurbs->bezt; + + for (int i = 0; i < totpoint; ++i, ++bezier, ++vertex_idx) { + const Imath::V3f &pos = (*positions)[vertex_idx]; + copy_yup_zup(bezier->vec[1], pos.getValue()); + } + } + } + } + + return CDDM_from_curve(ob); +} + +DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle, + Object *ob, + DerivedMesh *dm, + const char *object_path, + const float time, + const char **err_str, + int read_flag) +{ + IArchive *archive = archive_from_handle(handle); + + if (!archive || !archive->valid()) { + *err_str = "Invalid archive!"; + return NULL; + } + + IObject iobject; + find_iobject(archive->getTop(), iobject, object_path); + + if (!iobject.valid()) { + *err_str = "Invalid object: verify object path"; + return NULL; + } + + const ObjectHeader &header = iobject.getHeader(); + + if (IPolyMesh::matches(header)) { + if (ob->type != OB_MESH) { + *err_str = "Object type mismatch: object path points to a mesh!"; + return NULL; + } + + return read_mesh_sample(dm, iobject, time, read_flag); + } + else if (ISubD::matches(header)) { + if (ob->type != OB_MESH) { + *err_str = "Object type mismatch: object path points to a subdivision mesh!"; + return NULL; + } + + return read_subd_sample(dm, iobject, time, read_flag); + } + else if (IPoints::matches(header)) { + if (ob->type != OB_MESH) { + *err_str = "Object type mismatch: object path points to a point cloud (requires a mesh object)!"; + return NULL; + } + + return read_points_sample(dm, iobject, time); + } + else if (ICurves::matches(header)) { + if (ob->type != OB_CURVE) { + *err_str = "Object type mismatch: object path points to a curve!"; + return NULL; + } + + return read_curves_sample(ob, iobject, time); + } + + *err_str = "Unsupported object type: verify object path"; // or poke developer + return NULL; +} diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 618b36c5851..483fefbd89c 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -28,7 +28,7 @@ * and keep comment above the defines. * Use STRINGIFY() rather than defining with quotes */ #define BLENDER_VERSION 277 -#define BLENDER_SUBVERSION 1 +#define BLENDER_SUBVERSION 3 /* Several breakages with 270, e.g. constraint deg vs rad */ #define BLENDER_MINVERSION 270 #define BLENDER_MINSUBVERSION 6 diff --git a/source/blender/blenkernel/BKE_cachefile.h b/source/blender/blenkernel/BKE_cachefile.h new file mode 100644 index 00000000000..51b161f1a06 --- /dev/null +++ b/source/blender/blenkernel/BKE_cachefile.h @@ -0,0 +1,67 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BKE_CACHEFILE_H__ +#define __BKE_CACHEFILE_H__ + +/** \file BKE_cachefile.h + * \ingroup bke + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct CacheFile; +struct Main; +struct Scene; + +void *BKE_cachefile_add(struct Main *bmain, const char *name); + +void BKE_cachefile_init(struct CacheFile *cache_file); + +void BKE_cachefile_free(struct CacheFile *cache_file); + +struct CacheFile *BKE_cachefile_copy(struct Main *bmain, struct CacheFile *cache_file); + +void BKE_cachefile_make_local(struct Main *bmain, struct CacheFile *cache_file, const bool lib_local); + +void BKE_cachefile_reload(const struct Main *bmain, struct CacheFile *cache_file); + +void BKE_cachefile_ensure_handle(const struct Main *bmain, struct CacheFile *cache_file); + +void BKE_cachefile_update_frame(struct Main *bmain, struct Scene *scene, float ctime, const float fps); + +bool BKE_cachefile_filepath_get( + const struct Main *bmain, const struct CacheFile *cache_file, float frame, + char r_filename[1024]); + +float BKE_cachefile_time_offset(struct CacheFile *cache_file, const float time, const float fps); + +#ifdef __cplusplus +} +#endif + +#endif /* __BKE_CACHEFILE_H__ */ diff --git a/source/blender/blenkernel/BKE_collision.h b/source/blender/blenkernel/BKE_collision.h index d5b4a584ec6..8fedcd4ab06 100644 --- a/source/blender/blenkernel/BKE_collision.h +++ b/source/blender/blenkernel/BKE_collision.h @@ -146,6 +146,10 @@ void collision_get_collider_velocity(float vel_old[3], float vel_new[3], struct ///////////////////////////////////////////////// // used in effect.c ///////////////////////////////////////////////// + +/* explicit control over layer mask and dupli recursion */ +struct Object **get_collisionobjects_ext(struct Scene *scene, struct Object *self, struct Group *group, int layer, unsigned int *numcollobj, unsigned int modifier_type, bool dupli); + struct Object **get_collisionobjects(struct Scene *scene, struct Object *self, struct Group *group, unsigned int *numcollobj, unsigned int modifier_type); typedef struct ColliderCache { diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 65a68a4387c..4da6a61cbfa 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -39,6 +39,7 @@ extern "C" { struct ARegion; struct bScreen; +struct CacheFile; struct ListBase; struct Main; struct Object; @@ -58,6 +59,9 @@ struct bPoseChannel; struct bGPdata; struct bGPDlayer; struct bGPDframe; +struct bGPDpalette; +struct bGPDpalettecolor; +struct bGPDbrush; struct wmWindow; struct wmWindowManager; struct SpaceText; @@ -268,6 +272,8 @@ struct Text *CTX_data_edit_text(const bContext *C); struct MovieClip *CTX_data_edit_movieclip(const bContext *C); struct Mask *CTX_data_edit_mask(const bContext *C); +struct CacheFile *CTX_data_edit_cachefile(const bContext *C); + int CTX_data_selected_nodes(const bContext *C, ListBase *list); struct EditBone *CTX_data_active_bone(const bContext *C); @@ -283,6 +289,9 @@ int CTX_data_visible_pose_bones(const bContext *C, ListBase *list); struct bGPdata *CTX_data_gpencil_data(const bContext *C); struct bGPDlayer *CTX_data_active_gpencil_layer(const bContext *C); struct bGPDframe *CTX_data_active_gpencil_frame(const bContext *C); +struct bGPDpalette *CTX_data_active_gpencil_palette(const bContext *C); +struct bGPDpalettecolor *CTX_data_active_gpencil_palettecolor(const bContext *C); +struct bGPDbrush *CTX_data_active_gpencil_brush(const bContext *C); int CTX_data_visible_gpencil_layers(const bContext *C, ListBase *list); int CTX_data_editable_gpencil_layers(const bContext *C, ListBase *list); int CTX_data_editable_gpencil_strokes(const bContext *C, ListBase *list); diff --git a/source/blender/blenkernel/BKE_effect.h b/source/blender/blenkernel/BKE_effect.h index f8fee444d91..b934ec7166d 100644 --- a/source/blender/blenkernel/BKE_effect.h +++ b/source/blender/blenkernel/BKE_effect.h @@ -110,7 +110,7 @@ typedef struct EffectorCache { } EffectorCache; void free_partdeflect(struct PartDeflect *pd); -struct ListBase *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights, bool precalc); +struct ListBase *pdInitEffectors(struct Scene *scene, struct Object *ob_src, struct ParticleSystem *psys_src, struct EffectorWeights *weights, bool for_simulation); void pdEndEffectors(struct ListBase **effectors); void pdPrecalculateEffectors(struct ListBase *effectors); void pdDoEffectors(struct ListBase *effectors, struct ListBase *colliders, struct EffectorWeights *weights, struct EffectedPoint *point, float *force, float *impulse); diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h index e9e3cd3b16e..ab8b83f8271 100644 --- a/source/blender/blenkernel/BKE_gpencil.h +++ b/source/blender/blenkernel/BKE_gpencil.h @@ -31,38 +31,53 @@ * \author Joshua Leung */ +struct ToolSettings; struct ListBase; struct bGPdata; struct bGPDlayer; struct bGPDframe; struct bGPDstroke; +struct bGPDpalette; +struct bGPDpalettecolor; struct Main; /* ------------ Grease-Pencil API ------------------ */ -bool free_gpencil_strokes(struct bGPDframe *gpf); -void free_gpencil_frames(struct bGPDlayer *gpl); -void free_gpencil_layers(struct ListBase *list); -void BKE_gpencil_free(struct bGPdata *gpd); +void BKE_gpencil_free_stroke(struct bGPDstroke *gps); +bool BKE_gpencil_free_strokes(struct bGPDframe *gpf); +void BKE_gpencil_free_frames(struct bGPDlayer *gpl); +void BKE_gpencil_free_layers(struct ListBase *list); +void BKE_gpencil_free_brushes(struct ListBase *list); +void BKE_gpencil_free_palettes(struct ListBase *list); +void BKE_gpencil_free(struct bGPdata *gpd, bool free_palettes); -void gpencil_stroke_sync_selection(struct bGPDstroke *gps); +void BKE_gpencil_stroke_sync_selection(struct bGPDstroke *gps); -struct bGPDframe *gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe); -struct bGPDframe *gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe); -struct bGPDlayer *gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive); -struct bGPdata *gpencil_data_addnew(const char name[]); +struct bGPDframe *BKE_gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe); +struct bGPDframe *BKE_gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe); +struct bGPDlayer *BKE_gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive); +struct bGPdata *BKE_gpencil_data_addnew(const char name[]); -struct bGPDframe *gpencil_frame_duplicate(struct bGPDframe *src); -struct bGPDlayer *gpencil_layer_duplicate(struct bGPDlayer *src); -struct bGPdata *gpencil_data_duplicate(struct Main *bmain, struct bGPdata *gpd, bool internal_copy); +struct bGPDframe *BKE_gpencil_frame_duplicate(const struct bGPDframe *gpf_src); +struct bGPDlayer *BKE_gpencil_layer_duplicate(const struct bGPDlayer *gpl_src); +struct bGPdata *BKE_gpencil_data_duplicate(struct Main *bmain, struct bGPdata *gpd, bool internal_copy); void BKE_gpencil_make_local(struct Main *bmain, struct bGPdata *gpd, const bool lib_local); -void gpencil_frame_delete_laststroke(struct bGPDlayer *gpl, struct bGPDframe *gpf); +void BKE_gpencil_frame_delete_laststroke(struct bGPDlayer *gpl, struct bGPDframe *gpf); + +struct bGPDpalette *BKE_gpencil_palette_addnew(struct bGPdata *gpd, const char *name, bool setactive); +struct bGPDpalette *BKE_gpencil_palette_duplicate(const struct bGPDpalette *palette_src); +struct bGPDpalettecolor *BKE_gpencil_palettecolor_addnew(struct bGPDpalette *palette, const char *name, bool setactive); + +struct bGPDbrush *BKE_gpencil_brush_addnew(struct ToolSettings *ts, const char *name, bool setactive); +struct bGPDbrush *BKE_gpencil_brush_duplicate(const struct bGPDbrush *brush_src); +void BKE_gpencil_brush_init_presets(struct ToolSettings *ts); /* Stroke and Fill - Alpha Visibility Threshold */ #define GPENCIL_ALPHA_OPACITY_THRESH 0.001f +#define GPENCIL_STRENGTH_MIN 0.003f bool gpencil_layer_is_editable(const struct bGPDlayer *gpl); @@ -79,12 +94,28 @@ typedef enum eGP_GetFrame_Mode { GP_GETFRAME_ADD_COPY = 2 } eGP_GetFrame_Mode; -struct bGPDframe *gpencil_layer_getframe(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew); +struct bGPDframe *BKE_gpencil_layer_getframe(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew); struct bGPDframe *BKE_gpencil_layer_find_frame(struct bGPDlayer *gpl, int cframe); -bool gpencil_layer_delframe(struct bGPDlayer *gpl, struct bGPDframe *gpf); - -struct bGPDlayer *gpencil_layer_getactive(struct bGPdata *gpd); -void gpencil_layer_setactive(struct bGPdata *gpd, struct bGPDlayer *active); -void gpencil_layer_delete(struct bGPdata *gpd, struct bGPDlayer *gpl); +bool BKE_gpencil_layer_delframe(struct bGPDlayer *gpl, struct bGPDframe *gpf); + +struct bGPDlayer *BKE_gpencil_layer_getactive(struct bGPdata *gpd); +void BKE_gpencil_layer_setactive(struct bGPdata *gpd, struct bGPDlayer *active); +void BKE_gpencil_layer_delete(struct bGPdata *gpd, struct bGPDlayer *gpl); + +struct bGPDbrush *BKE_gpencil_brush_getactive(struct ToolSettings *ts); +void BKE_gpencil_brush_setactive(struct ToolSettings *ts, struct bGPDbrush *active); +void BKE_gpencil_brush_delete(struct ToolSettings *ts, struct bGPDbrush *palette); + +struct bGPDpalette *BKE_gpencil_palette_getactive(struct bGPdata *gpd); +void BKE_gpencil_palette_setactive(struct bGPdata *gpd, struct bGPDpalette *active); +void BKE_gpencil_palette_delete(struct bGPdata *gpd, struct bGPDpalette *palette); +void BKE_gpencil_palette_change_strokes(struct bGPdata *gpd); + +struct bGPDpalettecolor *BKE_gpencil_palettecolor_getactive(struct bGPDpalette *palette); +void BKE_gpencil_palettecolor_setactive(struct bGPDpalette *palette, struct bGPDpalettecolor *active); +void BKE_gpencil_palettecolor_delete(struct bGPDpalette *palette, struct bGPDpalettecolor *palcolor); +struct bGPDpalettecolor *BKE_gpencil_palettecolor_getbyname(struct bGPDpalette *palette, char *name); +void BKE_gpencil_palettecolor_changename(struct bGPdata *gpd, char *oldname, const char *newname); +void BKE_gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name); #endif /* __BKE_GPENCIL_H__ */ diff --git a/source/blender/blenkernel/BKE_idcode.h b/source/blender/blenkernel/BKE_idcode.h index 6de0efe2709..964a49435f1 100644 --- a/source/blender/blenkernel/BKE_idcode.h +++ b/source/blender/blenkernel/BKE_idcode.h @@ -42,6 +42,8 @@ bool BKE_idcode_is_valid(short idcode); int BKE_idcode_to_idfilter(const short idcode); short BKE_idcode_from_idfilter(const int idfilter); +int BKE_idcode_to_index(const short idcode); + /** * Return an ID code and steps the index forward 1. * diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index e32bc2ffb20..e49019fcfae 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -94,7 +94,7 @@ void id_clear_lib_data_ex(struct Main *bmain, struct ID *id, const bool id_in_ma struct ListBase *which_libbase(struct Main *mainlib, short type); -#define MAX_LIBARRAY 34 +#define MAX_LIBARRAY 35 int set_listbasepointers(struct Main *main, struct ListBase *lb[MAX_LIBARRAY]); /* Main API */ diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index 44e4da4e0a3..a4f5c425282 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -42,6 +42,8 @@ */ #include "DNA_listBase.h" +#include "BKE_library.h" + #ifdef __cplusplus extern "C" { #endif @@ -102,8 +104,9 @@ typedef struct Main { ListBase movieclip; ListBase mask; ListBase linestyle; + ListBase cachefiles; - char id_tag_update[256]; + char id_tag_update[MAX_LIBARRAY]; /* Evaluation context used by viewport */ struct EvaluationContext *eval_ctx; diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h index df739996c54..8ae5c2b3c45 100644 --- a/source/blender/blenkernel/BKE_material.h +++ b/source/blender/blenkernel/BKE_material.h @@ -49,7 +49,7 @@ void BKE_material_free(struct Material *ma); void BKE_material_free_ex(struct Material *ma, bool do_id_user); void test_object_materials(struct Object *ob, struct ID *id); void test_all_objects_materials(struct Main *bmain, struct ID *id); -void BKE_material_resize_object(struct Object *ob, const short totcol, bool do_id_user); +void BKE_material_resize_object(struct Main *bmain, struct Object *ob, const short totcol, bool do_id_user); void BKE_material_init(struct Material *ma); void BKE_material_remap_object(struct Object *ob, const unsigned int *remap); void BKE_material_remap_object_calc(struct Object *ob_dst, struct Object *ob_src, short *remap_src_to_dst); @@ -90,10 +90,10 @@ void BKE_texpaint_slot_refresh_cache(struct Scene *scene, struct Material *ma); void BKE_texpaint_slots_refresh_object(struct Scene *scene, struct Object *ob); /* rna api */ -void BKE_material_resize_id(struct ID *id, short totcol, bool do_id_user); -void BKE_material_append_id(struct ID *id, struct Material *ma); -struct Material *BKE_material_pop_id(struct ID *id, int index, bool update_data); /* index is an int because of RNA */ -void BKE_material_clear_id(struct ID *id, bool update_data); +void BKE_material_resize_id(struct Main *bmain, struct ID *id, short totcol, bool do_id_user); +void BKE_material_append_id(struct Main *bmain, struct ID *id, struct Material *ma); +struct Material *BKE_material_pop_id(struct Main *bmain, struct ID *id, int index, bool update_data); /* index is an int because of RNA */ +void BKE_material_clear_id(struct Main *bmain, struct ID *id, bool update_data); /* rendering */ void init_render_material(struct Material *, int, float *); diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index 37831728e6f..b3e3968ca9b 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -63,6 +63,8 @@ struct BVHTreeRay; struct BVHTreeRayHit; struct EdgeHash; +#define PARTICLE_COLLISION_MAX_COLLISIONS 10 + #define PARTICLE_P ParticleData * pa; int p #define LOOP_PARTICLES for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++) #define LOOP_EXISTING_PARTICLES for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++) if (!(pa->flag & PARS_UNEXIST)) @@ -205,8 +207,7 @@ typedef struct ParticleCollisionElement { typedef struct ParticleCollision { struct Object *current; struct Object *hit; - struct Object *prev; - struct Object *skip; + struct Object *skip[PARTICLE_COLLISION_MAX_COLLISIONS+1]; struct Object *emitter; struct CollisionModifierData *md; // collision modifier for current object; @@ -218,7 +219,7 @@ typedef struct ParticleCollision { float original_ray_length; //original length of co2-co1, needed for collision time evaluation - int prev_index; + int skip_count; ParticleCollisionElement pce; diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h index 8238ea64242..02f6c435ee2 100644 --- a/source/blender/blenkernel/BKE_pointcache.h +++ b/source/blender/blenkernel/BKE_pointcache.h @@ -304,7 +304,7 @@ void BKE_ptcache_mem_pointers_incr(struct PTCacheMem *pm); int BKE_ptcache_mem_pointers_seek(int point_index, struct PTCacheMem *pm); /* Main cache reading call. */ -int BKE_ptcache_read(PTCacheID *pid, float cfra); +int BKE_ptcache_read(PTCacheID *pid, float cfra, bool no_extrapolate_old); /* Main cache writing call. */ int BKE_ptcache_write(PTCacheID *pid, unsigned int cfra); diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index 30bb6954019..811e9136fc9 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -477,4 +477,6 @@ struct ImBuf *BKE_sequencer_render_mask_input( int cfra, int fra_offset, bool make_float); void BKE_sequencer_color_balance_apply(struct StripColorBalance *cb, struct ImBuf *ibuf, float mul, bool make_float, struct ImBuf *mask_input); +void BKE_sequencer_all_free_anim_ibufs(int cfra); + #endif /* __BKE_SEQUENCER_H__ */ diff --git a/source/blender/blenkernel/BKE_tracking.h b/source/blender/blenkernel/BKE_tracking.h index 1938bb08395..30873567297 100644 --- a/source/blender/blenkernel/BKE_tracking.h +++ b/source/blender/blenkernel/BKE_tracking.h @@ -277,9 +277,9 @@ void BKE_tracking_detect_harris(struct MovieTracking *tracking, struct ListBase bool place_outside_layer); /* **** 2D stabilization **** */ -void BKE_tracking_stabilization_data_get(struct MovieTracking *tracking, int framenr, int width, int height, +void BKE_tracking_stabilization_data_get(struct MovieClip *clip, int framenr, int width, int height, float translation[2], float *scale, float *angle); -struct ImBuf *BKE_tracking_stabilize_frame(struct MovieTracking *tracking, int framenr, struct ImBuf *ibuf, +struct ImBuf *BKE_tracking_stabilize_frame(struct MovieClip *clip, int framenr, struct ImBuf *ibuf, float translation[2], float *scale, float *angle); void BKE_tracking_stabilization_data_to_mat4(int width, int height, float aspect, float translation[2], float scale, float angle, float mat[4][4]); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index b7ff81d603b..157c4408d6a 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -81,6 +81,7 @@ set(SRC intern/brush.c intern/bullet.c intern/bvhutils.c + intern/cachefile.c intern/camera.c intern/cdderivedmesh.c intern/cloth.c @@ -207,6 +208,7 @@ set(SRC BKE_brush.h BKE_bullet.h BKE_bvhutils.h + BKE_cachefile.h BKE_camera.h BKE_ccg.h BKE_cdderivedmesh.h @@ -500,6 +502,13 @@ if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() +if(WITH_ALEMBIC) + list(APPEND INC + ../alembic + ) + add_definitions(-DWITH_ALEMBIC) +endif() + if(WITH_OPENSUBDIV) add_definitions(-DWITH_OPENSUBDIV) list(APPEND INC_SYS diff --git a/source/blender/blenkernel/depsgraph_private.h b/source/blender/blenkernel/depsgraph_private.h index 7b3199efb41..69ca75836d9 100644 --- a/source/blender/blenkernel/depsgraph_private.h +++ b/source/blender/blenkernel/depsgraph_private.h @@ -34,6 +34,11 @@ #include "DNA_constraint_types.h" #include "BKE_constraint.h" +struct Scene; +struct Group; +struct EffectorWeights; +struct ModifierData; + /* **** DAG relation types *** */ /* scene link to object */ @@ -152,6 +157,11 @@ DagNode *dag_get_node(DagForest *forest, void *fob); DagNode *dag_get_sub_node(DagForest *forest, void *fob); void dag_add_relation(DagForest *forest, DagNode *fob1, DagNode *fob2, short rel, const char *name); +typedef bool (*DagCollobjFilterFunction)(struct Object *obj, struct ModifierData *md); + +void dag_add_collision_relations(DagForest *dag, struct Scene *scene, Object *ob, DagNode *node, struct Group *group, int layer, unsigned int modifier_type, DagCollobjFilterFunction fn, bool dupli, const char *name); +void dag_add_forcefield_relations(DagForest *dag, struct Scene *scene, Object *ob, DagNode *node, struct EffectorWeights *eff, bool add_absorption, int skip_forcefield, const char *name); + void graph_print_queue(DagNodeQueue *nqueue); void graph_print_queue_dist(DagNodeQueue *nqueue); void graph_print_adj_list(DagForest *dag); diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 477c7036762..1a5d77bbc07 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -97,6 +97,7 @@ bool id_type_can_have_animdata(const short id_type) case ID_MC: case ID_MSK: case ID_GD: + case ID_CF: return true; /* no AnimData */ @@ -1160,6 +1161,9 @@ void BKE_animdata_main_cb(Main *mainptr, ID_AnimData_Edit_Callback func, void *u /* grease pencil */ ANIMDATA_IDS_CB(mainptr->gpencil.first); + + /* cache files */ + ANIMDATA_IDS_CB(mainptr->cachefiles.first); } /* Fix all RNA-Paths throughout the database (directly access the Global.main version) @@ -1250,6 +1254,9 @@ void BKE_animdata_fix_paths_rename_all(ID *ref_id, const char *prefix, const cha /* grease pencil */ RENAMEFIX_ANIM_IDS(mainptr->gpencil.first); + + /* cache files */ + RENAMEFIX_ANIM_IDS(mainptr->cachefiles.first); /* scenes */ RENAMEFIX_ANIM_NODETREE_IDS(mainptr->scene.first, Scene); @@ -2873,6 +2880,9 @@ void BKE_animsys_evaluate_all_animation(Main *main, Scene *scene, float ctime) /* grease pencil */ EVAL_ANIM_IDS(main->gpencil.first, ADT_RECALC_ANIM); + + /* cache files */ + EVAL_ANIM_IDS(main->cachefiles.first, ADT_RECALC_ANIM); /* objects */ /* ADT_RECALC_ANIM doesn't need to be supplied here, since object AnimData gets diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index a708c59fa97..487b8ffa2b5 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -48,6 +48,7 @@ #include "MEM_guardedalloc.h" #include "DNA_brush_types.h" +#include "DNA_cachefile_types.h" #include "DNA_image_types.h" #include "DNA_mesh_types.h" #include "DNA_modifier_types.h" @@ -653,6 +654,12 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int rewrite_path_fixed(clip->name, visit_cb, absbase, bpath_user_data); break; } + case ID_CF: + { + CacheFile *cache_file = (CacheFile *)id; + rewrite_path_fixed(cache_file->filepath, visit_cb, absbase, bpath_user_data); + break; + } default: /* Nothing to do for other IDs that don't contain file paths. */ break; diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c new file mode 100644 index 00000000000..16f263791db --- /dev/null +++ b/source/blender/blenkernel/intern/cachefile.c @@ -0,0 +1,173 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/cachefile.c + * \ingroup bke + */ + +#include "DNA_anim_types.h" +#include "DNA_cachefile_types.h" +#include "DNA_scene_types.h" + +#include "BLI_fileops.h" +#include "BLI_listbase.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "BKE_animsys.h" +#include "BKE_cachefile.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_scene.h" + +#ifdef WITH_ALEMBIC +# include "ABC_alembic.h" +#endif + +void *BKE_cachefile_add(Main *bmain, const char *name) +{ + CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, name); + + BKE_cachefile_init(cache_file); + + return cache_file; +} + +void BKE_cachefile_init(CacheFile *cache_file) +{ + cache_file->handle = NULL; + cache_file->filepath[0] = '\0'; + cache_file->override_frame = false; + cache_file->frame = 0.0f; + cache_file->is_sequence = false; + cache_file->scale = 1.0f; +} + +/** Free (or release) any data used by this cachefile (does not free the cachefile itself). */ +void BKE_cachefile_free(CacheFile *cache_file) +{ + BKE_animdata_free((ID *)cache_file, false); + +#ifdef WITH_ALEMBIC + ABC_free_handle(cache_file->handle); +#endif + + BLI_freelistN(&cache_file->object_paths); +} + +CacheFile *BKE_cachefile_copy(Main *bmain, CacheFile *cache_file) +{ + CacheFile *new_cache_file = BKE_libblock_copy(bmain, &cache_file->id); + new_cache_file->handle = NULL; + + BLI_listbase_clear(&cache_file->object_paths); + + BKE_id_copy_ensure_local(bmain, &cache_file->id, &new_cache_file->id); + + return new_cache_file; +} + +void BKE_cachefile_make_local(Main *bmain, CacheFile *cache_file, const bool lib_local) +{ + BKE_id_make_local_generic(bmain, &cache_file->id, true, lib_local); +} + +void BKE_cachefile_reload(const Main *bmain, CacheFile *cache_file) +{ + char filepath[FILE_MAX]; + + BLI_strncpy(filepath, cache_file->filepath, sizeof(filepath)); + BLI_path_abs(filepath, ID_BLEND_PATH(bmain, &cache_file->id)); + +#ifdef WITH_ALEMBIC + if (cache_file->handle) { + ABC_free_handle(cache_file->handle); + } + + cache_file->handle = ABC_create_handle(filepath, &cache_file->object_paths); +#endif +} + +void BKE_cachefile_ensure_handle(const Main *bmain, CacheFile *cache_file) +{ + if (cache_file->handle == NULL) { + BKE_cachefile_reload(bmain, cache_file); + } +} + +void BKE_cachefile_update_frame(Main *bmain, Scene *scene, const float ctime, const float fps) +{ + CacheFile *cache_file; + char filename[FILE_MAX]; + + for (cache_file = bmain->cachefiles.first; cache_file; cache_file = cache_file->id.next) { + /* Execute drivers only, as animation has already been done. */ + BKE_animsys_evaluate_animdata(scene, &cache_file->id, cache_file->adt, ctime, ADT_RECALC_DRIVERS); + + if (!cache_file->is_sequence) { + continue; + } + + const float time = BKE_cachefile_time_offset(cache_file, ctime, fps); + + if (BKE_cachefile_filepath_get(bmain, cache_file, time, filename)) { +#ifdef WITH_ALEMBIC + ABC_free_handle(cache_file->handle); + cache_file->handle = ABC_create_handle(filename, NULL); +#endif + } + } +} + +bool BKE_cachefile_filepath_get( + const Main *bmain, const CacheFile *cache_file, float frame, + char r_filepath[FILE_MAX]) +{ + BLI_strncpy(r_filepath, cache_file->filepath, FILE_MAX); + BLI_path_abs(r_filepath, ID_BLEND_PATH(bmain, &cache_file->id)); + + int fframe; + int frame_len; + + if (cache_file->is_sequence && BLI_path_frame_get(r_filepath, &fframe, &frame_len)) { + char ext[32]; + BLI_path_frame_strip(r_filepath, true, ext); + BLI_path_frame(r_filepath, frame, frame_len); + BLI_ensure_extension(r_filepath, FILE_MAX, ext); + + /* TODO(kevin): store sequence range? */ + return BLI_exists(r_filepath); + } + + return true; +} + +float BKE_cachefile_time_offset(CacheFile *cache_file, const float time, const float fps) +{ + const float frame = (cache_file->override_frame ? cache_file->frame : time); + return cache_file->is_sequence ? frame : frame / fps; +} diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 159d5b82a6c..d257a1cfcae 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -3402,7 +3402,7 @@ void CDDM_calc_edges(DerivedMesh *dm) BLI_edgehashIterator_getKey(ehi, &med->v1, &med->v2); j = GET_INT_FROM_POINTER(BLI_edgehashIterator_getValue(ehi)); - if (j == 0) { + if (j == 0 || !eindex) { med->flag = ME_EDGEDRAW | ME_EDGERENDER; *index = ORIGINDEX_NONE; } diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index 681b93172b2..28ef3f6f248 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -81,8 +81,10 @@ void cloth_init(ClothModifierData *clmd ) clmd->sim_parms->gravity[1] = 0.0; clmd->sim_parms->gravity[2] = -9.81; clmd->sim_parms->structural = 15.0; + clmd->sim_parms->max_struct = 15.0; clmd->sim_parms->shear = 15.0; clmd->sim_parms->bending = 0.5; + clmd->sim_parms->max_bend = 0.5; clmd->sim_parms->bending_damping = 0.5; clmd->sim_parms->Cdis = 5.0; clmd->sim_parms->Cvi = 1.0; @@ -449,9 +451,12 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived } /* try to read from cache */ - cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe); + bool can_simulate = (framenr == clmd->clothObject->last_frame+1) && !(cache->flag & PTCACHE_BAKED); - if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED) { + cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe, can_simulate); + + if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED || + (!can_simulate && cache_result == PTCACHE_READ_OLD)) { BKE_cloth_solver_set_positions(clmd); cloth_to_object (ob, clmd, vertexCos); @@ -473,7 +478,7 @@ void clothModifier_do(ClothModifierData *clmd, Scene *scene, Object *ob, Derived return; } - if (framenr!=clmd->clothObject->last_frame+1) + if (!can_simulate) return; /* if on second frame, write cache for first frame */ diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index 8cac856b560..35a7aafdbde 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -503,12 +503,13 @@ static void add_collision_object(Object ***objs, unsigned int *numobj, unsigned // return all collision objects in scene // collision object will exclude self -Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned int *numcollobj, unsigned int modifier_type) +Object **get_collisionobjects_ext(Scene *scene, Object *self, Group *group, int layer, unsigned int *numcollobj, unsigned int modifier_type, bool dupli) { Base *base; Object **objs; GroupObject *go; unsigned int numobj= 0, maxobj= 100; + int level = dupli ? 0 : 1; objs= MEM_callocN(sizeof(Object *)*maxobj, "CollisionObjectsArray"); @@ -516,16 +517,14 @@ Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned if (group) { /* use specified group */ for (go= group->gobject.first; go; go= go->next) - add_collision_object(&objs, &numobj, &maxobj, go->ob, self, 0, modifier_type); + add_collision_object(&objs, &numobj, &maxobj, go->ob, self, level, modifier_type); } else { Scene *sce_iter; /* add objects in same layer in scene */ for (SETLOOPER(scene, sce_iter, base)) { - /* Need to check for active layers, too. - Otherwise this check fails if the objects are not on the same layer - DG */ - if ((base->lay & self->lay) || (base->lay & scene->lay)) - add_collision_object(&objs, &numobj, &maxobj, base->object, self, 0, modifier_type); + if ( base->lay & layer ) + add_collision_object(&objs, &numobj, &maxobj, base->object, self, level, modifier_type); } } @@ -535,6 +534,13 @@ Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned return objs; } +Object **get_collisionobjects(Scene *scene, Object *self, Group *group, unsigned int *numcollobj, unsigned int modifier_type) +{ + /* Need to check for active layers, too. + Otherwise this check fails if the objects are not on the same layer - DG */ + return get_collisionobjects_ext(scene, self, group, self->lay | scene->lay, numcollobj, modifier_type, true); +} + static void add_collider_cache_object(ListBase **objs, Object *ob, Object *self, int level) { CollisionModifierData *cmd= NULL; diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 4c9ddd495e3..70fdd4aa72e 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -46,6 +46,7 @@ #include "BLT_translation.h" #include "DNA_armature_types.h" +#include "DNA_cachefile_types.h" #include "DNA_constraint_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" @@ -63,6 +64,7 @@ #include "BKE_anim.h" /* for the curve calculation part */ #include "BKE_armature.h" #include "BKE_bvhutils.h" +#include "BKE_cachefile.h" #include "BKE_camera.h" #include "BKE_constraint.h" #include "BKE_curve.h" @@ -86,6 +88,10 @@ # include "BPY_extern.h" #endif +#ifdef WITH_ALEMBIC +# include "ABC_alembic.h" +#endif + /* ---------------------------------------------------------------------------- */ /* Useful macros for testing various common flag combinations */ @@ -4333,6 +4339,73 @@ static bConstraintTypeInfo CTI_OBJECTSOLVER = { objectsolver_evaluate /* evaluate */ }; +/* ----------- Transform Cache ------------- */ + +static void transformcache_id_looper(bConstraint *con, ConstraintIDFunc func, void *userdata) +{ + bTransformCacheConstraint *data = con->data; + func(con, (ID **)&data->cache_file, false, userdata); +} + +static void transformcache_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targets) +{ +#ifdef WITH_ALEMBIC + bTransformCacheConstraint *data = con->data; + Scene *scene = cob->scene; + + const float frame = BKE_scene_frame_get(scene); + const float time = BKE_cachefile_time_offset(data->cache_file, frame, FPS); + + CacheFile *cache_file = data->cache_file; + + BKE_cachefile_ensure_handle(G.main, cache_file); + + ABC_get_transform(cache_file->handle, cob->ob, data->object_path, + cob->matrix, time, cache_file->scale); +#else + UNUSED_VARS(con, cob); +#endif + + UNUSED_VARS(targets); +} + +static void transformcache_copy(bConstraint *con, bConstraint *srccon) +{ + bTransformCacheConstraint *src = srccon->data; + bTransformCacheConstraint *dst = con->data; + + BLI_strncpy(dst->object_path, src->object_path, sizeof(dst->object_path)); + dst->cache_file = src->cache_file; + + if (dst->cache_file) { + id_us_plus(&dst->cache_file->id); + } +} + +static void transformcache_free(bConstraint *con) +{ + bTransformCacheConstraint *data = con->data; + + if (data->cache_file) { + id_us_min(&data->cache_file->id); + } +} + +static bConstraintTypeInfo CTI_TRANSFORM_CACHE = { + CONSTRAINT_TYPE_TRANSFORM_CACHE, /* type */ + sizeof(bTransformCacheConstraint), /* size */ + "Transform Cache", /* name */ + "bTransformCacheConstraint", /* struct name */ + transformcache_free, /* free data */ + transformcache_id_looper, /* id looper */ + transformcache_copy, /* copy data */ + NULL, /* new data */ + NULL, /* get constraint targets */ + NULL, /* flush constraint targets */ + NULL, /* get target matrix */ + transformcache_evaluate /* evaluate */ +}; + /* ************************* Constraints Type-Info *************************** */ /* All of the constraints api functions use bConstraintTypeInfo structs to carry out * and operations that involve constraint specific code. @@ -4374,6 +4447,7 @@ static void constraints_init_typeinfo(void) constraintsTypeInfo[26] = &CTI_FOLLOWTRACK; /* Follow Track Constraint */ constraintsTypeInfo[27] = &CTI_CAMERASOLVER; /* Camera Solver Constraint */ constraintsTypeInfo[28] = &CTI_OBJECTSOLVER; /* Object Solver Constraint */ + constraintsTypeInfo[29] = &CTI_TRANSFORM_CACHE; /* Transform Cache Constraint */ } /* This function should be used for getting the appropriate type-info when only diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 5b7698544e0..926ca8da192 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -1067,6 +1067,11 @@ struct EditBone *CTX_data_active_bone(const bContext *C) return ctx_data_pointer_get(C, "active_bone"); } +struct CacheFile *CTX_data_edit_cachefile(const bContext *C) +{ + return ctx_data_pointer_get(C, "edit_cachefile"); +} + int CTX_data_selected_bones(const bContext *C, ListBase *list) { return ctx_data_collection_get(C, "selected_bones", list); @@ -1112,6 +1117,21 @@ bGPDlayer *CTX_data_active_gpencil_layer(const bContext *C) return ctx_data_pointer_get(C, "active_gpencil_layer"); } +bGPDpalette *CTX_data_active_gpencil_palette(const bContext *C) +{ + return ctx_data_pointer_get(C, "active_gpencil_palette"); +} + +bGPDpalettecolor *CTX_data_active_gpencil_palettecolor(const bContext *C) +{ + return ctx_data_pointer_get(C, "active_gpencil_palettecolor"); +} + +bGPDbrush *CTX_data_active_gpencil_brush(const bContext *C) +{ + return ctx_data_pointer_get(C, "active_gpencil_brush"); +} + bGPDframe *CTX_data_active_gpencil_frame(const bContext *C) { return ctx_data_pointer_get(C, "active_gpencil_frame"); diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index 8ea65bf92c1..5f8332dcf0c 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -46,6 +46,7 @@ #include "DNA_anim_types.h" #include "DNA_camera_types.h" +#include "DNA_cachefile_types.h" #include "DNA_group_types.h" #include "DNA_lamp_types.h" #include "DNA_lattice_types.h" @@ -58,14 +59,18 @@ #include "DNA_windowmanager_types.h" #include "DNA_movieclip_types.h" #include "DNA_mask_types.h" +#include "DNA_modifier_types.h" +#include "DNA_rigidbody_types.h" #include "BKE_anim.h" #include "BKE_animsys.h" #include "BKE_action.h" #include "BKE_DerivedMesh.h" +#include "BKE_collision.h" #include "BKE_effect.h" #include "BKE_fcurve.h" #include "BKE_global.h" +#include "BKE_idcode.h" #include "BKE_image.h" #include "BKE_key.h" #include "BKE_library.h" @@ -448,49 +453,51 @@ static void dag_add_lamp_driver_relations(DagForest *dag, DagNode *node, Lamp *l la->id.tag &= ~LIB_TAG_DOIT; } -static void check_and_create_collision_relation(DagForest *dag, Object *ob, DagNode *node, Object *ob1, int skip_forcefield, bool no_collision) +static void create_collision_relation(DagForest *dag, DagNode *node, Object *ob1, const char *name) { - DagNode *node2; - if (ob1->pd && (ob1->pd->deflect || ob1->pd->forcefield) && (ob1 != ob)) { - if ((skip_forcefield && ob1->pd->forcefield == skip_forcefield) || (no_collision && ob1->pd->forcefield == 0)) - return; - node2 = dag_get_node(dag, ob1); - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Field Collision"); + DagNode *node2 = dag_get_node(dag, ob1); + dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, name); +} + +void dag_add_collision_relations(DagForest *dag, Scene *scene, Object *ob, DagNode *node, Group *group, int layer, unsigned int modifier_type, DagCollobjFilterFunction fn, bool dupli, const char *name) +{ + unsigned int numcollobj; + Object **collobjs = get_collisionobjects_ext(scene, ob, group, layer, &numcollobj, modifier_type, dupli); + + for (unsigned int i = 0; i < numcollobj; i++) { + Object *ob1 = collobjs[i]; + + if (!fn || fn(ob1, modifiers_findByType(ob1, modifier_type))) { + create_collision_relation(dag, node, ob1, name); + } } + + if (collobjs) + MEM_freeN(collobjs); } -static void dag_add_collision_field_relation(DagForest *dag, Scene *scene, Object *ob, DagNode *node, int skip_forcefield, bool no_collision) +void dag_add_forcefield_relations(DagForest *dag, Scene *scene, Object *ob, DagNode *node, EffectorWeights *effector_weights, bool add_absorption, int skip_forcefield, const char *name) { - Base *base; - ParticleSystem *particle_system; + ListBase *effectors = pdInitEffectors(scene, ob, NULL, effector_weights, false); - for (particle_system = ob->particlesystem.first; - particle_system; - particle_system = particle_system->next) - { - EffectorWeights *effector_weights = particle_system->part->effector_weights; - if (effector_weights->group) { - GroupObject *group_object; + if (effectors) { + for (EffectorCache *eff = effectors->first; eff; eff = eff->next) { + if (eff->ob != ob && eff->pd->forcefield != skip_forcefield) { + create_collision_relation(dag, node, eff->ob, name); - for (group_object = effector_weights->group->gobject.first; - group_object; - group_object = group_object->next) - { - if ((group_object->ob->lay & ob->lay)) { - check_and_create_collision_relation(dag, ob, node, group_object->ob, skip_forcefield, no_collision); + if (eff->pd->forcefield == PFIELD_SMOKEFLOW && eff->pd->f_source) { + create_collision_relation(dag, node, eff->pd->f_source, "Smoke Force Domain"); + } + + if (add_absorption && (eff->pd->flag & PFIELD_VISIBILITY)) { + /* Actual code uses get_collider_cache */ + dag_add_collision_relations(dag, scene, ob, node, NULL, eff->ob->lay, eModifierType_Collision, NULL, true, "Force Absorption"); } } } } - /* would be nice to have a list of colliders here - * so for now walk all objects in scene check 'same layer rule' */ - for (base = scene->base.first; base; base = base->next) { - if ((base->lay & ob->lay)) { - Object *ob1 = base->object; - check_and_create_collision_relation(dag, ob, node, ob1, skip_forcefield, no_collision); - } - } + pdEndEffectors(&effectors); } static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Scene *scene, Object *ob, int mask) @@ -641,23 +648,13 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc } } - /* softbody collision */ + /* rigidbody force fields */ if ((ob->type == OB_MESH) || (ob->type == OB_CURVE) || (ob->type == OB_LATTICE)) { - if (ob->particlesystem.first || - modifiers_isModifierEnabled(ob, eModifierType_Softbody) || - modifiers_isModifierEnabled(ob, eModifierType_Cloth) || - modifiers_isModifierEnabled(ob, eModifierType_DynamicPaint)) - { - dag_add_collision_field_relation(dag, scene, ob, node, 0, false); /* TODO: use effectorweight->group */ - } - else if (modifiers_isModifierEnabled(ob, eModifierType_Smoke)) { - dag_add_collision_field_relation(dag, scene, ob, node, PFIELD_SMOKEFLOW, false); - } - else if (ob->rigidbody_object) { - dag_add_collision_field_relation(dag, scene, ob, node, 0, true); + if (ob->rigidbody_object && scene->rigidbody_world) { + dag_add_forcefield_relations(dag, scene, ob, node, scene->rigidbody_world->effector_weights, true, 0, "Force Field"); } } - + /* object data drivers */ if (ob->data) { AnimData *adt = BKE_animdata_from_id((ID *)ob->data); @@ -761,8 +758,6 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc BoidRule *rule = NULL; BoidState *state = NULL; ParticleSettings *part = psys->part; - ListBase *effectors = NULL; - EffectorCache *eff; if (part->adt) { dag_add_driver_relation(part->adt, dag, node, 1); @@ -801,18 +796,12 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Sc } } - effectors = pdInitEffectors(scene, ob, psys, part->effector_weights, false); - - if (effectors) { - for (eff = effectors->first; eff; eff = eff->next) { - if (eff->psys) { - node2 = dag_get_node(dag, eff->ob); - dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Particle Field"); - } - } + if (part->type != PART_HAIR) { + /* Actual code uses get_collider_cache */ + dag_add_collision_relations(dag, scene, ob, node, part->collision_group, ob->lay, eModifierType_Collision, NULL, true, "Particle Collision"); } - pdEndEffectors(&effectors); + dag_add_forcefield_relations(dag, scene, ob, node, part->effector_weights, part->type == PART_HAIR, 0, "Particle Force Field"); if (part->boids) { for (state = part->boids->states.first; state; state = state->next) { @@ -2173,7 +2162,12 @@ static void dag_object_time_update_flags(Main *bmain, Scene *scene, Object *ob) if (cti) { /* special case for camera tracking -- it doesn't use targets to define relations */ - if (ELEM(cti->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_CAMERASOLVER, CONSTRAINT_TYPE_OBJECTSOLVER)) { + if (ELEM(cti->type, + CONSTRAINT_TYPE_FOLLOWTRACK, + CONSTRAINT_TYPE_CAMERASOLVER, + CONSTRAINT_TYPE_OBJECTSOLVER, + CONSTRAINT_TYPE_TRANSFORM_CACHE)) + { ob->recalc |= OB_RECALC_OB; } else if (cti->get_constraint_targets) { @@ -2799,9 +2793,7 @@ void DAG_ids_flush_tagged(Main *bmain) ListBase *lb = lbarray[a]; ID *id = lb->first; - /* we tag based on first ID type character to avoid - * looping over all ID's in case there are no tags */ - if (id && bmain->id_tag_update[id->name[0]]) { + if (id && bmain->id_tag_update[BKE_idcode_to_index(GS(id->name))]) { for (; id; id = id->next) { if (id->tag & (LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA)) { @@ -2841,9 +2833,7 @@ void DAG_ids_check_recalc(Main *bmain, Scene *scene, bool time) ListBase *lb = lbarray[a]; ID *id = lb->first; - /* we tag based on first ID type character to avoid - * looping over all ID's in case there are no tags */ - if (id && bmain->id_tag_update[id->name[0]]) { + if (id && bmain->id_tag_update[BKE_idcode_to_index(GS(id->name))]) { updated = true; break; } @@ -2924,9 +2914,7 @@ void DAG_ids_clear_recalc(Main *bmain) ListBase *lb = lbarray[a]; ID *id = lb->first; - /* we tag based on first ID type character to avoid - * looping over all ID's in case there are no tags */ - if (id && bmain->id_tag_update[id->name[0]]) { + if (id && bmain->id_tag_update[BKE_idcode_to_index(GS(id->name))]) { for (; id; id = id->next) { if (id->tag & (LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA)) id->tag &= ~(LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA); @@ -3001,6 +2989,33 @@ void DAG_id_tag_update_ex(Main *bmain, ID *id, short flag) /* BLI_assert(!"invalid flag for this 'idtype'"); */ } } + else if (GS(id->name) == ID_CF) { + for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { + ModifierData *md = modifiers_findByType(ob, eModifierType_MeshSequenceCache); + + if (md) { + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + + if (mcmd->cache_file && (&mcmd->cache_file->id == id)) { + ob->recalc |= OB_RECALC_DATA; + continue; + } + } + + for (bConstraint *con = ob->constraints.first; con; con = con->next) { + if (con->type != CONSTRAINT_TYPE_TRANSFORM_CACHE) { + continue; + } + + bTransformCacheConstraint *data = con->data; + + if (data->cache_file && (&data->cache_file->id == id)) { + ob->recalc |= OB_RECALC_DATA; + break; + } + } + } + } } void DAG_id_tag_update(ID *id, short flag) @@ -3020,12 +3035,12 @@ void DAG_id_type_tag(Main *bmain, short idtype) DAG_id_type_tag(bmain, ID_SCE); } - bmain->id_tag_update[((char *)&idtype)[0]] = 1; + bmain->id_tag_update[BKE_idcode_to_index(idtype)] = 1; } int DAG_id_type_tagged(Main *bmain, short idtype) { - return bmain->id_tag_update[((char *)&idtype)[0]]; + return bmain->id_tag_update[BKE_idcode_to_index(idtype)]; } #if 0 // UNUSED diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index d35de6fc5d3..24425720bd2 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -2015,11 +2015,13 @@ static void dynamicPaint_frameUpdate(DynamicPaintModifierData *pmd, Scene *scene } /* try to read from cache */ - if (BKE_ptcache_read(&pid, (float)scene->r.cfra)) { + bool can_simulate = ((int)scene->r.cfra == current_frame) && !(cache->flag & PTCACHE_BAKED); + + if (BKE_ptcache_read(&pid, (float)scene->r.cfra, can_simulate)) { BKE_ptcache_validate(cache, (int)scene->r.cfra); } /* if read failed and we're on surface range do recalculate */ - else if ((int)scene->r.cfra == current_frame && !(cache->flag & PTCACHE_BAKED)) { + else if (can_simulate) { /* calculate surface frame */ canvas->flags |= MOD_DPAINT_BAKING; dynamicPaint_calculateFrame(surface, scene, ob, current_frame); diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 5090e46f1fc..7e6897a2858 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -155,15 +155,20 @@ static EffectorCache *new_effector_cache(Scene *scene, Object *ob, ParticleSyste eff->frame = -1; return eff; } -static void add_object_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, Object *ob_src) +static void add_object_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, Object *ob_src, bool for_simulation) { EffectorCache *eff = NULL; - if ( ob == ob_src || weights->weight[ob->pd->forcefield] == 0.0f ) + if ( ob == ob_src ) return; - if (ob->pd->shape == PFIELD_SHAPE_POINTS && !ob->derivedFinal ) - return; + if (for_simulation) { + if (weights->weight[ob->pd->forcefield] == 0.0f ) + return; + + if (ob->pd->shape == PFIELD_SHAPE_POINTS && !ob->derivedFinal ) + return; + } if (*effectors == NULL) *effectors = MEM_callocN(sizeof(ListBase), "effectors list"); @@ -175,7 +180,7 @@ static void add_object_to_effectors(ListBase **effectors, Scene *scene, Effector BLI_addtail(*effectors, eff); } -static void add_particles_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, ParticleSystem *psys, ParticleSystem *psys_src) +static void add_particles_to_effectors(ListBase **effectors, Scene *scene, EffectorWeights *weights, Object *ob, ParticleSystem *psys, ParticleSystem *psys_src, bool for_simulation) { ParticleSettings *part= psys->part; @@ -185,14 +190,14 @@ static void add_particles_to_effectors(ListBase **effectors, Scene *scene, Effec if ( psys == psys_src && (part->flag & PART_SELF_EFFECT) == 0) return; - if ( part->pd && part->pd->forcefield && weights->weight[part->pd->forcefield] != 0.0f) { + if ( part->pd && part->pd->forcefield && (!for_simulation || weights->weight[part->pd->forcefield] != 0.0f)) { if (*effectors == NULL) *effectors = MEM_callocN(sizeof(ListBase), "effectors list"); BLI_addtail(*effectors, new_effector_cache(scene, ob, psys, part->pd)); } - if (part->pd2 && part->pd2->forcefield && weights->weight[part->pd2->forcefield] != 0.0f) { + if (part->pd2 && part->pd2->forcefield && (!for_simulation || weights->weight[part->pd2->forcefield] != 0.0f)) { if (*effectors == NULL) *effectors = MEM_callocN(sizeof(ListBase), "effectors list"); @@ -202,7 +207,7 @@ static void add_particles_to_effectors(ListBase **effectors, Scene *scene, Effec /* returns ListBase handle with objects taking part in the effecting */ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src, - EffectorWeights *weights, bool precalc) + EffectorWeights *weights, bool for_simulation) { Base *base; unsigned int layer= ob_src->lay; @@ -214,13 +219,13 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src for (go= weights->group->gobject.first; go; go= go->next) { if ( (go->ob->lay & layer) ) { if ( go->ob->pd && go->ob->pd->forcefield ) - add_object_to_effectors(&effectors, scene, weights, go->ob, ob_src); + add_object_to_effectors(&effectors, scene, weights, go->ob, ob_src, for_simulation); if ( go->ob->particlesystem.first ) { ParticleSystem *psys= go->ob->particlesystem.first; for ( ; psys; psys=psys->next ) - add_particles_to_effectors(&effectors, scene, weights, go->ob, psys, psys_src); + add_particles_to_effectors(&effectors, scene, weights, go->ob, psys, psys_src, for_simulation); } } } @@ -229,19 +234,19 @@ ListBase *pdInitEffectors(Scene *scene, Object *ob_src, ParticleSystem *psys_src for (base = scene->base.first; base; base= base->next) { if ( (base->lay & layer) ) { if ( base->object->pd && base->object->pd->forcefield ) - add_object_to_effectors(&effectors, scene, weights, base->object, ob_src); + add_object_to_effectors(&effectors, scene, weights, base->object, ob_src, for_simulation); if ( base->object->particlesystem.first ) { ParticleSystem *psys= base->object->particlesystem.first; for ( ; psys; psys=psys->next ) - add_particles_to_effectors(&effectors, scene, weights, base->object, psys, psys_src); + add_particles_to_effectors(&effectors, scene, weights, base->object, psys, psys_src, for_simulation); } } } } - if (precalc) + if (for_simulation) pdPrecalculateEffectors(effectors); return effectors; diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 8621da0d42e..e4bac0a947a 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2008, Blender Foundation * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -44,10 +44,12 @@ #include "DNA_gpencil_types.h" #include "DNA_userdef_types.h" +#include "DNA_scene_types.h" #include "BKE_animsys.h" #include "BKE_global.h" #include "BKE_gpencil.h" +#include "BKE_colortools.h" #include "BKE_library.h" #include "BKE_main.h" @@ -57,76 +59,155 @@ /* --------- Memory Management ------------ */ +/* free stroke, doesn't unlink from any listbase */ +void BKE_gpencil_free_stroke(bGPDstroke *gps) +{ + if (gps == NULL) { + return; + } + + /* free stroke memory arrays, then stroke itself */ + if (gps->points) + MEM_freeN(gps->points); + if (gps->triangles) + MEM_freeN(gps->triangles); + + MEM_freeN(gps); +} + /* Free strokes belonging to a gp-frame */ -bool free_gpencil_strokes(bGPDframe *gpf) +bool BKE_gpencil_free_strokes(bGPDframe *gpf) { - bGPDstroke *gps, *gpsn; + bGPDstroke *gps_next; bool changed = (BLI_listbase_is_empty(&gpf->strokes) == false); /* free strokes */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; - - /* free stroke memory arrays, then stroke itself */ - if (gps->points) MEM_freeN(gps->points); - if (gps->triangles) MEM_freeN(gps->triangles); - BLI_freelinkN(&gpf->strokes, gps); + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps_next) { + gps_next = gps->next; + BKE_gpencil_free_stroke(gps); } + BLI_listbase_clear(&gpf->strokes); return changed; } /* Free all of a gp-layer's frames */ -void free_gpencil_frames(bGPDlayer *gpl) +void BKE_gpencil_free_frames(bGPDlayer *gpl) { - bGPDframe *gpf, *gpfn; + bGPDframe *gpf_next; /* error checking */ if (gpl == NULL) return; /* free frames */ - for (gpf = gpl->frames.first; gpf; gpf = gpfn) { - gpfn = gpf->next; + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf_next) { + gpf_next = gpf->next; /* free strokes and their associated memory */ - free_gpencil_strokes(gpf); + BKE_gpencil_free_strokes(gpf); BLI_freelinkN(&gpl->frames, gpf); } gpl->actframe = NULL; } +/* Free all of a gp-colors */ +static void free_gpencil_colors(bGPDpalette *palette) +{ + /* error checking */ + if (palette == NULL) { + return; + } + + /* free colors */ + BLI_freelistN(&palette->colors); +} + +/* Free all of the gp-palettes and colors */ +void BKE_gpencil_free_palettes(ListBase *list) +{ + bGPDpalette *palette_next; + + /* error checking */ + if (list == NULL) { + return; + } + + /* delete palettes */ + for (bGPDpalette *palette = list->first; palette; palette = palette_next) { + palette_next = palette->next; + /* free palette colors */ + free_gpencil_colors(palette); + + MEM_freeN(palette); + } + BLI_listbase_clear(list); +} + +/* Free all of the gp-brushes for a viewport (list should be &gpd->brushes or so) */ +void BKE_gpencil_free_brushes(ListBase *list) +{ + bGPDbrush *brush_next; + + /* error checking */ + if (list == NULL) { + return; + } + + /* delete brushes */ + for (bGPDbrush *brush = list->first; brush; brush = brush_next) { + brush_next = brush->next; + /* free curves */ + if (brush->cur_sensitivity) { + curvemapping_free(brush->cur_sensitivity); + } + if (brush->cur_strength) { + curvemapping_free(brush->cur_strength); + } + if (brush->cur_jitter) { + curvemapping_free(brush->cur_jitter); + } + + MEM_freeN(brush); + } + BLI_listbase_clear(list); +} + /* Free all of the gp-layers for a viewport (list should be &gpd->layers or so) */ -void free_gpencil_layers(ListBase *list) +void BKE_gpencil_free_layers(ListBase *list) { - bGPDlayer *gpl, *gpln; + bGPDlayer *gpl_next; /* error checking */ if (list == NULL) return; /* delete layers */ - for (gpl = list->first; gpl; gpl = gpln) { - gpln = gpl->next; + for (bGPDlayer *gpl = list->first; gpl; gpl = gpl_next) { + gpl_next = gpl->next; /* free layers and their data */ - free_gpencil_frames(gpl); + BKE_gpencil_free_frames(gpl); BLI_freelinkN(list, gpl); } } -/* Free all of GPencil datablock's related data, but not the block itself */ /** Free (or release) any data used by this grease pencil (does not free the gpencil itself). */ -void BKE_gpencil_free(bGPdata *gpd) +void BKE_gpencil_free(bGPdata *gpd, bool free_palettes) { BKE_animdata_free(&gpd->id, false); /* free layers */ - free_gpencil_layers(&gpd->layers); + BKE_gpencil_free_layers(&gpd->layers); + + /* free palettes */ + if (free_palettes) { + BKE_gpencil_free_palettes(&gpd->palettes); + } } /* -------- Container Creation ---------- */ /* add a new gp-frame to the given layer */ -bGPDframe *gpencil_frame_addnew(bGPDlayer *gpl, int cframe) +bGPDframe *BKE_gpencil_frame_addnew(bGPDlayer *gpl, int cframe) { bGPDframe *gpf = NULL, *gf = NULL; short state = 0; @@ -178,9 +259,9 @@ bGPDframe *gpencil_frame_addnew(bGPDlayer *gpl, int cframe) } /* add a copy of the active gp-frame to the given layer */ -bGPDframe *gpencil_frame_addcopy(bGPDlayer *gpl, int cframe) +bGPDframe *BKE_gpencil_frame_addcopy(bGPDlayer *gpl, int cframe) { - bGPDframe *new_frame, *gpf; + bGPDframe *new_frame; bool found = false; /* Error checking/handling */ @@ -190,14 +271,14 @@ bGPDframe *gpencil_frame_addcopy(bGPDlayer *gpl, int cframe) } else if (gpl->actframe == NULL) { /* no active frame, so just create a new one from scratch */ - return gpencil_frame_addnew(gpl, cframe); + return BKE_gpencil_frame_addnew(gpl, cframe); } /* Create a copy of the frame */ - new_frame = gpencil_frame_duplicate(gpl->actframe); + new_frame = BKE_gpencil_frame_duplicate(gpl->actframe); /* Find frame to insert it before */ - for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { if (gpf->framenum > cframe) { /* Add it here */ BLI_insertlinkbefore(&gpl->frames, gpf, new_frame); @@ -209,7 +290,7 @@ bGPDframe *gpencil_frame_addcopy(bGPDlayer *gpl, int cframe) /* This only happens when we're editing with framelock on... * - Delete the new frame and don't do anything else here... */ - free_gpencil_strokes(new_frame); + BKE_gpencil_free_strokes(new_frame); MEM_freeN(new_frame); new_frame = NULL; @@ -233,7 +314,7 @@ bGPDframe *gpencil_frame_addcopy(bGPDlayer *gpl, int cframe) } /* add a new gp-layer and make it the active layer */ -bGPDlayer *gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive) +bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive) { bGPDlayer *gpl; @@ -249,8 +330,11 @@ bGPDlayer *gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive) /* set basic settings */ copy_v4_v4(gpl->color, U.gpencil_new_layer_col); - gpl->thickness = 3; - + /* Since GPv2 thickness must be 0 */ + gpl->thickness = 0; + + gpl->opacity = 1.0f; + /* onion-skinning settings */ if (gpd->flag & GP_DATA_SHOW_ONIONSKINS) gpl->flag |= GP_LAYER_ONIONSKIN; @@ -263,23 +347,280 @@ bGPDlayer *gpencil_layer_addnew(bGPdata *gpd, const char *name, bool setactive) /* high quality fill by default */ gpl->flag |= GP_LAYER_HQ_FILL; - /* default smooth iterations */ - gpl->draw_smoothlvl = 1; - /* auto-name */ BLI_strncpy(gpl->info, name, sizeof(gpl->info)); BLI_uniquename(&gpd->layers, gpl, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl->info)); /* make this one the active one */ if (setactive) - gpencil_layer_setactive(gpd, gpl); + BKE_gpencil_layer_setactive(gpd, gpl); /* return layer */ return gpl; } +/* add a new gp-palette and make it the active */ +bGPDpalette *BKE_gpencil_palette_addnew(bGPdata *gpd, const char *name, bool setactive) +{ + bGPDpalette *palette; + + /* check that list is ok */ + if (gpd == NULL) { + return NULL; + } + + /* allocate memory and add to end of list */ + palette = MEM_callocN(sizeof(bGPDpalette), "bGPDpalette"); + + /* add to datablock */ + BLI_addtail(&gpd->palettes, palette); + + /* set basic settings */ + /* auto-name */ + BLI_strncpy(palette->info, name, sizeof(palette->info)); + BLI_uniquename(&gpd->palettes, palette, DATA_("GP_Palette"), '.', offsetof(bGPDpalette, info), + sizeof(palette->info)); + + /* make this one the active one */ + if (setactive) { + BKE_gpencil_palette_setactive(gpd, palette); + } + + /* return palette */ + return palette; +} + +/* create a set of default drawing brushes with predefined presets */ +void BKE_gpencil_brush_init_presets(ToolSettings *ts) +{ + bGPDbrush *brush; + /* Basic brush */ + brush = BKE_gpencil_brush_addnew(ts, "Basic", true); + brush->thickness = 3.0f; + brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; + brush->draw_sensitivity = 1.0f; + brush->flag |= GP_BRUSH_USE_PRESSURE; + + brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; + brush->draw_strength = 1.0f; + brush->flag |= ~GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->draw_random_press = 0.0f; + + brush->draw_jitter = 0.0f; + brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->draw_angle = 0.0f; + brush->draw_angle_factor = 0.0f; + + brush->draw_smoothfac = 0.0f; + brush->draw_smoothlvl = 1; + brush->sublevel = 0; + brush->draw_random_sub = 0.0f; + + /* Pencil brush */ + brush = BKE_gpencil_brush_addnew(ts, "Pencil", false); + brush->thickness = 7.0f; + brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; + brush->draw_sensitivity = 1.0f; + brush->flag |= GP_BRUSH_USE_PRESSURE; + + brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; + brush->draw_strength = 0.7f; + brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->draw_random_press = 0.0f; + + brush->draw_jitter = 0.0f; + brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->draw_angle = 0.0f; + brush->draw_angle_factor = 0.0f; + + brush->draw_smoothfac = 1.0f; + brush->draw_smoothlvl = 2; + brush->sublevel = 2; + brush->draw_random_sub = 0.0f; + + /* Ink brush */ + brush = BKE_gpencil_brush_addnew(ts, "Ink", false); + brush->thickness = 7.0f; + brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; + brush->draw_sensitivity = 1.6f; + brush->flag |= GP_BRUSH_USE_PRESSURE; + + brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; + brush->draw_strength = 1.0f; + brush->flag &= ~GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->draw_random_press = 0.0f; + + brush->draw_jitter = 0.0f; + brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->draw_angle = 0.0f; + brush->draw_angle_factor = 0.0f; + + brush->draw_smoothfac = 1.1f; + brush->draw_smoothlvl = 2; + brush->sublevel = 2; + brush->draw_random_sub = 0.0f; + + /* Ink Noise brush */ + brush = BKE_gpencil_brush_addnew(ts, "Ink noise", false); + brush->thickness = 6.0f; + brush->flag |= GP_BRUSH_USE_RANDOM_PRESSURE; + brush->draw_sensitivity = 1.611f; + brush->flag |= GP_BRUSH_USE_PRESSURE; + + brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; + brush->draw_strength = 1.0f; + brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->draw_random_press = 1.0f; + + brush->draw_jitter = 0.0f; + brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->draw_angle = 0.0f; + brush->draw_angle_factor = 0.0f; + + brush->draw_smoothfac = 1.1f; + brush->draw_smoothlvl = 2; + brush->sublevel = 2; + brush->draw_random_sub = 0.0f; + + /* Marker brush */ + brush = BKE_gpencil_brush_addnew(ts, "Marker", false); + brush->thickness = 10.0f; + brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; + brush->draw_sensitivity = 2.0f; + brush->flag &= ~GP_BRUSH_USE_PRESSURE; + + brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; + brush->draw_strength = 1.0f; + brush->flag &= ~GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->draw_random_press = 0.0f; + + brush->draw_jitter = 0.0f; + brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->draw_angle = M_PI_4; /* 45 degrees */ + brush->draw_angle_factor = 1.0f; + + brush->draw_smoothfac = 1.0f; + brush->draw_smoothlvl = 2; + brush->sublevel = 2; + brush->draw_random_sub = 0.0f; + + /* Crayon brush */ + brush = BKE_gpencil_brush_addnew(ts, "Crayon", false); + brush->thickness = 10.0f; + brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE; + brush->draw_sensitivity = 3.0f; + brush->flag &= ~GP_BRUSH_USE_PRESSURE; + + brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH; + brush->draw_strength = 0.140f; + brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + + brush->draw_random_press = 0.0f; + + brush->draw_jitter = 0.0f; + brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + brush->draw_angle = 0.0f; + brush->draw_angle_factor = 0.0f; + + brush->draw_smoothfac = 0.0f; + brush->draw_smoothlvl = 1; + brush->sublevel = 2; + brush->draw_random_sub = 0.5f; + +} + +/* add a new gp-brush and make it the active */ +bGPDbrush *BKE_gpencil_brush_addnew(ToolSettings *ts, const char *name, bool setactive) +{ + bGPDbrush *brush; + + /* check that list is ok */ + if (ts == NULL) { + return NULL; + } + + /* allocate memory and add to end of list */ + brush = MEM_callocN(sizeof(bGPDbrush), "bGPDbrush"); + + /* add to datablock */ + BLI_addtail(&ts->gp_brushes, brush); + + /* set basic settings */ + brush->thickness = 3; + brush->draw_smoothlvl = 1; + brush->flag |= GP_BRUSH_USE_PRESSURE; + brush->draw_sensitivity = 1.0f; + brush->draw_strength = 1.0f; + brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE; + brush->draw_jitter = 0.0f; + brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE; + + /* curves */ + brush->cur_sensitivity = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + brush->cur_strength = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + brush->cur_jitter = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + + /* auto-name */ + BLI_strncpy(brush->info, name, sizeof(brush->info)); + BLI_uniquename(&ts->gp_brushes, brush, DATA_("GP_Brush"), '.', offsetof(bGPDbrush, info), sizeof(brush->info)); + + /* make this one the active one */ + if (setactive) { + BKE_gpencil_brush_setactive(ts, brush); + } + + /* return brush */ + return brush; +} + +/* add a new gp-palettecolor and make it the active */ +bGPDpalettecolor *BKE_gpencil_palettecolor_addnew(bGPDpalette *palette, const char *name, bool setactive) +{ + bGPDpalettecolor *palcolor; + + /* check that list is ok */ + if (palette == NULL) { + return NULL; + } + + /* allocate memory and add to end of list */ + palcolor = MEM_callocN(sizeof(bGPDpalettecolor), "bGPDpalettecolor"); + + /* add to datablock */ + BLI_addtail(&palette->colors, palcolor); + + /* set basic settings */ + palcolor->flag |= PC_COLOR_HQ_FILL; + copy_v4_v4(palcolor->color, U.gpencil_new_layer_col); + ARRAY_SET_ITEMS(palcolor->fill, 1.0f, 1.0f, 1.0f); + + /* auto-name */ + BLI_strncpy(palcolor->info, name, sizeof(palcolor->info)); + BLI_uniquename(&palette->colors, palcolor, DATA_("Color"), '.', offsetof(bGPDpalettecolor, info), + sizeof(palcolor->info)); + + /* make this one the active one */ + if (setactive) { + BKE_gpencil_palettecolor_setactive(palette, palcolor); + } + + /* return palette color */ + return palcolor; +} + /* add a new gp-datablock */ -bGPdata *gpencil_data_addnew(const char name[]) +bGPdata *BKE_gpencil_data_addnew(const char name[]) { bGPdata *gpd; @@ -300,94 +641,157 @@ bGPdata *gpencil_data_addnew(const char name[]) /* -------- Data Duplication ---------- */ /* make a copy of a given gpencil frame */ -bGPDframe *gpencil_frame_duplicate(bGPDframe *src) +bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src) { - bGPDstroke *gps, *gpsd; - bGPDframe *dst; + bGPDstroke *gps_dst; + bGPDframe *gpf_dst; /* error checking */ - if (src == NULL) + if (gpf_src == NULL) { return NULL; + } /* make a copy of the source frame */ - dst = MEM_dupallocN(src); - dst->prev = dst->next = NULL; + gpf_dst = MEM_dupallocN(gpf_src); + gpf_dst->prev = gpf_dst->next = NULL; /* copy strokes */ - BLI_listbase_clear(&dst->strokes); - for (gps = src->strokes.first; gps; gps = gps->next) { + BLI_listbase_clear(&gpf_dst->strokes); + for (bGPDstroke *gps_src = gpf_src->strokes.first; gps_src; gps_src = gps_src->next) { /* make copy of source stroke, then adjust pointer to points too */ - gpsd = MEM_dupallocN(gps); - gpsd->points = MEM_dupallocN(gps->points); - gpsd->triangles = MEM_dupallocN(gps->triangles); - gpsd->flag |= GP_STROKE_RECALC_CACHES; - BLI_addtail(&dst->strokes, gpsd); + gps_dst = MEM_dupallocN(gps_src); + gps_dst->points = MEM_dupallocN(gps_src->points); + gps_dst->triangles = MEM_dupallocN(gps_src->triangles); + gps_dst->flag |= GP_STROKE_RECALC_CACHES; + BLI_addtail(&gpf_dst->strokes, gps_dst); } /* return new frame */ - return dst; + return gpf_dst; +} + +/* make a copy of a given gpencil brush */ +bGPDbrush *BKE_gpencil_brush_duplicate(const bGPDbrush *brush_src) +{ + bGPDbrush *brush_dst; + + /* error checking */ + if (brush_src == NULL) { + return NULL; + } + + /* make a copy of source brush */ + brush_dst = MEM_dupallocN(brush_src); + brush_dst->prev = brush_dst->next = NULL; + /* make a copy of curves */ + brush_dst->cur_sensitivity = curvemapping_copy(brush_src->cur_sensitivity); + brush_dst->cur_strength = curvemapping_copy(brush_src->cur_strength); + brush_dst->cur_jitter = curvemapping_copy(brush_src->cur_jitter); + + /* return new brush */ + return brush_dst; } +/* make a copy of a given gpencil palette */ +bGPDpalette *BKE_gpencil_palette_duplicate(const bGPDpalette *palette_src) +{ + bGPDpalette *palette_dst; + const bGPDpalettecolor *palcolor_src; + bGPDpalettecolor *palcolord_dst; + + /* error checking */ + if (palette_src == NULL) { + return NULL; + } + + /* make a copy of source palette */ + palette_dst = MEM_dupallocN(palette_src); + palette_dst->prev = palette_dst->next = NULL; + + /* copy colors */ + BLI_listbase_clear(&palette_dst->colors); + for (palcolor_src = palette_src->colors.first; palcolor_src; palcolor_src = palcolor_src->next) { + /* make a copy of source */ + palcolord_dst = MEM_dupallocN(palcolor_src); + BLI_addtail(&palette_dst->colors, palcolord_dst); + } + + /* return new palette */ + return palette_dst; +} /* make a copy of a given gpencil layer */ -bGPDlayer *gpencil_layer_duplicate(bGPDlayer *src) +bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src) { - bGPDframe *gpf, *gpfd; - bGPDlayer *dst; + const bGPDframe *gpf_src; + bGPDframe *gpf_dst; + bGPDlayer *gpl_dst; /* error checking */ - if (src == NULL) + if (gpl_src == NULL) { return NULL; + } /* make a copy of source layer */ - dst = MEM_dupallocN(src); - dst->prev = dst->next = NULL; + gpl_dst = MEM_dupallocN(gpl_src); + gpl_dst->prev = gpl_dst->next = NULL; /* copy frames */ - BLI_listbase_clear(&dst->frames); - for (gpf = src->frames.first; gpf; gpf = gpf->next) { + BLI_listbase_clear(&gpl_dst->frames); + for (gpf_src = gpl_src->frames.first; gpf_src; gpf_src = gpf_src->next) { /* make a copy of source frame */ - gpfd = gpencil_frame_duplicate(gpf); - BLI_addtail(&dst->frames, gpfd); + gpf_dst = BKE_gpencil_frame_duplicate(gpf_src); + BLI_addtail(&gpl_dst->frames, gpf_dst); /* if source frame was the current layer's 'active' frame, reassign that too */ - if (gpf == dst->actframe) - dst->actframe = gpfd; + if (gpf_src == gpl_dst->actframe) + gpl_dst->actframe = gpf_dst; } /* return new layer */ - return dst; + return gpl_dst; } /* make a copy of a given gpencil datablock */ -bGPdata *gpencil_data_duplicate(Main *bmain, bGPdata *src, bool internal_copy) +bGPdata *BKE_gpencil_data_duplicate(Main *bmain, bGPdata *gpd_src, bool internal_copy) { - bGPDlayer *gpl, *gpld; - bGPdata *dst; - + const bGPDlayer *gpl_src; + bGPDlayer *gpl_dst; + bGPdata *gpd_dst; + /* error checking */ - if (src == NULL) + if (gpd_src == NULL) { return NULL; + } /* make a copy of the base-data */ if (internal_copy) { /* make a straight copy for undo buffers used during stroke drawing */ - dst = MEM_dupallocN(src); + gpd_dst = MEM_dupallocN(gpd_src); } else { /* make a copy when others use this */ - dst = BKE_libblock_copy(bmain, &src->id); + gpd_dst = BKE_libblock_copy(bmain, &gpd_src->id); } /* copy layers */ - BLI_listbase_clear(&dst->layers); - for (gpl = src->layers.first; gpl; gpl = gpl->next) { + BLI_listbase_clear(&gpd_dst->layers); + for (gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { /* make a copy of source layer and its data */ - gpld = gpencil_layer_duplicate(gpl); - BLI_addtail(&dst->layers, gpld); + gpl_dst = BKE_gpencil_layer_duplicate(gpl_src); + BLI_addtail(&gpd_dst->layers, gpl_dst); + } + if (!internal_copy) { + /* copy palettes */ + bGPDpalette *palette_src, *palette_dst; + BLI_listbase_clear(&gpd_dst->palettes); + for (palette_src = gpd_src->palettes.first; palette_src; palette_src = palette_src->next) { + palette_dst = BKE_gpencil_palette_duplicate(palette_src); + BLI_addtail(&gpd_dst->palettes, palette_dst); + } } /* return new */ - return dst; + return gpd_dst; } void BKE_gpencil_make_local(Main *bmain, bGPdata *gpd, const bool lib_local) @@ -398,7 +802,7 @@ void BKE_gpencil_make_local(Main *bmain, bGPdata *gpd, const bool lib_local) /* -------- GP-Stroke API --------- */ /* ensure selection status of stroke is in sync with its points */ -void gpencil_stroke_sync_selection(bGPDstroke *gps) +void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps) { bGPDspoint *pt; int i; @@ -423,7 +827,7 @@ void gpencil_stroke_sync_selection(bGPDstroke *gps) /* -------- GP-Frame API ---------- */ /* delete the last stroke of the given frame */ -void gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) +void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) { bGPDstroke *gps = (gpf) ? gpf->strokes.last : NULL; int cfra = (gpf) ? gpf->framenum : 0; /* assume that the current frame was not locked */ @@ -439,8 +843,8 @@ void gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf) /* if frame has no strokes after this, delete it */ if (BLI_listbase_is_empty(&gpf->strokes)) { - gpencil_layer_delframe(gpl, gpf); - gpencil_layer_getframe(gpl, cfra, 0); + BKE_gpencil_layer_delframe(gpl, gpf); + BKE_gpencil_layer_getframe(gpl, cfra, 0); } } @@ -458,7 +862,7 @@ bool gpencil_layer_is_editable(const bGPDlayer *gpl) /* Opacity must be sufficiently high that it is still "visible" * Otherwise, it's not really "visible" to the user, so no point editing... */ - if ((gpl->color[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH)) { + if (gpl->opacity > GPENCIL_ALPHA_OPACITY_THRESH) { return true; } } @@ -488,7 +892,7 @@ bGPDframe *BKE_gpencil_layer_find_frame(bGPDlayer *gpl, int cframe) * - this sets the layer's actframe var (if allowed to) * - extension beyond range (if first gp-frame is after all frame in interest and cannot add) */ -bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew) +bGPDframe *BKE_gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew) { bGPDframe *gpf = NULL; short found = 0; @@ -527,9 +931,9 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode if ((found) && (gpf->framenum == cframe)) gpl->actframe = gpf; else if (addnew == GP_GETFRAME_ADD_COPY) - gpl->actframe = gpencil_frame_addcopy(gpl, cframe); + gpl->actframe = BKE_gpencil_frame_addcopy(gpl, cframe); else - gpl->actframe = gpencil_frame_addnew(gpl, cframe); + gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe); } else if (found) gpl->actframe = gpf; @@ -549,9 +953,9 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode if ((found) && (gpf->framenum == cframe)) gpl->actframe = gpf; else if (addnew == GP_GETFRAME_ADD_COPY) - gpl->actframe = gpencil_frame_addcopy(gpl, cframe); + gpl->actframe = BKE_gpencil_frame_addcopy(gpl, cframe); else - gpl->actframe = gpencil_frame_addnew(gpl, cframe); + gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe); } else if (found) gpl->actframe = gpf; @@ -588,7 +992,7 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode if ((found) && (gpf->framenum == cframe)) gpl->actframe = gpf; else - gpl->actframe = gpencil_frame_addnew(gpl, cframe); + gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe); } else if (found) gpl->actframe = gpf; @@ -601,7 +1005,7 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode else { /* currently no frames (add if allowed to) */ if (addnew) - gpl->actframe = gpencil_frame_addnew(gpl, cframe); + gpl->actframe = BKE_gpencil_frame_addnew(gpl, cframe); else { /* don't do anything... this may be when no frames yet! */ /* gpl->actframe should still be NULL */ @@ -613,7 +1017,7 @@ bGPDframe *gpencil_layer_getframe(bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode } /* delete the given frame from a layer */ -bool gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf) +bool BKE_gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf) { bool changed = false; @@ -630,14 +1034,14 @@ bool gpencil_layer_delframe(bGPDlayer *gpl, bGPDframe *gpf) gpl->actframe = NULL; /* free the frame and its data */ - changed = free_gpencil_strokes(gpf); + changed = BKE_gpencil_free_strokes(gpf); BLI_freelinkN(&gpl->frames, gpf); return changed; } /* get the active gp-layer for editing */ -bGPDlayer *gpencil_layer_getactive(bGPdata *gpd) +bGPDlayer *BKE_gpencil_layer_getactive(bGPdata *gpd) { bGPDlayer *gpl; @@ -656,7 +1060,7 @@ bGPDlayer *gpencil_layer_getactive(bGPdata *gpd) } /* set the active gp-layer */ -void gpencil_layer_setactive(bGPdata *gpd, bGPDlayer *active) +void BKE_gpencil_layer_setactive(bGPdata *gpd, bGPDlayer *active) { bGPDlayer *gpl; @@ -673,15 +1077,255 @@ void gpencil_layer_setactive(bGPdata *gpd, bGPDlayer *active) } /* delete the active gp-layer */ -void gpencil_layer_delete(bGPdata *gpd, bGPDlayer *gpl) +void BKE_gpencil_layer_delete(bGPdata *gpd, bGPDlayer *gpl) { /* error checking */ if (ELEM(NULL, gpd, gpl)) return; /* free layer */ - free_gpencil_frames(gpl); + BKE_gpencil_free_frames(gpl); BLI_freelinkN(&gpd->layers, gpl); } /* ************************************************** */ +/* get the active gp-brush for editing */ +bGPDbrush *BKE_gpencil_brush_getactive(ToolSettings *ts) +{ + bGPDbrush *brush; + + /* error checking */ + if (ELEM(NULL, ts, ts->gp_brushes.first)) { + return NULL; + } + + /* loop over brushes until found (assume only one active) */ + for (brush = ts->gp_brushes.first; brush; brush = brush->next) { + if (brush->flag & GP_BRUSH_ACTIVE) { + return brush; + } + } + + /* no active brush found */ + return NULL; +} + +/* set the active gp-brush */ +void BKE_gpencil_brush_setactive(ToolSettings *ts, bGPDbrush *active) +{ + bGPDbrush *brush; + + /* error checking */ + if (ELEM(NULL, ts, ts->gp_brushes.first, active)) { + return; + } + + /* loop over brushes deactivating all */ + for (brush = ts->gp_brushes.first; brush; brush = brush->next) { + brush->flag &= ~GP_BRUSH_ACTIVE; + } + + /* set as active one */ + active->flag |= GP_BRUSH_ACTIVE; +} + +/* delete the active gp-brush */ +void BKE_gpencil_brush_delete(ToolSettings *ts, bGPDbrush *brush) +{ + /* error checking */ + if (ELEM(NULL, ts, brush)) { + return; + } + + /* free curves */ + if (brush->cur_sensitivity) { + curvemapping_free(brush->cur_sensitivity); + } + if (brush->cur_strength) { + curvemapping_free(brush->cur_strength); + } + if (brush->cur_jitter) { + curvemapping_free(brush->cur_jitter); + } + + /* free */ + BLI_freelinkN(&ts->gp_brushes, brush); +} + +/* ************************************************** */ +/* get the active gp-palette for editing */ +bGPDpalette *BKE_gpencil_palette_getactive(bGPdata *gpd) +{ + bGPDpalette *palette; + + /* error checking */ + if (ELEM(NULL, gpd, gpd->palettes.first)) { + return NULL; + } + + /* loop over palettes until found (assume only one active) */ + for (palette = gpd->palettes.first; palette; palette = palette->next) { + if (palette->flag & PL_PALETTE_ACTIVE) + return palette; + } + + /* no active palette found */ + return NULL; +} + +/* set the active gp-palette */ +void BKE_gpencil_palette_setactive(bGPdata *gpd, bGPDpalette *active) +{ + bGPDpalette *palette; + + /* error checking */ + if (ELEM(NULL, gpd, gpd->palettes.first, active)) { + return; + } + + /* loop over palettes deactivating all */ + for (palette = gpd->palettes.first; palette; palette = palette->next) { + palette->flag &= ~PL_PALETTE_ACTIVE; + } + + /* set as active one */ + active->flag |= PL_PALETTE_ACTIVE; + /* force color recalc */ + BKE_gpencil_palette_change_strokes(gpd); +} + +/* delete the active gp-palette */ +void BKE_gpencil_palette_delete(bGPdata *gpd, bGPDpalette *palette) +{ + /* error checking */ + if (ELEM(NULL, gpd, palette)) { + return; + } + + /* free colors */ + free_gpencil_colors(palette); + BLI_freelinkN(&gpd->palettes, palette); + /* force color recalc */ + BKE_gpencil_palette_change_strokes(gpd); +} + +/* Set all strokes to recalc the palette color */ +void BKE_gpencil_palette_change_strokes(bGPdata *gpd) +{ + bGPDlayer *gpl; + bGPDframe *gpf; + bGPDstroke *gps; + + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (gps = gpf->strokes.first; gps; gps = gps->next) { + gps->flag |= GP_STROKE_RECALC_COLOR; + } + } + } +} + + +/* get the active gp-palettecolor for editing */ +bGPDpalettecolor *BKE_gpencil_palettecolor_getactive(bGPDpalette *palette) +{ + bGPDpalettecolor *palcolor; + + /* error checking */ + if (ELEM(NULL, palette, palette->colors.first)) { + return NULL; + } + + /* loop over colors until found (assume only one active) */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + if (palcolor->flag & PC_COLOR_ACTIVE) { + return palcolor; + } + } + + /* no active color found */ + return NULL; +} +/* get the gp-palettecolor looking for name */ +bGPDpalettecolor *BKE_gpencil_palettecolor_getbyname(bGPDpalette *palette, char *name) +{ + /* error checking */ + if (ELEM(NULL, palette, name)) { + return NULL; + } + + return BLI_findstring(&palette->colors, name, offsetof(bGPDpalettecolor, info)); +} + +/* Change color name in all strokes */ +void BKE_gpencil_palettecolor_changename(bGPdata *gpd, char *oldname, const char *newname) +{ + bGPDlayer *gpl; + bGPDframe *gpf; + bGPDstroke *gps; + + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (gps = gpf->strokes.first; gps; gps = gps->next) { + if (STREQ(gps->colorname, oldname)) { + strcpy(gps->colorname, newname); + } + } + } + } + +} + +/* Delete all strokes of the color */ +void BKE_gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name) +{ + bGPDlayer *gpl; + bGPDframe *gpf; + bGPDstroke *gps, *gpsn; + + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + + if (STREQ(gps->colorname, name)) { + if (gps->points) MEM_freeN(gps->points); + if (gps->triangles) MEM_freeN(gps->triangles); + BLI_freelinkN(&gpf->strokes, gps); + } + } + } + } + +} + +/* set the active gp-palettecolor */ +void BKE_gpencil_palettecolor_setactive(bGPDpalette *palette, bGPDpalettecolor *active) +{ + bGPDpalettecolor *palcolor; + + /* error checking */ + if (ELEM(NULL, palette, palette->colors.first, active)) { + return; + } + + /* loop over colors deactivating all */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag &= ~PC_COLOR_ACTIVE; + } + + /* set as active one */ + active->flag |= PC_COLOR_ACTIVE; +} + +/* delete the active gp-palettecolor */ +void BKE_gpencil_palettecolor_delete(bGPDpalette *palette, bGPDpalettecolor *palcolor) +{ + /* error checking */ + if (ELEM(NULL, palette, palcolor)) { + return; + } + + /* free */ + BLI_freelinkN(&palette->colors, palcolor); +} diff --git a/source/blender/blenkernel/intern/idcode.c b/source/blender/blenkernel/intern/idcode.c index 90b3713e47c..70d037d85f3 100644 --- a/source/blender/blenkernel/intern/idcode.c +++ b/source/blender/blenkernel/intern/idcode.c @@ -60,6 +60,7 @@ static IDType idtypes[] = { { ID_AR, "Armature", "armatures", BLT_I18NCONTEXT_ID_ARMATURE, IDTYPE_FLAGS_ISLINKABLE }, { ID_BR, "Brush", "brushes", BLT_I18NCONTEXT_ID_BRUSH, IDTYPE_FLAGS_ISLINKABLE }, { ID_CA, "Camera", "cameras", BLT_I18NCONTEXT_ID_CAMERA, IDTYPE_FLAGS_ISLINKABLE }, + { ID_CF, "CacheFile", "cache_files", BLT_I18NCONTEXT_ID_CACHEFILE, IDTYPE_FLAGS_ISLINKABLE }, { ID_CU, "Curve", "curves", BLT_I18NCONTEXT_ID_CURVE, IDTYPE_FLAGS_ISLINKABLE }, { ID_GD, "GPencil", "grease_pencil", BLT_I18NCONTEXT_ID_GPENCIL, IDTYPE_FLAGS_ISLINKABLE }, /* rename gpencil */ { ID_GR, "Group", "groups", BLT_I18NCONTEXT_ID_GROUP, IDTYPE_FLAGS_ISLINKABLE }, @@ -184,6 +185,7 @@ int BKE_idcode_to_idfilter(const short idcode) CASE_IDFILTER(AR); CASE_IDFILTER(BR); CASE_IDFILTER(CA); + CASE_IDFILTER(CF); CASE_IDFILTER(CU); CASE_IDFILTER(GD); CASE_IDFILTER(GR); @@ -227,6 +229,7 @@ short BKE_idcode_from_idfilter(const int idfilter) CASE_IDFILTER(AR); CASE_IDFILTER(BR); CASE_IDFILTER(CA); + CASE_IDFILTER(CF); CASE_IDFILTER(CU); CASE_IDFILTER(GD); CASE_IDFILTER(GR); @@ -259,6 +262,56 @@ short BKE_idcode_from_idfilter(const int idfilter) } /** + * Convert an idcode into an index (e.g. ID_OB -> INDEX_ID_OB). + */ +int BKE_idcode_to_index(const short idcode) +{ +#define CASE_IDINDEX(_id) case ID_##_id: return INDEX_ID_##_id + + switch ((ID_Type)idcode) { + CASE_IDINDEX(AC); + CASE_IDINDEX(AR); + CASE_IDINDEX(BR); + CASE_IDINDEX(CA); + CASE_IDINDEX(CF); + CASE_IDINDEX(CU); + CASE_IDINDEX(GD); + CASE_IDINDEX(GR); + CASE_IDINDEX(IM); + CASE_IDINDEX(KE); + CASE_IDINDEX(IP); + CASE_IDINDEX(LA); + CASE_IDINDEX(LI); + CASE_IDINDEX(LS); + CASE_IDINDEX(LT); + CASE_IDINDEX(MA); + CASE_IDINDEX(MB); + CASE_IDINDEX(MC); + CASE_IDINDEX(ME); + CASE_IDINDEX(MSK); + CASE_IDINDEX(NT); + CASE_IDINDEX(OB); + CASE_IDINDEX(PA); + CASE_IDINDEX(PAL); + CASE_IDINDEX(PC); + CASE_IDINDEX(SCE); + CASE_IDINDEX(SCR); + CASE_IDINDEX(SPK); + CASE_IDINDEX(SO); + CASE_IDINDEX(TE); + CASE_IDINDEX(TXT); + CASE_IDINDEX(VF); + CASE_IDINDEX(WM); + CASE_IDINDEX(WO); + } + + BLI_assert(0); + return -1; + +#undef CASE_IDINDEX +} + +/** * Convert an idcode into a name (plural). * * \param idcode: The code to convert. diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index ea28dabb945..626d389ac2d 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -436,6 +436,7 @@ static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src) Image *BKE_image_copy(Main *bmain, Image *ima) { Image *nima = image_alloc(bmain, ima->id.name + 2, ima->source, ima->type); + ima->id.newid = &nima->id; BLI_strncpy(nima->name, ima->name, sizeof(ima->name)); diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index d3f20859f06..6d94cd28b31 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -45,6 +45,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_brush_types.h" +#include "DNA_cachefile_types.h" #include "DNA_camera_types.h" #include "DNA_group_types.h" #include "DNA_gpencil_types.h" @@ -56,6 +57,7 @@ #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" +#include "DNA_modifier_types.h" #include "DNA_movieclip_types.h" #include "DNA_mask_types.h" #include "DNA_node_types.h" @@ -81,6 +83,7 @@ #include "BKE_bpath.h" #include "BKE_brush.h" #include "BKE_camera.h" +#include "BKE_cachefile.h" #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_depsgraph.h" @@ -332,8 +335,10 @@ void BKE_id_make_local_generic(Main *bmain, ID *id, const bool id_in_mainlist, c */ bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local) { - if (id->tag & LIB_TAG_INDIRECT) + /* We don't care whether ID is directly or indirectly linked in case we are making a whole lib local... */ + if (!lib_local && (id->tag & LIB_TAG_INDIRECT)) { return false; + } switch ((ID_Type)GS(id->name)) { case ID_SCE: @@ -423,6 +428,9 @@ bool id_make_local(Main *bmain, ID *id, const bool test, const bool lib_local) case ID_PC: if (!test) BKE_paint_curve_make_local(bmain, (PaintCurve *)id, lib_local); return true; + case ID_CF: + if (!test) BKE_cachefile_make_local(bmain, (CacheFile *)id, lib_local); + return true; case ID_SCR: case ID_LI: case ID_KE: @@ -510,7 +518,7 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test) if (!test) *newid = (ID *)BKE_particlesettings_copy(bmain, (ParticleSettings *)id); return true; case ID_GD: - if (!test) *newid = (ID *)gpencil_data_duplicate(bmain, (bGPdata *)id, false); + if (!test) *newid = (ID *)BKE_gpencil_data_duplicate(bmain, (bGPdata *)id, false); return true; case ID_MC: if (!test) *newid = (ID *)BKE_movieclip_copy(bmain, (MovieClip *)id); @@ -527,6 +535,9 @@ bool id_copy(Main *bmain, ID *id, ID **newid, bool test) case ID_PC: if (!test) *newid = (ID *)BKE_paint_curve_copy(bmain, (PaintCurve *)id); return true; + case ID_CF: + if (!test) *newid = (ID *)BKE_cachefile_copy(bmain, (CacheFile *)id); + return true; case ID_SCE: case ID_LI: case ID_SCR: @@ -639,6 +650,8 @@ ListBase *which_libbase(Main *mainlib, short type) return &(mainlib->palettes); case ID_PC: return &(mainlib->paintcurves); + case ID_CF: + return &(mainlib->cachefiles); } return NULL; } @@ -740,59 +753,56 @@ void BKE_main_lib_objects_recalc_all(Main *bmain) * \note MAX_LIBARRAY define should match this code */ int set_listbasepointers(Main *main, ListBase **lb) { - int a = 0; - /* BACKWARDS! also watch order of free-ing! (mesh<->mat), first items freed last. * This is important because freeing data decreases usercounts of other datablocks, * if this data is its self freed it can crash. */ - lb[a++] = &(main->library); /* Libraries may be accessed from pretty much any other ID... */ - lb[a++] = &(main->ipo); - lb[a++] = &(main->action); /* moved here to avoid problems when freeing with animato (aligorith) */ - lb[a++] = &(main->key); - lb[a++] = &(main->gpencil); /* referenced by nodes, objects, view, scene etc, before to free after. */ - lb[a++] = &(main->nodetree); - lb[a++] = &(main->image); - lb[a++] = &(main->tex); - lb[a++] = &(main->mat); - lb[a++] = &(main->vfont); + lb[INDEX_ID_LI] = &(main->library); /* Libraries may be accessed from pretty much any other ID... */ + lb[INDEX_ID_IP] = &(main->ipo); + lb[INDEX_ID_AC] = &(main->action); /* moved here to avoid problems when freeing with animato (aligorith) */ + lb[INDEX_ID_KE] = &(main->key); + lb[INDEX_ID_GD] = &(main->gpencil); /* referenced by nodes, objects, view, scene etc, before to free after. */ + lb[INDEX_ID_NT] = &(main->nodetree); + lb[INDEX_ID_IM] = &(main->image); + lb[INDEX_ID_TE] = &(main->tex); + lb[INDEX_ID_MA] = &(main->mat); + lb[INDEX_ID_VF] = &(main->vfont); /* Important!: When adding a new object type, * the specific data should be inserted here */ - lb[a++] = &(main->armature); - - lb[a++] = &(main->mesh); - lb[a++] = &(main->curve); - lb[a++] = &(main->mball); - - lb[a++] = &(main->latt); - lb[a++] = &(main->lamp); - lb[a++] = &(main->camera); - - lb[a++] = &(main->text); - lb[a++] = &(main->sound); - lb[a++] = &(main->group); - lb[a++] = &(main->palettes); - lb[a++] = &(main->paintcurves); - lb[a++] = &(main->brush); - lb[a++] = &(main->particle); - lb[a++] = &(main->speaker); - - lb[a++] = &(main->world); - lb[a++] = &(main->movieclip); - lb[a++] = &(main->screen); - lb[a++] = &(main->object); - lb[a++] = &(main->linestyle); /* referenced by scenes */ - lb[a++] = &(main->scene); - lb[a++] = &(main->wm); - lb[a++] = &(main->mask); + lb[INDEX_ID_AR] = &(main->armature); + + lb[INDEX_ID_CF] = &(main->cachefiles); + lb[INDEX_ID_ME] = &(main->mesh); + lb[INDEX_ID_CU] = &(main->curve); + lb[INDEX_ID_MB] = &(main->mball); + + lb[INDEX_ID_LT] = &(main->latt); + lb[INDEX_ID_LA] = &(main->lamp); + lb[INDEX_ID_CA] = &(main->camera); + + lb[INDEX_ID_TXT] = &(main->text); + lb[INDEX_ID_SO] = &(main->sound); + lb[INDEX_ID_GR] = &(main->group); + lb[INDEX_ID_PAL] = &(main->palettes); + lb[INDEX_ID_PC] = &(main->paintcurves); + lb[INDEX_ID_BR] = &(main->brush); + lb[INDEX_ID_PA] = &(main->particle); + lb[INDEX_ID_SPK] = &(main->speaker); + + lb[INDEX_ID_WO] = &(main->world); + lb[INDEX_ID_MC] = &(main->movieclip); + lb[INDEX_ID_SCR] = &(main->screen); + lb[INDEX_ID_OB] = &(main->object); + lb[INDEX_ID_LS] = &(main->linestyle); /* referenced by scenes */ + lb[INDEX_ID_SCE] = &(main->scene); + lb[INDEX_ID_WM] = &(main->wm); + lb[INDEX_ID_MSK] = &(main->mask); - lb[a] = NULL; - - BLI_assert(a + 1 == MAX_LIBARRAY); + lb[INDEX_ID_NULL] = NULL; - return a; + return (MAX_LIBARRAY - 1); } /* *********** ALLOC AND FREE ***************** @@ -913,6 +923,9 @@ void *BKE_libblock_alloc_notest(short type) case ID_PC: id = MEM_callocN(sizeof(PaintCurve), "Paint Curve"); break; + case ID_CF: + id = MEM_callocN(sizeof(CacheFile), "Cache File"); + break; } return id; } @@ -1039,6 +1052,9 @@ void BKE_libblock_init_empty(ID *id) case ID_LS: BKE_linestyle_init((FreestyleLineStyle *)id); break; + case ID_CF: + BKE_cachefile_init((CacheFile *)id); + break; case ID_KE: /* Shapekeys are a complex topic too - they depend on their 'user' data type... * They are not linkable, though, so it should never reach here anyway. */ @@ -1226,6 +1242,7 @@ void BKE_main_free(Main *mainvar) case 31: BKE_libblock_free_ex(mainvar, id, false); break; case 32: BKE_libblock_free_ex(mainvar, id, false); break; case 33: BKE_libblock_free_ex(mainvar, id, false); break; + case 34: BKE_libblock_free_ex(mainvar, id, false); break; default: BLI_assert(0); break; @@ -1639,22 +1656,20 @@ void BKE_library_make_local(Main *bmain, const Library *lib, const bool untagged for (a = set_listbasepointers(bmain, lbarray); a--; ) { id = lbarray[a]->first; - if (!id || !BKE_idcode_is_linkable(GS(id->name))) { - /* Do not explicitly make local non-linkable IDs (shapekeys, in fact), they are assumed to be handled - * by real datablocks responsible of them. */ - continue; - } + /* Do not explicitly make local non-linkable IDs (shapekeys, in fact), they are assumed to be handled + * by real datablocks responsible of them. */ + const bool do_skip = (id && !BKE_idcode_is_linkable(GS(id->name))); for (; id; id = id_next) { id->newid = NULL; id_next = id->next; /* id is possibly being inserted again */ - + /* The check on the second line (LIB_TAG_PRE_EXISTING) is done so its * possible to tag data you don't want to be made local, used for * appending data, so any libdata already linked wont become local * (very nasty to discover all your links are lost after appending) * */ - if (id->tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW) && + if (!do_skip && id->tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW) && ((untagged_only == false) || !(id->tag & LIB_TAG_PRE_EXISTING))) { if (lib == NULL || id->lib == lib) { diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c index d71e8012e10..cb864334208 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -305,7 +305,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u library_foreach_animationData(&data, adt); } - switch (GS(id->name)) { + switch ((ID_Type)GS(id->name)) { case ID_LI: { Library *lib = (Library *) id; @@ -842,6 +842,25 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u } break; } + + /* Nothing needed for those... */ + case ID_IM: + case ID_VF: + case ID_TXT: + case ID_SO: + case ID_AR: + case ID_AC: + case ID_GD: + case ID_WM: + case ID_PAL: + case ID_PC: + case ID_CF: + break; + + /* Deprecated. */ + case ID_IP: + break; + } } while ((id = (flag & IDWALK_RECURSE) ? BLI_LINKSTACK_POP(data.ids_todo) : NULL)); @@ -884,7 +903,7 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id return id_type_can_have_animdata(id_type_owner); } - switch (id_type_owner) { + switch ((ID_Type)id_type_owner) { case ID_LI: return ELEM(id_type_used, ID_LI); case ID_SCE: @@ -943,9 +962,24 @@ bool BKE_library_idtype_can_use_idtype(const short id_type_owner, const short id return ELEM(id_type_used, ID_MC); /* WARNING! mask->parent.id, not typed. */ case ID_LS: return (ELEM(id_type_used, ID_TE, ID_OB) || BKE_library_idtype_can_use_idtype(ID_NT, id_type_used)); - default: + case ID_IM: + case ID_VF: + case ID_TXT: + case ID_SO: + case ID_AR: + case ID_AC: + case ID_GD: + case ID_WM: + case ID_PAL: + case ID_PC: + case ID_CF: + /* Those types never use/reference other IDs... */ + return false; + case ID_IP: + /* Deprecated... */ return false; } + return false; } diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c index 74b3b1b6c18..1830ca0bd90 100644 --- a/source/blender/blenkernel/intern/library_remap.c +++ b/source/blender/blenkernel/intern/library_remap.c @@ -38,6 +38,7 @@ #include "DNA_armature_types.h" #include "DNA_brush_types.h" #include "DNA_camera_types.h" +#include "DNA_cachefile_types.h" #include "DNA_group_types.h" #include "DNA_gpencil_types.h" #include "DNA_ipo_types.h" @@ -69,6 +70,7 @@ #include "BKE_armature.h" #include "BKE_brush.h" #include "BKE_camera.h" +#include "BKE_cachefile.h" #include "BKE_curve.h" #include "BKE_depsgraph.h" #include "BKE_fcurve.h" @@ -795,7 +797,7 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user) free_windowmanager_cb(NULL, (wmWindowManager *)id); break; case ID_GD: - BKE_gpencil_free((bGPdata *)id); + BKE_gpencil_free((bGPdata *)id, true); break; case ID_MC: BKE_movieclip_free((MovieClip *)id); @@ -812,6 +814,9 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user) case ID_PC: BKE_paint_curve_free((PaintCurve *)id); break; + case ID_CF: + BKE_cachefile_free((CacheFile *)id); + break; } /* avoid notifying on removed data */ diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 470108545b8..54945242fe4 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -58,6 +58,7 @@ #include "BKE_animsys.h" #include "BKE_displist.h" #include "BKE_global.h" +#include "BKE_depsgraph.h" #include "BKE_icons.h" #include "BKE_image.h" #include "BKE_library.h" @@ -398,7 +399,7 @@ static void material_data_index_clear_id(ID *id) } } -void BKE_material_resize_id(struct ID *id, short totcol, bool do_id_user) +void BKE_material_resize_id(Main *bmain, ID *id, short totcol, bool do_id_user) { Material ***matar = give_matarar_id(id); short *totcolp = give_totcolp_id(id); @@ -424,9 +425,11 @@ void BKE_material_resize_id(struct ID *id, short totcol, bool do_id_user) *matar = MEM_recallocN(*matar, sizeof(void *) * totcol); } *totcolp = totcol; + + DAG_relations_tag_update(bmain); } -void BKE_material_append_id(ID *id, Material *ma) +void BKE_material_append_id(Main *bmain, ID *id, Material *ma) { Material ***matar; if ((matar = give_matarar_id(id))) { @@ -439,11 +442,12 @@ void BKE_material_append_id(ID *id, Material *ma) (*matar)[(*totcol)++] = ma; id_us_plus((ID *)ma); - test_all_objects_materials(G.main, id); + test_all_objects_materials(bmain, id); + DAG_relations_tag_update(bmain); } } -Material *BKE_material_pop_id(ID *id, int index_i, bool update_data) +Material *BKE_material_pop_id(Main *bmain, ID *id, int index_i, bool update_data) { short index = (short)index_i; Material *ret = NULL; @@ -472,13 +476,15 @@ Material *BKE_material_pop_id(ID *id, int index_i, bool update_data) /* decrease mat_nr index */ material_data_index_remove_id(id, index); } + + DAG_relations_tag_update(bmain); } } return ret; } -void BKE_material_clear_id(struct ID *id, bool update_data) +void BKE_material_clear_id(Main *bmain, ID *id, bool update_data) { Material ***matar; if ((matar = give_matarar_id(id))) { @@ -497,6 +503,8 @@ void BKE_material_clear_id(struct ID *id, bool update_data) /* decrease mat_nr index */ material_data_index_clear_id(id); } + + DAG_relations_tag_update(bmain); } } @@ -553,7 +561,7 @@ Material *give_node_material(Material *ma) return NULL; } -void BKE_material_resize_object(Object *ob, const short totcol, bool do_id_user) +void BKE_material_resize_object(Main *bmain, Object *ob, const short totcol, bool do_id_user) { Material **newmatar; char *newmatbits; @@ -590,6 +598,8 @@ void BKE_material_resize_object(Object *ob, const short totcol, bool do_id_user) ob->totcol = totcol; if (ob->totcol && ob->actcol == 0) ob->actcol = 1; if (ob->actcol > ob->totcol) ob->actcol = ob->totcol; + + DAG_relations_tag_update(bmain); } void test_object_materials(Object *ob, ID *id) @@ -601,7 +611,7 @@ void test_object_materials(Object *ob, ID *id) return; } - BKE_material_resize_object(ob, *totcol, false); + BKE_material_resize_object(G.main, ob, *totcol, false); } void test_all_objects_materials(Main *bmain, ID *id) @@ -617,7 +627,7 @@ void test_all_objects_materials(Main *bmain, ID *id) BKE_main_lock(bmain); for (ob = bmain->object.first; ob; ob = ob->id.next) { if (ob->data == id) { - BKE_material_resize_object(ob, *totcol, false); + BKE_material_resize_object(bmain, ob, *totcol, false); } } BKE_main_unlock(bmain); @@ -1881,7 +1891,7 @@ static short mesh_getmaterialnumber(Mesh *me, Material *ma) /* append material */ static short mesh_addmaterial(Mesh *me, Material *ma) { - BKE_material_append_id(&me->id, NULL); + BKE_material_append_id(G.main, &me->id, NULL); me->mat[me->totcol - 1] = ma; id_us_plus(&ma->id); @@ -2020,7 +2030,7 @@ static void convert_tfacematerial(Main *main, Material *ma) /* remove material from mesh */ for (a = 0; a < me->totcol; ) { if (me->mat[a] == ma) { - BKE_material_pop_id(&me->id, a, true); + BKE_material_pop_id(main, &me->id, a, true); } else { a++; diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 0d362086134..482015d3b26 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -1033,7 +1033,7 @@ static ImBuf *get_stable_cached_frame(MovieClip *clip, MovieClipUser *user, ImBu stableibuf = cache->stabilized.ibuf; - BKE_tracking_stabilization_data_get(&clip->tracking, clip_framenr, stableibuf->x, stableibuf->y, tloc, &tscale, &tangle); + BKE_tracking_stabilization_data_get(clip, clip_framenr, stableibuf->x, stableibuf->y, tloc, &tscale, &tangle); /* check for stabilization parameters */ if (tscale != cache->stabilized.scale || @@ -1057,7 +1057,7 @@ static ImBuf *put_stabilized_frame_to_cache(MovieClip *clip, MovieClipUser *user float tloc[2], tscale, tangle; int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, framenr); - stableibuf = BKE_tracking_stabilize_frame(&clip->tracking, clip_framenr, ibuf, tloc, &tscale, &tangle); + stableibuf = BKE_tracking_stabilize_frame(clip, clip_framenr, ibuf, tloc, &tscale, &tangle); copy_v2_v2(cache->stabilized.loc, tloc); @@ -1270,8 +1270,6 @@ void BKE_movieclip_reload(MovieClip *clip) /* clear cache */ free_buffers(clip); - clip->tracking.stabilization.ok = false; - /* update clip source */ detect_clip_source(clip); diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 42b818b35a5..4fb302c71ea 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -4050,13 +4050,16 @@ void psys_get_dupli_texture(ParticleSystem *psys, ParticleSettings *part, uv[0] = uv[1] = 0.f; + /* Grid distribution doesn't support UV or emit from vertex mode */ + bool is_grid = (part->distr == PART_DISTR_GRID && part->from != PART_FROM_VERT); + if (cpa) { if ((part->childtype == PART_CHILD_FACES) && (psmd->dm_final != NULL)) { CustomData *mtf_data = psmd->dm_final->getTessFaceDataLayout(psmd->dm_final); const int uv_idx = CustomData_get_render_layer(mtf_data, CD_MTFACE); mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx); - if (mtface) { + if (mtface && !is_grid) { mface = psmd->dm_final->getTessFaceData(psmd->dm_final, cpa->num, CD_MFACE); mtface += cpa->num; psys_interpolate_uvs(mtface, mface->v4, cpa->fuv, uv); @@ -4070,7 +4073,7 @@ void psys_get_dupli_texture(ParticleSystem *psys, ParticleSettings *part, } } - if ((part->from == PART_FROM_FACE) && (psmd->dm_final != NULL)) { + if ((part->from == PART_FROM_FACE) && (psmd->dm_final != NULL) && !is_grid) { CustomData *mtf_data = psmd->dm_final->getTessFaceDataLayout(psmd->dm_final); const int uv_idx = CustomData_get_render_layer(mtf_data, CD_MTFACE); mtface = CustomData_get_layer_n(mtf_data, CD_MTFACE, uv_idx); diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 8e3e2f5d6d0..b4e951ce04a 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -2183,7 +2183,6 @@ static void basic_rotate(ParticleSettings *part, ParticleData *pa, float dfra, f * http://en.wikipedia.org/wiki/Newton's_method * ************************************************/ -#define COLLISION_MAX_COLLISIONS 10 #define COLLISION_MIN_RADIUS 0.001f #define COLLISION_MIN_DISTANCE 0.0001f #define COLLISION_ZERO 0.00001f @@ -2570,10 +2569,6 @@ void BKE_psys_collision_neartest_cb(void *userdata, int index, const BVHTreeRay pce.inside = 0; pce.index = index; - /* don't collide with same face again */ - if (col->hit == col->current && col->pce.index == index && col->pce.tot == 3) - return; - collision = collision_sphere_to_tri(col, ray->radius, &pce, &t); if (col->pce.inside == 0) { collision += collision_sphere_to_edges(col, ray->radius, &pce, &t); @@ -2609,8 +2604,17 @@ static int collision_detect(ParticleData *pa, ParticleCollision *col, BVHTreeRay hit->dist = col->original_ray_length = 0.000001f; for (coll = colliders->first; coll; coll=coll->next) { - /* for boids: don't check with current ground object */ - if (coll->ob == col->skip) + /* for boids: don't check with current ground object; also skip if permeated */ + bool skip = false; + + for (int i = 0; i < col->skip_count; i++) { + if (coll->ob == col->skip[i]) { + skip = true; + break; + } + } + + if (skip) continue; /* particles should not collide with emitter at birth */ @@ -2746,7 +2750,7 @@ static int collision_response(ParticleData *pa, ParticleCollision *col, BVHTreeR if (through==0 && ((vc_dot>0.0f && v0_dot>0.0f && vc_dot>v0_dot) || (vc_dot<0.0f && v0_dot<0.0f && vc_dot<v0_dot))) mul_v3_v3fl(v0_nor, pce->nor, vc_dot); else if (v0_dot > 0.f) - mul_v3_v3fl(v0_nor, pce->nor, vc_dot + (through ? -1.0f : 1.0f) * v0_dot); + mul_v3_v3fl(v0_nor, pce->nor, vc_dot + v0_dot); else mul_v3_v3fl(v0_nor, pce->nor, vc_dot + (through ? 1.0f : -1.0f) * v0_dot); @@ -2801,8 +2805,10 @@ static int collision_response(ParticleData *pa, ParticleCollision *col, BVHTreeR col->f = f; } - col->prev = col->hit; - col->prev_index = hit->index; + /* if permeability random roll succeeded, disable collider for this sim step */ + if (through) { + col->skip[col->skip_count++] = col->hit; + } return 1; } @@ -2863,16 +2869,16 @@ static void collision_check(ParticleSimulationData *sim, int p, float dfra, floa if (part->phystype == PART_PHYS_BOIDS && part->boids->options & BOID_ALLOW_LAND) { col.boid = 1; col.boid_z = pa->state.co[2]; - col.skip = pa->boid->ground; + col.skip[col.skip_count++] = pa->boid->ground; } /* 10 iterations to catch multiple collisions */ - while (collision_count < COLLISION_MAX_COLLISIONS) { + while (collision_count < PARTICLE_COLLISION_MAX_COLLISIONS) { if (collision_detect(pa, &col, &hit, sim->colliders)) { collision_count++; - if (collision_count == COLLISION_MAX_COLLISIONS) + if (collision_count == PARTICLE_COLLISION_MAX_COLLISIONS) collision_fail(pa, &col); else if (collision_response(pa, &col, &hit, part->flag & PART_DIE_ON_COL, part->flag & PART_ROT_DYN)==0) return; @@ -3919,7 +3925,7 @@ static void system_step(ParticleSimulationData *sim, float cfra, const bool use_ /* 2. try to read from the cache */ if (pid) { - int cache_result = BKE_ptcache_read(pid, cache_cfra); + int cache_result = BKE_ptcache_read(pid, cache_cfra, true); if (ELEM(cache_result, PTCACHE_READ_EXACT, PTCACHE_READ_INTERPOLATED)) { cached_step(sim, cfra); diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 69a98c06000..d08814e3373 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -2632,7 +2632,7 @@ static int ptcache_interpolate(PTCacheID *pid, float cfra, int cfra1, int cfra2) } /* reads cache from disk or memory */ /* possible to get old or interpolated result */ -int BKE_ptcache_read(PTCacheID *pid, float cfra) +int BKE_ptcache_read(PTCacheID *pid, float cfra, bool no_extrapolate_old) { int cfrai = (int)floor(cfra), cfra1=0, cfra2=0; int ret = 0; @@ -2658,10 +2658,17 @@ int BKE_ptcache_read(PTCacheID *pid, float cfra) return 0; /* don't read old cache if already simulated past cached frame */ - if (cfra1 == 0 && cfra2 && cfra2 <= pid->cache->simframe) - return 0; - if (cfra1 && cfra1 == cfra2) - return 0; + if (no_extrapolate_old) { + if (cfra1 == 0 && cfra2 && cfra2 <= pid->cache->simframe) + return 0; + if (cfra1 && cfra1 == cfra2) + return 0; + } + else { + /* avoid calling interpolate between the same frame values */ + if (cfra1 && cfra1 == cfra2) + cfra1 = 0; + } if (cfra1) { if (pid->file_type == PTCACHE_FILE_OPENVDB && pid->read_openvdb_stream) { diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 0f1f9b4bdf7..c3ae5736aa9 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -1560,14 +1560,16 @@ void BKE_rigidbody_do_simulation(Scene *scene, float ctime) /* try to read from cache */ // RB_TODO deal with interpolated, old and baked results - if (BKE_ptcache_read(&pid, ctime)) { + bool can_simulate = (ctime == rbw->ltime + 1) && !(cache->flag & PTCACHE_BAKED); + + if (BKE_ptcache_read(&pid, ctime, can_simulate)) { BKE_ptcache_validate(cache, (int)ctime); rbw->ltime = ctime; return; } /* advance simulation, we can only step one frame forward */ - if (ctime == rbw->ltime + 1 && !(cache->flag & PTCACHE_BAKED)) { + if (can_simulate) { /* write cache for first frame when on second frame */ if (rbw->ltime == startframe && (cache->flag & PTCACHE_OUTDATED || cache->last_exact == 0)) { BKE_ptcache_write(&pid, startframe); diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 3e37ee83cea..c3c23756b70 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -49,6 +49,7 @@ #include "DNA_space_types.h" #include "DNA_view3d_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_gpencil_types.h" #include "BLI_math.h" #include "BLI_blenlib.h" @@ -64,6 +65,7 @@ #include "BKE_animsys.h" #include "BKE_action.h" #include "BKE_armature.h" +#include "BKE_cachefile.h" #include "BKE_colortools.h" #include "BKE_depsgraph.h" #include "BKE_editmesh.h" @@ -286,6 +288,13 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type) ts->imapaint.paintcursor = NULL; id_us_plus((ID *)ts->imapaint.stencil); ts->particle.paintcursor = NULL; + /* duplicate Grease Pencil Drawing Brushes */ + BLI_listbase_clear(&ts->gp_brushes); + for (bGPDbrush *brush = sce->toolsettings->gp_brushes.first; brush; brush = brush->next) { + bGPDbrush *newbrush = BKE_gpencil_brush_duplicate(brush); + BLI_addtail(&ts->gp_brushes, newbrush); + } + } /* make a private copy of the avicodecdata */ @@ -334,7 +343,7 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type) /* grease pencil */ if (scen->gpd) { if (type == SCE_COPY_FULL) { - scen->gpd = gpencil_data_duplicate(bmain, scen->gpd, false); + scen->gpd = BKE_gpencil_data_duplicate(bmain, scen->gpd, false); } else if (type == SCE_COPY_EMPTY) { scen->gpd = NULL; @@ -432,6 +441,10 @@ void BKE_scene_free(Scene *sce) BKE_paint_free(&sce->toolsettings->uvsculpt->paint); MEM_freeN(sce->toolsettings->uvsculpt); } + /* free Grease Pencil Drawing Brushes */ + BKE_gpencil_free_brushes(&sce->toolsettings->gp_brushes); + BLI_freelistN(&sce->toolsettings->gp_brushes); + BKE_paint_free(&sce->toolsettings->imapaint.paint); MEM_freeN(sce->toolsettings); @@ -764,6 +777,11 @@ void BKE_scene_init(Scene *sce) gp_brush->strength = 0.5f; gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_STRENGTH]; + gp_brush->size = 25; + gp_brush->strength = 0.5f; + gp_brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + gp_brush = &gset->brush[GP_EDITBRUSH_TYPE_GRAB]; gp_brush->size = 50; gp_brush->strength = 0.3f; @@ -1909,6 +1927,9 @@ void BKE_scene_update_for_newframe_ex(EvaluationContext *eval_ctx, Main *bmain, BKE_mask_evaluate_all_masks(bmain, ctime, true); + /* Update animated cache files for modifiers. */ + BKE_cachefile_update_frame(bmain, sce, ctime, (((double)sce->r.frs_sec) / (double)sce->r.frs_sec_base)); + #ifdef POSE_ANIMATION_WORKAROUND scene_armature_depsgraph_workaround(bmain); #endif diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 6067a8b2d9b..c240aa27343 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -1656,6 +1656,9 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render else if ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE)) { BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); } + else if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) { + BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); + } else if (sanim && sanim->anim && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR)) { char fname[FILE_MAXFILE]; BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); @@ -1675,13 +1678,21 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render if (view_id > 0) BLI_snprintf(suffix, sizeof(suffix), "_%d", view_id); - if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE && sanim && sanim->anim && + if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE && ed->proxy_storage != SEQ_EDIT_PROXY_DIR_STORAGE) { - BLI_join_dirfile(name, PROXY_MAXFILE, - dir, proxy->file); - BLI_path_abs(name, G.main->name); - BLI_snprintf(name, PROXY_MAXFILE, "%s_%s", name, suffix); + char fname[FILE_MAXFILE]; + BLI_join_dirfile(fname, PROXY_MAXFILE, dir, proxy->file); + BLI_path_abs(fname, G.main->name); + if (suffix[0] != '\0') { + /* TODO(sergey): This will actually append suffix after extension + * which is weird but how was originally coded in multiview branch. + */ + BLI_snprintf(name, PROXY_MAXFILE, "%s_%s", fname, suffix); + } + else { + BLI_strncpy(name, fname, PROXY_MAXFILE); + } return true; } @@ -5595,3 +5606,31 @@ int BKE_sequencer_find_next_prev_edit( return best_frame; } + +static void sequencer_all_free_anim_ibufs(ListBase *seqbase, int cfra) +{ + for (Sequence *seq = seqbase->first; seq != NULL; seq = seq->next) { + if (seq->enddisp < cfra || seq->startdisp > cfra) { + BKE_sequence_free_anim(seq); + } + if (seq->type == SEQ_TYPE_META) { + sequencer_all_free_anim_ibufs(&seq->seqbase, cfra); + } + } +} + +void BKE_sequencer_all_free_anim_ibufs(int cfra) +{ + BKE_sequencer_cache_cleanup(); + for (Scene *scene = G.main->scene.first; + scene != NULL; + scene = scene->id.next) + { + Editing *ed = BKE_sequencer_editing_get(scene, false); + if (ed == NULL) { + /* Ignore scenes without sequencer. */ + continue; + } + sequencer_all_free_anim_ibufs(&ed->seqbase, cfra); + } +} diff --git a/source/blender/blenkernel/intern/smoke.c b/source/blender/blenkernel/intern/smoke.c index 43569f9ded2..3fc72628c97 100644 --- a/source/blender/blenkernel/intern/smoke.c +++ b/source/blender/blenkernel/intern/smoke.c @@ -2737,19 +2737,18 @@ static void smokeModifier_process(SmokeModifierData *smd, Scene *scene, Object * return; } + /* only calculate something when we advanced a single frame */ + /* don't simulate if viewing start frame, but scene frame is not real start frame */ + bool can_simulate = (framenr == (int)smd->time + 1) && (framenr == scene->r.cfra); + /* try to read from cache */ - if (BKE_ptcache_read(&pid, (float)framenr) == PTCACHE_READ_EXACT) { + if (BKE_ptcache_read(&pid, (float)framenr, can_simulate) == PTCACHE_READ_EXACT) { BKE_ptcache_validate(cache, framenr); smd->time = framenr; return; } - /* only calculate something when we advanced a single frame */ - if (framenr != (int)smd->time + 1) - return; - - /* don't simulate if viewing start frame, but scene frame is not real start frame */ - if (framenr != scene->r.cfra) + if (!can_simulate) return; #ifdef DEBUG_TIME diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 901926dd8d4..03cf33083da 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -3719,9 +3719,12 @@ void sbObjectStep(Scene *scene, Object *ob, float cfra, float (*vertexCos)[3], i } /* try to read from cache */ - cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe); + bool can_simulate = (framenr == sb->last_frame+1) && !(cache->flag & PTCACHE_BAKED); - if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED) { + cache_result = BKE_ptcache_read(&pid, (float)framenr+scene->r.subframe, can_simulate); + + if (cache_result == PTCACHE_READ_EXACT || cache_result == PTCACHE_READ_INTERPOLATED || + (!can_simulate && cache_result == PTCACHE_READ_OLD)) { softbody_to_object(ob, vertexCos, numVerts, sb->local); BKE_ptcache_validate(cache, framenr); @@ -3742,7 +3745,7 @@ void sbObjectStep(Scene *scene, Object *ob, float cfra, float (*vertexCos)[3], i return; } - if (framenr!=sb->last_frame+1) + if (!can_simulate) return; /* if on second frame, write cache for first frame */ diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index a56fc0f9abe..d5d3384bb48 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -241,13 +241,9 @@ static void tracking_reconstruction_copy( /* Copy stabilization structure. */ static void tracking_stabilization_copy( - MovieTrackingStabilization *stabilization_dst, MovieTrackingStabilization *stabilization_src, - GHash *tracks_mapping) + MovieTrackingStabilization *stabilization_dst, MovieTrackingStabilization *stabilization_src) { *stabilization_dst = *stabilization_src; - if (stabilization_src->rot_track) { - stabilization_dst->rot_track = BLI_ghash_lookup(tracks_mapping, stabilization_src->rot_track); - } } /* Copy tracking object. */ @@ -284,7 +280,7 @@ void BKE_tracking_copy(MovieTracking *tracking_dst, MovieTracking *tracking_src) tracking_tracks_copy(&tracking_dst->tracks, &tracking_src->tracks, tracks_mapping); tracking_plane_tracks_copy(&tracking_dst->plane_tracks, &tracking_src->plane_tracks, tracks_mapping); tracking_reconstruction_copy(&tracking_dst->reconstruction, &tracking_src->reconstruction); - tracking_stabilization_copy(&tracking_dst->stabilization, &tracking_src->stabilization, tracks_mapping); + tracking_stabilization_copy(&tracking_dst->stabilization, &tracking_src->stabilization); if (tracking_src->act_track) { tracking_dst->act_track = BLI_ghash_lookup(tracks_mapping, tracking_src->act_track); } @@ -316,7 +312,7 @@ void BKE_tracking_copy(MovieTracking *tracking_dst, MovieTracking *tracking_src) } /* Initialize motion tracking settings to default values, - * used when new movie clip datablock is creating. + * used when new movie clip datablock is created. */ void BKE_tracking_settings_init(MovieTracking *tracking) { @@ -334,10 +330,22 @@ void BKE_tracking_settings_init(MovieTracking *tracking) tracking->settings.object_distance = 1; tracking->stabilization.scaleinf = 1.0f; + tracking->stabilization.anchor_frame = MINFRAME; + zero_v2(tracking->stabilization.target_pos); + tracking->stabilization.target_rot = 0.0f; + tracking->stabilization.scale = 1.0f; + + tracking->stabilization.act_track = 0; + tracking->stabilization.act_rot_track = 0; + tracking->stabilization.tot_track = 0; + tracking->stabilization.tot_rot_track = 0; + + tracking->stabilization.scaleinf = 1.0f; tracking->stabilization.locinf = 1.0f; tracking->stabilization.rotinf = 1.0f; tracking->stabilization.maxscale = 2.0f; tracking->stabilization.filter = TRACKING_FILTER_BILINEAR; + tracking->stabilization.flag |= TRACKING_SHOW_STAB_TRACKS; BKE_tracking_object_add(tracking, "Camera"); } @@ -552,6 +560,7 @@ MovieTrackingTrack *BKE_tracking_track_add(MovieTracking *tracking, ListBase *tr track->flag = settings->default_flag; track->algorithm_flag = settings->default_algorithm_flag; track->weight = settings->default_weight; + track->weight_stab = settings->default_weight; memset(&marker, 0, sizeof(marker)); marker.pos[0] = x; @@ -590,6 +599,12 @@ MovieTrackingTrack *BKE_tracking_track_duplicate(MovieTrackingTrack *track) new_track->markers = MEM_dupallocN(new_track->markers); + /* Orevent duplicate from being used for 2D stabilization. + * If necessary, it shall be added explicitly. + */ + new_track->flag &= ~TRACK_USE_2D_STAB; + new_track->flag &= ~TRACK_USE_2D_STAB_ROT; + return new_track; } diff --git a/source/blender/blenkernel/intern/tracking_stabilize.c b/source/blender/blenkernel/intern/tracking_stabilize.c index eb224020977..1d571099723 100644 --- a/source/blender/blenkernel/intern/tracking_stabilize.c +++ b/source/blender/blenkernel/intern/tracking_stabilize.c @@ -21,6 +21,7 @@ * Contributor(s): Blender Foundation, * Sergey Sharybin * Keir Mierle + * Ichthyostega * * ***** END GPL LICENSE BLOCK ***** */ @@ -28,292 +29,1305 @@ /** \file blender/blenkernel/intern/tracking_stabilize.c * \ingroup bke * - * This file contains implementation of 2D frame stabilization. + * This file contains implementation of 2D image stabilization. */ #include <limits.h> #include "DNA_movieclip_types.h" +#include "DNA_scene_types.h" +#include "DNA_anim_types.h" +#include "RNA_access.h" #include "BLI_utildefines.h" +#include "BLI_sort_utils.h" +#include "BLI_math_vector.h" #include "BLI_math.h" #include "BKE_tracking.h" +#include "BKE_movieclip.h" +#include "BKE_fcurve.h" +#include "BLI_ghash.h" +#include "MEM_guardedalloc.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" -/* Calculate median point of markers of tracks marked as used for - * 2D stabilization. - * - * NOTE: frame number should be in clip space, not scene space + +/* == Parameterization constants == */ + +/* When measuring the scale changes relative to the rotation pivot point, it + * might happen accidentally that a probe point (tracking point), which doesn't + * actually move on a circular path, gets very close to the pivot point, causing + * the measured scale contribution to go toward infinity. We damp this undesired + * effect by adding a bias (floor) to the measured distances, which will + * dominate very small distances and thus cause the corresponding track's + * contribution to diminish. + * Measurements happen in normalized (0...1) coordinates within a frame. + */ +static float SCALE_ERROR_LIMIT_BIAS = 0.01f; + +/* When to consider a track as completely faded out. + * This is used in conjunction with the "disabled" flag of the track + * to determine start positions, end positions and gaps */ -static bool stabilization_median_point_get(MovieTracking *tracking, int framenr, float median[2]) +static float EPSILON_WEIGHT = 0.005f; + + + +/* == private working data == */ + +/* Per track baseline for stabilization, defined at reference frame. + * A track's reference frame is chosen as close as possible to the (global) + * anchor_frame. Baseline holds the constant part of each track's contribution + * to the observed movement; it is calculated at initialization pass, using the + * measurement value at reference frame plus the average contribution to fill + * the gap between global anchor_frame and the reference frame for this track. + * This struct with private working data is associated to the local call context + * via `StabContext::private_track_data` + */ +typedef struct TrackStabilizationBase { + float stabilization_offset_base[2]; + + /* measured relative to translated pivot */ + float stabilization_rotation_base[2][2]; + + /* measured relative to translated pivot */ + float stabilization_scale_base; + + bool is_init_for_stabilization; + FCurve *track_weight_curve; +} TrackStabilizationBase; + +/* Tracks are reordered for initialization, starting as close as possible to + * anchor_frame + */ +typedef struct TrackInitOrder { + int sort_value; + int reference_frame; + MovieTrackingTrack *data; +} TrackInitOrder; + +/* Per frame private working data, for accessing possibly animated values. */ +typedef struct StabContext { + MovieClip *clip; + MovieTracking *tracking; + MovieTrackingStabilization *stab; + GHash *private_track_data; + FCurve *locinf; + FCurve *rotinf; + FCurve *scaleinf; + FCurve *target_pos[2]; + FCurve *target_rot; + FCurve *target_scale; + bool use_animation; +} StabContext; + + +static TrackStabilizationBase *access_stabilization_baseline_data( + StabContext *ctx, + MovieTrackingTrack *track) { - bool ok = false; - float min[2], max[2]; - MovieTrackingTrack *track; + return BLI_ghash_lookup(ctx->private_track_data, track); +} - INIT_MINMAX2(min, max); +static void attach_stabilization_baseline_data( + StabContext *ctx, + MovieTrackingTrack *track, + TrackStabilizationBase *private_data) +{ + return BLI_ghash_insert(ctx->private_track_data, track, private_data); +} - track = tracking->tracks.first; - while (track) { - if (track->flag & TRACK_USE_2D_STAB) { - MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); +static void discard_stabilization_baseline_data(void *val) +{ + if (val != NULL) { + MEM_freeN(val); + } +} - minmax_v2v2_v2(min, max, marker->pos); - ok = true; - } +/* == access animated values for given frame == */ + +static FCurve *retrieve_stab_animation(MovieClip *clip, + const char *data_path, + int idx) +{ + return id_data_find_fcurve(&clip->id, + &clip->tracking.stabilization, + &RNA_MovieTrackingStabilization, + data_path, + idx, + NULL); +} + +static FCurve *retrieve_track_weight_animation(MovieClip *clip, + MovieTrackingTrack *track) +{ + return id_data_find_fcurve(&clip->id, + track, + &RNA_MovieTrackingTrack, + "weight_stab", + 0, + NULL); +} - track = track->next; +static float fetch_from_fcurve(FCurve *animationCurve, + int framenr, + StabContext *ctx, + float default_value) +{ + if (ctx && ctx->use_animation && animationCurve) { + int scene_framenr = BKE_movieclip_remap_clip_to_scene_frame(ctx->clip, + framenr); + return evaluate_fcurve(animationCurve, scene_framenr); } + return default_value; +} - median[0] = (max[0] + min[0]) / 2.0f; - median[1] = (max[1] + min[1]) / 2.0f; - return ok; +static float get_animated_locinf(StabContext *ctx, int framenr) +{ + return fetch_from_fcurve(ctx->locinf, framenr, ctx, ctx->stab->locinf); } -/* Calculate stabilization data (translation, scale and rotation) from - * given median of first and current frame medians, tracking data and - * frame number. - * - * NOTE: frame number should be in clip space, not scene space +static float get_animated_rotinf(StabContext *ctx, int framenr) +{ + return fetch_from_fcurve(ctx->rotinf, framenr, ctx, ctx->stab->rotinf); +} + +static float get_animated_scaleinf(StabContext *ctx, int framenr) +{ + return fetch_from_fcurve(ctx->scaleinf, framenr, ctx, ctx->stab->scaleinf); +} + +static void get_animated_target_pos(StabContext *ctx, + int framenr, + float target_pos[2]) +{ + target_pos[0] = fetch_from_fcurve(ctx->target_pos[0], + framenr, + ctx, + ctx->stab->target_pos[0]); + target_pos[1] = fetch_from_fcurve(ctx->target_pos[1], + framenr, + ctx, + ctx->stab->target_pos[1]); +} + +static float get_animated_target_rot(StabContext *ctx, int framenr) +{ + return fetch_from_fcurve(ctx->target_rot, + framenr, + ctx, + ctx->stab->target_rot); +} + +static float get_animated_target_scale(StabContext *ctx, int framenr) +{ + return fetch_from_fcurve(ctx->target_scale, framenr, ctx, ctx->stab->scale); +} + +static float get_animated_weight(StabContext *ctx, + MovieTrackingTrack *track, + int framenr) +{ + TrackStabilizationBase *working_data = + access_stabilization_baseline_data(ctx, track); + if (working_data && working_data->track_weight_curve) { + int scene_framenr = BKE_movieclip_remap_clip_to_scene_frame(ctx->clip, + framenr); + return evaluate_fcurve(working_data->track_weight_curve, scene_framenr); + } + /* Use weight at global 'current frame' as fallback default. */ + return track->weight_stab; +} + +static void use_values_from_fcurves(StabContext *ctx, bool toggle) +{ + if (ctx != NULL) { + ctx->use_animation = toggle; + } +} + + +/* Prepare per call private working area. + * Used for access to possibly animated values: retrieve available F-curves. */ -static void stabilization_calculate_data(MovieTracking *tracking, int framenr, int width, int height, - const float firstmedian[2], const float median[2], - float translation[2], float *scale, float *angle) +static StabContext *initialize_stabilization_working_context(MovieClip *clip) { - MovieTrackingStabilization *stab = &tracking->stabilization; + StabContext *ctx = MEM_callocN(sizeof(StabContext), + "2D stabilization animation runtime data"); + ctx->clip = clip; + ctx->tracking = &clip->tracking; + ctx->stab = &clip->tracking.stabilization; + ctx->private_track_data = BLI_ghash_ptr_new( + "2D stabilization per track private working data"); + ctx->locinf = retrieve_stab_animation(clip, "influence_location", 0); + ctx->rotinf = retrieve_stab_animation(clip, "influence_rotation", 0); + ctx->scaleinf = retrieve_stab_animation(clip, "influence_scale", 0); + ctx->target_pos[0] = retrieve_stab_animation(clip, "target_pos", 0); + ctx->target_pos[1] = retrieve_stab_animation(clip, "target_pos", 1); + ctx->target_rot = retrieve_stab_animation(clip, "target_rot", 0); + ctx->target_scale = retrieve_stab_animation(clip, "target_zoom", 0); + ctx->use_animation = true; + return ctx; +} - *scale = (stab->scale - 1.0f) * stab->scaleinf + 1.0f; - *angle = 0.0f; +/* Discard all private working data attached to this call context. + * NOTE: We allocate the record for the per track baseline contribution + * locally for each call context (i.e. call to + * BKE_tracking_stabilization_data_get() + * Thus it is correct to discard all allocations found within the + * corresponding _local_ GHash + */ +static void discard_stabilization_working_context(StabContext *ctx) +{ + if (ctx != NULL) { + BLI_ghash_free(ctx->private_track_data, + NULL, + discard_stabilization_baseline_data); + MEM_freeN(ctx); + } +} - translation[0] = (firstmedian[0] - median[0]) * width * (*scale); - translation[1] = (firstmedian[1] - median[1]) * height * (*scale); +static bool is_init_for_stabilization(StabContext *ctx, + MovieTrackingTrack *track) +{ + TrackStabilizationBase *working_data = + access_stabilization_baseline_data(ctx, track); + return (working_data != NULL && working_data->is_init_for_stabilization); +} + +static bool is_usable_for_stabilization(StabContext *ctx, + MovieTrackingTrack *track) +{ + return (track->flag & TRACK_USE_2D_STAB) && + is_init_for_stabilization(ctx, track); +} - mul_v2_fl(translation, stab->locinf); +static bool is_effectively_disabled(StabContext *ctx, + MovieTrackingTrack *track, + MovieTrackingMarker *marker) +{ + return (marker->flag & MARKER_DISABLED) || + (EPSILON_WEIGHT > get_animated_weight(ctx, track, marker->framenr)); +} - if ((stab->flag & TRACKING_STABILIZE_ROTATION) && stab->rot_track && stab->rotinf) { - MovieTrackingMarker *marker; - float a[2], b[2]; - float x0 = (float)width / 2.0f, y0 = (float)height / 2.0f; - float x = median[0] * width, y = median[1] * height; - marker = BKE_tracking_marker_get(stab->rot_track, 1); - sub_v2_v2v2(a, marker->pos, firstmedian); - a[0] *= width; - a[1] *= height; +static int search_closest_marker_index(MovieTrackingTrack *track, + int ref_frame) +{ + MovieTrackingMarker *markers = track->markers; + int end = track->markersnr; + int i = track->last_marker; + + i = MAX2(0, i); + i = MIN2(i, end - 1); + for ( ; i < end - 1 && markers[i].framenr <= ref_frame; ++i); + for ( ; 0 < i && markers[i].framenr > ref_frame; --i); + + track->last_marker = i; + return i; +} + +static void retrieve_next_higher_usable_frame(StabContext *ctx, + MovieTrackingTrack *track, + int i, + int ref_frame, + int *next_higher) +{ + MovieTrackingMarker *markers = track->markers; + int end = track->markersnr; + BLI_assert(0 <= i && i < end); + + while (i < end && + (markers[i].framenr < ref_frame || + is_effectively_disabled(ctx, track, &markers[i]))) + { + ++i; + } + if (i < end && markers[i].framenr < *next_higher) { + BLI_assert(markers[i].framenr >= ref_frame); + *next_higher = markers[i].framenr; + } +} + +static void retrieve_next_lower_usable_frame(StabContext *ctx, + MovieTrackingTrack *track, + int i, + int ref_frame, + int *next_lower) +{ + MovieTrackingMarker *markers = track->markers; + BLI_assert(0 <= i && i < track->markersnr); + while (i >= 0 && + (markers[i].framenr > ref_frame || + is_effectively_disabled(ctx, track, &markers[i]))) + { + --i; + } + if (0 <= i && markers[i].framenr > *next_lower) { + BLI_assert(markers[i].framenr <= ref_frame); + *next_lower = markers[i].framenr; + } +} + +/* Find closest frames with usable stabilization data. + * A frame counts as _usable_ when there is at least one track marked for + * translation stabilization, which has an enabled tracking marker at this very + * frame. We search both for the next lower and next higher position, to allow + * the caller to interpolate gaps and to extrapolate at the ends of the + * definition range. + * + * NOTE: Regarding performance note that the individual tracks will cache the + * last search position. + */ +static void find_next_working_frames(StabContext *ctx, + int framenr, + int *next_lower, + int *next_higher) +{ + for (MovieTrackingTrack *track = ctx->tracking->tracks.first; + track != NULL; + track = track->next) + { + if (is_usable_for_stabilization(ctx, track)) { + int startpoint = search_closest_marker_index(track, framenr); + retrieve_next_higher_usable_frame(ctx, + track, + startpoint, + framenr, + next_higher); + retrieve_next_lower_usable_frame(ctx, + track, + startpoint, + framenr, + next_lower); + } + } +} + - marker = BKE_tracking_marker_get(stab->rot_track, framenr); - sub_v2_v2v2(b, marker->pos, median); - b[0] *= width; - b[1] *= height; +/* Find active (enabled) marker closest to the reference frame. */ +static MovieTrackingMarker *get_closest_marker(StabContext *ctx, + MovieTrackingTrack *track, + int ref_frame) +{ + if (track->markersnr > 0) { + int next_lower = MINAFRAME; + int next_higher = MAXFRAME; + int i = search_closest_marker_index(track, ref_frame); + retrieve_next_higher_usable_frame(ctx, track, i, ref_frame, &next_higher); + retrieve_next_lower_usable_frame(ctx, track, i, ref_frame, &next_lower); + + if ((next_higher - ref_frame) < (ref_frame - next_lower)) { + return BKE_tracking_marker_get_exact(track, next_higher); + } + else { + return BKE_tracking_marker_get_exact(track, next_lower); + } + } + return NULL; +} - *angle = -atan2f(a[0] * b[1] - a[1] * b[0], a[0] * b[0] + a[1] * b[1]); - *angle *= stab->rotinf; - /* convert to rotation around image center */ - translation[0] -= (x0 + (x - x0) * cosf(*angle) - (y - y0) * sinf(*angle) - x) * (*scale); - translation[1] -= (y0 + (x - x0) * sinf(*angle) + (y - y0) * cosf(*angle) - y) * (*scale); +/* Retrieve tracking data, if available and applicable for this frame. + * The returned weight value signals the validity; data recorded for this + * tracking marker on the exact requested frame is output with the full weight + * of this track, while gaps in the data sequence cause the weight to go to zero. + */ +static MovieTrackingMarker *get_tracking_data_point( + StabContext *ctx, + MovieTrackingTrack *track, + int framenr, + float *weight) +{ + MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); + if (marker && marker->framenr == framenr && !(marker->flag & MARKER_DISABLED)) { + *weight = get_animated_weight(ctx, track, framenr); + return marker; + } + else { + /* no marker at this frame (=gap) or marker disabled */ + *weight = 0.0f; + return NULL; } } -/* Calculate factor of a scale, which will eliminate black areas - * appearing on the frame caused by frame translation. +/* Calculate the contribution of a single track at the time position (frame) of + * the given marker. Each track has a local reference frame, which is as close + * as possible to the global anchor_frame. Thus the translation contribution is + * comprised of the offset relative to the image position at that reference + * frame, plus a guess of the contribution for the time span between the + * anchor_frame and the local reference frame of this track. The constant part + * of this contribution is precomputed initially. At the anchor_frame, by + * definition the contribution of all tracks is zero, keeping the frame in place. + * + * track_ref is per track baseline contribution at reference frame; filled in at + * initialization + * marker is tracking data to use as contribution for current frame. + * result_offset is a total cumulated contribution of this track, + * relative to the stabilization anchor_frame, + * in normalized (0...1) coordinates. + */ +static void translation_contribution(TrackStabilizationBase *track_ref, + MovieTrackingMarker *marker, + float result_offset[2]) +{ + add_v2_v2v2(result_offset, + track_ref->stabilization_offset_base, + marker->pos); +} + +/* Similar to the ::translation_contribution(), the rotation contribution is + * comprised of the contribution by this individual track, and the averaged + * contribution from anchor_frame to the ref point of this track. + * - Contribution is in terms of angles, -pi < angle < +pi, and all averaging + * happens in this domain. + * - Yet the actual measurement happens as vector between pivot and the current + * tracking point + * - Currently we use the center of frame as approximation for the rotation pivot + * point. + * - Moreover, the pivot point has to be compensated for the already determined + * shift offset, in order to get the pure rotation around the pivot. + * To turn this into a _contribution_, the likewise corrected angle at the + * reference frame has to be subtracted, to get only the pure angle difference + * this tracking point has captured. + * - To get from vectors to angles, we have to go through an arcus tangens, + * which involves the issue of the definition range: the resulting angles will + * flip by 360deg when the measured vector passes from the 2nd to the third + * quadrant, thus messing up the average calculation. Since _any_ tracking + * point might be used, these problems are quite common in practice. + * - Thus we perform the subtraction of the reference and the addition of the + * baseline contribution in polar coordinates as simple addition of angles; + * since these parts are fixed, we can bake them into a rotation matrix. + * With this approach, the border of the arcus tangens definition range will + * be reached only, when the _whole_ contribution approaches +- 180deg, + * meaning we've already tilted the frame upside down. This situation is way + * less common and can be tolerated. + * - As an additional feature, when activated, also changes in image scale + * relative to the rotation center can be picked up. To handle those values + * in the same framework, we average the scales as logarithms. + * + * aspect is a total aspect ratio of the undistorted image (includes fame and + * pixel aspect). + */ +static void rotation_contribution(TrackStabilizationBase *track_ref, + MovieTrackingMarker *marker, + float aspect, + float target_pos[2], + float averaged_translation_contribution[2], + float *result_angle, + float *result_scale) +{ + float len; + float pos[2]; + float pivot[2]; + copy_v2_fl(pivot, 0.5f); /* Use center of frame as hard wired pivot. */ + add_v2_v2(pivot, averaged_translation_contribution); + sub_v2_v2(pivot, target_pos); + sub_v2_v2v2(pos, marker->pos, pivot); + + pos[0] *= aspect; + mul_m2v2(track_ref->stabilization_rotation_base, pos); + + *result_angle = atan2f(pos[1],pos[0]); + + len = len_v2(pos) + SCALE_ERROR_LIMIT_BIAS; + *result_scale = len * track_ref->stabilization_scale_base; + BLI_assert(0.0 < *result_scale); +} + + +/* Weighted average of the per track cumulated contributions at given frame. + * Returns truth if all desired calculations could be done and all averages are + * available. + * + * NOTE: Even if the result is not `true`, the returned translation and angle + * are always sensible and as good as can be. Especially in the + * initialization phase we might not be able to get any average (yet) or + * get only a translation value. Since initialization visits tracks in a + * specific order, starting from anchor_frame, the result is logically + * correct non the less. But under normal operation conditions, + * a result of `false` should disable the stabilization function */ -static float stabilization_calculate_autoscale_factor(MovieTracking *tracking, int width, int height) +static bool average_track_contributions(StabContext *ctx, + int framenr, + float aspect, + float r_translation[2], + float *r_angle, + float *r_scale_step) { - float firstmedian[2]; + bool ok; + float weight_sum; + MovieTrackingTrack *track; + MovieTracking *tracking = ctx->tracking; MovieTrackingStabilization *stab = &tracking->stabilization; - float aspect = tracking->camera.pixel_aspect; - - /* Early output if stabilization data is already up-to-date. */ - if (stab->ok) - return stab->scale; - - /* See comment in BKE_tracking_stabilization_data_get about first frame. */ - if (stabilization_median_point_get(tracking, 1, firstmedian)) { - int sfra = INT_MAX, efra = INT_MIN, cfra; - float scale = 1.0f; - MovieTrackingTrack *track; - - stab->scale = 1.0f; - - /* Calculate frame range of tracks used for stabilization. */ - track = tracking->tracks.first; - while (track) { - if (track->flag & TRACK_USE_2D_STAB || - ((stab->flag & TRACKING_STABILIZE_ROTATION) && track == stab->rot_track)) - { - sfra = min_ii(sfra, track->markers[0].framenr); - efra = max_ii(efra, track->markers[track->markersnr - 1].framenr); - } + BLI_assert(stab->flag & TRACKING_2D_STABILIZATION); - track = track->next; + zero_v2(r_translation); + *r_scale_step = 0.0f; /* logarithm */ + *r_angle = 0.0f; + + ok = false; + weight_sum = 0.0f; + for (track = tracking->tracks.first; track; track = track->next) { + if (!is_init_for_stabilization(ctx, track)) { + continue; + } + if (track->flag & TRACK_USE_2D_STAB) { + float weight = 0.0f; + MovieTrackingMarker *marker = get_tracking_data_point(ctx, + track, + framenr, + &weight); + if (marker) { + TrackStabilizationBase *stabilization_base = + access_stabilization_baseline_data(ctx, track); + BLI_assert(stabilization_base != NULL); + float offset[2]; + weight_sum += weight; + translation_contribution(stabilization_base, marker, offset); + mul_v2_fl(offset, weight); + add_v2_v2(r_translation, offset); + ok |= (weight_sum > EPSILON_WEIGHT); + } } + } + if (!ok) { + return false; + } - /* For every frame we calculate scale factor needed to eliminate black - * area and choose largest scale factor as final one. - */ - for (cfra = sfra; cfra <= efra; cfra++) { - float median[2]; - float translation[2], angle, tmp_scale; - int i; - float mat[4][4]; - float points[4][2] = {{0.0f, 0.0f}, {0.0f, height}, {width, height}, {width, 0.0f}}; - float si, co; + r_translation[0] /= weight_sum; + r_translation[1] /= weight_sum; - stabilization_median_point_get(tracking, cfra, median); + if (!(stab->flag & TRACKING_STABILIZE_ROTATION)) { + return ok; + } - stabilization_calculate_data(tracking, cfra, width, height, firstmedian, median, translation, - &tmp_scale, &angle); + ok = false; + weight_sum = 0.0f; + for (track = tracking->tracks.first; track; track = track->next) { + if (!is_init_for_stabilization(ctx, track)) { + continue; + } + if (track->flag & TRACK_USE_2D_STAB_ROT) { + float weight = 0.0f; + MovieTrackingMarker *marker = get_tracking_data_point(ctx, + track, + framenr, + &weight); + if (marker) { + TrackStabilizationBase *stabilization_base = + access_stabilization_baseline_data(ctx, track); + BLI_assert(stabilization_base != NULL); + float rotation, scale; + float target_pos[2]; + weight_sum += weight; + get_animated_target_pos(ctx, framenr, target_pos); + rotation_contribution(stabilization_base, + marker, + aspect, + target_pos, + r_translation, + &rotation, + &scale); + *r_angle += rotation * weight; + if (stab->flag & TRACKING_STABILIZE_SCALE) { + *r_scale_step += logf(scale) * weight; + } + else { + *r_scale_step = 0; + } + ok |= (weight_sum > EPSILON_WEIGHT); + } + } + } + if (ok) { + *r_scale_step /= weight_sum; + *r_angle /= weight_sum; + } + else { + /* We reach this point because translation could be calculated, + * but rotation/scale found no data to work on. + */ + *r_scale_step = 0.0f; + *r_angle = 0.0f; + } + return true; +} - BKE_tracking_stabilization_data_to_mat4(width, height, aspect, translation, 1.0f, angle, mat); - si = sinf(angle); - co = cosf(angle); +/* Linear interpolation of data retrieved at two measurement points. + * This function is used to fill gaps in the middle of the covered area, + * at frames without any usable tracks for stabilization. + * + * framenr is a position to interpolate for. + * frame_a is a valid measurement point below framenr + * frame_b is a valid measurement point above framenr + * Returns truth if both measurements could actually be retrieved. + * Otherwise output parameters remain unaltered + */ +static bool interpolate_averaged_track_contributions(StabContext *ctx, + int framenr, + int frame_a, + int frame_b, + float aspect, + float translation[2], + float *r_angle, + float *r_scale_step) +{ + float t, s; + float trans_a[2], trans_b[2]; + float angle_a, angle_b; + float scale_a, scale_b; + bool success = false; + + BLI_assert(frame_a <= frame_b); + BLI_assert(frame_a <= framenr); + BLI_assert(framenr <= frame_b); + + t = ((float)framenr - frame_a) / (frame_b - frame_a); + s = 1.0f - t; + + success = average_track_contributions(ctx, frame_a, aspect, trans_a, &angle_a, &scale_a); + if (!success) { + return false; + } + success = average_track_contributions(ctx, frame_b, aspect, trans_b, &angle_b, &scale_b); + if (!success) { + return false; + } - for (i = 0; i < 4; i++) { - int j; - float a[3] = {0.0f, 0.0f, 0.0f}, b[3] = {0.0f, 0.0f, 0.0f}; + interp_v2_v2v2(translation, trans_a, trans_b, t); + *r_scale_step = s * scale_a + t * scale_b; + *r_angle = s * angle_a + t * angle_b; + return true; +} - copy_v3_v3(a, points[i]); - copy_v3_v3(b, points[(i + 1) % 4]); - mul_m4_v3(mat, a); - mul_m4_v3(mat, b); +/* Reorder tracks starting with those providing a tracking data frame + * closest to the global anchor_frame. Tracks with a gap at anchor_frame or + * starting farer away from anchor_frame altogether will be visited later. + * This allows to build up baseline contributions incrementally. + * + * order is an array for sorting the tracks. Must be of suitable size to hold + * all tracks. + * Returns number of actually usable tracks, can be less than the overall number + * of tracks. + * + * NOTE: After returning, the order array holds entries up to the number of + * usable tracks, appropriately sorted starting with the closest tracks. + * Initialization includes disabled tracks, since they might be enabled + * through automation later. + */ +static int establish_track_initialization_order(StabContext *ctx, + TrackInitOrder *order) +{ + size_t tracknr = 0; + MovieTrackingTrack *track; + MovieTracking *tracking = ctx->tracking; + int anchor_frame = tracking->stabilization.anchor_frame; - for (j = 0; j < 4; j++) { - float point[3] = {points[j][0], points[j][1], 0.0f}; - float v1[3], v2[3]; + for (track = tracking->tracks.first; track != NULL; track = track->next) { + MovieTrackingMarker *marker; + order[tracknr].data = track; + marker = get_closest_marker(ctx, track, anchor_frame); + if (marker != NULL && + (track->flag & (TRACK_USE_2D_STAB | TRACK_USE_2D_STAB_ROT))) + { + order[tracknr].sort_value = abs(marker->framenr - anchor_frame); + order[tracknr].reference_frame = marker->framenr; + ++tracknr; + } + } + if (tracknr) { + qsort(order, tracknr, sizeof(TrackInitOrder), BLI_sortutil_cmp_int); + } + return tracknr; +} - sub_v3_v3v3(v1, b, a); - sub_v3_v3v3(v2, point, a); - if (cross_v2v2(v1, v2) >= 0.0f) { - const float rotDx[4][2] = {{1.0f, 0.0f}, {0.0f, -1.0f}, {-1.0f, 0.0f}, {0.0f, 1.0f}}; - const float rotDy[4][2] = {{0.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, -1.0f}, {-1.0f, 0.0f}}; +/* Setup the constant part of this track's contribution to the determined frame + * movement. Tracks usually don't provide tracking data for every frame. Thus, + * for determining data at a given frame, we split up the contribution into a + * part covered by actual measurements on this track, and the initial gap + * between this track's reference frame and the global anchor_frame. + * The (missing) data for the gap can be substituted by the average offset + * observed by the other tracks covering the gap. This approximation doesn't + * introduce wrong data, but it records data with incorrect weight. A totally + * correct solution would require us to average the contribution per frame, and + * then integrate stepwise over all frames -- which of course would be way more + * expensive, especially for longer clips. To the contrary, our solution + * cumulates the total contribution per track and averages afterwards over all + * tracks; it can thus be calculated just based on the data of a single frame, + * plus the "baseline" for the reference frame, which is what we are computing + * here. + * + * Since we're averaging _contributions_, we have to calculate the _difference_ + * of the measured position at current frame and the position at the reference + * frame. But the "reference" part of this difference is constant and can thus + * be packed together with the baseline contribution into a single precomputed + * vector per track. + * + * In case of the rotation contribution, the principle is the same, but we have + * to compensate for the already determined translation and measure the pure + * rotation, simply because this is how we model the offset: shift plus rotation + * around the shifted rotation center. To circumvent problems with the + * definition range of the arcus tangens function, we perform this baseline + * addition and reference angle subtraction in polar coordinates and bake this + * operation into a precomputed rotation matrix. + * + * track is a track to be initialized to initialize + * reference_frame is a local frame for this track, the closest pick to the + * global anchor_frame. + * aspect is a total aspect ratio of the undistorted image (includes fame and + * pixel aspect). + * target_pos is a possibly animated target position as set by the user for + * the reference_frame + * average_translation is a value observed by the _other_ tracks for the gap + * between reference_frame and anchor_frame. This + * average must not contain contributions of frames + * not yet initialized + * average_angle in a similar way, the rotation value observed by the + * _other_ tracks. + * average_scale_step is an image scale factor observed on average by the other + * tracks for this frame. This value is recorded and + * averaged as logarithm. The recorded scale changes + * are damped for very small contributions, to limit + * the effect of probe points approaching the pivot + * too closely. + * + * NOTE: when done, this track is marked as initialized + */ +static void initialize_track_for_stabilization(StabContext *ctx, + MovieTrackingTrack *track, + int reference_frame, + float aspect, + const float target_pos[2], + const float average_translation[2], + float average_angle, + float average_scale_step) +{ + float pos[2], angle, len; + float pivot[2]; + TrackStabilizationBase *local_data = + access_stabilization_baseline_data(ctx, track); + MovieTrackingMarker *marker = + BKE_tracking_marker_get_exact(track, reference_frame); + /* Logic for initialization order ensures there *is* a marker on that + * very frame. + */ + BLI_assert(marker != NULL); + BLI_assert(local_data != NULL); - float dx = translation[0] * rotDx[j][0] + translation[1] * rotDx[j][1], - dy = translation[0] * rotDy[j][0] + translation[1] * rotDy[j][1]; + /* Per track baseline value for translation. */ + sub_v2_v2v2(local_data->stabilization_offset_base, + average_translation, + marker->pos); - float w, h, E, F, G, H, I, J, K, S; + /* Per track baseline value for rotation. */ + copy_v2_fl(pivot, 0.5f); /* Use center of frame as hard wired pivot. */ + add_v2_v2(pivot, average_translation); + sub_v2_v2(pivot, target_pos); + sub_v2_v2v2(pos, marker->pos, pivot); - if (j % 2) { - w = (float)height / 2.0f; - h = (float)width / 2.0f; - } - else { - w = (float)width / 2.0f; - h = (float)height / 2.0f; - } + pos[0] *= aspect; + angle = average_angle - atan2f(pos[1],pos[0]); + rotate_m2(local_data->stabilization_rotation_base, angle); - E = -w * co + h * si; - F = -h * co - w * si; + /* Per track baseline value for zoom. */ + len = len_v2(pos) + SCALE_ERROR_LIMIT_BIAS; + local_data->stabilization_scale_base = expf(average_scale_step) / len; - if ((i % 2) == (j % 2)) { - G = -w * co - h * si; - H = h * co - w * si; - } - else { - G = w * co + h * si; - H = -h * co + w * si; - } + local_data->is_init_for_stabilization = true; +} - I = F - H; - J = G - E; - K = G * F - E * H; - S = (-w * I - h * J) / (dx * I + dy * J + K); +static void initialize_all_tracks(StabContext *ctx, float aspect) +{ + size_t i, track_cnt = 0; + MovieClip *clip = ctx->clip; + MovieTracking *tracking = ctx->tracking; + MovieTrackingTrack *track; + TrackInitOrder *order; - scale = max_ff(scale, S); - } - } - } + /* Attempt to start initialization at anchor_frame. + * By definition, offset contribution is zero there. + */ + int reference_frame = tracking->stabilization.anchor_frame; + float average_angle=0, average_scale_step=0; + float average_translation[2]; + float target_pos_at_ref_frame[2]; + zero_v2(target_pos_at_ref_frame); + zero_v2(average_translation); + + /* Initialize private working data. */ + for (track = tracking->tracks.first; track != NULL; track = track->next) { + TrackStabilizationBase *local_data = + access_stabilization_baseline_data(ctx, track); + if (!local_data) { + local_data = MEM_callocN(sizeof(TrackStabilizationBase), + "2D stabilization per track baseline data"); + attach_stabilization_baseline_data(ctx, track, local_data); } + BLI_assert(local_data != NULL); + local_data->track_weight_curve = retrieve_track_weight_animation(clip, + track); + local_data->is_init_for_stabilization = false; - stab->scale = scale; + ++track_cnt; + } + if (!track_cnt) { + return; + } - if (stab->maxscale > 0.0f) - stab->scale = min_ff(stab->scale, stab->maxscale); + order = MEM_mallocN(track_cnt * sizeof(TrackInitOrder), + "stabilization track order"); + if (!order) { + return; } - else { - stab->scale = 1.0f; + + track_cnt = establish_track_initialization_order(ctx, order); + if (track_cnt == 0) { + goto cleanup; } - stab->ok = true; + for (i = 0; i < track_cnt; ++i) { + track = order[i].data; + if (reference_frame != order[i].reference_frame) { + reference_frame = order[i].reference_frame; + average_track_contributions(ctx, + reference_frame, + aspect, + average_translation, + &average_angle, + &average_scale_step); + get_animated_target_pos(ctx, + reference_frame, + target_pos_at_ref_frame); + } + initialize_track_for_stabilization(ctx, + track, + reference_frame, + aspect, + target_pos_at_ref_frame, + average_translation, + average_angle, + average_scale_step); + } - return stab->scale; +cleanup: + MEM_freeN(order); } -/* Get stabilization data (translation, scaling and angle) for a given frame. + +/* Retrieve the measurement of frame movement by averaging contributions of + * active tracks. * - * NOTE: frame number should be in clip space, not scene space + * translation is a measurement in normalized 0..1 coordinates. + * angle is a measurement in radians -pi..+pi counter clockwise relative to + * translation compensated frame center + * scale_step is a measurement of image scale changes, in logarithmic scale + * (zero means scale == 1) + * Returns calculation enabled and all data retrieved as expected for this frame. + * + * NOTE: when returning `false`, output parameters are reset to neutral values. */ -void BKE_tracking_stabilization_data_get(MovieTracking *tracking, int framenr, int width, int height, - float translation[2], float *scale, float *angle) +static bool stabilization_determine_offset_for_frame(StabContext *ctx, + int framenr, + float aspect, + float r_translation[2], + float *r_angle, + float *r_scale_step) { - float firstmedian[2], median[2]; - MovieTrackingStabilization *stab = &tracking->stabilization; + bool success = false; /* Early output if stabilization is disabled. */ - if ((stab->flag & TRACKING_2D_STABILIZATION) == 0) { - zero_v2(translation); - *scale = 1.0f; - *angle = 0.0f; + if ((ctx->stab->flag & TRACKING_2D_STABILIZATION) == 0) { + zero_v2(r_translation); + *r_scale_step = 0.0f; + *r_angle = 0.0f; + return false; + } - return; + success = average_track_contributions(ctx, + framenr, + aspect, + r_translation, + r_angle, + r_scale_step); + if (!success) { + /* Try to hold extrapolated settings beyond the definition range + * and to interpolate in gaps without any usable tracking data + * to prevent sudden jump to image zero position. + */ + int next_lower = MINAFRAME; + int next_higher = MAXFRAME; + use_values_from_fcurves(ctx, true); + find_next_working_frames(ctx, framenr, &next_lower, &next_higher); + if (next_lower >= MINFRAME && next_higher < MAXFRAME) { + success = interpolate_averaged_track_contributions(ctx, + framenr, + next_lower, + next_higher, + aspect, + r_translation, + r_angle, + r_scale_step); + } + else if (next_higher < MAXFRAME) { + /* Before start of stabilized range: extrapolate start point + * settings. + */ + success = average_track_contributions(ctx, + next_higher, + aspect, + r_translation, + r_angle, + r_scale_step); + } + else if (next_lower >= MINFRAME) { + /* After end of stabilized range: extrapolate end point settings. */ + success = average_track_contributions(ctx, + next_lower, + aspect, + r_translation, + r_angle, + r_scale_step); + } + use_values_from_fcurves(ctx, false); } + return success; +} - /* Even if tracks does not start at frame 1, their position will - * be estimated at this frame, which will give reasonable result - * in most of cases. - * - * However, it's still better to replace this with real first - * frame number at which tracks are appearing. +/* Calculate stabilization data (translation, scale and rotation) from given raw + * measurements. Result is in absolute image dimensions (expanded image, square + * pixels), includes automatic or manual scaling and compensates for a target + * frame position, if given. + * + * size is a size of the expanded image, the width in pixels is size * aspect. + * aspect is a ratio (width / height) of the effective canvas (square pixels). + * do_compensate denotes whether to actually output values necessary to + * _compensate_ the determined frame movement. + * Otherwise, the effective target movement is returned. + */ +static void stabilization_calculate_data(StabContext *ctx, + int framenr, + int size, + float aspect, + bool do_compensate, + float scale_step, + float r_translation[2], + float *r_scale, + float *r_angle) +{ + float target_pos[2]; + float scaleinf = get_animated_scaleinf(ctx, framenr); + + *r_scale = (get_animated_target_scale(ctx,framenr) - 1.0f) * scaleinf + 1.0f; + + if (ctx->stab->flag & TRACKING_STABILIZE_SCALE) { + *r_scale *= expf(scale_step * scaleinf); /* Averaged in log scale */ + } + + mul_v2_fl(r_translation, get_animated_locinf(ctx, framenr)); + *r_angle *= get_animated_rotinf(ctx, framenr); + + /* Compensate for a target frame position. + * This allows to follow tracking / panning shots in a semi manual fashion, + * when animating the settings for the target frame position. */ - if (stabilization_median_point_get(tracking, 1, firstmedian)) { - stabilization_median_point_get(tracking, framenr, median); + get_animated_target_pos(ctx, framenr, target_pos); + sub_v2_v2(r_translation, target_pos); + *r_angle -= get_animated_target_rot(ctx,framenr); - if ((stab->flag & TRACKING_AUTOSCALE) == 0) - stab->scale = 1.0f; + /* Convert from relative to absolute coordinates, square pixels. */ + r_translation[0] *= (float)size * aspect; + r_translation[1] *= (float)size; + + /* Output measured data, or inverse of the measured values for + * compensation? + */ + if (do_compensate) { + mul_v2_fl(r_translation, -1.0f); + *r_angle *= -1.0f; + if (*r_scale != 0.0f) { + *r_scale = 1.0f / *r_scale; + } + } +} - if (!stab->ok) { - if (stab->flag & TRACKING_AUTOSCALE) - stabilization_calculate_autoscale_factor(tracking, width, height); - stabilization_calculate_data(tracking, framenr, width, height, firstmedian, median, - translation, scale, angle); +/* Determine the inner part of the frame, which is always safe to use. + * When enlarging the image by the inverse of this factor, any black areas + * appearing due to frame translation and rotation will be removed. + * + * NOTE: When calling this function, basic initialization of tracks must be + * done already + */ +static void stabilization_determine_safe_image_area(StabContext *ctx, + int size, + float image_aspect) +{ + MovieTrackingStabilization *stab = ctx->stab; + float pixel_aspect = ctx->tracking->camera.pixel_aspect; - stab->ok = true; + int sfra = INT_MAX, efra = INT_MIN, cfra; + float scale = 1.0f, scale_step = 0.0f; + MovieTrackingTrack *track; + stab->scale = 1.0f; + + /* Calculate maximal frame range of tracks where stabilization is active. */ + for (track = ctx->tracking->tracks.first; track; track = track->next) { + if ((track->flag & TRACK_USE_2D_STAB) || + ((stab->flag & TRACKING_STABILIZE_ROTATION) && + (track->flag & TRACK_USE_2D_STAB_ROT))) + { + int first_frame = track->markers[0].framenr; + int last_frame = track->markers[track->markersnr - 1].framenr; + sfra = min_ii(sfra, first_frame); + efra = max_ii(efra, last_frame); } - else { - stabilization_calculate_data(tracking, framenr, width, height, firstmedian, median, - translation, scale, angle); + } + + /* For every frame we calculate scale factor needed to eliminate black border area + * and choose largest scale factor as final one. + */ + for (cfra = sfra; cfra <= efra; cfra++) { + float translation[2], angle, tmp_scale; + int i; + float mat[4][4]; + float points[4][2] = {{0.0f, 0.0f}, + {0.0f, size}, + {image_aspect * size, size}, + {image_aspect * size, 0.0f}}; + float si, co; + bool do_compensate = true; + + stabilization_determine_offset_for_frame(ctx, + cfra, + image_aspect, + translation, + &angle, + &scale_step); + stabilization_calculate_data(ctx, + cfra, + size, + image_aspect, + do_compensate, + scale_step, + translation, + &tmp_scale, + &angle); + + BKE_tracking_stabilization_data_to_mat4(size * image_aspect, + size, + pixel_aspect, + translation, + 1.0f, + angle, + mat); + + si = sinf(angle); + co = cosf(angle); + + /* Investigate the transformed border lines for this frame; + * find out, where it cuts the original frame. + */ + for (i = 0; i < 4; i++) { + int j; + float a[3] = {0.0f, 0.0f, 0.0f}, + b[3] = {0.0f, 0.0f, 0.0f}; + + copy_v2_v2(a, points[i]); + copy_v2_v2(b, points[(i + 1) % 4]); + a[2] = b[2] = 0.0f; + + mul_m4_v3(mat, a); + mul_m4_v3(mat, b); + + for (j = 0; j < 4; j++) { + float point[3] = {points[j][0], points[j][1], 0.0f}; + float v1[3], v2[3]; + + sub_v3_v3v3(v1, b, a); + sub_v3_v3v3(v2, point, a); + + if (cross_v2v2(v1, v2) >= 0.0f) { + const float rot_dx[4][2] = {{1.0f, 0.0f}, + {0.0f, -1.0f}, + {-1.0f, 0.0f}, + {0.0f, 1.0f}}; + const float rot_dy[4][2] = {{0.0f, 1.0f}, + {1.0f, 0.0f}, + {0.0f, -1.0f}, + {-1.0f, 0.0f}}; + + float dx = translation[0] * rot_dx[j][0] + + translation[1] * rot_dx[j][1], + dy = translation[0] * rot_dy[j][0] + + translation[1] * rot_dy[j][1]; + + float w, h, E, F, G, H, I, J, K, S; + + if (j % 2) { + w = (float)size / 2.0f; + h = image_aspect*size / 2.0f; + } + else { + w = image_aspect*size / 2.0f; + h = (float)size / 2.0f; + } + + E = -w * co + h * si; + F = -h * co - w * si; + + if ((i % 2) == (j % 2)) { + G = -w * co - h * si; + H = h * co - w * si; + } + else { + G = w * co + h * si; + H = -h * co + w * si; + } + + I = F - H; + J = G - E; + K = G * F - E * H; + + S = (dx * I + dy * J + K) / (-w * I - h * J); + + scale = min_ff(scale, S); + } + } } } + + stab->scale = scale; + + if (stab->maxscale > 0.0f) { + stab->scale = max_ff(stab->scale, 1.0f / stab->maxscale); + } +} + + +/* Prepare working data and determine reference point for each track. + * + * NOTE: These calculations _could_ be cached and reused for all frames of the + * same clip. However, since proper initialization depends on (weight) + * animation and setup of tracks, ensuring consistency of cached init data + * turns out to be tricky, hard to maintain and generally not worth the + * effort. Thus we'll re-initialize on every frame. + */ +static StabContext *init_stabilizer(MovieClip *clip, int width, int height) +{ + MovieTracking *tracking = &clip->tracking; + MovieTrackingStabilization *stab = &tracking->stabilization; + float pixel_aspect = tracking->camera.pixel_aspect; + float aspect = (float)width * pixel_aspect / height; + int size = height; + + StabContext *ctx = initialize_stabilization_working_context(clip); + BLI_assert(ctx != NULL); + initialize_all_tracks(ctx, aspect); + if (stab->flag & TRACKING_AUTOSCALE) { + stabilization_determine_safe_image_area(ctx, size, aspect); + } + /* By default, just use values for the global current frame. */ + use_values_from_fcurves(ctx, false); + return ctx; +} + + +/* === public interface functions === */ + +/* Get stabilization data (translation, scaling and angle) for a given frame. + * Returned data describes how to compensate the detected movement, but with any + * chosen scale factor already applied and any target frame position already + * compensated. In case stabilization fails or is disabled, neutral values are + * returned. + * + * framenr is a frame number, relative to the clip (not relative to the scene + * timeline) + * width is an effective width of the canvas (square pixels), used to scale the + * determined translation + * + * Outputs: + * - translation of the lateral shift, absolute canvas coordinates + * (square pixels). + * - scale of the scaling to apply + * - angle of the rotation angle, relative to the frame center + */ +/* TODO(sergey): Use r_ prefix for output parameters here. */ +void BKE_tracking_stabilization_data_get(MovieClip *clip, + int framenr, + int width, + int height, + float translation[2], + float *scale, + float *angle) +{ + StabContext *ctx = NULL; + MovieTracking *tracking = &clip->tracking; + bool enabled = (tracking->stabilization.flag & TRACKING_2D_STABILIZATION); + /* Might become a parameter of a stabilization compositor node. */ + bool do_compensate = true; + float scale_step = 0.0f; + float pixel_aspect = tracking->camera.pixel_aspect; + float aspect = (float)width * pixel_aspect / height; + int size = height; + + if (enabled) { + ctx = init_stabilizer(clip, width, height); + } + + if (enabled && + stabilization_determine_offset_for_frame(ctx, + framenr, + aspect, + translation, + angle, + &scale_step)) + { + stabilization_calculate_data(ctx, + framenr, + size, + aspect, + do_compensate, + scale_step, + translation, + scale, + angle); + } else { zero_v2(translation); *scale = 1.0f; *angle = 0.0f; } + discard_stabilization_working_context(ctx); } -/* Stabilize given image buffer using stabilization data for - * a specified frame number. +/* Stabilize given image buffer using stabilization data for a specified + * frame number. * - * NOTE: frame number should be in clip space, not scene space + * NOTE: frame number should be in clip space, not scene space. */ -ImBuf *BKE_tracking_stabilize_frame(MovieTracking *tracking, int framenr, ImBuf *ibuf, - float translation[2], float *scale, float *angle) +/* TODO(sergey): Use r_ prefix for output parameters here. */ +ImBuf *BKE_tracking_stabilize_frame(MovieClip *clip, + int framenr, + ImBuf *ibuf, + float translation[2], + float *scale, + float *angle) { float tloc[2], tscale, tangle; + MovieTracking *tracking = &clip->tracking; MovieTrackingStabilization *stab = &tracking->stabilization; ImBuf *tmpibuf; int width = ibuf->x, height = ibuf->y; - float aspect = tracking->camera.pixel_aspect; + float pixel_aspect = tracking->camera.pixel_aspect; float mat[4][4]; int j, filter = tracking->stabilization.filter; void (*interpolation)(struct ImBuf *, struct ImBuf *, float, float, int, int) = NULL; @@ -349,8 +1363,12 @@ ImBuf *BKE_tracking_stabilize_frame(MovieTracking *tracking, int framenr, ImBuf tmpibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, ibuf_flags); /* Calculate stabilization matrix. */ - BKE_tracking_stabilization_data_get(tracking, framenr, width, height, tloc, &tscale, &tangle); - BKE_tracking_stabilization_data_to_mat4(ibuf->x, ibuf->y, aspect, tloc, tscale, tangle, mat); + BKE_tracking_stabilization_data_get(clip, framenr, width, height, tloc, &tscale, &tangle); + BKE_tracking_stabilization_data_to_mat4(ibuf->x, ibuf->y, pixel_aspect, tloc, tscale, tangle, mat); + + /* The following code visits each nominal target grid position + * and picks interpolated data "backwards" from source. + * thus we need the inverse of the transformation to apply. */ invert_m4(mat); if (filter == TRACKING_FILTER_NEAREST) @@ -397,48 +1415,61 @@ ImBuf *BKE_tracking_stabilize_frame(MovieTracking *tracking, int framenr, ImBuf return tmpibuf; } -/* Get 4x4 transformation matrix which corresponds to - * stabilization data and used for easy coordinate - * transformation. + +/* Build a 4x4 transformation matrix based on the given 2D stabilization data. + * mat is a 4x4 matrix in homogeneous coordinates, adapted to the + * final image buffer size and compensated for pixel aspect ratio, + * ready for direct OpenGL drawing. * - * NOTE: The reason it is 4x4 matrix is because it's - * used for OpenGL drawing directly. + * TODO(sergey): The signature of this function should be changed. we actually + * don't need the dimensions of the image buffer. Instead we + * should consider to provide the pivot point of the rotation as a + * further stabilization data parameter. */ -void BKE_tracking_stabilization_data_to_mat4(int width, int height, float aspect, +void BKE_tracking_stabilization_data_to_mat4(int buffer_width, + int buffer_height, + float pixel_aspect, float translation[2], float scale, float angle, float mat[4][4]) { float translation_mat[4][4], rotation_mat[4][4], scale_mat[4][4], - center_mat[4][4], inv_center_mat[4][4], + pivot_mat[4][4], inv_pivot_mat[4][4], aspect_mat[4][4], inv_aspect_mat[4][4]; - float scale_vector[3] = {scale, scale, scale}; + float scale_vector[3] = {scale, scale, 1.0f}; + + float pivot[2]; /* XXX this should be a parameter, it is part of the stabilization data */ + + /* Use the motion compensated image center as rotation center. + * This is not 100% correct, but reflects the way the rotation data was + * measured. Actually we'd need a way to find a good pivot, and use that + * both for averaging and for compensation. + */ + /* TODO(sergey) pivot shouldn't be calculated here, rather received + * as a parameter. + */ + pivot[0] = pixel_aspect * buffer_width / 2.0f - translation[0]; + pivot[1] = (float)buffer_height / 2.0f - translation[1]; unit_m4(translation_mat); unit_m4(rotation_mat); unit_m4(scale_mat); - unit_m4(center_mat); unit_m4(aspect_mat); + unit_m4(pivot_mat); + unit_m4(inv_pivot_mat); /* aspect ratio correction matrix */ - aspect_mat[0][0] = 1.0f / aspect; + aspect_mat[0][0] /= pixel_aspect; invert_m4_m4(inv_aspect_mat, aspect_mat); - /* image center as rotation center - * - * Rotation matrix is constructing in a way rotation happens around image center, - * and it's matter of calculating translation in a way, that applying translation - * after rotation would make it so rotation happens around median point of tracks - * used for translation stabilization. - */ - center_mat[3][0] = (float)width / 2.0f; - center_mat[3][1] = (float)height / 2.0f; - invert_m4_m4(inv_center_mat, center_mat); + add_v2_v2(pivot_mat[3], pivot); + sub_v2_v2(inv_pivot_mat[3], pivot); size_to_mat4(scale_mat, scale_vector); /* scale matrix */ add_v2_v2(translation_mat[3], translation); /* translation matrix */ rotate_m4(rotation_mat, 'Z', angle); /* rotation matrix */ /* compose transformation matrix */ - mul_m4_series(mat, translation_mat, center_mat, aspect_mat, rotation_mat, inv_aspect_mat, - scale_mat, inv_center_mat); + mul_m4_series(mat, aspect_mat, translation_mat, + pivot_mat, scale_mat, rotation_mat, inv_pivot_mat, + inv_aspect_mat); } diff --git a/source/blender/blenlib/BLI_system.h b/source/blender/blenlib/BLI_system.h index cb8cb6f5a0d..f51b9623803 100644 --- a/source/blender/blenlib/BLI_system.h +++ b/source/blender/blenlib/BLI_system.h @@ -21,15 +21,14 @@ #ifndef __BLI_SYSTEM_H__ #define __BLI_SYSTEM_H__ +#include <stdio.h> + /** \file BLI_system.h * \ingroup bli */ int BLI_cpu_support_sse2(void); - -#if defined(NDEBUG) || !defined(__BLI_UTILDEFINES_H__) void BLI_system_backtrace(FILE *fp); -#endif /* getpid */ #ifdef WIN32 diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index d504e503c68..746eb922c65 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -633,7 +633,7 @@ extern bool BLI_memory_is_zero(const void *arr, const size_t arr_size); * for aborting need to define WITH_ASSERT_ABORT */ #ifndef NDEBUG -extern void BLI_system_backtrace(FILE *fp); +# include "BLI_system.h" # ifdef WITH_ASSERT_ABORT # define _BLI_DUMMY_ABORT abort # else diff --git a/source/blender/blenlib/intern/system.c b/source/blender/blenlib/intern/system.c index 5d1bdd6d978..898075e651e 100644 --- a/source/blender/blenlib/intern/system.c +++ b/source/blender/blenlib/intern/system.c @@ -25,6 +25,7 @@ #include <stdio.h> #include <stdlib.h> +#include "BLI_utildefines.h" #include "BLI_system.h" #include "MEM_guardedalloc.h" diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index 479d3a15e6c..8cb9ef837b2 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -77,6 +77,13 @@ if(WITH_CODEC_FFMPEG) add_definitions(-DWITH_FFMPEG) endif() +if(WITH_ALEMBIC) + list(APPEND INC + ../alembic + ) + add_definitions(-DWITH_ALEMBIC) +endif() + blender_add_lib(bf_blenloader "${SRC}" "${INC}" "${INC_SYS}") # needed so writefile.c can use dna_type_offsets.h diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 7eabcd18fb8..ded60af4af2 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -59,6 +59,7 @@ #include "DNA_actuator_types.h" #include "DNA_brush_types.h" #include "DNA_camera_types.h" +#include "DNA_cachefile_types.h" #include "DNA_cloth_types.h" #include "DNA_controller_types.h" #include "DNA_constraint_types.h" @@ -114,6 +115,7 @@ #include "BKE_action.h" #include "BKE_armature.h" #include "BKE_brush.h" +#include "BKE_cachefile.h" #include "BKE_cloth.h" #include "BKE_constraint.h" #include "BKE_context.h" @@ -144,7 +146,7 @@ #include "BKE_sequencer.h" #include "BKE_outliner_treehash.h" #include "BKE_sound.h" - +#include "BKE_colortools.h" #include "NOD_common.h" #include "NOD_socket.h" @@ -2691,6 +2693,36 @@ static void direct_link_animdata(FileData *fd, AnimData *adt) adt->actstrip = newdataadr(fd, adt->actstrip); } +/* ************ READ CACHEFILES *************** */ + +static void lib_link_cachefiles(FileData *fd, Main *bmain) +{ + CacheFile *cache_file; + + /* only link ID pointers */ + for (cache_file = bmain->cachefiles.first; cache_file; cache_file = cache_file->id.next) { + if (cache_file->id.tag & LIB_TAG_NEED_LINK) { + cache_file->id.tag &= ~LIB_TAG_NEED_LINK; + } + + BLI_listbase_clear(&cache_file->object_paths); + cache_file->handle = NULL; + + if (cache_file->adt) { + lib_link_animdata(fd, &cache_file->id, cache_file->adt); + } + } +} + +static void direct_link_cachefile(FileData *fd, CacheFile *cache_file) +{ + cache_file->handle = NULL; + + /* relink animdata */ + cache_file->adt = newdataadr(fd, cache_file->adt); + direct_link_animdata(fd, cache_file->adt); +} + /* ************ READ MOTION PATHS *************** */ /* direct data for cache */ @@ -4742,7 +4774,7 @@ static void lib_link_object(FileData *fd, Main *main) /* Only expand so as not to loose any object materials that might be set. */ if (totcol_data && (*totcol_data > ob->totcol)) { /* printf("'%s' %d -> %d\n", ob->id.name, ob->totcol, *totcol_data); */ - BKE_material_resize_object(ob, *totcol_data, false); + BKE_material_resize_object(main, ob, *totcol_data, false); } } @@ -5872,6 +5904,22 @@ static void direct_link_scene(FileData *fd, Scene *sce) sce->toolsettings->wpaint->wpaint_prev = NULL; sce->toolsettings->wpaint->tot = 0; } + /* relink grease pencil drawing brushes */ + link_list(fd, &sce->toolsettings->gp_brushes); + for (bGPDbrush *brush = sce->toolsettings->gp_brushes.first; brush; brush = brush->next) { + brush->cur_sensitivity = newdataadr(fd, brush->cur_sensitivity); + if (brush->cur_sensitivity) { + direct_link_curvemapping(fd, brush->cur_sensitivity); + } + brush->cur_strength = newdataadr(fd, brush->cur_strength); + if (brush->cur_strength) { + direct_link_curvemapping(fd, brush->cur_strength); + } + brush->cur_jitter = newdataadr(fd, brush->cur_jitter); + if (brush->cur_jitter) { + direct_link_curvemapping(fd, brush->cur_jitter); + } + } } if (sce->ed) { @@ -6154,7 +6202,8 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd) bGPDlayer *gpl; bGPDframe *gpf; bGPDstroke *gps; - + bGPDpalette *palette; + /* we must firstly have some grease-pencil data to link! */ if (gpd == NULL) return; @@ -6162,11 +6211,19 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd) /* relink animdata */ gpd->adt = newdataadr(fd, gpd->adt); direct_link_animdata(fd, gpd->adt); - + + /* relink palettes */ + link_list(fd, &gpd->palettes); + for (palette = gpd->palettes.first; palette; palette = palette->next) { + link_list(fd, &palette->colors); + } + /* relink layers */ link_list(fd, &gpd->layers); for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* parent */ + gpl->parent = newlibadr(fd, gpd->id.lib, gpl->parent); /* relink frames */ link_list(fd, &gpl->frames); gpl->actframe = newdataadr(fd, gpl->actframe); @@ -6179,9 +6236,12 @@ static void direct_link_gpencil(FileData *fd, bGPdata *gpd) gps->points = newdataadr(fd, gps->points); /* the triangulation is not saved, so need to be recalculated */ - gps->flag |= GP_STROKE_RECALC_CACHES; gps->triangles = NULL; gps->tot_triangles = 0; + gps->flag |= GP_STROKE_RECALC_CACHES; + /* the color pointer is not saved, so need to be recalculated using the color name */ + gps->palcolor = NULL; + gps->flag |= GP_STROKE_RECALC_COLOR; } } } @@ -7418,7 +7478,7 @@ static void direct_link_movieclip(FileData *fd, MovieClip *clip) clip->tracking_context = NULL; clip->tracking.stats = NULL; - clip->tracking.stabilization.ok = 0; + /* Needed for proper versioning, will be NULL for all newer files anyway. */ clip->tracking.stabilization.rot_track = newdataadr(fd, clip->tracking.stabilization.rot_track); clip->tracking.dopesheet.ok = 0; @@ -7884,6 +7944,7 @@ static const char *dataname(short id_code) case ID_MC: return "Data from MC"; case ID_MSK: return "Data from MSK"; case ID_LS: return "Data from LS"; + case ID_CF: return "Data from CF"; } return "Data from Lib Block"; @@ -8135,6 +8196,9 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const short case ID_PC: direct_link_paint_curve(fd, (PaintCurve *)id); break; + case ID_CF: + direct_link_cachefile(fd, (CacheFile *)id); + break; } oldnewmap_free_unused(fd->datamap); @@ -8328,6 +8392,7 @@ static void lib_link_all(FileData *fd, Main *main) lib_link_mask(fd, main); lib_link_linestyle(fd, main); lib_link_gpencil(fd, main); + lib_link_cachefiles(fd, main); lib_link_mesh(fd, main); /* as last: tpage images with users at zero */ @@ -9434,6 +9499,13 @@ static void expand_camera(FileData *fd, Main *mainvar, Camera *ca) expand_animdata(fd, mainvar, ca->adt); } +static void expand_cachefile(FileData *fd, Main *mainvar, CacheFile *cache_file) +{ + if (cache_file->adt) { + expand_animdata(fd, mainvar, cache_file->adt); + } +} + static void expand_speaker(FileData *fd, Main *mainvar, Speaker *spk) { expand_doit(fd, mainvar, spk->sound); @@ -9629,6 +9701,9 @@ void BLO_expand_main(void *fdhandle, Main *mainvar) case ID_GD: expand_gpencil(fd, mainvar, (bGPdata *)id); break; + case ID_CF: + expand_cachefile(fd, mainvar, (CacheFile *)id); + break; } do_it = true; diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 3e6b0d34ba6..d735f099dc0 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -63,6 +63,8 @@ #include "BKE_scene.h" #include "BKE_sequencer.h" #include "BKE_screen.h" +#include "BKE_tracking.h" +#include "BKE_gpencil.h" #include "BLI_math.h" #include "BLI_listbase.h" @@ -74,6 +76,26 @@ #include "MEM_guardedalloc.h" +/** + * Setup rotation stabilization from ancient single track spec. + * Former Version of 2D stabilization used a single tracking marker to determine the rotation + * to be compensated. Now several tracks can contribute to rotation detection and this feature + * is enabled by the MovieTrackingTrack#flag on a per track base. + */ +static void migrate_single_rot_stabilization_track_settings(MovieTrackingStabilization *stab) +{ + if (stab->rot_track) { + if (!(stab->rot_track->flag & TRACK_USE_2D_STAB_ROT)) { + stab->tot_rot_track++; + stab->rot_track->flag |= TRACK_USE_2D_STAB_ROT; + } + } + stab->rot_track = NULL; /* this field is now ignored */ + + /* by default show the track lists expanded, to improve "discoverability" */ + stab->flag |= TRACKING_SHOW_STAB_TRACKS; +} + static void do_version_constraints_radians_degrees_270_1(ListBase *lb) { bConstraint *con; @@ -1075,15 +1097,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } } - /* init grease pencil smooth level iterations */ - for (bGPdata *gpd = main->gpencil.first; gpd; gpd = gpd->id.next) { - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpl->draw_smoothlvl == 0) { - gpl->draw_smoothlvl = 1; - } - } - } - for (bScreen *screen = main->screen.first; screen; screen = screen->id.next) { for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { @@ -1192,7 +1205,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) for (Camera *camera = main->camera.first; camera != NULL; camera = camera->id.next) { if (camera->stereo.pole_merge_angle_from == 0.0f && - camera->stereo.pole_merge_angle_to == 0.0f) + camera->stereo.pole_merge_angle_to == 0.0f) { camera->stereo.pole_merge_angle_from = DEG2RADF(60.0f); camera->stereo.pole_merge_angle_to = DEG2RADF(75.0f); @@ -1251,4 +1264,119 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } } } + + if (!MAIN_VERSION_ATLEAST(main, 277, 3)) { + /* ------- init of grease pencil initialization --------------- */ + if (!DNA_struct_elem_find(fd->filesdna, "bGPDstroke", "bGPDpalettecolor", "*palcolor")) { + for (Scene *scene = main->scene.first; scene; scene = scene->id.next) { + ToolSettings *ts = scene->toolsettings; + /* initialize use position for sculpt brushes */ + ts->gp_sculpt.flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; + /* initialize selected vertices alpha factor */ + ts->gp_sculpt.alpha = 1.0f; + + /* new strength sculpt brush */ + if (ts->gp_sculpt.brush[0].size >= 11) { + GP_BrushEdit_Settings *gset = &ts->gp_sculpt; + GP_EditBrush_Data *brush; + + brush = &gset->brush[GP_EDITBRUSH_TYPE_STRENGTH]; + brush->size = 25; + brush->strength = 0.5f; + brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + } + } + /* create a default grease pencil drawing brushes set */ + if (!BLI_listbase_is_empty(&main->gpencil)) { + for (Scene *scene = main->scene.first; scene; scene = scene->id.next) { + ToolSettings *ts = scene->toolsettings; + if (BLI_listbase_is_empty(&ts->gp_brushes)) { + BKE_gpencil_brush_init_presets(ts); + } + } + } + /* Convert Grease Pencil to new palettes/brushes + * Loop all strokes and create the palette and all colors + */ + for (bGPdata *gpd = main->gpencil.first; gpd; gpd = gpd->id.next) { + if (BLI_listbase_is_empty(&gpd->palettes)) { + /* create palette */ + bGPDpalette *palette = BKE_gpencil_palette_addnew(gpd, "GP_Palette", true); + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* create color using layer name */ + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_addnew(palette, gpl->info, true); + if (palcolor != NULL) { + /* set color attributes */ + copy_v4_v4(palcolor->color, gpl->color); + copy_v4_v4(palcolor->fill, gpl->fill); + palcolor->flag = gpl->flag; + /* set layer opacity to 1 */ + gpl->opacity = 1.0f; + /* set tint color */ + ARRAY_SET_ITEMS(gpl->tintcolor, 0.0f, 0.0f, 0.0f, 0.0f); + + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* set stroke to palette and force recalculation */ + strcpy(gps->colorname, gpl->info); + gps->palcolor = NULL; + gps->flag |= GP_STROKE_RECALC_COLOR; + gps->thickness = gpl->thickness; + /* set alpha strength to 1 */ + for (int i = 0; i < gps->totpoints; i++) { + gps->points[i].strength = 1.0f; + } + + } + } + } + /* set thickness to 0 (now it is a factor to override stroke thickness) */ + gpl->thickness = 0.0f; + } + /* set first color as active */ + if (palette->colors.first) + BKE_gpencil_palettecolor_setactive(palette, palette->colors.first); + } + } + } + /* ------- end of grease pencil initialization --------------- */ + } + + { + if (!DNA_struct_elem_find(fd->filesdna, "MovieTrackingTrack", "float", "weight_stab")) { + MovieClip *clip; + for (clip = main->movieclip.first; clip; clip = clip->id.next) { + MovieTracking *tracking = &clip->tracking; + MovieTrackingObject *tracking_object; + for (tracking_object = tracking->objects.first; + tracking_object != NULL; + tracking_object = tracking_object->next) + { + ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object); + MovieTrackingTrack *track; + for (track = tracksbase->first; + track != NULL; + track = track->next) + { + track->weight_stab = track->weight; + } + } + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "MovieTrackingStabilization", "int", "tot_rot_track")) { + MovieClip *clip; + for (clip = main->movieclip.first; clip != NULL; clip = clip->id.next) { + if (clip->tracking.stabilization.rot_track) { + migrate_single_rot_stabilization_track_settings(&clip->tracking.stabilization); + if (!clip->tracking.stabilization.scale) { + /* ensure init. + * Was previously used for autoscale only, + * now used always (as "target scale") */ + clip->tracking.stabilization.scale = 1.0f; + } + } + } + } + } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 0ed7a397e0b..ec817b9b261 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -108,6 +108,11 @@ void BLO_update_defaults_startup_blend(Main *bmain) brush->strength = 0.5f; brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + brush = &gset->brush[GP_EDITBRUSH_TYPE_STRENGTH]; + brush->size = 25; + brush->strength = 0.5f; + brush->flag = GP_EDITBRUSH_FLAG_USE_FALLOFF; + brush = &gset->brush[GP_EDITBRUSH_TYPE_GRAB]; brush->size = 50; brush->strength = 0.3f; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index ba783e08b39..b6a54715763 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -107,6 +107,7 @@ #include "DNA_armature_types.h" #include "DNA_actuator_types.h" #include "DNA_brush_types.h" +#include "DNA_cachefile_types.h" #include "DNA_camera_types.h" #include "DNA_cloth_types.h" #include "DNA_constraint_types.h" @@ -2673,6 +2674,20 @@ static void write_scenes(WriteData *wd, ListBase *scebase) writestruct(wd, DATA, UvSculpt, 1, tos->uvsculpt); write_paint(wd, &tos->uvsculpt->paint); } + /* write grease-pencil drawing brushes to file */ + writelist(wd, DATA, bGPDbrush, &tos->gp_brushes); + for (bGPDbrush *brush = tos->gp_brushes.first; brush; brush = brush->next) { + if (brush->cur_sensitivity) { + write_curvemapping(wd, brush->cur_sensitivity); + } + if (brush->cur_strength) { + write_curvemapping(wd, brush->cur_strength); + } + if (brush->cur_jitter) { + write_curvemapping(wd, brush->cur_jitter); + } + } + write_paint(wd, &tos->imapaint.paint); @@ -2835,6 +2850,7 @@ static void write_gpencils(WriteData *wd, ListBase *lb) bGPDlayer *gpl; bGPDframe *gpf; bGPDstroke *gps; + bGPDpalette *palette; for (gpd = lb->first; gpd; gpd = gpd->id.next) { if (gpd->id.us > 0 || wd->current) { @@ -2861,6 +2877,11 @@ static void write_gpencils(WriteData *wd, ListBase *lb) } } } + /* write grease-pencil palettes */ + writelist(wd, DATA, bGPDpalette, &gpd->palettes); + for (palette = gpd->palettes.first; palette; palette = palette->next) { + writelist(wd, DATA, bGPDpalettecolor, &palette->colors); + } } } @@ -3862,6 +3883,21 @@ static void write_linestyles(WriteData *wd, ListBase *idbase) } } +static void write_cachefiles(WriteData *wd, ListBase *idbase) +{ + CacheFile *cache_file; + + for (cache_file = idbase->first; cache_file; cache_file = cache_file->id.next) { + if (cache_file->id.us > 0 || wd->current) { + writestruct(wd, ID_CF, CacheFile, 1, cache_file); + + if (cache_file->adt) { + write_animdata(wd, cache_file->adt); + } + } + } +} + /* Keep it last of write_foodata functions. */ static void write_libraries(WriteData *wd, Main *main) { @@ -4059,6 +4095,7 @@ static bool write_file_handle( write_paintcurves(wd, &mainvar->paintcurves); write_gpencils(wd, &mainvar->gpencil); write_linestyles(wd, &mainvar->linestyle); + write_cachefiles(wd, &mainvar->cachefiles); write_libraries(wd, mainvar->next); /* So changes above don't cause a 'DNA1' to be detected as changed on undo. */ diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h index 3838e8c827c..1d76077c9f1 100644 --- a/source/blender/blentranslation/BLT_translation.h +++ b/source/blender/blentranslation/BLT_translation.h @@ -120,6 +120,7 @@ bool BLT_lang_is_ime_supported(void); #define BLT_I18NCONTEXT_ID_ARMATURE "Armature" #define BLT_I18NCONTEXT_ID_BRUSH "Brush" #define BLT_I18NCONTEXT_ID_CAMERA "Camera" +#define BLT_I18NCONTEXT_ID_CACHEFILE "CacheFile" #define BLT_I18NCONTEXT_ID_CURVE "Curve" #define BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE "FreestyleLineStyle" #define BLT_I18NCONTEXT_ID_GPENCIL "GPencil" @@ -171,6 +172,7 @@ typedef struct { BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_ARMATURE, "id_armature"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_BRUSH, "id_brush"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CAMERA, "id_camera"), \ + BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CACHEFILE, "id_cachefile"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVE, "id_curve"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, "id_fs_linestyle"), \ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_GPENCIL, "id_gpencil"), \ diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.cpp b/source/blender/compositor/nodes/COM_MovieClipNode.cpp index 933223dacac..b3f1b5a4458 100644 --- a/source/blender/compositor/nodes/COM_MovieClipNode.cpp +++ b/source/blender/compositor/nodes/COM_MovieClipNode.cpp @@ -91,7 +91,7 @@ void MovieClipNode::convertToOperations(NodeConverter &converter, const Composit if (stab->flag & TRACKING_2D_STABILIZATION) { int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movieClip, context.getFramenumber()); - BKE_tracking_stabilization_data_get(&movieClip->tracking, clip_framenr, ibuf->x, ibuf->y, loc, &scale, &angle); + BKE_tracking_stabilization_data_get(movieClip, clip_framenr, ibuf->x, ibuf->y, loc, &scale, &angle); } } diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cpp b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cpp index 5ddf15f7684..41f7da7c49f 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cpp +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cpp @@ -47,7 +47,7 @@ void MovieClipAttributeOperation::executePixelSampled(float output[4], angle = 0.0f; if (this->m_clip) { int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_clip, this->m_framenumber); - BKE_tracking_stabilization_data_get(&this->m_clip->tracking, clip_framenr, getWidth(), getHeight(), loc, &scale, &angle); + BKE_tracking_stabilization_data_get(this->m_clip, clip_framenr, getWidth(), getHeight(), loc, &scale, &angle); } switch (this->m_attribute) { case MCA_SCALE: diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index 49b648c7dae..0945da439ef 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -42,6 +42,8 @@ struct Depsgraph; struct Main; struct Scene; +struct Group; +struct EffectorWeights; #ifdef __cplusplus extern "C" { @@ -79,6 +81,7 @@ void DEG_scene_graph_free(struct Scene *scene); */ struct DepsNodeHandle; +struct CacheFile; struct Object; typedef enum eDepsSceneComponentType { @@ -100,15 +103,23 @@ typedef enum eDepsObjectComponentType { DEG_OB_COMP_EVAL_PARTICLES, /* Particle Systems Component */ DEG_OB_COMP_SHADING, /* Material Shading Component */ + DEG_OB_COMP_CACHE, /* Cache Component */ } eDepsObjectComponentType; void DEG_add_scene_relation(struct DepsNodeHandle *node, struct Scene *scene, eDepsSceneComponentType component, const char *description); void DEG_add_object_relation(struct DepsNodeHandle *node, struct Object *ob, eDepsObjectComponentType component, const char *description); void DEG_add_bone_relation(struct DepsNodeHandle *handle, struct Object *ob, const char *bone_name, eDepsObjectComponentType component, const char *description); +void DEG_add_object_cache_relation(struct DepsNodeHandle *handle, struct CacheFile *cache_file, eDepsObjectComponentType component, const char *description); /* TODO(sergey): Remove once all geometry update is granular. */ void DEG_add_special_eval_flag(struct Depsgraph *graph, struct ID *id, short flag); +/* Utility functions for physics modifiers */ +typedef bool (*DEG_CollobjFilterFunction)(struct Object *obj, struct ModifierData *md); + +void DEG_add_collision_relations(struct DepsNodeHandle *handle, struct Scene *scene, Object *ob, struct Group *group, int layer, unsigned int modifier_type, DEG_CollobjFilterFunction fn, bool dupli, const char *name); +void DEG_add_forcefield_relations(struct DepsNodeHandle *handle, struct Scene *scene, Object *ob, struct EffectorWeights *eff, bool add_absorption, int skip_forcefield, const char *name); + /* ************************************************ */ #ifdef __cplusplus diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index a397b48e19c..1812384440f 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -46,6 +46,7 @@ extern "C" { #include "DNA_action_types.h" #include "DNA_anim_types.h" #include "DNA_armature_types.h" +#include "DNA_cachefile_types.h" #include "DNA_camera_types.h" #include "DNA_constraint_types.h" #include "DNA_curve_types.h" @@ -339,6 +340,14 @@ void DepsgraphNodeBuilder::build_scene(Main *bmain, Scene *scene) if (scene->gpd) { build_gpencil(scene->gpd); } + + /* cache files */ + for (CacheFile *cachefile = static_cast<CacheFile *>(bmain->cachefiles.first); + cachefile; + cachefile = static_cast<CacheFile *>(cachefile->id.next)) + { + build_cachefile(cachefile); + } } void DepsgraphNodeBuilder::build_group(Scene *scene, @@ -1259,4 +1268,18 @@ void DepsgraphNodeBuilder::build_gpencil(bGPdata *gpd) build_animdata(gpd_id); } +void DepsgraphNodeBuilder::build_cachefile(CacheFile *cache_file) +{ + ID *cache_file_id = &cache_file->id; + + add_component_node(cache_file_id, DEPSNODE_TYPE_CACHE); + + add_operation_node(cache_file_id, DEPSNODE_TYPE_CACHE, + DEPSOP_TYPE_EXEC, NULL, + DEG_OPCODE_PLACEHOLDER, "Cache File Update"); + + add_id_node(cache_file_id); + build_animdata(cache_file_id); +} + } // namespace DEG diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 6ee0b8406a1..f378f076804 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -33,6 +33,7 @@ #include "intern/depsgraph_types.h" struct Base; +struct CacheFile; struct bGPdata; struct ListBase; struct GHash; @@ -144,6 +145,7 @@ struct DepsgraphNodeBuilder { void build_world(World *world); void build_compositor(Scene *scene); void build_gpencil(bGPdata *gpd); + void build_cachefile(CacheFile *cache_file); protected: Main *m_bmain; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 42b8260c05a..2148a3501d8 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -47,6 +47,7 @@ extern "C" { #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_camera_types.h" +#include "DNA_cachefile_types.h" #include "DNA_constraint_types.h" #include "DNA_curve_types.h" #include "DNA_effect_types.h" @@ -64,6 +65,7 @@ extern "C" { #include "DNA_scene_types.h" #include "DNA_texture_types.h" #include "DNA_world_types.h" +#include "DNA_object_force.h" #include "BKE_action.h" #include "BKE_armature.h" @@ -71,6 +73,7 @@ extern "C" { #include "BKE_constraint.h" #include "BKE_curve.h" #include "BKE_effect.h" +#include "BKE_collision.h" #include "BKE_fcurve.h" #include "BKE_group.h" #include "BKE_key.h" @@ -242,6 +245,69 @@ void DepsgraphRelationBuilder::add_operation_relation( } } +void DepsgraphRelationBuilder::add_collision_relations(const OperationKey &key, Scene *scene, Object *ob, Group *group, int layer, bool dupli, const char *name) +{ + unsigned int numcollobj; + Object **collobjs = get_collisionobjects_ext(scene, ob, group, layer, &numcollobj, eModifierType_Collision, dupli); + + for (unsigned int i = 0; i < numcollobj; i++) + { + Object *ob1 = collobjs[i]; + + ComponentKey trf_key(&ob1->id, DEPSNODE_TYPE_TRANSFORM); + add_relation(trf_key, key, DEPSREL_TYPE_STANDARD, name); + + ComponentKey coll_key(&ob1->id, DEPSNODE_TYPE_GEOMETRY); + add_relation(coll_key, key, DEPSREL_TYPE_STANDARD, name); + } + + if (collobjs) + MEM_freeN(collobjs); +} + +void DepsgraphRelationBuilder::add_forcefield_relations(const OperationKey &key, Scene *scene, Object *ob, ParticleSystem *psys, EffectorWeights *eff, bool add_absorption, const char *name) +{ + ListBase *effectors = pdInitEffectors(scene, ob, psys, eff, false); + + if (effectors) { + for (EffectorCache *eff = (EffectorCache *)effectors->first; eff; eff = eff->next) { + if (eff->ob != ob) { + ComponentKey eff_key(&eff->ob->id, DEPSNODE_TYPE_TRANSFORM); + add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, name); + } + + if (eff->psys) { + if (eff->ob != ob) { + ComponentKey eff_key(&eff->ob->id, DEPSNODE_TYPE_EVAL_PARTICLES); + add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, name); + + /* TODO: remove this when/if EVAL_PARTICLES is sufficient for up to date particles */ + ComponentKey mod_key(&eff->ob->id, DEPSNODE_TYPE_GEOMETRY); + add_relation(mod_key, key, DEPSREL_TYPE_STANDARD, name); + } + else if (eff->psys != psys) { + OperationKey eff_key(&eff->ob->id, DEPSNODE_TYPE_EVAL_PARTICLES, DEG_OPCODE_PSYS_EVAL, eff->psys->name); + add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, name); + } + } + + if (eff->pd->forcefield == PFIELD_SMOKEFLOW && eff->pd->f_source) { + ComponentKey trf_key(&eff->pd->f_source->id, DEPSNODE_TYPE_TRANSFORM); + add_relation(trf_key, key, DEPSREL_TYPE_STANDARD, "Smoke Force Domain"); + + ComponentKey eff_key(&eff->pd->f_source->id, DEPSNODE_TYPE_GEOMETRY); + add_relation(eff_key, key, DEPSREL_TYPE_STANDARD, "Smoke Force Domain"); + } + + if (add_absorption && (eff->pd->flag & PFIELD_VISIBILITY)) { + add_collision_relations(key, scene, ob, NULL, eff->ob->lay, true, "Force Absorption"); + } + } + } + + pdEndEffectors(&effectors); +} + /* **** Functions to build relations between entities **** */ void DepsgraphRelationBuilder::build_scene(Main *bmain, Scene *scene) @@ -599,6 +665,18 @@ void DepsgraphRelationBuilder::build_constraints(Scene *scene, ID *id, eDepsNode TimeSourceKey time_src_key; add_relation(time_src_key, constraint_op_key, DEPSREL_TYPE_TIME, "[TimeSrc -> Animation]"); } + else if (cti->type == CONSTRAINT_TYPE_TRANSFORM_CACHE) { + /* TODO(kevin): This is more a TimeSource -> CacheFile -> Constraint dependency chain. */ + TimeSourceKey time_src_key; + add_relation(time_src_key, constraint_op_key, DEPSREL_TYPE_TIME, "[TimeSrc -> Animation]"); + + bTransformCacheConstraint *data = (bTransformCacheConstraint *)con->data; + + if (data->cache_file) { + ComponentKey cache_key(&data->cache_file->id, DEPSNODE_TYPE_CACHE); + add_relation(cache_key, constraint_op_key, DEPSREL_TYPE_CACHE, cti->name); + } + } else if (cti->get_constraint_targets) { ListBase targets = {NULL, NULL}; cti->get_constraint_targets(con, &targets); @@ -1124,20 +1202,13 @@ void DepsgraphRelationBuilder::build_particles(Scene *scene, Object *ob) } #endif - /* effectors */ - ListBase *effectors = pdInitEffectors(scene, ob, psys, part->effector_weights, false); - - if (effectors) { - for (EffectorCache *eff = (EffectorCache *)effectors->first; eff; eff = eff->next) { - if (eff->psys) { - // XXX: DAG_RL_DATA_DATA | DAG_RL_OB_DATA - ComponentKey eff_key(&eff->ob->id, DEPSNODE_TYPE_GEOMETRY); // xxx: particles instead? - add_relation(eff_key, psys_key, DEPSREL_TYPE_STANDARD, "Particle Field"); - } - } + /* collisions */ + if (part->type != PART_HAIR) { + add_collision_relations(psys_key, scene, ob, part->collision_group, ob->lay, true, "Particle Collision"); } - pdEndEffectors(&effectors); + /* effectors */ + add_forcefield_relations(psys_key, scene, ob, psys, part->effector_weights, part->type == PART_HAIR, "Particle Field"); /* boids */ if (part->boids) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index ce6d2c961fd..46e65d464a4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -63,6 +63,8 @@ struct bConstraint; struct Scene; struct Tex; struct World; +struct EffectorWeights; +struct ParticleSystem; struct PropertyRNA; @@ -244,6 +246,9 @@ struct DepsgraphRelationBuilder void build_compositor(Scene *scene); void build_gpencil(ID *owner, bGPdata *gpd); + void add_collision_relations(const OperationKey &key, Scene *scene, Object *ob, Group *group, int layer, bool dupli, const char *name); + void add_forcefield_relations(const OperationKey &key, Scene *scene, Object *ob, ParticleSystem *psys, EffectorWeights *eff, bool add_absorption, const char *name); + template <typename KeyType> OperationDepsNode *find_operation_node(const KeyType &key); diff --git a/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc index 9088e3bf403..70cd5f11a47 100644 --- a/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc +++ b/source/blender/depsgraph/intern/debug/deg_debug_graphviz.cc @@ -90,6 +90,7 @@ static const int deg_debug_node_type_color_map[][2] = { {DEPSNODE_TYPE_GEOMETRY, 8}, {DEPSNODE_TYPE_SEQUENCER, 9}, {DEPSNODE_TYPE_SHADING, 10}, + {DEPSNODE_TYPE_CACHE, 11}, {-1, 0} }; #endif @@ -401,6 +402,7 @@ static void deg_debug_graphviz_node(const DebugContext &ctx, case DEPSNODE_TYPE_EVAL_POSE: case DEPSNODE_TYPE_BONE: case DEPSNODE_TYPE_SHADING: + case DEPSNODE_TYPE_CACHE: case DEPSNODE_TYPE_EVAL_PARTICLES: { ComponentDepsNode *comp_node = (ComponentDepsNode *)node; diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index b1271c39851..7a3b19e82c6 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -33,13 +33,18 @@ #include "MEM_guardedalloc.h" extern "C" { +#include "DNA_cachefile_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_object_force.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" #include "BKE_main.h" +#include "BKE_collision.h" +#include "BKE_effect.h" +#include "BKE_modifier.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_debug.h" @@ -89,6 +94,7 @@ static DEG::eDepsNode_Type deg_build_object_component_type( case DEG_OB_COMP_BONE: return DEG::DEPSNODE_TYPE_BONE; case DEG_OB_COMP_EVAL_PARTICLES: return DEG::DEPSNODE_TYPE_EVAL_PARTICLES; case DEG_OB_COMP_SHADING: return DEG::DEPSNODE_TYPE_SHADING; + case DEG_OB_COMP_CACHE: return DEG::DEPSNODE_TYPE_CACHE; } return DEG::DEPSNODE_TYPE_UNDEFINED; } @@ -126,6 +132,20 @@ void DEG_add_object_relation(DepsNodeHandle *handle, description); } +void DEG_add_object_cache_relation(DepsNodeHandle *handle, + CacheFile *cache_file, + eDepsObjectComponentType component, + const char *description) +{ + DEG::eDepsNode_Type type = deg_build_object_component_type(component); + DEG::ComponentKey comp_key(&cache_file->id, type); + DEG::DepsNodeHandle *deg_handle = get_handle(handle); + deg_handle->builder->add_node_handle_relation(comp_key, + deg_handle, + DEG::DEPSREL_TYPE_CACHE, + description); +} + void DEG_add_bone_relation(DepsNodeHandle *handle, Object *ob, const char *bone_name, @@ -288,3 +308,52 @@ void DEG_scene_graph_free(Scene *scene) scene->depsgraph = NULL; } } + +void DEG_add_collision_relations(DepsNodeHandle *handle, Scene *scene, Object *ob, Group *group, int layer, unsigned int modifier_type, DEG_CollobjFilterFunction fn, bool dupli, const char *name) +{ + unsigned int numcollobj; + Object **collobjs = get_collisionobjects_ext(scene, ob, group, layer, &numcollobj, modifier_type, dupli); + + for (unsigned int i = 0; i < numcollobj; i++) { + Object *ob1 = collobjs[i]; + + if (!fn || fn(ob1, modifiers_findByType(ob1, (ModifierType)modifier_type))) { + DEG_add_object_relation(handle, ob1, DEG_OB_COMP_TRANSFORM, name); + DEG_add_object_relation(handle, ob1, DEG_OB_COMP_GEOMETRY, name); + } + } + + if (collobjs) + MEM_freeN(collobjs); +} + +void DEG_add_forcefield_relations(DepsNodeHandle *handle, Scene *scene, Object *ob, EffectorWeights *effector_weights, bool add_absorption, int skip_forcefield, const char *name) +{ + ListBase *effectors = pdInitEffectors(scene, ob, NULL, effector_weights, false); + + if (effectors) { + for (EffectorCache *eff = (EffectorCache*)effectors->first; eff; eff = eff->next) { + if (eff->ob != ob && eff->pd->forcefield != skip_forcefield) { + DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_TRANSFORM, name); + + if (eff->psys) { + DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_EVAL_PARTICLES, name); + + /* TODO: remove this when/if EVAL_PARTICLES is sufficient for up to date particles */ + DEG_add_object_relation(handle, eff->ob, DEG_OB_COMP_GEOMETRY, name); + } + + if (eff->pd->forcefield == PFIELD_SMOKEFLOW && eff->pd->f_source) { + DEG_add_object_relation(handle, eff->pd->f_source, DEG_OB_COMP_TRANSFORM, "Smoke Force Domain"); + DEG_add_object_relation(handle, eff->pd->f_source, DEG_OB_COMP_GEOMETRY, "Smoke Force Domain"); + } + + if (add_absorption && (eff->pd->flag & PFIELD_VISIBILITY)) { + DEG_add_collision_relations(handle, scene, ob, NULL, eff->ob->lay, eModifierType_Collision, NULL, true, "Force Absorption"); + } + } + } + } + + pdEndEffectors(&effectors); +} diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc index cac4eaae215..7f2f6a65f5e 100644 --- a/source/blender/depsgraph/intern/depsgraph_query.cc +++ b/source/blender/depsgraph/intern/depsgraph_query.cc @@ -33,6 +33,7 @@ #include "MEM_guardedalloc.h" extern "C" { +#include "BKE_idcode.h" #include "BKE_main.h" #include "DEG_depsgraph_query.h" @@ -42,7 +43,7 @@ extern "C" { bool DEG_id_type_tagged(Main *bmain, short idtype) { - return bmain->id_tag_update[((unsigned char *)&idtype)[0]] != 0; + return bmain->id_tag_update[BKE_idcode_to_index(idtype)] != 0; } short DEG_get_eval_flags_for_id(Depsgraph *graph, ID *id) diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index ea5afaab3f7..b7b62bd59f9 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -44,6 +44,7 @@ extern "C" { #include "BLI_task.h" +#include "BKE_idcode.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_node.h" @@ -259,10 +260,8 @@ void DEG_id_type_tag(Main *bmain, short idtype) DEG_id_type_tag(bmain, ID_WO); DEG_id_type_tag(bmain, ID_SCE); } - /* We tag based on first ID type character to avoid - * looping over all ID's in case there are no tags. - */ - bmain->id_tag_update[((unsigned char *)&idtype)[0]] = 1; + + bmain->id_tag_update[BKE_idcode_to_index(idtype)] = 1; } /* Recursively push updates out to all nodes dependent on this, @@ -373,10 +372,7 @@ void DEG_ids_check_recalc(Main *bmain, Scene *scene, bool time) ListBase *lb = lbarray[a]; ID *id = (ID *)lb->first; - /* We tag based on first ID type character to avoid - * looping over all ID's in case there are no tags. - */ - if (id && bmain->id_tag_update[(unsigned char)id->name[0]]) { + if (id && bmain->id_tag_update[BKE_idcode_to_index(GS(id->name))]) { updated = true; break; } @@ -401,10 +397,7 @@ void DEG_ids_clear_recalc(Main *bmain) ListBase *lb = lbarray[a]; ID *id = (ID *)lb->first; - /* We tag based on first ID type character to avoid - * looping over all ID's in case there are no tags. - */ - if (id && bmain->id_tag_update[(unsigned char)id->name[0]]) { + if (id && bmain->id_tag_update[BKE_idcode_to_index(GS(id->name))]) { for (; id; id = (ID *)id->next) { id->tag &= ~(LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA); diff --git a/source/blender/depsgraph/intern/depsgraph_types.h b/source/blender/depsgraph/intern/depsgraph_types.h index 7516ccbfdc2..effd34a0eb9 100644 --- a/source/blender/depsgraph/intern/depsgraph_types.h +++ b/source/blender/depsgraph/intern/depsgraph_types.h @@ -133,6 +133,8 @@ typedef enum eDepsNode_Type { DEPSNODE_TYPE_EVAL_PARTICLES = 23, /* Material Shading Component */ DEPSNODE_TYPE_SHADING = 24, + /* Cache Component */ + DEPSNODE_TYPE_CACHE = 25, } eDepsNode_Type; /* Identifiers for common operations (as an enum). */ @@ -330,6 +332,9 @@ typedef enum eDepsRelation_Type { /* relationship is used to trigger editor/screen updates */ DEPSREL_TYPE_UPDATE_UI, + + /* cache dependency */ + DEPSREL_TYPE_CACHE, } eDepsRelation_Type; } // namespace DEG diff --git a/source/blender/depsgraph/intern/nodes/deg_node_component.cc b/source/blender/depsgraph/intern/nodes/deg_node_component.cc index 5832c458896..01f33b6368b 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node_component.cc +++ b/source/blender/depsgraph/intern/nodes/deg_node_component.cc @@ -366,6 +366,11 @@ static DepsNodeFactoryImpl<ParticlesComponentDepsNode> DNTI_EVAL_PARTICLES; DEG_DEPSNODE_DEFINE(ShadingComponentDepsNode, DEPSNODE_TYPE_SHADING, "Shading Component"); static DepsNodeFactoryImpl<ShadingComponentDepsNode> DNTI_SHADING; +/* Cache Component Defines ============================ */ + +DEG_DEPSNODE_DEFINE(CacheComponentDepsNode, DEPSNODE_TYPE_CACHE, "Cache Component"); +static DepsNodeFactoryImpl<CacheComponentDepsNode> DNTI_CACHE; + /* Node Types Register =================================== */ @@ -383,6 +388,8 @@ void deg_register_component_depsnodes() deg_register_node_typeinfo(&DNTI_EVAL_PARTICLES); deg_register_node_typeinfo(&DNTI_SHADING); + + deg_register_node_typeinfo(&DNTI_CACHE); } } // namespace DEG diff --git a/source/blender/depsgraph/intern/nodes/deg_node_component.h b/source/blender/depsgraph/intern/nodes/deg_node_component.h index acccb1cdcd4..7dec8eaaa90 100644 --- a/source/blender/depsgraph/intern/nodes/deg_node_component.h +++ b/source/blender/depsgraph/intern/nodes/deg_node_component.h @@ -209,6 +209,10 @@ struct ShadingComponentDepsNode : public ComponentDepsNode { DEG_DEPSNODE_DECLARE; }; +struct CacheComponentDepsNode : public ComponentDepsNode { + DEG_DEPSNODE_DECLARE; +}; + void deg_register_component_depsnodes(); diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index ea2f7fc5588..3085e383909 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -40,6 +40,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" +#include "DNA_cachefile_types.h" #include "DNA_camera_types.h" #include "DNA_object_types.h" #include "DNA_particle_types.h" @@ -1578,6 +1579,88 @@ static bAnimChannelType ACF_DSTEX = /* Camera Expander ------------------------------------------- */ // TODO: just get this from RNA? +static int acf_dscachefile_icon(bAnimListElem *ale) +{ + UNUSED_VARS(ale); + return ICON_FILE; +} + +/* get the appropriate flag(s) for the setting when it is valid */ +static int acf_dscachefile_setting_flag(bAnimContext *ac, eAnimChannel_Settings setting, bool *neg) +{ + /* clear extra return data first */ + *neg = false; + + switch (setting) { + case ACHANNEL_SETTING_EXPAND: /* expanded */ + return CACHEFILE_DS_EXPAND; + + case ACHANNEL_SETTING_MUTE: /* mute (only in NLA) */ + return ADT_NLA_EVAL_OFF; + + case ACHANNEL_SETTING_VISIBLE: /* visible (only in Graph Editor) */ + *neg = true; + return ADT_CURVES_NOT_VISIBLE; + + case ACHANNEL_SETTING_SELECT: /* selected */ + return ADT_UI_SELECTED; + + default: /* unsupported */ + return 0; + } + + UNUSED_VARS(ac); +} + +/* get pointer to the setting */ +static void *acf_dscachefile_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings setting, short *type) +{ + CacheFile *cache_file = (CacheFile *)ale->data; + + /* clear extra return data first */ + *type = 0; + + switch (setting) { + case ACHANNEL_SETTING_EXPAND: /* expanded */ + return GET_ACF_FLAG_PTR(cache_file->flag, type); + + case ACHANNEL_SETTING_SELECT: /* selected */ + case ACHANNEL_SETTING_MUTE: /* muted (for NLA only) */ + case ACHANNEL_SETTING_VISIBLE: /* visible (for Graph Editor only) */ + if (cache_file->adt) { + return GET_ACF_FLAG_PTR(cache_file->adt->flag, type); + } + + return NULL; + + default: /* unsupported */ + return NULL; + } +} + +/* CacheFile expander type define. */ +static bAnimChannelType ACF_DSCACHEFILE = +{ + "Cache File Expander", /* type name */ + ACHANNEL_ROLE_EXPANDER, /* role */ + + acf_generic_dataexpand_color, /* backdrop color */ + acf_generic_dataexpand_backdrop, /* backdrop */ + acf_generic_indention_1, /* indent level */ + acf_generic_basic_offset, /* offset */ + + acf_generic_idblock_name, /* name */ + acf_generic_idfill_name_prop, /* name prop */ + acf_dscachefile_icon, /* icon */ + + acf_generic_dataexpand_setting_valid, /* has setting */ + acf_dscachefile_setting_flag, /* flag for setting */ + acf_dscachefile_setting_ptr /* pointer for setting */ +}; + +/* Camera Expander ------------------------------------------- */ + +// TODO: just get this from RNA? static int acf_dscam_icon(bAnimListElem *UNUSED(ale)) { return ICON_CAMERA_DATA; @@ -3388,6 +3471,7 @@ static void ANIM_init_channel_typeinfo_data(void) animchannelTypeInfo[type++] = &ACF_DSMAT; /* Material Channel */ animchannelTypeInfo[type++] = &ACF_DSLAM; /* Lamp Channel */ animchannelTypeInfo[type++] = &ACF_DSCAM; /* Camera Channel */ + animchannelTypeInfo[type++] = &ACF_DSCACHEFILE; /* CacheFile Channel */ animchannelTypeInfo[type++] = &ACF_DSCUR; /* Curve Channel */ animchannelTypeInfo[type++] = &ACF_DSSKEY; /* ShapeKey Channel */ animchannelTypeInfo[type++] = &ACF_DSWOR; /* World Channel */ @@ -4208,6 +4292,8 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle offset += ICON_WIDTH; } else if (ale->type == ANIMTYPE_GPLAYER) { +#if 0 + /* XXX: Maybe need a better design */ /* color swatch for layer color */ bGPDlayer *gpl = (bGPDlayer *)ale->data; PointerRNA ptr; @@ -4216,7 +4302,6 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle RNA_pointer_create(ale->id, &RNA_GPencilLayer, ale->data, &ptr); UI_block_align_begin(block); - UI_block_emboss_set(block, RNA_boolean_get(&ptr, "is_stroke_visible") ? UI_EMBOSS : UI_EMBOSS_NONE); uiDefButR(block, UI_BTYPE_COLOR, 1, "", offset, yminc, w, ICON_WIDTH, &ptr, "color", -1, @@ -4226,11 +4311,11 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle uiDefButR(block, UI_BTYPE_COLOR, 1, "", offset + w, yminc, w, ICON_WIDTH, &ptr, "fill_color", -1, 0, 0, 0, 0, gpl->info); - UI_block_emboss_set(block, UI_EMBOSS_NONE); UI_block_align_end(block); - + offset += ICON_WIDTH; +#endif } } diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index af9b0a176f5..cb65a9aecad 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -120,6 +120,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: + case ANIMTYPE_DSCACHEFILE: case ANIMTYPE_DSCUR: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: @@ -175,6 +176,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: + case ANIMTYPE_DSCACHEFILE: case ANIMTYPE_DSCUR: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: @@ -275,6 +277,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types d case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: + case ANIMTYPE_DSCACHEFILE: case ANIMTYPE_DSCUR: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: @@ -370,6 +373,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types d case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: + case ANIMTYPE_DSCACHEFILE: case ANIMTYPE_DSCUR: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: @@ -1686,7 +1690,7 @@ static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op)) bGPDlayer *gpl = (bGPDlayer *)ale->data; /* try to delete the layer's data and the layer itself */ - free_gpencil_frames(gpl); + BKE_gpencil_free_frames(gpl); BLI_freelinkN(&gpd->layers, gpl); break; } @@ -2716,6 +2720,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: + case ANIMTYPE_DSCACHEFILE: case ANIMTYPE_DSCUR: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 88d96c531e0..5cd305f69f5 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -53,6 +53,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_camera_types.h" +#include "DNA_cachefile_types.h" #include "DNA_lamp_types.h" #include "DNA_lattice_types.h" #include "DNA_linestyle_types.h" @@ -199,6 +200,16 @@ static bool actedit_get_context(bAnimContext *ac, SpaceAction *saction) ac->mode = saction->mode; return true; + + case SACTCONT_CACHEFILE: /* Cache File */ /* XXX review how this mode is handled... */ + /* update scene-pointer (no need to check for pinning yet, as not implemented) */ + saction->ads.source = (ID *)ac->scene; + + ac->datatype = ANIMCONT_CHANNEL; + ac->data = &saction->ads; + + ac->mode = saction->mode; + return true; case SACTCONT_MASK: /* Mask */ /* XXX review how this mode is handled... */ { @@ -660,6 +671,19 @@ static bAnimListElem *make_new_animlistelem(void *data, short datatype, ID *owne ale->adt = BKE_animdata_from_id(data); break; } + case ANIMTYPE_DSCACHEFILE: + { + CacheFile *cache_file = (CacheFile *)data; + AnimData *adt = cache_file->adt; + + ale->flag = FILTER_CACHEFILE_OBJD(cache_file); + + ale->key_data = (adt) ? adt->action : NULL; + ale->datatype = ALE_ACT; + + ale->adt = BKE_animdata_from_id(data); + break; + } case ANIMTYPE_DSCUR: { Curve *cu = (Curve *)data; @@ -1751,6 +1775,42 @@ static size_t animdata_filter_ds_gpencil(bAnimContext *ac, ListBase *anim_data, return items; } +/* Helper for Cache File data integrated with main DopeSheet */ +static size_t animdata_filter_ds_cachefile(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, CacheFile *cache_file, int filter_mode) +{ + ListBase tmp_data = {NULL, NULL}; + size_t tmp_items = 0; + size_t items = 0; + + /* add relevant animation channels for Cache File */ + BEGIN_ANIMFILTER_SUBCHANNELS(FILTER_CACHEFILE_OBJD(cache_file)) + { + /* add animation channels */ + tmp_items += animfilter_block_data(ac, &tmp_data, ads, &cache_file->id, filter_mode); + } + END_ANIMFILTER_SUBCHANNELS; + + /* did we find anything? */ + if (tmp_items) { + /* include data-expand widget first */ + if (filter_mode & ANIMFILTER_LIST_CHANNELS) { + /* check if filtering by active status */ + // XXX: active check here needs checking + if (ANIMCHANNEL_ACTIVEOK(cache_file)) { + ANIMCHANNEL_NEW_CHANNEL(cache_file, ANIMTYPE_DSCACHEFILE, cache_file); + } + } + + /* now add the list of collected channels */ + BLI_movelisttolist(anim_data, &tmp_data); + BLI_assert(BLI_listbase_is_empty(&tmp_data)); + items += tmp_items; + } + + /* return the number of items added to the list */ + return items; +} + /* Helper for Mask Editing - mask layers */ static size_t animdata_filter_mask_data(ListBase *anim_data, Mask *mask, const int filter_mode) { @@ -2839,6 +2899,12 @@ static size_t animdata_filter_dopesheet(bAnimContext *ac, ListBase *anim_data, b filter_mode |= ANIMFILTER_SELEDIT; } + /* Cache files level animations (frame duration and such). */ + CacheFile *cache_file = G.main->cachefiles.first; + for (; cache_file; cache_file = cache_file->id.next) { + items += animdata_filter_ds_cachefile(ac, anim_data, ads, cache_file, filter_mode); + } + /* scene-linked animation - e.g. world, compositing nodes, scene anim (including sequencer currently) */ items += animdata_filter_dopesheet_scene(ac, anim_data, ads, scene, filter_mode); @@ -2950,7 +3016,11 @@ static size_t animdata_filter_animchan(bAnimContext *ac, ListBase *anim_data, bD case ANIMTYPE_OBJECT: items += animdata_filter_dopesheet_ob(ac, anim_data, ads, channel->data, filter_mode); break; - + + case ANIMTYPE_DSCACHEFILE: + items += animdata_filter_ds_cachefile(ac, anim_data, ads, channel->data, filter_mode); + break; + case ANIMTYPE_ANIMDATA: items += animfilter_block_data(ac, anim_data, ads, channel->id, filter_mode); break; diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 6e776953356..5f675e690b9 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -44,6 +44,7 @@ #include "BLI_utildefines.h" #include "DNA_anim_types.h" +#include "DNA_cachefile_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_gpencil_types.h" @@ -965,6 +966,37 @@ void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, DLRBT_Tree *bl ANIM_animdata_freelist(&anim_data); } +void cachefile_to_keylist(bDopeSheet *ads, CacheFile *cache_file, DLRBT_Tree *keys, DLRBT_Tree *blocks) +{ + if (cache_file == NULL) { + return; + } + + /* create a dummy wrapper data to work with */ + bAnimListElem dummychan = {NULL}; + dummychan.type = ANIMTYPE_DSCACHEFILE; + dummychan.data = cache_file; + dummychan.id = &cache_file->id; + dummychan.adt = cache_file->adt; + + bAnimContext ac = {NULL}; + ac.ads = ads; + ac.data = &dummychan; + ac.datatype = ANIMCONT_CHANNEL; + + /* get F-Curves to take keyframes from */ + ListBase anim_data = { NULL, NULL }; + int filter = ANIMFILTER_DATA_VISIBLE; // curves only + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + /* loop through each F-Curve, grabbing the keyframes */ + for (bAnimListElem *ale = anim_data.first; ale; ale = ale->next) { + fcurve_to_keylist(ale->adt, ale->data, keys, blocks); + } + + ANIM_animdata_freelist(&anim_data); +} + void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, DLRBT_Tree *blocks) { BezTriple *bezt; diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 79a2c494239..4ef76f5ee25 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2008, Blender Foundation * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -52,6 +52,7 @@ #include "DNA_space_types.h" #include "DNA_view3d_types.h" #include "DNA_userdef_types.h" +#include "DNA_object_types.h" #include "BKE_context.h" #include "BKE_global.h" @@ -94,9 +95,32 @@ typedef enum eDrawStrokeFlags { #define GP_DRAWTHICKNESS_SPECIAL 3 /* ----- Tool Buffer Drawing ------ */ +/* helper function to set color of buffer point */ +static void gp_set_tpoint_color(tGPspoint *pt, float ink[4]) +{ + float alpha = ink[3] * pt->strength; + CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); + glColor4f(ink[0], ink[1], ink[2], alpha); +} + +/* helper function to set color of point */ +static void gp_set_point_color(bGPDspoint *pt, float ink[4]) +{ + float alpha = ink[3] * pt->strength; + CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); + glColor4f(ink[0], ink[1], ink[2], alpha); +} + +/* helper function to set color and point */ +static void gp_set_color_and_tpoint(tGPspoint *pt, float ink[4]) +{ + gp_set_tpoint_color(pt, ink); + glVertex2iv(&pt->x); +} /* draw stroke defined in buffer (simple ogl lines/points for now, as dotted lines) */ -static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickness, short dflag, short sflag) +static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickness, + short dflag, short sflag, float ink[4]) { tGPspoint *pt; int i; @@ -113,7 +137,8 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn /* if drawing a single point, draw it larger */ glPointSize((float)(thickness + 2) * points->pressure); glBegin(GL_POINTS); - glVertex2iv(&points->x); + + gp_set_color_and_tpoint(points, ink); glEnd(); } else if (sflag & GP_STROKE_ERASER) { @@ -138,15 +163,18 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn glBegin(GL_LINE_STRIP); /* need to roll-back one point to ensure that there are no gaps in the stroke */ - if (i != 0) glVertex2iv(&(pt - 1)->x); + if (i != 0) { + gp_set_color_and_tpoint((pt - 1), ink); + } /* now the point we want... */ - glVertex2iv(&pt->x); + gp_set_color_and_tpoint(pt, ink); oldpressure = pt->pressure; } - else - glVertex2iv(&pt->x); + else { + gp_set_color_and_tpoint(pt, ink); + } } glEnd(); @@ -155,37 +183,35 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn } /* --------- 2D Stroke Drawing Helpers --------- */ - -/* helper function to calculate x-y drawing coordinates for 2D points */ -static void gp_calc_2d_stroke_xy(bGPDspoint *pt, short sflag, int offsx, int offsy, int winx, int winy, float r_co[2]) +/* change in parameter list */ +static void gp_calc_2d_stroke_fxy(float pt[3], short sflag, int offsx, int offsy, int winx, int winy, float r_co[2]) { if (sflag & GP_STROKE_2DSPACE) { - r_co[0] = pt->x; - r_co[1] = pt->y; + r_co[0] = pt[0]; + r_co[1] = pt[1]; } else if (sflag & GP_STROKE_2DIMAGE) { - const float x = (float)((pt->x * winx) + offsx); - const float y = (float)((pt->y * winy) + offsy); - + const float x = (float)((pt[0] * winx) + offsx); + const float y = (float)((pt[1] * winy) + offsy); + r_co[0] = x; r_co[1] = y; } else { - const float x = (float)(pt->x / 100 * winx) + offsx; - const float y = (float)(pt->y / 100 * winy) + offsy; - + const float x = (float)(pt[0] / 100 * winx) + offsx; + const float y = (float)(pt[1] / 100 * winy) + offsy; + r_co[0] = x; r_co[1] = y; } } - /* ----------- Volumetric Strokes --------------- */ /* draw a 2D buffer stroke in "volumetric" style * NOTE: the stroke buffer doesn't have any coordinate offsets/transforms */ static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, short thickness, - short dflag, short UNUSED(sflag)) + short dflag, short UNUSED(sflag), float ink[4]) { GLUquadricObj *qobj = gluNewQuadric(); float modelview[4][4]; @@ -216,6 +242,7 @@ static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, s glLoadMatrixf((float *)modelview); /* draw the disk using the current state... */ + gp_set_tpoint_color(pt, ink); gluDisk(qobj, 0.0, pt->pressure * thickness, 32, 1); @@ -229,7 +256,8 @@ static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, s /* draw a 2D strokes in "volumetric" style */ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, short thickness, short dflag, short sflag, - int offsx, int offsy, int winx, int winy) + int offsx, int offsy, int winx, int winy, + float diff_mat[4][4], float ink[4]) { GLUquadricObj *qobj = gluNewQuadric(); float modelview[4][4]; @@ -238,7 +266,7 @@ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, shor bGPDspoint *pt; int i; - + float fpt[3]; /* HACK: We need a scale factor for the drawing in the image editor, * which seems to use 1 unit as it's maximum size, whereas everything @@ -256,10 +284,14 @@ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, shor glPushMatrix(); for (i = 0, pt = points; i < totpoints; i++, pt++) { + /* color of point */ + gp_set_point_color(pt, ink); + /* set the transformed position */ float co[2]; - gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); translate_m4(modelview, co[0], co[1], 0.0f); glLoadMatrixf((float *)modelview); @@ -276,8 +308,9 @@ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, shor } /* draw a 3D stroke in "volumetric" style */ -static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, short thickness, - short UNUSED(dflag), short UNUSED(sflag)) +static void gp_draw_stroke_volumetric_3d( + bGPDspoint *points, int totpoints, short thickness, + short UNUSED(dflag), short UNUSED(sflag), float diff_mat[4][4], float ink[4]) { GLUquadricObj *qobj = gluNewQuadric(); @@ -286,7 +319,7 @@ static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, shor bGPDspoint *pt; int i; - + float fpt[3]; /* Get the basic modelview matrix we use for performing calculations */ glGetFloatv(GL_MODELVIEW_MATRIX, (float *)base_modelview); @@ -305,8 +338,13 @@ static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, shor glPushMatrix(); for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { + /* color of point */ + gp_set_point_color(pt, ink); + + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* apply translation to base_modelview, so that the translated point is put in the right place */ - translate_m4(base_modelview, pt->x, pt->y, pt->z); + translate_m4(base_modelview, fpt[0], fpt[1], fpt[2]); /* copy the translation component to the billboard matrix we're going to use, * then reset the base matrix to the original values so that we can do the same @@ -378,9 +416,9 @@ static void gp_stroke_2d_flat(bGPDspoint *points, int totpoints, float(*points2d static void gp_triangulate_stroke_fill(bGPDstroke *gps) { BLI_assert(gps->totpoints >= 3); - gps->tot_triangles = gps->totpoints - 2; - + /* allocate memory for temporary areas */ + gps->tot_triangles = gps->totpoints - 2; unsigned int (*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles, "GP Stroke temp triangulation"); float (*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, "GP Stroke temp 2d points"); @@ -390,6 +428,8 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps) gp_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction); BLI_polyfill_calc((const float(*)[2])points2d, (unsigned int)gps->totpoints, direction, (unsigned int(*)[3])tmp_triangles); + /* Number of triangles */ + gps->tot_triangles = gps->totpoints - 2; /* save triangulation data in stroke cache */ if (gps->tot_triangles > 0) { if (gps->triangles == NULL) { @@ -399,9 +439,7 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps) gps->triangles = MEM_recallocN(gps->triangles, sizeof(*gps->triangles) * gps->tot_triangles); } - int i; - - for (i = 0; i < gps->tot_triangles; i++) { + for (int i = 0; i < gps->tot_triangles; i++) { bGPDtriangle *stroke_triangle = &gps->triangles[i]; stroke_triangle->v1 = tmp_triangles[i][0]; stroke_triangle->v2 = tmp_triangles[i][1]; @@ -428,21 +466,27 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps) /* draw fills for shapes */ -static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short dflag, int offsx, int offsy, int winx, int winy) +static void gp_draw_stroke_fill( + bGPdata *gpd, bGPDstroke *gps, + int offsx, int offsy, int winx, int winy, float diff_mat[4][4]) { + bGPDpalettecolor *palcolor; + int i; + float fpt[3]; + BLI_assert(gps->totpoints >= 3); - + + palcolor = ED_gpencil_stroke_getcolor(gpd, gps); + /* Triangulation fill if high quality flag is enabled */ - if (dflag & GP_DRAWDATA_HQ_FILL) { + if (palcolor->flag & PC_COLOR_HQ_FILL) { bGPDtriangle *stroke_triangle; bGPDspoint *pt; - int i; - + /* Calculate triangles cache for filling area (must be done only after changes) */ if ((gps->flag & GP_STROKE_RECALC_CACHES) || (gps->tot_triangles == 0) || (gps->triangles == NULL)) { gp_triangulate_stroke_fill(gps); } - /* Draw all triangles for filling the polygon (cache must be calculated before) */ BLI_assert(gps->tot_triangles >= 1); glBegin(GL_TRIANGLES); @@ -450,32 +494,33 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short if (gps->flag & GP_STROKE_3DSPACE) { /* vertex 1 */ pt = &gps->points[stroke_triangle->v1]; - glVertex3fv(&pt->x); - + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); /* vertex 2 */ pt = &gps->points[stroke_triangle->v2]; - glVertex3fv(&pt->x); - + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); /* vertex 3 */ pt = &gps->points[stroke_triangle->v3]; - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); } else { float co[2]; - /* vertex 1 */ pt = &gps->points[stroke_triangle->v1]; - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); - /* vertex 2 */ pt = &gps->points[stroke_triangle->v2]; - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); - /* vertex 3 */ pt = &gps->points[stroke_triangle->v3]; - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); } } @@ -483,30 +528,31 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short } else { /* As an initial implementation, we use the OpenGL filled polygon drawing - * here since it's the easiest option to implement for this case. It does - * come with limitations (notably for concave shapes), though it works well - * enough for many simple situations. - * - * We keep this legacy implementation around despite now having the high quality - * fills, as this is necessary for keeping everything working nicely for files - * created using old versions of Blender which may have depended on the artifacts - * the old fills created. - */ + * here since it's the easiest option to implement for this case. It does + * come with limitations (notably for concave shapes), though it shouldn't + * be much of an issue in most cases. + * + * We keep this legacy implementation around despite now having the high quality + * fills, as this is necessary for keeping everything working nicely for files + * created using old versions of Blender which may have depended on the artifacts + * the old fills created. + */ bGPDspoint *pt; - int i; - + glBegin(GL_POLYGON); for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if (gps->flag & GP_STROKE_3DSPACE) { - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); } else { float co[2]; - - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); } } + glEnd(); } } @@ -514,23 +560,33 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short /* ----- Existing Strokes Drawing (3D and Point) ------ */ /* draw a given stroke - just a single dot (only one point) */ -static void gp_draw_stroke_point(bGPDspoint *points, short thickness, short dflag, short sflag, - int offsx, int offsy, int winx, int winy) +static void gp_draw_stroke_point( + bGPDspoint *points, short thickness, short dflag, short sflag, + int offsx, int offsy, int winx, int winy, float diff_mat[4][4], float ink[4]) { + float fpt[3]; + bGPDspoint *pt = &points[0]; + + /* color of point */ + gp_set_point_color(pt, ink); + /* set point thickness (since there's only one of these) */ glPointSize((float)(thickness + 2) * points->pressure); + /* get final position using parent matrix */ + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* draw point */ if (sflag & GP_STROKE_3DSPACE) { glBegin(GL_POINTS); - glVertex3fv(&points->x); + glVertex3fv(fpt); glEnd(); } else { float co[2]; /* get coordinates of point */ - gp_calc_2d_stroke_xy(points, sflag, offsx, offsy, winx, winy, co); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); /* if thickness is less than GP_DRAWTHICKNESS_SPECIAL, simple dot looks ok * - also mandatory in if Image Editor 'image-based' dot @@ -559,16 +615,21 @@ static void gp_draw_stroke_point(bGPDspoint *points, short thickness, short dfla } /* draw a given stroke in 3d (i.e. in 3d-space), using simple ogl lines */ -static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness, bool debug, short UNUSED(sflag)) +static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness, bool debug, + short UNUSED(sflag), float diff_mat[4][4], float ink[4], bool cyclic) { - bGPDspoint *pt; + bGPDspoint *pt, *pt2; float curpressure = points[0].pressure; int i; - + float fpt[3]; + float cyclic_fpt[3]; + /* draw stroke curve */ glLineWidth(max_ff(curpressure * thickness, 1.0f)); glBegin(GL_LINE_STRIP); for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { + gp_set_point_color(pt, ink); + /* if there was a significant pressure change, stop the curve, change the thickness of the stroke, * and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP) * Note: we want more visible levels of pressures when thickness is bigger. @@ -580,15 +641,29 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness glBegin(GL_LINE_STRIP); /* need to roll-back one point to ensure that there are no gaps in the stroke */ - if (i != 0) glVertex3fv(&(pt - 1)->x); + if (i != 0) { + pt2 = pt - 1; + mul_v3_m4v3(fpt, diff_mat, &pt2->x); + glVertex3fv(fpt); + } /* now the point we want... */ - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); } else { - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); + } + /* saves first point to use in cyclic */ + if (i == 0) { + copy_v3_v3(cyclic_fpt, fpt); } } + /* if cyclic draw line to first point */ + if (cyclic) { + glVertex3fv(cyclic_fpt); + } glEnd(); /* draw debug points of curve on top? */ @@ -597,9 +672,12 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness glPointSize((float)(thickness + 2)); glBegin(GL_POINTS); - for (i = 0, pt = points; i < totpoints && pt; i++, pt++) - glVertex3fv(&pt->x); + for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); + } glEnd(); + } } @@ -607,7 +685,7 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness /* draw a given stroke in 2d */ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag, - bool debug, int offsx, int offsy, int winx, int winy) + bool debug, int offsx, int offsy, int winx, int winy, float diff_mat[4][4], float ink[4]) { /* otherwise thickness is twice that of the 3D view */ float thickness = (float)thickness_s * 0.5f; @@ -625,6 +703,7 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness bGPDspoint *pt1, *pt2; float pm[2]; int i; + float fpt[3]; glShadeModel(GL_FLAT); glBegin(GL_QUADS); @@ -635,10 +714,13 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness float m1[2], m2[2]; /* gradient and normal */ float mt[2], sc[2]; /* gradient for thickness, point for end-cap */ float pthick; /* thickness at segment point */ - + /* get x and y coordinates from points */ - gp_calc_2d_stroke_xy(pt1, sflag, offsx, offsy, winx, winy, s0); - gp_calc_2d_stroke_xy(pt2, sflag, offsx, offsy, winx, winy, s1); + mul_v3_m4v3(fpt, diff_mat, &pt1->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s0); + + mul_v3_m4v3(fpt, diff_mat, &pt2->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s1); /* calculate gradient and normal - 'angle'=(ny/nx) */ m1[1] = s1[1] - s0[1]; @@ -650,6 +732,9 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness /* always use pressure from first point here */ pthick = (pt1->pressure * thickness * scalefac); + /* color of point */ + gp_set_point_color(pt1, ink); + /* if the first segment, start of segment is segment's normal */ if (i == 0) { /* draw start cap first @@ -725,6 +810,9 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness /* for once, we use second point's pressure (otherwise it won't be drawn) */ pthick = (pt2->pressure * thickness * scalefac); + /* color of point */ + gp_set_point_color(pt2, ink); + /* calculate points for end of segment */ mt[0] = m2[0] * pthick; mt[1] = m2[1] * pthick; @@ -770,14 +858,15 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness if (debug) { bGPDspoint *pt; int i; - + float fpt[3]; + glPointSize((float)(thickness_s + 2)); glBegin(GL_POINTS); for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { float co[2]; - - gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); glVertex2fv(co); } glEnd(); @@ -818,26 +907,45 @@ static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag) } /* draw a set of strokes */ -static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int winy, int dflag, - bool debug, short lthick, const float color[4], const float fill_color[4]) +static void gp_draw_strokes( + bGPdata *gpd, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, int dflag, + bool debug, short lthick, const float opacity, const float tintcolor[4], + const bool onion, const bool custonion, float diff_mat[4][4]) { bGPDstroke *gps; - + float tcolor[4]; + float tfill[4]; + short sthickness; + float ink[4]; + for (gps = gpf->strokes.first; gps; gps = gps->next) { /* check if stroke can be drawn */ - if (gp_can_draw_stroke(gps, dflag) == false) + if (gp_can_draw_stroke(gps, dflag) == false) { continue; - + } + /* check if the color is visible */ + bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps); + if ((palcolor == NULL) || + (palcolor->flag & PC_COLOR_HIDE) || + /* if onion and ghost flag do not draw*/ + (onion && (palcolor->flag & PC_COLOR_ONIONSKIN))) + { + continue; + } + + /* calculate thickness */ + sthickness = gps->thickness + lthick; + /* check which stroke-drawer to use */ if (dflag & GP_DRAWDATA_ONLY3D) { const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY); int mask_orig = 0; - + if (no_xray) { glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig); glDepthMask(0); glEnable(GL_DEPTH_TEST); - + /* first arg is normally rv3d->dist, but this isn't * available here and seems to work quite well without */ bglPolygonOffset(1.0f, 1.0f); @@ -846,34 +954,65 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int glPolygonOffset(-1.0f, -1.0f); #endif } - + /* 3D Fill */ - if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { - glColor4fv(fill_color); - gp_draw_stroke_fill(gps, lthick, dflag, offsx, offsy, winx, winy); + //if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { + if (gps->totpoints >= 3) { + /* set color using palette, tint color and opacity */ + interp_v3_v3v3(tfill, palcolor->fill, tintcolor, tintcolor[3]); + tfill[3] = palcolor->fill[3] * opacity; + if (tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) { + if (!onion) { + glColor4fv(tfill); + } + else { + if (custonion) { + glColor4fv(tintcolor); + } + else { + ARRAY_SET_ITEMS(tfill, UNPACK3(palcolor->fill), tintcolor[3]); + glColor4fv(tfill); + } + } + gp_draw_stroke_fill(gpd, gps, offsx, offsy, winx, winy, diff_mat); + } } - + /* 3D Stroke */ - glColor4fv(color); - - if (dflag & GP_DRAWDATA_VOLUMETRIC) { + /* set color using palette, tint color and opacity */ + if (!onion) { + interp_v3_v3v3(tcolor, palcolor->color, tintcolor, tintcolor[3]); + tcolor[3] = palcolor->color[3] * opacity; + copy_v4_v4(ink, tcolor); + } + else { + if (custonion) { + copy_v4_v4(ink, tintcolor); + } + else { + ARRAY_SET_ITEMS(tcolor, palcolor->color[0], palcolor->color[1], palcolor->color[2], opacity); + copy_v4_v4(ink, tcolor); + } + } + if (palcolor->flag & PC_COLOR_VOLUMETRIC) { /* volumetric stroke drawing */ - gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, lthick, dflag, gps->flag); + gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, diff_mat, ink); } else { /* 3D Lines - OpenGL primitives-based */ if (gps->totpoints == 1) { - gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy); + gp_draw_stroke_point(gps->points, sthickness, dflag, gps->flag, offsx, offsy, winx, winy, + diff_mat, ink); } else { - gp_draw_stroke_3d(gps->points, gps->totpoints, lthick, debug, gps->flag); + gp_draw_stroke_3d(gps->points, gps->totpoints, sthickness, debug, gps->flag, + diff_mat, ink, gps->flag & GP_STROKE_CYCLIC); } } - if (no_xray) { glDepthMask(mask_orig); glDisable(GL_DEPTH_TEST); - + bglPolygonOffset(0.0, 0.0); #if 0 glDisable(GL_POLYGON_OFFSET_LINE); @@ -883,25 +1022,58 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int } else { /* 2D - Fill */ - if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) { - glColor4fv(fill_color); - gp_draw_stroke_fill(gps, lthick, dflag, offsx, offsy, winx, winy); + if (gps->totpoints >= 3) { + /* set color using palette, tint color and opacity */ + interp_v3_v3v3(tfill, palcolor->fill, tintcolor, tintcolor[3]); + tfill[3] = palcolor->fill[3] * opacity; + if (tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) { + if (!onion) { + glColor4fv(tfill); + } + else { + if (custonion) { + glColor4fv(tintcolor); + } + else { + ARRAY_SET_ITEMS(tfill, palcolor->fill[0], palcolor->fill[1], palcolor->fill[2], + tintcolor[3]); + glColor4fv(tfill); + } + } + gp_draw_stroke_fill(gpd, gps, offsx, offsy, winx, winy, diff_mat); + } } - + /* 2D Strokes... */ - glColor4fv(color); - - if (dflag & GP_DRAWDATA_VOLUMETRIC) { + /* set color using palette, tint color and opacity */ + if (!onion) { + interp_v3_v3v3(tcolor, palcolor->color, tintcolor, tintcolor[3]); + tcolor[3] = palcolor->color[3] * opacity; + copy_v4_v4(ink, tcolor); + } + else { + if (custonion) { + copy_v4_v4(ink, tintcolor); + } + else { + ARRAY_SET_ITEMS(tcolor, palcolor->color[0], palcolor->color[1], palcolor->color[2], opacity); + copy_v4_v4(ink, tcolor); + } + } + if (palcolor->flag & PC_COLOR_VOLUMETRIC) { /* blob/disk-based "volumetric" drawing */ - gp_draw_stroke_volumetric_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, offsx, offsy, winx, winy); + gp_draw_stroke_volumetric_2d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, + offsx, offsy, winx, winy, diff_mat, ink); } else { /* normal 2D strokes */ if (gps->totpoints == 1) { - gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy); + gp_draw_stroke_point(gps->points, sthickness, dflag, gps->flag, offsx, offsy, winx, winy, + diff_mat, ink); } else { - gp_draw_stroke_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, offsx, offsy, winx, winy); + gp_draw_stroke_2d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, debug, + offsx, offsy, winx, winy, diff_mat, ink); } } } @@ -909,13 +1081,19 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int } /* Draw selected verts for strokes being edited */ -static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, int winy, short dflag, const float tcolor[3]) +static void gp_draw_strokes_edit( + bGPdata *gpd, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, short dflag, + short lflag, float diff_mat[4][4], float alpha) { bGPDstroke *gps; + /* if alpha 0 do not draw */ + if (alpha == 0.0f) + return; + const bool no_xray = (dflag & GP_DRAWDATA_NO_XRAY) != 0; int mask_orig = 0; - + /* set up depth masks... */ if (dflag & GP_DRAWDATA_ONLY3D) { if (no_xray) { @@ -939,7 +1117,8 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, bGPDspoint *pt; float vsize, bsize; int i; - + float fpt[3]; + /* check if stroke can be drawn */ if (gp_can_draw_stroke(gps, dflag) == false) continue; @@ -951,6 +1130,19 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, if ((gps->flag & GP_STROKE_SELECT) == 0) continue; + /* verify palette color lock */ + { + bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps); + if (palcolor != NULL) { + if (palcolor->flag & PC_COLOR_HIDE) { + continue; + } + if (((lflag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED)) { + continue; + } + } + } + /* Get size of verts: * - The selected state needs to be larger than the unselected state so that * they stand out more. @@ -966,25 +1158,23 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, } /* First Pass: Draw all the verts (i.e. these become the unselected state) */ - if (tcolor != NULL) { - /* for now, we assume that the base color of the points is not too close to the real color */ - glColor3fv(tcolor); - } - else { - /* this doesn't work well with the default theme and black strokes... */ - UI_ThemeColor(TH_GP_VERTEX); - } + /* for now, we assume that the base color of the points is not too close to the real color */ + /* set color using palette */ + bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps); + glColor3fv(palcolor->color); + glPointSize(bsize); glBegin(GL_POINTS); for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) { if (gps->flag & GP_STROKE_3DSPACE) { - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); } else { float co[2]; - - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); } } @@ -992,24 +1182,54 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, /* Second Pass: Draw only verts which are selected */ - UI_ThemeColor(TH_GP_VERTEX_SELECT); + float curColor[4]; + UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, curColor); + glColor4f(curColor[0], curColor[1], curColor[2], alpha); + glPointSize(vsize); glBegin(GL_POINTS); for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) { if (pt->flag & GP_SPOINT_SELECT) { if (gps->flag & GP_STROKE_3DSPACE) { - glVertex3fv(&pt->x); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + glVertex3fv(fpt); } else { float co[2]; - gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); glVertex2fv(co); } } } glEnd(); + + /* Draw start and end point if enabled stroke direction hint */ + if ((gpd->flag & GP_DATA_SHOW_DIRECTION) && (gps->totpoints > 1)) { + bGPDspoint *p; + + glPointSize(vsize + 4); + glBegin(GL_POINTS); + + /* start point in green bigger */ + glColor3f(0.0f, 1.0f, 0.0f); + p = &gps->points[0]; + mul_v3_m4v3(fpt, diff_mat, &p->x); + glVertex3fv(fpt); + glEnd(); + + /* end point in red smaller */ + glPointSize(vsize + 1); + glBegin(GL_POINTS); + + glColor3f(1.0f, 0.0f, 0.0f); + p = &gps->points[gps->totpoints - 1]; + mul_v3_m4v3(fpt, diff_mat, &p->x); + glVertex3fv(fpt); + glEnd(); + } } @@ -1031,18 +1251,20 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, /* ----- General Drawing ------ */ /* draw onion-skinning for a layer */ -static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, - int UNUSED(cfra), int dflag, bool debug, short lthick) +static void gp_draw_onionskins( + bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, + int UNUSED(cfra), int dflag, bool debug, float diff_mat[4][4]) { - const float alpha = gpl->color[3]; + const float default_color[3] = {UNPACK3(U.gpencil_new_layer_col)}; + const float alpha = 1.0f; float color[4]; - + /* 1) Draw Previous Frames First */ if (gpl->flag & GP_LAYER_GHOST_PREVCOL) { copy_v3_v3(color, gpl->gcolor_prev); } else { - copy_v3_v3(color, gpl->color); + copy_v3_v3(color, default_color); } if (gpl->gstep > 0) { @@ -1056,7 +1278,8 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of /* alpha decreases with distance from curframe index */ fac = 1.0f - ((float)(gpf->framenum - gf->framenum) / (float)(gpl->gstep + 1)); color[3] = alpha * fac * 0.66f; - gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, color, color); + gp_draw_strokes(gpd, gf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, + true, gpl->flag & GP_LAYER_GHOST_PREVCOL, diff_mat); } else break; @@ -1066,7 +1289,8 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of /* draw the strokes for the ghost frames (at half of the alpha set by user) */ if (gpf->prev) { color[3] = (alpha / 7); - gp_draw_strokes(gpf->prev, offsx, offsy, winx, winy, dflag, debug, lthick, color, color); + gp_draw_strokes(gpd, gpf->prev, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, + true, gpl->flag & GP_LAYER_GHOST_PREVCOL, diff_mat); } } else { @@ -1079,7 +1303,7 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of copy_v3_v3(color, gpl->gcolor_next); } else { - copy_v3_v3(color, gpl->color); + copy_v3_v3(color, default_color); } if (gpl->gstep_next > 0) { @@ -1093,7 +1317,8 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of /* alpha decreases with distance from curframe index */ fac = 1.0f - ((float)(gf->framenum - gpf->framenum) / (float)(gpl->gstep_next + 1)); color[3] = alpha * fac * 0.66f; - gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, color, color); + gp_draw_strokes(gpd, gf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, + true, gpl->flag & GP_LAYER_GHOST_NEXTCOL, diff_mat); } else break; @@ -1103,34 +1328,38 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of /* draw the strokes for the ghost frames (at half of the alpha set by user) */ if (gpf->next) { color[3] = (alpha / 4); - gp_draw_strokes(gpf->next, offsx, offsy, winx, winy, dflag, debug, lthick, color, color); + gp_draw_strokes(gpd, gpf->next, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, + true, gpl->flag & GP_LAYER_GHOST_NEXTCOL, diff_mat); } } else { /* don't draw - disabled */ } - /* 3) restore alpha */ - glColor4fv(gpl->color); } /* loop over gpencil data layers, drawing them */ -static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) +static void gp_draw_data_layers( + bGPDbrush *brush, float alpha, bGPdata *gpd, + int offsx, int offsy, int winx, int winy, int cfra, int dflag) { bGPDlayer *gpl; - + float diff_mat[4][4]; + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { bGPDframe *gpf; - + /* calculate parent position */ + ED_gpencil_parent_location(gpl, diff_mat); + bool debug = (gpl->flag & GP_LAYER_DRAWDEBUG) ? true : false; - short lthick = gpl->thickness; + short lthick = brush->thickness + gpl->thickness; /* don't draw layer if hidden */ if (gpl->flag & GP_LAYER_HIDE) continue; /* get frame to draw */ - gpf = gpencil_layer_getframe(gpl, cfra, 0); + gpf = BKE_gpencil_layer_getframe(gpl, cfra, 0); if (gpf == NULL) continue; @@ -1155,9 +1384,6 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in /* HQ fills... */ GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_HQ_FILL), GP_DRAWDATA_HQ_FILL); - /* fill strokes... */ - // XXX: this is not a very good limit - GP_DRAWFLAG_APPLY((gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH), GP_DRAWDATA_FILL); #undef GP_DRAWFLAG_APPLY /* draw 'onionskins' (frame left + right) */ @@ -1165,11 +1391,12 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in /* Drawing method - only immediately surrounding (gstep = 0), * or within a frame range on either side (gstep > 0) */ - gp_draw_onionskins(gpl, gpf, offsx, offsy, winx, winy, cfra, dflag, debug, lthick); + gp_draw_onionskins(gpd, gpl, gpf, offsx, offsy, winx, winy, cfra, dflag, debug, diff_mat); } /* draw the strokes already in active frame */ - gp_draw_strokes(gpf, offsx, offsy, winx, winy, dflag, debug, lthick, gpl->color, gpl->fill); + gp_draw_strokes(gpd, gpf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, + gpl->opacity, gpl->tintcolor, false, false, diff_mat); /* Draw verts of selected strokes * - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering @@ -1183,8 +1410,7 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in (gpl->flag & GP_LAYER_LOCKED) == 0 && (gpd->flag & GP_DATA_STROKE_EDITMODE)) { - gp_draw_strokes_edit(gpf, offsx, offsy, winx, winy, dflag, - (gpl->color[3] < 0.95f) ? gpl->color : NULL); + gp_draw_strokes_edit(gpd, gpf, offsx, offsy, winx, winy, dflag, gpl->flag, diff_mat, alpha); } /* Check if may need to draw the active stroke cache, only if this layer is the active layer @@ -1194,7 +1420,7 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in (gpf->flag & GP_FRAME_PAINT)) { /* Set color for drawing buffer stroke - since this may not be set yet */ - glColor4fv(gpl->color); + // glColor4fv(gpl->color); /* Buffer stroke needs to be drawn with a different linestyle * to help differentiate them from normal strokes. @@ -1202,11 +1428,12 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in * It should also be noted that sbuffer contains temporary point types * i.e. tGPspoints NOT bGPDspoints */ - if (gpl->flag & GP_LAYER_VOLUMETRIC) { - gp_draw_stroke_volumetric_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag); + if (gpd->sflag & PC_COLOR_VOLUMETRIC) { + gp_draw_stroke_volumetric_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, + dflag, gpd->sbuffer_sflag, gpd->scolor); } else { - gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag); + gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag, gpd->scolor); } } } @@ -1258,7 +1485,9 @@ static void gp_draw_status_text(bGPdata *gpd, ARegion *ar) } /* draw grease-pencil datablock */ -static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) +static void gp_draw_data( + bGPDbrush *brush, float alpha, bGPdata *gpd, + int offsx, int offsy, int winx, int winy, int cfra, int dflag) { /* reset line drawing style (in case previous user didn't reset) */ setlinestyle(0); @@ -1276,7 +1505,7 @@ static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy, glEnable(GL_BLEND); /* draw! */ - gp_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag); + gp_draw_data_layers(brush, alpha, gpd, offsx, offsy, winx, winy, cfra, dflag); /* turn off alpha blending, then smooth lines */ glDisable(GL_BLEND); // alpha blending @@ -1303,14 +1532,25 @@ static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, i } if (gpd_source) { - gp_draw_data(gpd_source, offsx, offsy, winx, winy, cfra, dflag); + ToolSettings *ts = scene->toolsettings; + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); + if (brush != NULL) { + gp_draw_data(brush, ts->gp_sculpt.alpha, gpd_source, + offsx, offsy, winx, winy, cfra, dflag); + } + } } /* scene/clip data has already been drawn, only object/track data is drawn here * if gpd_source == gpd, we don't have any object/track data and we can skip */ if (gpd_source == NULL || (gpd_source && gpd_source != gpd)) { - gp_draw_data(gpd, offsx, offsy, winx, winy, cfra, dflag); + ToolSettings *ts = scene->toolsettings; + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); + if (brush != NULL) { + gp_draw_data(brush, ts->gp_sculpt.alpha, gpd, + offsx, offsy, winx, winy, cfra, dflag); + } } } @@ -1479,6 +1719,7 @@ void ED_gpencil_draw_view3d(wmWindowManager *wm, Scene *scene, View3D *v3d, AReg /* draw it! */ gp_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype); + } void ED_gpencil_draw_ex(Scene *scene, bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype) diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c index 738496a67c6..bd4856f1b93 100644 --- a/source/blender/editors/gpencil/editaction_gpencil.c +++ b/source/blender/editors/gpencil/editaction_gpencil.c @@ -253,7 +253,7 @@ bool ED_gplayer_frames_delete(bGPDlayer *gpl) gpfn = gpf->next; if (gpf->flag & GP_FRAME_SELECT) - changed |= gpencil_layer_delframe(gpl, gpf); + changed |= BKE_gpencil_layer_delframe(gpl, gpf); } return changed; @@ -277,7 +277,7 @@ void ED_gplayer_frames_duplicate(bGPDlayer *gpl) bGPDframe *gpfd; /* duplicate frame, and deselect self */ - gpfd = gpencil_frame_duplicate(gpf); + gpfd = BKE_gpencil_frame_duplicate(gpf); gpf->flag &= ~GP_FRAME_SELECT; BLI_insertlinkafter(&gpl->frames, gpf, gpfd); @@ -323,7 +323,7 @@ static int gp_anim_copy_cfra = 0; /* This function frees any MEM_calloc'ed copy/paste buffer data */ void ED_gpencil_anim_copybuf_free(void) { - free_gpencil_layers(&gp_anim_copybuf); + BKE_gpencil_free_layers(&gp_anim_copybuf); BLI_listbase_clear(&gp_anim_copybuf); gp_anim_copy_firstframe = 999999999; @@ -364,7 +364,7 @@ bool ED_gpencil_anim_copybuf_copy(bAnimContext *ac) /* if frame is selected, make duplicate it and its strokes */ if (gpf->flag & GP_FRAME_SELECT) { /* make a copy of this frame */ - bGPDframe *new_frame = gpencil_frame_duplicate(gpf); + bGPDframe *new_frame = BKE_gpencil_frame_duplicate(gpf); BLI_addtail(&copied_frames, new_frame); /* extend extents for keyframes encountered */ @@ -475,7 +475,7 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) gpfs->framenum += offset; /* get frame to copy data into (if no frame returned, then just ignore) */ - gpf = gpencil_layer_getframe(gpld, gpfs->framenum, 1); + gpf = BKE_gpencil_layer_getframe(gpld, gpfs->framenum, 1); if (gpf) { bGPDstroke *gps, *gpsn; @@ -498,7 +498,7 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) /* if no strokes (i.e. new frame) added, free gpf */ if (BLI_listbase_is_empty(&gpf->strokes)) - gpencil_layer_delframe(gpld, gpf); + BKE_gpencil_layer_delframe(gpld, gpf); } /* unapply offset from buffer-frame */ diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index 0271afd6827..fcb2ce02bde 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2015, Blender Foundation * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** * @@ -51,6 +51,7 @@ #include "DNA_space_types.h" #include "DNA_view3d_types.h" #include "DNA_gpencil_types.h" +#include "DNA_object_types.h" #include "BKE_context.h" #include "BKE_global.h" @@ -223,9 +224,26 @@ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i GP_EditBrush_Data *brush = gso->brush; float inf = gp_brush_influence_calc(gso, radius, co); bool affect_pressure = (brush->flag & GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE) != 0; - + /* need one flag enabled by default */ + if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION | + GP_BRUSHEDIT_FLAG_APPLY_STRENGTH | + GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0) + { + gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; + } + /* perform smoothing */ - return gp_smooth_stroke(gps, i, inf, affect_pressure); + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_POSITION) { + gp_smooth_stroke(gps, i, inf, affect_pressure); + } + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_STRENGTH) { + gp_smooth_stroke_strength(gps, i, inf); + } + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_THICKNESS) { + gp_smooth_stroke_thickness(gps, i, inf); + } + + return true; } /* ----------------------------------------------- */ @@ -268,6 +286,41 @@ static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in /* ----------------------------------------------- */ +/* Color Strength Brush */ + +/* Make color more or less transparent by the specified amounts */ +static bool gp_brush_strength_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int i, + const int radius, const int co[2]) +{ + bGPDspoint *pt = gps->points + i; + float inf; + + /* Compute strength of effect + * - We divide the strength by 10, so that users can set "sane" values. + * Otherwise, good default values are in the range of 0.093 + */ + inf = gp_brush_influence_calc(gso, radius, co) / 10.0f; + + /* apply */ + // XXX: this is much too strong, and it should probably do some smoothing with the surrounding stuff + if (gp_brush_invert_check(gso)) { + /* make line thinner - reduce stroke pressure */ + pt->strength -= inf; + } + else { + /* make line thicker - increase stroke pressure */ + pt->strength += inf; + } + + /* Strength should stay within [0.0, 1.0] */ + CLAMP(pt->strength, 0.0f, 1.0f); + + return true; +} + + +/* ----------------------------------------------- */ /* Grab Brush */ /* Custom data per stroke for the Grab Brush @@ -373,11 +426,12 @@ static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso) } /* Apply grab transform to all relevant points of the affected strokes */ -static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, bGPDstroke *gps) +static void gp_brush_grab_apply_cached( + tGP_BrushEditData *gso, bGPDstroke *gps, bool parented, float diff_mat[4][4]) { tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); int i; - + /* Apply dvec to all of the stored points */ for (i = 0; i < data->size; i++) { bGPDspoint *pt = &gps->points[data->points[i]]; @@ -385,9 +439,23 @@ static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, bGPDstroke *gps) /* adjust the amount of displacement to apply */ mul_v3_v3fl(delta, gso->dvec, data->weights[i]); + if (!parented) { + /* apply */ + add_v3_v3(&pt->x, delta); + } + else { + float fpt[3]; + /* apply transformation */ + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* apply */ + add_v3_v3(fpt, delta); + copy_v3_v3(&pt->x, fpt); + /* undo transformation to the init parent position */ + float inverse_diff_mat[4][4]; + invert_m4_m4(inverse_diff_mat, diff_mat); + mul_m4_v3(inverse_diff_mat, &pt->x); + } - /* apply */ - add_v3_v3(&pt->x, delta); } } @@ -592,62 +660,92 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in */ const float inf = gp_brush_influence_calc(gso, radius, co) / 2.0f; const float fac = BLI_frand() * inf; - - /* Jitter is applied perpendicular to the mouse movement vector - * - We compute all effects in screenspace (since it's easier) - * and then project these to get the points/distances in - * viewspace as needed - */ - float mvec[2], svec[2]; - - /* mouse movement in ints -> floats */ - mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); - mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); - - /* rotate mvec by 90 degrees... */ - svec[0] = -mvec[1]; - svec[1] = mvec[0]; - - //printf("svec = %f %f, ", svec[0], svec[1]); - - /* scale the displacement by the random displacement, and apply */ - if (BLI_frand() > 0.5f) { - mul_v2_fl(svec, -fac); - } - else { - mul_v2_fl(svec, fac); + /* need one flag enabled by default */ + if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION | + GP_BRUSHEDIT_FLAG_APPLY_STRENGTH | + GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0) + { + gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; } - - //printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", svec[0], svec[1], fac, nco[0], nco[1], co[0], co[1]); - - /* convert to dataspace */ - if (gps->flag & GP_STROKE_3DSPACE) { - /* 3D: Project to 3D space */ - if (gso->sa->spacetype == SPACE_VIEW3D) { - bool flip; - RegionView3D *rv3d = gso->ar->regiondata; - float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip); - if (flip == false) { - float dvec[3]; - ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac); - add_v3_v3(&pt->x, dvec); + + /* apply random to position */ + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_POSITION) { + /* Jitter is applied perpendicular to the mouse movement vector + * - We compute all effects in screenspace (since it's easier) + * and then project these to get the points/distances in + * viewspace as needed + */ + float mvec[2], svec[2]; + + /* mouse movement in ints -> floats */ + mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]); + mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]); + + /* rotate mvec by 90 degrees... */ + svec[0] = -mvec[1]; + svec[1] = mvec[0]; + + /* scale the displacement by the random displacement, and apply */ + if (BLI_frand() > 0.5f) { + mul_v2_fl(svec, -fac); + } + else { + mul_v2_fl(svec, fac); + } + + //printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", svec[0], svec[1], fac, nco[0], nco[1], co[0], co[1]); + + /* convert to dataspace */ + if (gps->flag & GP_STROKE_3DSPACE) { + /* 3D: Project to 3D space */ + if (gso->sa->spacetype == SPACE_VIEW3D) { + bool flip; + RegionView3D *rv3d = gso->ar->regiondata; + float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip); + if (flip == false) { + float dvec[3]; + ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac); + add_v3_v3(&pt->x, dvec); + } + } + else { + /* ERROR */ + BLI_assert("3D stroke being sculpted in non-3D view"); } } else { - /* ERROR */ - BLI_assert("3D stroke being sculpted in non-3D view"); + /* 2D: As-is */ + // XXX: v2d scaling/offset? + float nco[2]; + nco[0] = (float)co[0] + svec[0]; + nco[1] = (float)co[1] + svec[1]; + + copy_v2_v2(&pt->x, nco); } } - else { - /* 2D: As-is */ - // XXX: v2d scaling/offset? - float nco[2]; - nco[0] = (float)co[0] + svec[0]; - nco[1] = (float)co[1] + svec[1]; - - copy_v2_v2(&pt->x, nco); + /* apply random to strength */ + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_STRENGTH) { + if (BLI_frand() > 0.5f) { + pt->strength += fac; + } + else { + pt->strength -= fac; + } + CLAMP_MIN(pt->strength, 0.0f); + CLAMP_MAX(pt->strength, 1.0f); } - + /* apply random to thickness (use pressure) */ + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_THICKNESS) { + if (BLI_frand() > 0.5f) { + pt->pressure += fac; + } + else { + pt->pressure -= fac; + } + /* only limit lower value */ + CLAMP_MIN(pt->pressure, 0.0f); + } + /* done */ return true; } @@ -743,7 +841,7 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso) Scene *scene = gso->scene; bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, true); + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true); bGPDstroke *gps; float delta[3]; @@ -1087,7 +1185,7 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) */ // XXX: should this be allowed when framelock is enabled? if (gpf->framenum != cfra) { - gpencil_frame_addcopy(gpl, cfra); + BKE_gpencil_frame_addcopy(gpl, cfra); } } } @@ -1099,7 +1197,9 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) /* Apply ----------------------------------------------- */ /* Apply brush operation to points in this stroke */ -static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, GP_BrushApplyCb apply) +static bool gpsculpt_brush_do_stroke( + tGP_BrushEditData *gso, bGPDstroke *gps, bool parented, + float diff_mat[4][4], GP_BrushApplyCb apply) { GP_SpaceConversion *gsc = &gso->gsc; rcti *rect = &gso->brush_rect; @@ -1111,9 +1211,16 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, GP int i; bool include_last = false; bool changed = false; - + if (gps->totpoints == 1) { - gp_point_to_xy(gsc, gps, gps->points, &pc1[0], &pc1[1]); + if (!parented) { + gp_point_to_xy(gsc, gps, gps->points, &pc1[0], &pc1[1]); + } + else { + bGPDspoint pt_temp; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]); + } /* do boundbox check first */ if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { @@ -1140,10 +1247,20 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, GP continue; } } - - gp_point_to_xy(gsc, gps, pt1, &pc1[0], &pc1[1]); - gp_point_to_xy(gsc, gps, pt2, &pc2[0], &pc2[1]); - + if (!parented) { + gp_point_to_xy(gsc, gps, pt1, &pc1[0], &pc1[1]); + gp_point_to_xy(gsc, gps, pt2, &pc2[0], &pc2[1]); + } + else { + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]); + + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]); + } + + /* Check that point segment of the boundbox of the selection stroke */ if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) @@ -1228,76 +1345,105 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) /* Find visible strokes, and perform operations on those if hit */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + float diff_mat[4][4]; + bool parented = false; + + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { - switch (gso->brush_type) { - case GP_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_smooth_apply); - break; - } - - case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_thickness_apply); - break; + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + parented = true; + } + else { + parented = false; + } + + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps; + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; } - - case GP_EDITBRUSH_TYPE_GRAB: /* Grab points */ - { - if (gso->first) { - /* First time this brush stroke is being applied: - * 1) Prepare data buffers (init/clear) for this stroke - * 2) Use the points now under the cursor - */ - gp_brush_grab_stroke_init(gso, gps); - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_grab_store_points); + + switch (gso->brush_type) { + case GP_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_smooth_apply); + break; } - else { - /* Apply effect to the stored points */ - gp_brush_grab_apply_cached(gso, gps); - changed |= true; + + case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_thickness_apply); + break; } - break; - } - - case GP_EDITBRUSH_TYPE_PUSH: /* Push points */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_push_apply); - break; - } - - case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_pinch_apply); - break; - } - - case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_twist_apply); - break; + + case GP_EDITBRUSH_TYPE_STRENGTH: /* Adjust stroke color strength */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_strength_apply); + break; + } + + case GP_EDITBRUSH_TYPE_GRAB: /* Grab points */ + { + if (gso->first) { + /* First time this brush stroke is being applied: + * 1) Prepare data buffers (init/clear) for this stroke + * 2) Use the points now under the cursor + */ + gp_brush_grab_stroke_init(gso, gps); + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_grab_store_points); + } + else { + /* Apply effect to the stored points */ + gp_brush_grab_apply_cached(gso, gps, parented, diff_mat); + changed |= true; + } + break; + } + + case GP_EDITBRUSH_TYPE_PUSH: /* Push points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_push_apply); + break; + } + + case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_pinch_apply); + break; + } + + case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_twist_apply); + break; + } + + case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_randomize_apply); + break; + } + + default: + printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type); + break; } - - case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_randomize_apply); - break; + /* Triangulation must be calculated if changed */ + if (changed) { + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; } - - default: - printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type); - break; - } - - /* Triangulation must be calculated if changed */ - if (changed) { - gps->flag |= GP_STROKE_RECALC_CACHES; - gps->tot_triangles = 0; } } CTX_DATA_END; - + return changed; } @@ -1441,6 +1587,11 @@ static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *eve needs_timer = true; break; + case GP_EDITBRUSH_TYPE_STRENGTH: + brush_rate = 0.01f; // XXX: hardcoded + needs_timer = true; + break; + case GP_EDITBRUSH_TYPE_PINCH: brush_rate = 0.001f; // XXX: hardcoded needs_timer = true; diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index c47985ebc1b..c502ed1aa83 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -142,12 +142,31 @@ static EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C), PointerRN /* convert the coordinates from the given stroke point into 3d-coordinates * - assumes that the active space is the 3D-View */ -static void gp_strokepoint_convertcoords(bContext *C, bGPDstroke *gps, bGPDspoint *pt, float p3d[3], rctf *subrect) +static void gp_strokepoint_convertcoords( + bContext *C, bGPDlayer *gpl, bGPDstroke *gps, bGPDspoint *source_pt, + float p3d[3], const rctf *subrect) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); ARegion *ar = CTX_wm_region(C); - + bGPDspoint mypt, *pt; + + float diff_mat[4][4]; + pt = &mypt; + + /* calculate difference matrix if parent object */ + if (gpl->parent == NULL) { + copy_v3_v3(&pt->x, &source_pt->x); + } + else { + /* apply parent transform */ + float fpt[3]; + ED_gpencil_parent_location(gpl, diff_mat); + mul_v3_m4v3(fpt, diff_mat, &source_pt->x); + copy_v3_v3(&pt->x, fpt); + } + + if (gps->flag & GP_STROKE_3DSPACE) { /* directly use 3d-coordinates */ copy_v3_v3(p3d, &pt->x); @@ -628,7 +647,7 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv bp = &nu->bp[old_nbp - 1]; /* First point */ - gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect); if (prev_bp) { interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC); if (do_gtd) { @@ -649,7 +668,7 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv /* Second point */ /* Note dt2 is always negative, which marks the gap. */ if (gps->totpoints > 1) { - gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect); interp_v3_v3v3(p2, p, next_p, -GAP_DFAC); if (do_gtd) { dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); @@ -670,9 +689,9 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv float p[3], next_p[3]; float dt = 0.0f; - gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect); if (gps->totpoints > 1) { - gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect); interp_v3_v3v3(p, p, next_p, -GAP_DFAC); if (do_gtd) { dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); @@ -701,10 +720,10 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv i++, pt++, bp++) { float p[3]; - float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC; + float width = pt->pressure * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC; /* get coordinates to add at */ - gp_strokepoint_convertcoords(C, gps, pt, p, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, pt, p, subrect); gp_stroke_to_path_add_point(gtd, bp, p, (prev_bp) ? prev_bp->vec : p, do_gtd, gps->inittime, pt->time, width, rad_fac, minmax_weights); @@ -816,12 +835,12 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu /* get initial coordinates */ pt = gps->points; if (tot) { - gp_strokepoint_convertcoords(C, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect); if (tot > 1) { - gp_strokepoint_convertcoords(C, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect); } if (stitch && tot > 2) { - gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect); } } @@ -940,7 +959,7 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu /* add points */ for (i = stitch ? 1 : 0, bezt = &nu->bezt[old_nbezt]; i < tot; i++, pt++, bezt++) { - float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC; + float width = pt->pressure * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC; if (i || old_nbezt) { interp_v3_v3v3(h1, p3d_cur, p3d_prev, BEZT_HANDLE_FAC); @@ -964,7 +983,7 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu copy_v3_v3(p3d_cur, p3d_next); if (i + 2 < tot) { - gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect); + gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect); } prev_bezt = bezt; @@ -1104,7 +1123,7 @@ static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bG struct Main *bmain = CTX_data_main(C); View3D *v3d = CTX_wm_view3d(C); /* may be NULL */ Scene *scene = CTX_data_scene(C); - bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0); + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); bGPDstroke *gps, *prev_gps = NULL; Object *ob; Curve *cu; @@ -1216,7 +1235,7 @@ static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOpe int i; bool valid = true; - if (!gpl || !(gpf = gpencil_layer_getframe(gpl, CFRA, 0)) || !(gps = gpf->strokes.first)) + if (!gpl || !(gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0)) || !(gps = gpf->strokes.first)) return false; do { @@ -1273,8 +1292,8 @@ static int gp_convert_poll(bContext *C) * and if we are not in edit mode! */ return ((sa && sa->spacetype == SPACE_VIEW3D) && - (gpl = gpencil_layer_getactive(gpd)) && - (gpf = gpencil_layer_getframe(gpl, CFRA, 0)) && + (gpl = BKE_gpencil_layer_getactive(gpd)) && + (gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0)) && (gpf->strokes.first) && (scene->obedit == NULL)); } @@ -1283,7 +1302,7 @@ static int gp_convert_layer_exec(bContext *C, wmOperator *op) { PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_timing_data"); bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); Scene *scene = CTX_data_scene(C); const int mode = RNA_enum_get(op->ptr, "type"); const bool norm_weights = RNA_boolean_get(op->ptr, "use_normalize_weights"); diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 746497f0ff5..876f873e5fa 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** * @@ -40,6 +40,8 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_ghash.h" +#include "BLI_math.h" #include "BLT_translation.h" @@ -57,6 +59,7 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "BKE_colortools.h" #include "UI_interface.h" #include "UI_resources.h" @@ -72,6 +75,8 @@ #include "gpencil_intern.h" +/* maximum sizes of gp-session buffer */ +#define GP_STROKE_BUFFER_MAX 5000 /* ************************************************ */ /* Datablock Operators */ @@ -92,7 +97,7 @@ static int gp_data_add_exec(bContext *C, wmOperator *op) bGPdata *gpd = (*gpd_ptr); id_us_min(&gpd->id); - *gpd_ptr = gpencil_data_addnew(DATA_("GPencil")); + *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil")); } /* notifiers */ @@ -179,10 +184,10 @@ static int gp_layer_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } if (*gpd_ptr == NULL) - *gpd_ptr = gpencil_data_addnew(DATA_("GPencil")); + *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil")); /* add new layer now */ - gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -209,7 +214,7 @@ void GPENCIL_OT_layer_add(wmOperatorType *ot) static int gp_layer_remove_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); /* sanity checks */ if (ELEM(NULL, gpd, gpl)) @@ -225,12 +230,12 @@ static int gp_layer_remove_exec(bContext *C, wmOperator *op) * - if this is the only layer, this naturally becomes NULL */ if (gpl->prev) - gpencil_layer_setactive(gpd, gpl->prev); + BKE_gpencil_layer_setactive(gpd, gpl->prev); else - gpencil_layer_setactive(gpd, gpl->next); + BKE_gpencil_layer_setactive(gpd, gpl->next); /* delete the layer now... */ - gpencil_layer_delete(gpd, gpl); + BKE_gpencil_layer_delete(gpd, gpl); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -262,7 +267,7 @@ enum { static int gp_layer_move_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); int direction = RNA_enum_get(op->ptr, "type"); @@ -316,7 +321,7 @@ void GPENCIL_OT_layer_move(wmOperatorType *ot) static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); bGPDlayer *new_layer; /* sanity checks */ @@ -324,12 +329,12 @@ static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; /* make copy of layer, and add it immediately after the existing layer */ - new_layer = gpencil_layer_duplicate(gpl); + new_layer = BKE_gpencil_layer_duplicate(gpl); BLI_insertlinkafter(&gpd->layers, gpl, new_layer); /* ensure new layer has a unique name, and is now the active layer */ BLI_uniquename(&gpd->layers, new_layer, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(new_layer->info)); - gpencil_layer_setactive(gpd, new_layer); + BKE_gpencil_layer_setactive(gpd, new_layer); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -357,7 +362,7 @@ void GPENCIL_OT_layer_duplicate(wmOperatorType *ot) static int gp_hide_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *layer = gpencil_layer_getactive(gpd); + bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd); bool unselected = RNA_boolean_get(op->ptr, "unselected"); /* sanity checks */ @@ -525,7 +530,7 @@ void GPENCIL_OT_unlock_all(wmOperatorType *ot) static int gp_isolate_layer_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *layer = gpencil_layer_getactive(gpd); + bGPDlayer *layer = BKE_gpencil_layer_getactive(gpd); bGPDlayer *gpl; int flags = GP_LAYER_LOCKED; bool isolate = false; @@ -596,6 +601,61 @@ void GPENCIL_OT_layer_isolate(wmOperatorType *ot) "In addition to toggling the editability, also affect the visibility"); } +/* ********************** Merge Layer with the next layer **************************** */ + +static int gp_merge_layer_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl_current = BKE_gpencil_layer_getactive(gpd); + bGPDlayer *gpl_next = gpl_current->next; + + if (ELEM(NULL, gpd, gpl_current, gpl_next)) { + BKE_report(op->reports, RPT_ERROR, "No layers to merge"); + return OPERATOR_CANCELLED; + } + + /* Collect frames of gpl_current in hash table to avoid O(n^2) lookups */ + GHash *gh_frames_cur = BLI_ghash_int_new_ex(__func__, 64); + for (bGPDframe *gpf = gpl_current->frames.first; gpf; gpf = gpf->next) { + BLI_ghash_insert(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum), gpf); + } + + /* read all frames from next layer */ + for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) { + /* try to find frame in active layer */ + bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum)); + if (!frame) { + /* nothing found, create new */ + frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum); + } + /* add to tail all strokes */ + BLI_movelisttolist(&frame->strokes, &gpf->strokes); + } + /* Now delete next layer */ + BKE_gpencil_layer_delete(gpd, gpl_next); + BLI_ghash_free(gh_frames_cur, NULL, NULL); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_layer_merge(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Merge Down"; + ot->idname = "GPENCIL_OT_layer_merge"; + ot->description = "Merge the current layer with the layer below"; + + /* callbacks */ + ot->exec = gp_merge_layer_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* ********************** Change Layer ***************************** */ static int gp_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) @@ -621,7 +681,7 @@ static int gp_layer_change_exec(bContext *C, wmOperator *op) /* Get layer or create new one */ if (layer_num == -1) { /* Create layer */ - gpl = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); } else { /* Try to get layer */ @@ -634,7 +694,7 @@ static int gp_layer_change_exec(bContext *C, wmOperator *op) } /* Set active layer */ - gpencil_layer_setactive(gpd, gpl); + BKE_gpencil_layer_setactive(gpd, gpl); /* updates */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -663,3 +723,1741 @@ void GPENCIL_OT_layer_change(wmOperatorType *ot) } /* ************************************************ */ + +/* ******************* Arrange Stroke Up/Down in drawing order ************************** */ + +enum { + GP_STROKE_MOVE_UP = -1, + GP_STROKE_MOVE_DOWN = 1, + GP_STROKE_MOVE_TOP = 2, + GP_STROKE_MOVE_BOTTOM = 3 +}; + +static int gp_stroke_arrange_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDstroke *gps; + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl, gpl->actframe)) { + return OPERATOR_CANCELLED; + } + + bGPDframe *gpf = gpl->actframe; + /* temp listbase to store selected strokes */ + ListBase selected = {NULL}; + const int direction = RNA_enum_get(op->ptr, "direction"); + + /* verify if any selected stroke is in the extreme of the stack and select to move */ + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* only if selected */ + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + /* some stroke is already at front*/ + if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) { + if (gps == gpf->strokes.last) { + BKE_report(op->reports, RPT_ERROR, "Some selected stroke is already on top"); + return OPERATOR_CANCELLED; + } + } + /* some stroke is already at botom */ + if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) { + if (gps == gpf->strokes.first) { + BKE_report(op->reports, RPT_ERROR, "Some selected stroke is already on bottom"); + return OPERATOR_CANCELLED; + } + } + /* add to list */ + BLI_addtail(&selected, BLI_genericNodeN(gps)); + } + } + + /* Now do the movement of the stroke */ + switch (direction) { + /* Bring to Front */ + case GP_STROKE_MOVE_TOP: + for (LinkData *link = selected.first; link; link = link->next) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_insertlinkafter(&gpf->strokes, gpf->strokes.last, gps); + } + break; + /* Bring Forward */ + case GP_STROKE_MOVE_UP: + for (LinkData *link = selected.last; link; link = link->prev) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_insertlinkafter(&gpf->strokes, gps->next, gps); + } + break; + /* Send Backward */ + case GP_STROKE_MOVE_DOWN: + for (LinkData *link = selected.first; link; link = link->next) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_insertlinkbefore(&gpf->strokes, gps->prev, gps); + } + break; + /* Send to Back */ + case GP_STROKE_MOVE_BOTTOM: + for (LinkData *link = selected.last; link; link = link->prev) { + gps = link->data; + BLI_remlink(&gpf->strokes, gps); + BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps); + } + break; + default: + BLI_assert(0); + break; + } + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_arrange(wmOperatorType *ot) +{ + static EnumPropertyItem slot_move[] = { + {GP_STROKE_MOVE_UP, "UP", 0, "Bring Forward", ""}, + {GP_STROKE_MOVE_DOWN, "DOWN", 0, "Send Backward", ""}, + {GP_STROKE_MOVE_TOP, "TOP", 0, "Bring to Front", ""}, + {GP_STROKE_MOVE_BOTTOM, "BOTTOM", 0, "Send to Back", ""}, + {0, NULL, 0, NULL, NULL } + }; + + /* identifiers */ + ot->name = "Arrange Stroke"; + ot->idname = "GPENCIL_OT_stroke_arrange"; + ot->description = "Arrange selected strokes up/down in the drawing order of the active layer"; + + /* api callbacks */ + ot->exec = gp_stroke_arrange_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", ""); +} +/* ******************* Move Stroke to new color ************************** */ + +static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette; + bGPDpalettecolor *color; + + /* sanity checks */ + if (ELEM(NULL, gpd)) { + return OPERATOR_CANCELLED; + } + + palette = BKE_gpencil_palette_getactive(gpd); + color = BKE_gpencil_palettecolor_getactive(palette); + if (ELEM(NULL, palette, color)) { + return OPERATOR_CANCELLED; + } + + /* loop all strokes */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + /* only if selected */ + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) + continue; + + /* asign new color (only if different) */ + if (STREQ(gps->colorname, color->info) == false) { + strcpy(gps->colorname, color->info); + gps->flag |= GP_STROKE_RECALC_COLOR; + } + } + } + } + } + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_change_color(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Change Stroke Color"; + ot->idname = "GPENCIL_OT_stroke_change_color"; + ot->description = "Move selected strokes to active color"; + + /* api callbacks */ + ot->exec = gp_stroke_change_color_exec; + ot->poll = gp_active_layer_poll; +} + +/* ******************* Lock color of non selected Strokes colors ************************** */ + +static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette; + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + palette = BKE_gpencil_palette_getactive(gpd); + if (ELEM(NULL, palette)) + return OPERATOR_CANCELLED; + + /* first lock all colors */ + for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag |= PC_COLOR_LOCKED; + } + + /* loop all selected strokes and unlock any color */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + /* only if selected */ + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* unlock color */ + if (gps->palcolor != NULL) { + gps->palcolor->flag &= ~PC_COLOR_LOCKED; + } + } + } + } + } + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Lock Unused Colors"; + ot->idname = "GPENCIL_OT_stroke_lock_color"; + ot->description = "Lock any color not used in any selected stroke"; + + /* api callbacks */ + ot->exec = gp_stroke_lock_color_exec; + ot->poll = gp_active_layer_poll; +} + +/* ******************* Apply layer thickness change to Strokes ************************** */ + +static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl, gpl->frames.first)) + return OPERATOR_CANCELLED; + + /* loop all strokes */ + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* Apply thickness */ + gps->thickness = gps->thickness + gpl->thickness; + } + } + /* clear value */ + gpl->thickness = 0.0f; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Apply Stroke Thickness"; + ot->idname = "GPENCIL_OT_stroke_apply_thickness"; + ot->description = "Apply the thickness change of the layer to its strokes"; + + /* api callbacks */ + ot->exec = gp_stroke_apply_thickness_exec; + ot->poll = gp_active_layer_poll; +} + +/* ******************* Close Strokes ************************** */ + +enum { + GP_STROKE_CYCLIC_CLOSE = 1, + GP_STROKE_CYCLIC_OPEN = 2, + GP_STROKE_CYCLIC_TOGGLE = 3 +}; + +static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + const int type = RNA_enum_get(op->ptr, "type"); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* loop all selected strokes */ + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + bGPDpalettecolor *palcolor = gps->palcolor; + + /* skip strokes that are not selected or invalid for current view */ + if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* skip hidden or locked colors */ + if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED)) + continue; + + switch (type) { + case GP_STROKE_CYCLIC_CLOSE: + /* Close all (enable) */ + gps->flag |= GP_STROKE_CYCLIC; + break; + case GP_STROKE_CYCLIC_OPEN: + /* Open all (disable) */ + gps->flag &= ~GP_STROKE_CYCLIC; + break; + case GP_STROKE_CYCLIC_TOGGLE: + /* Just toggle flag... */ + gps->flag ^= GP_STROKE_CYCLIC; + break; + default: + BLI_assert(0); + break; + } + } + } + CTX_DATA_END; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +/** + * Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with + * option to force opened/closed strokes instead of just toggle behavior. + */ +void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot) +{ + static EnumPropertyItem cyclic_type[] = { + {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""}, + {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""}, + {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Set Cyclical State"; + ot->idname = "GPENCIL_OT_stroke_cyclical_set"; + ot->description = "Close or open the selected stroke adding an edge from last to first point"; + + /* api callbacks */ + ot->exec = gp_stroke_cyclical_set_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", ""); +} + +/* ******************* Stroke join ************************** */ + +/* Helper: flip stroke */ +static void gpencil_flip_stroke(bGPDstroke *gps) +{ + bGPDspoint pt, *point, *point2; + int end = gps->totpoints - 1; + + for (int i = 0; i < gps->totpoints / 2; i++) { + /* save first point */ + point = &gps->points[i]; + pt.x = point->x; + pt.y = point->y; + pt.z = point->z; + pt.flag = point->flag; + pt.pressure = point->pressure; + pt.strength = point->strength; + pt.time = point->time; + + /* replace first point with last point */ + point2 = &gps->points[end]; + point->x = point2->x; + point->y = point2->y; + point->z = point2->z; + point->flag = point2->flag; + point->pressure = point2->pressure; + point->strength = point2->strength; + point->time = point2->time; + + /* replace last point with first saved before */ + point = &gps->points[end]; + point->x = pt.x; + point->y = pt.y; + point->z = pt.z; + point->flag = pt.flag; + point->pressure = pt.pressure; + point->strength = pt.strength; + point->time = pt.time; + + end--; + } +} + +/* Helper: copy point between strokes */ +static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3], + float pressure, float strength, float deltatime) +{ + bGPDspoint *newpoint; + + gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); + gps->totpoints++; + + newpoint = &gps->points[gps->totpoints - 1]; + newpoint->x = point->x * delta[0]; + newpoint->y = point->y * delta[1]; + newpoint->z = point->z * delta[2]; + newpoint->flag = point->flag; + newpoint->pressure = pressure; + newpoint->strength = strength; + newpoint->time = point->time + deltatime; +} + +/* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */ +static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps) +{ + bGPDspoint point, *pt; + int i; + float delta[3] = {1.0f, 1.0f, 1.0f}; + float deltatime = 0.0f; + + /* sanity checks */ + if (ELEM(NULL, gps_a, gps_b)) + return; + + if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0)) + return; + + /* define start and end points of each stroke */ + float sa[3], sb[3], ea[3], eb[3]; + pt = &gps_a->points[0]; + copy_v3_v3(sa, &pt->x); + + pt = &gps_a->points[gps_a->totpoints - 1]; + copy_v3_v3(ea, &pt->x); + + pt = &gps_b->points[0]; + copy_v3_v3(sb, &pt->x); + + pt = &gps_b->points[gps_b->totpoints - 1]; + copy_v3_v3(eb, &pt->x); + + /* review if need flip stroke B */ + float ea_sb = len_squared_v3v3(ea, sb); + float ea_eb = len_squared_v3v3(ea, eb); + /* flip if distance to end point is shorter */ + if (ea_eb < ea_sb) { + gpencil_flip_stroke(gps_b); + } + + /* don't visibly link the first and last points? */ + if (leave_gaps) { + /* 1st: add one tail point to start invisible area */ + point = gps_a->points[gps_a->totpoints - 1]; + deltatime = point.time; + gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, 0.0f); + + /* 2nd: add one head point to finish invisible area */ + point = gps_b->points[0]; + gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, deltatime); + } + + /* 3rd: add all points */ + for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) { + /* check if still room in buffer */ + if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) { + gpencil_stroke_copy_point(gps_a, pt, delta, pt->pressure, pt->strength, deltatime); + } + } +} + +static int gp_stroke_join_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd); + bGPDstroke *gps, *gpsn; + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + + bGPDframe *gpf_a = NULL; + bGPDstroke *stroke_a = NULL; + bGPDstroke *stroke_b = NULL; + bGPDstroke *new_stroke = NULL; + + const int type = RNA_enum_get(op->ptr, "type"); + const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps"); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + if (activegpl->flag & GP_LAYER_LOCKED) + return OPERATOR_CANCELLED; + + BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY)); + + + /* read all selected strokes */ + bool first = false; + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + bGPDframe *gpf = gpl->actframe; + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + /* to join strokes, cyclic must be disabled */ + gps->flag &= ~GP_STROKE_CYCLIC; + /* saves first frame and stroke */ + if (!first) { + first = true; + gpf_a = gpf; + stroke_a = gps; + } + else { + stroke_b = gps; + /* create a new stroke if was not created before (only created if something to join) */ + if (new_stroke == NULL) { + new_stroke = MEM_dupallocN(stroke_a); + new_stroke->points = MEM_dupallocN(stroke_a->points); + new_stroke->triangles = NULL; + new_stroke->tot_triangles = 0; + new_stroke->flag |= GP_STROKE_RECALC_CACHES; + /* if new, set current color */ + if (type == GP_STROKE_JOINCOPY) { + new_stroke->palcolor = palcolor; + strcpy(new_stroke->colorname, palcolor->info); + new_stroke->flag |= GP_STROKE_RECALC_COLOR; + } + } + /* join new_stroke and stroke B. New stroke will contain all the previous data */ + gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps); + + /* if join only, delete old strokes */ + if (type == GP_STROKE_JOIN) { + if (stroke_a) { + BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke); + BLI_remlink(&gpf->strokes, stroke_a); + BKE_gpencil_free_stroke(stroke_a); + stroke_a = NULL; + } + if (stroke_b) { + BLI_remlink(&gpf->strokes, stroke_b); + BKE_gpencil_free_stroke(stroke_b); + stroke_b = NULL; + } + } + } + } + } + } + CTX_DATA_END; + /* add new stroke if was not added before */ + if (type == GP_STROKE_JOINCOPY) { + if (new_stroke) { + /* Add a new frame if needed */ + if (activegpl->actframe == NULL) + activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum); + + BLI_addtail(&activegpl->actframe->strokes, new_stroke); + } + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_join(wmOperatorType *ot) +{ + static EnumPropertyItem join_type[] = { + {GP_STROKE_JOIN, "JOIN", 0, "Join", ""}, + {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Join Strokes"; + ot->idname = "GPENCIL_OT_stroke_join"; + ot->description = "Join selected strokes (optionally as new stroke)"; + + /* api callbacks */ + ot->exec = gp_stroke_join_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", ""); + RNA_def_boolean(ot->srna, "leave_gaps", false, "Leave Gaps", "Leave gaps between joined strokes instead of linking them"); +} + +/* ******************* Stroke flip ************************** */ + +static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* read all selected strokes */ + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + bGPDframe *gpf = gpl->actframe; + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + if (gps->flag & GP_STROKE_SELECT) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + /* flip stroke */ + gpencil_flip_stroke(gps); + } + } + } + CTX_DATA_END; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_flip(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Flip Stroke"; + ot->idname = "GPENCIL_OT_stroke_flip"; + ot->description = "Change direction of the points of the selected strokes"; + + /* api callbacks */ + ot->exec = gp_stroke_flip_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ************************************************ */ +/* Drawing Brushes Operators */ + +/* ******************* Add New Brush ************************ */ + +/* add new brush - wrapper around API */ +static int gp_brush_add_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + + /* if there's no existing container */ + if (ts == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go"); + return OPERATOR_CANCELLED; + } + /* add new brush now */ + BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Brush"; + ot->idname = "GPENCIL_OT_brush_add"; + ot->description = "Add new Grease Pencil drawing brush for the active Grease Pencil datablock"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_brush_add_exec; + ot->poll = gp_add_poll; +} + +/* ******************* Remove Active Brush ************************* */ + +static int gp_brush_remove_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); + + /* sanity checks */ + if (ELEM(NULL, ts, brush)) + return OPERATOR_CANCELLED; + + if (BLI_listbase_count(&ts->gp_brushes) < 2) { + BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a brush. Unable to delete brush"); + return OPERATOR_CANCELLED; + } + + + /* make the brush before this the new active brush + * - use the one after if this is the first + * - if this is the only brush, this naturally becomes NULL + */ + if (brush->prev) + BKE_gpencil_brush_setactive(ts, brush->prev); + else + BKE_gpencil_brush_setactive(ts, brush->next); + + /* delete the brush now... */ + BKE_gpencil_brush_delete(ts, brush); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove brush"; + ot->idname = "GPENCIL_OT_brush_remove"; + ot->description = "Remove active Grease Pencil drawing brush"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_brush_remove_exec; + ot->poll = gp_active_brush_poll; +} + +/* ********************** Change Brush ***************************** */ + +static int gp_brush_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) +{ + uiPopupMenu *pup; + uiLayout *layout; + + /* call the menu, which will call this operator again, hence the canceled */ + pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); + layout = UI_popup_menu_layout(pup); + uiItemsEnumO(layout, "GPENCIL_OT_brush_change", "brush"); + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; +} + +static int gp_brush_change_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPDbrush *brush = NULL; + int brush_num = RNA_enum_get(op->ptr, "brush"); + + /* Get brush or create new one */ + if (brush_num == -1) { + /* Create brush */ + brush = BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); + } + else { + /* Try to get brush */ + brush = BLI_findlink(&ts->gp_brushes, brush_num); + + if (brush == NULL) { + BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent brush (index = %d)", brush_num); + return OPERATOR_CANCELLED; + } + } + + /* Set active brush */ + BKE_gpencil_brush_setactive(ts, brush); + + /* updates */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_change(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Change Brush"; + ot->idname = "GPENCIL_OT_brush_change"; + ot->description = "Change active Grease Pencil drawing brush"; + + /* callbacks */ + ot->invoke = gp_brush_change_invoke; + ot->exec = gp_brush_change_exec; + ot->poll = gp_active_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* gp brush to use (dynamic enum) */ + ot->prop = RNA_def_enum(ot->srna, "brush", DummyRNA_DEFAULT_items, 0, "Grease Pencil Brush", ""); + RNA_def_enum_funcs(ot->prop, ED_gpencil_brushes_enum_itemf); +} + +/* ******************* Move Brush Up/Down ************************** */ + +enum { + GP_BRUSH_MOVE_UP = -1, + GP_BRUSH_MOVE_DOWN = 1 +}; + +static int gp_brush_move_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); + + int direction = RNA_enum_get(op->ptr, "type"); + + /* sanity checks */ + if (ELEM(NULL, ts, brush)) { + return OPERATOR_CANCELLED; + } + + /* up or down? */ + if (direction == GP_BRUSH_MOVE_UP) { + /* up */ + BLI_remlink(&ts->gp_brushes, brush); + BLI_insertlinkbefore(&ts->gp_brushes, brush->prev, brush); + } + else if (direction == GP_BRUSH_MOVE_DOWN) { + /* down */ + BLI_remlink(&ts->gp_brushes, brush); + BLI_insertlinkafter(&ts->gp_brushes, brush->next, brush); + } + else { + BLI_assert(0); + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_move(wmOperatorType *ot) +{ + static EnumPropertyItem slot_move[] = { + {GP_BRUSH_MOVE_UP, "UP", 0, "Up", ""}, + {GP_BRUSH_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL } + }; + + /* identifiers */ + ot->name = "Move Brush"; + ot->idname = "GPENCIL_OT_brush_move"; + ot->description = "Move the active Grease Pencil drawing brush up/down in the list"; + + /* api callbacks */ + ot->exec = gp_brush_move_exec; + ot->poll = gp_active_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", slot_move, GP_BRUSH_MOVE_UP, "Type", ""); +} + +/* ******************* Brush create presets ************************** */ + +static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op)) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + BKE_gpencil_brush_init_presets(ts); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_presets_create(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Create Preset Brushes"; + ot->idname = "GPENCIL_OT_brush_presets_create"; + ot->description = "Create a set of predefined Grease Pencil drawing brushes"; + + /* api callbacks */ + ot->exec = gp_brush_presets_create_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + +} + +/* ***************** Copy Brush ************************ */ + +static int gp_brush_copy_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + + /* if there's no existing container */ + if (ts == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go"); + return OPERATOR_CANCELLED; + } + + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); + bGPDbrush *newbrush; + + /* sanity checks */ + if (ELEM(NULL, brush)) + return OPERATOR_CANCELLED; + + /* create a brush and duplicate data */ + newbrush = BKE_gpencil_brush_addnew(ts, brush->info, true); + newbrush->thickness = brush->thickness; + newbrush->draw_smoothfac = brush->draw_smoothfac; + newbrush->draw_smoothlvl = brush->draw_smoothlvl; + newbrush->sublevel = brush->sublevel; + newbrush->flag = brush->flag; + newbrush->draw_sensitivity = brush->draw_sensitivity; + newbrush->draw_strength = brush->draw_strength; + newbrush->draw_jitter = brush->draw_jitter; + newbrush->draw_angle = brush->draw_angle; + newbrush->draw_angle_factor = brush->draw_angle_factor; + newbrush->draw_random_press = brush->draw_random_press; + newbrush->draw_random_sub = brush->draw_random_sub; + + /* free automatic curves created by default (replaced by copy) */ + curvemapping_free(newbrush->cur_sensitivity); + curvemapping_free(newbrush->cur_strength); + curvemapping_free(newbrush->cur_jitter); + + /* make a copy of curves */ + newbrush->cur_sensitivity = curvemapping_copy(brush->cur_sensitivity); + newbrush->cur_strength = curvemapping_copy(brush->cur_strength); + newbrush->cur_jitter = curvemapping_copy(brush->cur_jitter); + + BKE_gpencil_brush_setactive(ts, newbrush); + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy Brush"; + ot->idname = "GPENCIL_OT_brush_copy"; + ot->description = "Copy current Grease Pencil drawing brush"; + + /* callbacks */ + ot->exec = gp_brush_copy_exec; + ot->poll = gp_active_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ***************** Select Brush ************************ */ + +static int gp_brush_select_exec(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + + /* if there's no existing container */ + if (ts == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere to go"); + return OPERATOR_CANCELLED; + } + + const int index = RNA_int_get(op->ptr, "index"); + bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, index); + /* sanity checks */ + if (ELEM(NULL, brush)) { + return OPERATOR_CANCELLED; + } + + BKE_gpencil_brush_setactive(ts, brush); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_brush_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Brush"; + ot->idname = "GPENCIL_OT_brush_select"; + ot->description = "Select a Grease Pencil drawing brush"; + + /* callbacks */ + ot->exec = gp_brush_select_exec; + ot->poll = gp_active_brush_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Drawing Brush", 0, INT_MAX); +} + +/* ************************************************ */ +/* Palette Operators */ + +/* ******************* Add New Palette ************************ */ + +/* add new palette - wrapper around API */ +static int gp_palette_add_exec(bContext *C, wmOperator *op) +{ + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + + /* if there's no existing Grease-Pencil data there, add some */ + if (gpd_ptr == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); + return OPERATOR_CANCELLED; + } + if (*gpd_ptr == NULL) + *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil")); + + /* add new palette now */ + BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palette_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Palette"; + ot->idname = "GPENCIL_OT_palette_add"; + ot->description = "Add new Grease Pencil palette for the active Grease Pencil datablock"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_palette_add_exec; + ot->poll = gp_add_poll; +} + +/* ******************* Remove Active Palette ************************* */ + +static int gp_palette_remove_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + + /* sanity checks */ + if (ELEM(NULL, gpd, palette)) + return OPERATOR_CANCELLED; + + if (BLI_listbase_count(&gpd->palettes) < 2) { + BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a palette. Unable to delete palette"); + return OPERATOR_CANCELLED; + } + + + /* make the palette before this the new active palette + * - use the one after if this is the first + * - if this is the only palette, this naturally becomes NULL + */ + if (palette->prev) + BKE_gpencil_palette_setactive(gpd, palette->prev); + else + BKE_gpencil_palette_setactive(gpd, palette->next); + + /* delete the palette now... */ + BKE_gpencil_palette_delete(gpd, palette); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palette_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove palette"; + ot->idname = "GPENCIL_OT_palette_remove"; + ot->description = "Remove active Grease Pencil palette"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_palette_remove_exec; + ot->poll = gp_active_palette_poll; +} + +/* ********************** Change Palette ***************************** */ + +static int gp_palette_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) +{ + uiPopupMenu *pup; + uiLayout *layout; + + /* call the menu, which will call this operator again, hence the canceled */ + pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE); + layout = UI_popup_menu_layout(pup); + uiItemsEnumO(layout, "GPENCIL_OT_palette_change", "palette"); + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; +} + +static int gp_palette_change_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDpalette *palette = NULL; + int palette_num = RNA_enum_get(op->ptr, "palette"); + + /* Get palette or create new one */ + if (palette_num == -1) { + /* Create palette */ + palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); + } + else { + /* Try to get palette */ + palette = BLI_findlink(&gpd->palettes, palette_num); + + if (palette == NULL) { + BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent palette (index = %d)", palette_num); + return OPERATOR_CANCELLED; + } + } + + /* Set active palette */ + BKE_gpencil_palette_setactive(gpd, palette); + + /* updates */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palette_change(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Change Palette"; + ot->idname = "GPENCIL_OT_palette_change"; + ot->description = "Change active Grease Pencil palette"; + + /* callbacks */ + ot->invoke = gp_palette_change_invoke; + ot->exec = gp_palette_change_exec; + ot->poll = gp_active_palette_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* gp palette to use (dynamic enum) */ + ot->prop = RNA_def_enum(ot->srna, "palette", DummyRNA_DEFAULT_items, 0, "Grease Pencil Palette", ""); + RNA_def_enum_funcs(ot->prop, ED_gpencil_palettes_enum_itemf); +} + +/* ******************* Lock and hide any color non used in current layer ************************** */ + +static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette; + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + palette = BKE_gpencil_palette_getactive(gpd); + if (ELEM(NULL, palette)) + return OPERATOR_CANCELLED; + + /* first lock and hide all colors */ + for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag |= PC_COLOR_LOCKED; + palcolor->flag |= PC_COLOR_HIDE; + } + + /* loop all selected strokes and unlock any color used in active layer */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) && (gpl->flag & GP_LAYER_ACTIVE)) { + for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + /* unlock/unhide color if not unlocked before */ + if (gps->palcolor != NULL) { + gps->palcolor->flag &= ~PC_COLOR_LOCKED; + gps->palcolor->flag &= ~PC_COLOR_HIDE; + } + } + } + } + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palette_lock_layer(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Disable Unused Layer Colors"; + ot->idname = "GPENCIL_OT_palette_lock_layer"; + ot->description = "Lock and hide any color not used in any layer"; + + /* api callbacks */ + ot->exec = gp_palette_lock_layer_exec; + ot->poll = gp_active_layer_poll; +} + +/* ************************************************ */ +/* Palette Colors Operators */ + +/* ******************* Add New Palette ************************ */ + +/* add new palette - wrapper around API */ +static int gp_palettecolor_add_exec(bContext *C, wmOperator *op) +{ + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + + /* if there's no existing Grease-Pencil data there, add some */ + if (gpd_ptr == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); + return OPERATOR_CANCELLED; + } + if (*gpd_ptr == NULL) + *gpd_ptr = BKE_gpencil_data_addnew(DATA_("GPencil")); + + /* verify palette */ + bGPDpalette *palette = BKE_gpencil_palette_getactive(*gpd_ptr); + if (palette == NULL) + palette = BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); + + /* add new palette color now */ + BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Palette Color"; + ot->idname = "GPENCIL_OT_palettecolor_add"; + ot->description = "Add new Grease Pencil palette color for the active Grease Pencil datablock"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_palettecolor_add_exec; + ot->poll = gp_add_poll; +} + +/* ******************* Remove Active Palette color ************************* */ + +static int gp_palettecolor_remove_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *color = BKE_gpencil_palettecolor_getactive(palette); + + /* sanity checks */ + if (ELEM(NULL, gpd, palette, color)) + return OPERATOR_CANCELLED; + + /* make the palette color before this the new active color + * - use the one after if this is the first + * - if this is the only color, this naturally becomes NULL + */ + if (color->prev) + BKE_gpencil_palettecolor_setactive(palette, color->prev); + else + BKE_gpencil_palettecolor_setactive(palette, color->next); + + /* delete the strokes */ + BKE_gpencil_palettecolor_delete_strokes(gpd, color->info); + + /* delete the palette color now... */ + BKE_gpencil_palettecolor_delete(palette, color); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove palette color"; + ot->idname = "GPENCIL_OT_palettecolor_remove"; + ot->description = "Remove active Grease Pencil palette color"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_palettecolor_remove_exec; + ot->poll = gp_active_palettecolor_poll; +} + +/* ********************** Isolate palette color **************************** */ + +static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *active_color = BKE_gpencil_palettecolor_getactive(palette); + bGPDpalettecolor *palcolor; + + int flags = PC_COLOR_LOCKED; + bool isolate = false; + + if (RNA_boolean_get(op->ptr, "affect_visibility")) + flags |= PC_COLOR_HIDE; + + if (ELEM(NULL, gpd, active_color)) { + BKE_report(op->reports, RPT_ERROR, "No active color to isolate"); + return OPERATOR_CANCELLED; + } + + /* Test whether to isolate or clear all flags */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + /* Skip if this is the active one */ + if (palcolor == active_color) + continue; + + /* If the flags aren't set, that means that the color is + * not alone, so we have some colors to isolate still + */ + if ((palcolor->flag & flags) == 0) { + isolate = true; + break; + } + } + + /* Set/Clear flags as appropriate */ + if (isolate) { + /* Set flags on all "other" colors */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + if (palcolor == active_color) + continue; + else + palcolor->flag |= flags; + } + } + else { + /* Clear flags - Restore everything else */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag &= ~flags; + } + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_isolate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Isolate Palette Color"; + ot->idname = "GPENCIL_OT_palettecolor_isolate"; + ot->description = "Toggle whether the active color is the only one that is editable and/or visible"; + + /* callbacks */ + ot->exec = gp_isolate_palettecolor_exec; + ot->poll = gp_active_palettecolor_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility", "In addition to toggling " + "the editability, also affect the visibility"); +} + +/* *********************** Hide Palette colors ******************************** */ + +static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + + bool unselected = RNA_boolean_get(op->ptr, "unselected"); + + /* sanity checks */ + if (ELEM(NULL, gpd, palette, palcolor)) + return OPERATOR_CANCELLED; + + if (unselected) { + bGPDpalettecolor *color; + + /* hide unselected */ + for (color = palette->colors.first; color; color = color->next) { + if (color != palcolor) { + color->flag |= PC_COLOR_HIDE; + } + } + } + else { + /* hide selected/active */ + palcolor->flag |= PC_COLOR_HIDE; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_hide(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Hide Color(s)"; + ot->idname = "GPENCIL_OT_palettecolor_hide"; + ot->description = "Hide selected/unselected Grease Pencil colors"; + + /* callbacks */ + ot->exec = gp_palettecolor_hide_exec; + ot->poll = gp_active_palettecolor_poll; /* NOTE: we need an active color to play with */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected colors"); +} + +/* ********************** Show All Colors ***************************** */ + +/* poll callback for showing colors */ +static int gp_palettecolor_reveal_poll(bContext *C) +{ + return ED_gpencil_data_get_active(C) != NULL; +} + +static int gp_palettecolor_reveal_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor; + + /* sanity checks */ + if (ELEM(NULL, gpd, palette)) + return OPERATOR_CANCELLED; + + /* make all colors visible */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag &= ~PC_COLOR_HIDE; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_reveal(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Show All Colors"; + ot->idname = "GPENCIL_OT_palettecolor_reveal"; + ot->description = "Unhide all hidden Grease Pencil palette colors"; + + /* callbacks */ + ot->exec = gp_palettecolor_reveal_exec; + ot->poll = gp_palettecolor_reveal_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ***************** Lock/Unlock All Palette colors ************************ */ + +static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor; + + /* sanity checks */ + if (ELEM(NULL, gpd, palette)) + return OPERATOR_CANCELLED; + + /* make all layers non-editable */ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag |= PC_COLOR_LOCKED; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Lock All Colors"; + ot->idname = "GPENCIL_OT_palettecolor_lock_all"; + ot->description = "Lock all Grease Pencil colors to prevent them from being accidentally modified"; + + /* callbacks */ + ot->exec = gp_palettecolor_lock_all_exec; + ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* -------------------------- */ + +static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor; + + /* sanity checks */ + if (ELEM(NULL, gpd, palette)) + return OPERATOR_CANCELLED; + + /* make all layers editable again*/ + for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + palcolor->flag &= ~PC_COLOR_LOCKED; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_unlock_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Unlock All Colors"; + ot->idname = "GPENCIL_OT_palettecolor_unlock_all"; + ot->description = "Unlock all Grease Pencil colors so that they can be edited"; + + /* callbacks */ + ot->exec = gp_palettecolor_unlock_all_exec; + ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ******************* Move Color Up/Down ************************** */ + +enum { + GP_COLOR_MOVE_UP = -1, + GP_COLOR_MOVE_DOWN = 1 +}; + +static int gp_palettecolor_move_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + + int direction = RNA_enum_get(op->ptr, "direction"); + + /* sanity checks */ + if (ELEM(NULL, gpd, palette, palcolor)) + return OPERATOR_CANCELLED; + + /* up or down? */ + if (direction == GP_COLOR_MOVE_UP) { + /* up */ + BLI_remlink(&palette->colors, palcolor); + BLI_insertlinkbefore(&palette->colors, palcolor->prev, palcolor); + } + else if (direction == GP_COLOR_MOVE_DOWN) { + /* down */ + BLI_remlink(&palette->colors, palcolor); + BLI_insertlinkafter(&palette->colors, palcolor->next, palcolor); + } + else { + BLI_assert(0); + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_move(wmOperatorType *ot) +{ + static EnumPropertyItem slot_move[] = { + {GP_COLOR_MOVE_UP, "UP", 0, "Up", ""}, + {GP_COLOR_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Move Palette color"; + ot->idname = "GPENCIL_OT_palettecolor_move"; + ot->description = "Move the active Grease Pencil palette color up/down in the list"; + + /* api callbacks */ + ot->exec = gp_palettecolor_move_exec; + ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_COLOR_MOVE_UP, "Direction", ""); +} + +/* ***************** Select all strokes using Palette color ************************ */ + +static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + + /* sanity checks */ + if (ELEM(NULL, gpd, palette, palcolor)) + return OPERATOR_CANCELLED; + + /* read all strokes and select*/ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + /* verify something to do */ + for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) + continue; + + /* select */ + if (strcmp(palcolor->info, gps->colorname) == 0) { + bGPDspoint *pt; + int i; + + gps->flag |= GP_STROKE_SELECT; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag |= GP_SPOINT_SELECT; + } + } + } + } + } + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Color"; + ot->idname = "GPENCIL_OT_palettecolor_select"; + ot->description = "Select all Grease Pencil strokes using current color"; + + /* callbacks */ + ot->exec = gp_palettecolor_select_exec; + ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ***************** Copy Palette color ************************ */ + +static int gp_palettecolor_copy_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + bGPDpalettecolor *newcolor; + + /* sanity checks */ + if (ELEM(NULL, gpd, palette, palcolor)) + return OPERATOR_CANCELLED; + + /* create a new color and duplicate data */ + newcolor = BKE_gpencil_palettecolor_addnew(palette, palcolor->info, true); + copy_v4_v4(newcolor->color, palcolor->color); + copy_v4_v4(newcolor->fill, palcolor->fill); + newcolor->flag = palcolor->flag; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_palettecolor_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy Color"; + ot->idname = "GPENCIL_OT_palettecolor_copy"; + ot->description = "Copy current Grease Pencil palette color"; + + /* callbacks */ + ot->exec = gp_palettecolor_copy_exec; + ot->poll = gp_active_palettecolor_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index ac49a51c716..b298769c6dc 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -125,10 +125,48 @@ static int gp_stroke_edit_poll(bContext *C) return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0; } +/* ************ Stroke Hide selection Toggle ************** */ + +static int gpencil_hideselect_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + + if (ts == NULL) + return OPERATOR_CANCELLED; + + /* Just toggle alpha... */ + if (ts->gp_sculpt.alpha > 0.0f) { + ts->gp_sculpt.alpha = 0.0f; + } + else { + ts->gp_sculpt.alpha = 1.0f; + } + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Hide Selection"; + ot->idname = "GPENCIL_OT_selection_opacity_toggle"; + ot->description = "Hide/Unhide selected points for Grease Pencil strokes setting alpha factor"; + + /* callbacks */ + ot->exec = gpencil_hideselect_toggle_exec; + ot->poll = gp_stroke_edit_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; +} + /* ************** Duplicate Selected Strokes **************** */ /* Make copies of selected point segments in a selected stroke */ -static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes) +static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, const char *layername) { bGPDspoint *pt; int i; @@ -169,6 +207,7 @@ static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes) /* make a stupid copy first of the entire stroke (to get the flags too) */ gpsd = MEM_dupallocN(gps); + strcpy(gpsd->tmp_layerinfo, layername); /* saves original layer name */ /* initialize triangle memory - will be calculated on next redraw */ gpsd->triangles = NULL; @@ -216,8 +255,9 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) /* make copies of selected strokes, and deselect these once we're done */ for (gps = gpf->strokes.first; gps; gps = gps->next) { /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) + if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; + } if (gps->flag & GP_STROKE_SELECT) { if (gps->totpoints == 1) { @@ -226,8 +266,9 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) /* make direct copies of the stroke and its points */ gpsd = MEM_dupallocN(gps); + strcpy(gpsd->tmp_layerinfo, gpl->info); gpsd->points = MEM_dupallocN(gps->points); - + /* triangle information - will be calculated on next redraw */ gpsd->flag |= GP_STROKE_RECALC_CACHES; gpsd->triangles = NULL; @@ -238,7 +279,7 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) } else { /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */ - gp_duplicate_points(gps, &new_strokes); + gp_duplicate_points(gps, &new_strokes, gpl->info); } /* deselect original stroke, or else the originals get moved too @@ -345,6 +386,7 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) /* make direct copies of the stroke and its points */ gpsd = MEM_dupallocN(gps); + strcpy(gpsd->tmp_layerinfo, gpl->info); /* saves original layer name */ gpsd->points = MEM_dupallocN(gps->points); /* triangles cache - will be recalculated on next redraw */ @@ -358,7 +400,7 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) } else { /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */ - gp_duplicate_points(gps, &gp_strokes_copypastebuf); + gp_duplicate_points(gps, &gp_strokes_copypastebuf, gpl->info); } } } @@ -395,13 +437,20 @@ static int gp_strokes_paste_poll(bContext *C) return (CTX_data_active_gpencil_layer(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf)); } +enum { + GP_COPY_ONLY = -1, + GP_COPY_MERGE = 1 +}; + static int gp_strokes_paste_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */ bGPDframe *gpf; - + + int type = RNA_enum_get(op->ptr, "type"); + /* check for various error conditions */ if (gpd == NULL) { BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); @@ -413,9 +462,9 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) } else if (gpl == NULL) { /* no active layer - let's just create one */ - gpl = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); } - else if (gpencil_layer_is_editable(gpl) == false) { + else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_MERGE)) { BKE_report(op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked"); return OPERATOR_CANCELLED; } @@ -463,26 +512,34 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) * we are obliged to add a new frame if one * doesn't exist already */ - gpf = gpencil_layer_getframe(gpl, CFRA, true); - if (gpf) { bGPDstroke *gps; - /* Copy each stroke into the layer */ for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { if (ED_gpencil_stroke_can_use(C, gps)) { - bGPDstroke *new_stroke = MEM_dupallocN(gps); - - new_stroke->points = MEM_dupallocN(gps->points); - - new_stroke->flag |= GP_STROKE_RECALC_CACHES; - new_stroke->triangles = NULL; - - new_stroke->next = new_stroke->prev = NULL; - BLI_addtail(&gpf->strokes, new_stroke); + /* need to verify if layer exist nad frame */ + if (type != GP_COPY_MERGE) { + gpl = BLI_findstring(&gpd->layers, gps->tmp_layerinfo, offsetof(bGPDlayer, info)); + if (gpl == NULL) { + /* no layer - use active (only if layer deleted before paste) */ + gpl = CTX_data_active_gpencil_layer(C); + } + } + gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true); + if (gpf) { + bGPDstroke *new_stroke = MEM_dupallocN(gps); + new_stroke->tmp_layerinfo[0] = '\0'; + + new_stroke->points = MEM_dupallocN(gps->points); + + new_stroke->flag |= GP_STROKE_RECALC_CACHES; + new_stroke->triangles = NULL; + + new_stroke->next = new_stroke->prev = NULL; + BLI_addtail(&gpf->strokes, new_stroke); + } } } - } /* updates */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -492,10 +549,16 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) void GPENCIL_OT_paste(wmOperatorType *ot) { + static EnumPropertyItem copy_type[] = { + {GP_COPY_ONLY, "COPY", 0, "Copy", ""}, + {GP_COPY_MERGE, "MERGE", 0, "Merge", ""}, + {0, NULL, 0, NULL, NULL} + }; + /* identifiers */ ot->name = "Paste Strokes"; ot->idname = "GPENCIL_OT_paste"; - ot->description = "Paste previously copied strokes into active layer"; + ot->description = "Paste previously copied strokes or copy and merge in active layer"; /* callbacks */ ot->exec = gp_strokes_paste_exec; @@ -503,6 +566,8 @@ void GPENCIL_OT_paste(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", ""); } /* ******************* Move To Layer ****************************** */ @@ -532,7 +597,7 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op) /* Get layer or create new one */ if (layer_num == -1) { /* Create layer */ - target_layer = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + target_layer = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); } else { /* Try to get layer */ @@ -577,7 +642,7 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op) /* Paste them all in one go */ if (strokes.first) { Scene *scene = CTX_data_scene(C); - bGPDframe *gpf = gpencil_layer_getframe(target_layer, CFRA, true); + bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, CFRA, true); BLI_movelisttolist(&gpf->strokes, &strokes); BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); @@ -614,7 +679,7 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot) static int gp_actframe_delete_poll(bContext *C) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); /* only if there's an active layer with an active frame */ return (gpl && gpl->actframe); @@ -625,8 +690,8 @@ static int gp_actframe_delete_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); - bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); /* if there's no existing Grease-Pencil data there, add some */ if (gpd == NULL) { @@ -639,7 +704,7 @@ static int gp_actframe_delete_exec(bContext *C, wmOperator *op) } /* delete it... */ - gpencil_layer_delframe(gpl, gpf); + BKE_gpencil_layer_delframe(gpl, gpf); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -681,13 +746,13 @@ static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op) CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { /* try to get the "active" frame - but only if it actually occurs on this frame */ - bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0); + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); if (gpf == NULL) continue; /* delete it... */ - gpencil_layer_delframe(gpl, gpf); + BKE_gpencil_layer_delframe(gpl, gpf); /* we successfully modified something */ success = true; @@ -1069,7 +1134,7 @@ void GPENCIL_OT_delete(wmOperatorType *ot) }; /* identifiers */ - ot->name = "Delete..."; + ot->name = "Delete"; ot->idname = "GPENCIL_OT_delete"; ot->description = "Delete selected Grease Pencil strokes, vertices, or frames"; @@ -1124,25 +1189,65 @@ static int gp_snap_poll(bContext *C) static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) { RegionView3D *rv3d = CTX_wm_region_data(C); - float gridf = rv3d->gridview; + const float gridf = rv3d->gridview; - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - bGPDspoint *pt; - int i; - - // TOOD: if entire stroke is selected, offset entire stroke by same amount? - - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - /* only if point is selected.. */ - if (pt->flag & GP_SPOINT_SELECT) { - pt->x = gridf * floorf(0.5f + pt->x / gridf); - pt->y = gridf * floorf(0.5f + pt->y / gridf); - pt->z = gridf * floorf(0.5f + pt->z / gridf); + bGPdata *gpd = ED_gpencil_data_get_active(C); + float diff_mat[4][4]; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + } + + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps; + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + + bGPDspoint *pt; + int i; + + // TOOD: if entire stroke is selected, offset entire stroke by same amount? + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + + /* only if point is selected.. */ + if (pt->flag & GP_SPOINT_SELECT) { + if (gpl->parent == NULL) { + pt->x = gridf * floorf(0.5f + pt->x / gridf); + pt->y = gridf * floorf(0.5f + pt->y / gridf); + pt->z = gridf * floorf(0.5f + pt->z / gridf); + } + else { + /* apply parent transformations */ + float fpt[3]; + mul_v3_m4v3(fpt, diff_mat, &pt->x); + + fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf); + fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf); + fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf); + + /* return data */ + copy_v3_v3(&pt->x, fpt); + gp_apply_parent_point(gpl, pt); + } + + } + } + } } } - CTX_DATA_END; WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -1169,41 +1274,68 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); - + const bool use_offset = RNA_boolean_get(op->ptr, "use_offset"); const float *cursor_global = ED_view3d_cursor3d_get(scene, v3d); - - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - bGPDspoint *pt; - int i; - - /* only continue if this stroke is selected (editable doesn't guarantee this)... */ - if ((gps->flag & GP_STROKE_SELECT) == 0) - continue; - - if (use_offset) { - float offset[3]; - - /* compute offset from first point of stroke to cursor */ - /* TODO: Allow using midpoint instead? */ - sub_v3_v3v3(offset, cursor_global, &gps->points->x); - - /* apply offset to all points in the stroke */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - add_v3_v3(&pt->x, offset); + + bGPdata *gpd = ED_gpencil_data_get_active(C); + float diff_mat[4][4]; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); } - } - else { - /* affect each selected point */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - copy_v3_v3(&pt->x, cursor_global); + + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps; + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + + bGPDspoint *pt; + int i; + + /* only continue if this stroke is selected (editable doesn't guarantee this)... */ + if ((gps->flag & GP_STROKE_SELECT) == 0) + continue; + + if (use_offset) { + float offset[3]; + + /* compute offset from first point of stroke to cursor */ + /* TODO: Allow using midpoint instead? */ + sub_v3_v3v3(offset, cursor_global, &gps->points->x); + + /* apply offset to all points in the stroke */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + add_v3_v3(&pt->x, offset); + } + } + else { + /* affect each selected point */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + copy_v3_v3(&pt->x, cursor_global); + if (gpl->parent != NULL) { + gp_apply_parent_point(gpl, pt); + } + } + } } + + } } } - CTX_DATA_END; WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -1243,24 +1375,57 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) INIT_MINMAX(min, max); /* calculate midpoints from selected points */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - bGPDspoint *pt; - int i; - - /* only continue if this stroke is selected (editable doesn't guarantee this)... */ - if ((gps->flag & GP_STROKE_SELECT) == 0) - continue; - - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - add_v3_v3(centroid, &pt->x); - minmax_v3v3_v3(min, max, &pt->x); - count++; + bGPdata *gpd = ED_gpencil_data_get_active(C); + float diff_mat[4][4]; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + } + + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps; + for (gps = gpf->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + + bGPDspoint *pt; + int i; + + /* only continue if this stroke is selected (editable doesn't guarantee this)... */ + if ((gps->flag & GP_STROKE_SELECT) == 0) + continue; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + if (gpl->parent == NULL) { + add_v3_v3(centroid, &pt->x); + minmax_v3v3_v3(min, max, &pt->x); + } + else { + /* apply parent transformations */ + float fpt[3]; + mul_v3_m4v3(fpt, diff_mat, &pt->x); + + add_v3_v3(centroid, fpt); + minmax_v3v3_v3(min, max, fpt); + } + count++; + } + } + } } } - CTX_DATA_END; if (v3d->around == V3D_AROUND_CENTER_MEAN && count) { mul_v3_fl(centroid, 1.0f / (float)count); diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 53fb33eeb9b..f37fba4212d 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -101,6 +101,23 @@ void gp_point_to_xy(GP_SpaceConversion *settings, struct bGPDstroke *gps, struct int *r_x, int *r_y); /** + * Convert point to parent space + * + * \param pt Original point + * \param diff_mat Matrix with the difference between original parent matrix + * \param[out] r_pt Pointer to new point after apply matrix + */ +void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint *r_pt); +/** + * Change points position relative to parent object + */ +void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps); +/** + * Change point position relative to parent object + */ +void gp_apply_parent_point(bGPDlayer *gpl, bGPDspoint *pt); + +/** * Convert a screenspace point to a 3D Grease Pencil coordinate. * * For use with editing tools where it is easier to perform the operations in 2D, @@ -116,6 +133,10 @@ bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, struct Scene *scene, const float int gp_add_poll(struct bContext *C); int gp_active_layer_poll(struct bContext *C); +int gp_active_brush_poll(struct bContext *C); +int gp_active_palette_poll(struct bContext *C); +int gp_active_palettecolor_poll(struct bContext *C); +int gp_brush_crt_presets_poll(bContext *C); /* Copy/Paste Buffer --------------------------------- */ /* gpencil_edit.c */ @@ -137,17 +158,47 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure); /** +* Apply smooth for strength to stroke point +* \param gps Stroke to smooth +* \param i Point index +* \param inf Amount of smoothing to apply +*/ +bool gp_smooth_stroke_strength(bGPDstroke *gps, int i, float inf); + +/** +* Apply smooth for thickness to stroke point (use pressure) +* \param gps Stroke to smooth +* \param i Point index +* \param inf Amount of smoothing to apply +*/ +bool gp_smooth_stroke_thickness(bGPDstroke *gps, int i, float inf); + +/** * Subdivide a stroke once, by adding points at the midpoint between each pair of points * \param gps Stroke data * \param new_totpoints Total number of points (after subdividing) */ void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints); +/** +* Add randomness to stroke +* \param gps Stroke data +* \param brsuh Brush data +*/ +void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush); + /* Layers Enums -------------------------------------- */ struct EnumPropertyItem *ED_gpencil_layers_enum_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); struct EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free); +/* Enums of GP Brushes */ +EnumPropertyItem *ED_gpencil_brushes_enum_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), + bool *r_free); + +/* Enums of GP palettes */ +EnumPropertyItem *ED_gpencil_palettes_enum_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), + bool *r_free); /* ***************************************************** */ /* Operator Defines */ @@ -155,7 +206,7 @@ struct EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(struct bContext * void GPENCIL_OT_draw(struct wmOperatorType *ot); -/* Paint Modes for operator*/ +/* Paint Modes for operator */ typedef enum eGPencil_PaintModes { GP_PAINTMODE_DRAW = 0, GP_PAINTMODE_ERASER, @@ -166,6 +217,7 @@ typedef enum eGPencil_PaintModes { /* stroke editing ----- */ void GPENCIL_OT_editmode_toggle(struct wmOperatorType *ot); +void GPENCIL_OT_selection_opacity_toggle(struct wmOperatorType *ot); void GPENCIL_OT_select(struct wmOperatorType *ot); void GPENCIL_OT_select_all(struct wmOperatorType *ot); @@ -216,12 +268,50 @@ void GPENCIL_OT_lock_all(struct wmOperatorType *ot); void GPENCIL_OT_unlock_all(struct wmOperatorType *ot); void GPENCIL_OT_layer_isolate(struct wmOperatorType *ot); +void GPENCIL_OT_layer_merge(struct wmOperatorType *ot); void GPENCIL_OT_active_frame_delete(struct wmOperatorType *ot); void GPENCIL_OT_active_frames_delete_all(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); +enum { + GP_STROKE_JOIN = -1, + GP_STROKE_JOINCOPY = 1 +}; + +void GPENCIL_OT_stroke_arrange(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_change_color(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_lock_color(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_apply_thickness(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_cyclical_set(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_join(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_flip(struct wmOperatorType *ot); + +void GPENCIL_OT_brush_add(struct wmOperatorType *ot); +void GPENCIL_OT_brush_remove(struct wmOperatorType *ot); +void GPENCIL_OT_brush_change(struct wmOperatorType *ot); +void GPENCIL_OT_brush_move(struct wmOperatorType *ot); +void GPENCIL_OT_brush_presets_create(struct wmOperatorType *ot); +void GPENCIL_OT_brush_copy(struct wmOperatorType *ot); +void GPENCIL_OT_brush_select(struct wmOperatorType *ot); + +void GPENCIL_OT_palette_add(struct wmOperatorType *ot); +void GPENCIL_OT_palette_remove(struct wmOperatorType *ot); +void GPENCIL_OT_palette_change(struct wmOperatorType *ot); +void GPENCIL_OT_palette_lock_layer(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_add(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_remove(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_isolate(struct wmOperatorType *ot); + +void GPENCIL_OT_palettecolor_hide(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_reveal(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_lock_all(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_unlock_all(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_move(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_select(struct wmOperatorType *ot); +void GPENCIL_OT_palettecolor_copy(struct wmOperatorType *ot); + /* undo stack ---------- */ void gpencil_undo_init(struct bGPdata *gpd); @@ -273,4 +363,39 @@ typedef enum ACTCONT_TYPES { ACTCONT_GPENCIL } ACTCONT_TYPES; +/** +* Iterate over all editable strokes in the current context, +* stopping on each usable layer + stroke pair (i.e. gpl and gps) +* to perform some operations on the stroke. +* +* \param gpl The identifier to use for the layer of the stroke being processed. +* Choose a suitable value to avoid name clashes. +* \param gps The identifier to use for current stroke being processed. +* Choose a suitable value to avoid name clashes. +*/ +#define GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) \ +{ \ + CTX_DATA_BEGIN(C, bGPDlayer*, gpl, editable_gpencil_layers) \ + { \ + if (gpl->actframe == NULL) \ + continue; \ + /* calculate difference matrix if parent object */ \ + float diff_mat[4][4]; \ + ED_gpencil_parent_location(gpl, diff_mat); \ + /* loop over strokes */ \ + for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { \ + /* skip strokes that are invalid for current view */ \ + if (ED_gpencil_stroke_can_use(C, gps) == false) \ + continue; \ + /* check if the color is editable */ \ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) \ + continue; \ + /* ... Do Stuff With Strokes ... */ + +#define GP_EDITABLE_STROKES_END \ + } \ + } \ + CTX_DATA_END; \ +} (void)0 + #endif /* __GPENCIL_INTERN_H__ */ diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 65ee1122b56..50f4e795d70 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2009, Blender Foundation, Joshua Leung * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -140,8 +140,8 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) * that the only data being edited is that of the Grease Pencil strokes */ - /* FKEY = Eraser Radius */ - kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0); + /* CTRL + FKEY = Eraser Radius */ + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0); RNA_string_set(kmi->ptr, "data_path_primary", "user_preferences.edit.grease_pencil_eraser_radius"); @@ -169,8 +169,8 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0); RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.strength"); - /* Ctrl-FKEY = Sculpt Brush Size */ - kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0); + /* FKEY = Sculpt Brush Size */ + kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.size"); @@ -240,6 +240,12 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "GPENCIL_OT_active_frames_delete_all", XKEY, KM_PRESS, KM_SHIFT, 0); + /* join strokes */ + WM_keymap_add_item(keymap, "GPENCIL_OT_stroke_join", JKEY, KM_PRESS, KM_CTRL, 0); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_stroke_join", JKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0); + RNA_enum_set(kmi->ptr, "type", GP_STROKE_JOINCOPY); + /* copy + paste */ WM_keymap_add_item(keymap, "GPENCIL_OT_copy", CKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "GPENCIL_OT_paste", VKEY, KM_PRESS, KM_CTRL, 0); @@ -266,14 +272,37 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_hide", HKEY, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "unselected", true); + + WM_keymap_add_item(keymap, "GPENCIL_OT_selection_opacity_toggle", HKEY, KM_PRESS, KM_CTRL, 0); /* Isolate Layer */ WM_keymap_add_item(keymap, "GPENCIL_OT_layer_isolate", PADASTERKEY, KM_PRESS, 0, 0); /* Move to Layer */ WM_keymap_add_item(keymap, "GPENCIL_OT_move_to_layer", MKEY, KM_PRESS, 0, 0); - - + + /* Select drawing brush using index */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", ONEKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 0); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", TWOKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 1); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", THREEKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 2); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", FOURKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 3); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", FIVEKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 4); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", SIXKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 5); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", SEVENKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 6); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", EIGHTKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 7); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", NINEKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 8); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", ZEROKEY, KM_PRESS, 0, 0); + RNA_int_set(kmi->ptr, "index", 9); + /* Transform Tools */ kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_translate", GKEY, KM_PRESS, 0, 0); @@ -318,7 +347,8 @@ void ED_operatortypes_gpencil(void) /* Editing (Strokes) ------------ */ WM_operatortype_append(GPENCIL_OT_editmode_toggle); - + WM_operatortype_append(GPENCIL_OT_selection_opacity_toggle); + WM_operatortype_append(GPENCIL_OT_select); WM_operatortype_append(GPENCIL_OT_select_all); WM_operatortype_append(GPENCIL_OT_select_circle); @@ -362,12 +392,44 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_lock_all); WM_operatortype_append(GPENCIL_OT_unlock_all); WM_operatortype_append(GPENCIL_OT_layer_isolate); - + WM_operatortype_append(GPENCIL_OT_layer_merge); + WM_operatortype_append(GPENCIL_OT_active_frame_delete); WM_operatortype_append(GPENCIL_OT_active_frames_delete_all); WM_operatortype_append(GPENCIL_OT_convert); + WM_operatortype_append(GPENCIL_OT_stroke_arrange); + WM_operatortype_append(GPENCIL_OT_stroke_change_color); + WM_operatortype_append(GPENCIL_OT_stroke_lock_color); + WM_operatortype_append(GPENCIL_OT_stroke_apply_thickness); + WM_operatortype_append(GPENCIL_OT_stroke_cyclical_set); + WM_operatortype_append(GPENCIL_OT_stroke_join); + WM_operatortype_append(GPENCIL_OT_stroke_flip); + + WM_operatortype_append(GPENCIL_OT_palette_add); + WM_operatortype_append(GPENCIL_OT_palette_remove); + WM_operatortype_append(GPENCIL_OT_palette_change); + WM_operatortype_append(GPENCIL_OT_palette_lock_layer); + WM_operatortype_append(GPENCIL_OT_palettecolor_add); + WM_operatortype_append(GPENCIL_OT_palettecolor_remove); + WM_operatortype_append(GPENCIL_OT_palettecolor_isolate); + WM_operatortype_append(GPENCIL_OT_palettecolor_hide); + WM_operatortype_append(GPENCIL_OT_palettecolor_reveal); + WM_operatortype_append(GPENCIL_OT_palettecolor_lock_all); + WM_operatortype_append(GPENCIL_OT_palettecolor_unlock_all); + WM_operatortype_append(GPENCIL_OT_palettecolor_move); + WM_operatortype_append(GPENCIL_OT_palettecolor_select); + WM_operatortype_append(GPENCIL_OT_palettecolor_copy); + + WM_operatortype_append(GPENCIL_OT_brush_add); + WM_operatortype_append(GPENCIL_OT_brush_remove); + WM_operatortype_append(GPENCIL_OT_brush_change); + WM_operatortype_append(GPENCIL_OT_brush_move); + WM_operatortype_append(GPENCIL_OT_brush_presets_create); + WM_operatortype_append(GPENCIL_OT_brush_copy); + WM_operatortype_append(GPENCIL_OT_brush_select); + /* Editing (Time) --------------- */ } diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index a570d586f50..e7e39a85792 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -39,21 +39,26 @@ #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_rand.h" #include "BLT_translation.h" #include "PIL_time.h" +#include "BKE_main.h" +#include "BKE_paint.h" #include "BKE_gpencil.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_report.h" #include "BKE_screen.h" #include "BKE_tracking.h" +#include "BKE_colortools.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_gpencil_types.h" +#include "DNA_brush_types.h" #include "DNA_windowmanager_types.h" #include "UI_view2d.h" @@ -151,6 +156,10 @@ typedef struct tGPsdata { float custom_color[4]; /* custom color - hack for enforcing a particular color for track/mask editing */ void *erasercursor; /* radial cursor data for drawing eraser */ + + bGPDpalettecolor *palettecolor; /* current palette color */ + bGPDbrush *brush; /* current drawing brush */ + short straight[2]; /* 1: line horizontal, 2: line vertical, other: not defined, second element position */ } tGPsdata; /* ------ */ @@ -333,10 +342,90 @@ static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3] } } +/* apply jitter to stroke */ +static void gp_brush_jitter(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const int mval[2], int r_mval[2]) +{ + float pressure = pt->pressure; + float tmp_pressure = pt->pressure; + if (brush->draw_jitter > 0.0f) { + float curvef = curvemapping_evaluateF(brush->cur_jitter, 0, pressure); + tmp_pressure = curvef * brush->draw_sensitivity; + } + const float exfactor = (brush->draw_jitter + 2.0f) * (brush->draw_jitter + 2.0f); /* exponential value */ + const float fac = BLI_frand() * exfactor * tmp_pressure; + /* Jitter is applied perpendicular to the mouse movement vector (2D space) */ + float mvec[2], svec[2]; + /* mouse movement in ints -> floats */ + if (gpd->sbuffer_size > 1) { + mvec[0] = (float)(mval[0] - (pt - 1)->x); + mvec[1] = (float)(mval[1] - (pt - 1)->y); + normalize_v2(mvec); + } + else { + mvec[0] = 0.0f; + mvec[1] = 0.0f; + } + /* rotate mvec by 90 degrees... */ + svec[0] = -mvec[1]; + svec[1] = mvec[0]; + /* scale the displacement by the random, and apply */ + if (BLI_frand() > 0.5f) { + mul_v2_fl(svec, -fac); + } + else { + mul_v2_fl(svec, fac); + } + + r_mval[0] = mval[0] + svec[0]; + r_mval[1] = mval[1] + svec[1]; + +} + +/* apply pressure change depending of the angle of the stroke to simulate a pen with shape */ +static void gp_brush_angle(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const int mval[2]) +{ + float mvec[2]; + float sen = brush->draw_angle_factor; /* sensitivity */; + float fac; + float mpressure; + + float angle = brush->draw_angle; /* default angle of brush in radians */; + float v0[2] = { cos(angle), sin(angle) }; /* angle vector of the brush with full thickness */ + + /* Apply to first point (only if there are 2 points because before no data to do it ) */ + if (gpd->sbuffer_size == 1) { + mvec[0] = (float)(mval[0] - (pt - 1)->x); + mvec[1] = (float)(mval[1] - (pt - 1)->y); + normalize_v2(mvec); + + /* uses > 1.0f to get a smooth transition in first point */ + fac = 1.4f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */ + (pt - 1)->pressure = (pt - 1)->pressure - (sen * fac); + + CLAMP((pt - 1)->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f); + } + + /* apply from second point */ + if (gpd->sbuffer_size >= 1) { + mvec[0] = (float)(mval[0] - (pt - 1)->x); + mvec[1] = (float)(mval[1] - (pt - 1)->y); + normalize_v2(mvec); + + fac = 1.0f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */ + /* interpolate with previous point for smoother transitions */ + mpressure = interpf(pt->pressure - (sen * fac), (pt - 1)->pressure, 0.3f); + pt->pressure = mpressure; + + CLAMP(pt->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f); + } + +} + /* add current stroke-point to buffer (returns whether point was successfully added) */ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, double curtime) { bGPdata *gpd = p->gpd; + bGPDbrush *brush = p->brush; tGPspoint *pt; /* check painting mode */ @@ -349,6 +438,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, /* store settings */ copy_v2_v2_int(&pt->x, mval); pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->strength = 1.0f; pt->time = (float)(curtime - p->inittime); /* increment buffer size */ @@ -363,6 +453,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, /* store settings */ copy_v2_v2_int(&pt->x, mval); pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->strength = 1.0f; pt->time = (float)(curtime - p->inittime); /* now the buffer has 2 points (and shouldn't be allowed to get any larger) */ @@ -381,8 +472,65 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, pt = ((tGPspoint *)(gpd->sbuffer) + gpd->sbuffer_size); /* store settings */ - copy_v2_v2_int(&pt->x, mval); - pt->pressure = pressure; + /* pressure */ + if (brush->flag & GP_BRUSH_USE_PRESSURE) { + float curvef = curvemapping_evaluateF(brush->cur_sensitivity, 0, pressure); + pt->pressure = curvef * brush->draw_sensitivity; + } + else { + pt->pressure = 1.0f; + } + /* Apply jitter to position */ + if (brush->draw_jitter > 0.0f) { + int r_mval[2]; + gp_brush_jitter(gpd, brush, pt, mval, r_mval); + copy_v2_v2_int(&pt->x, r_mval); + } + else { + copy_v2_v2_int(&pt->x, mval); + } + /* apply randomness to pressure */ + if ((brush->draw_random_press > 0.0f) && (brush->flag & GP_BRUSH_USE_RANDOM_PRESSURE)) { + float curvef = curvemapping_evaluateF(brush->cur_sensitivity, 0, pressure); + float tmp_pressure = curvef * brush->draw_sensitivity; + if (BLI_frand() > 0.5f) { + pt->pressure -= tmp_pressure * brush->draw_random_press * BLI_frand(); + } + else { + pt->pressure += tmp_pressure * brush->draw_random_press * BLI_frand(); + } + CLAMP(pt->pressure, GPENCIL_STRENGTH_MIN, 1.0f); + } + + /* apply angle of stroke to brush size */ + if (brush->draw_angle_factor > 0.0f) { + gp_brush_angle(gpd, brush, pt, mval); + } + + /* color strength */ + if (brush->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { + float curvef = curvemapping_evaluateF(brush->cur_strength, 0, pressure); + float tmp_pressure = curvef * brush->draw_sensitivity; + + pt->strength = tmp_pressure * brush->draw_strength; + } + else { + pt->strength = brush->draw_strength; + } + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + + /* apply randomness to color strength */ + if ((brush->draw_random_press > 0.0f) && (brush->flag & GP_BRUSH_USE_RANDOM_STRENGTH)) { + if (BLI_frand() > 0.5f) { + pt->strength -= pt->strength * brush->draw_random_press * BLI_frand(); + } + else { + pt->strength += pt->strength * brush->draw_random_press * BLI_frand(); + } + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + } + + /* point time */ pt->time = (float)(curtime - p->inittime); /* increment counters */ @@ -395,12 +543,15 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, return GP_STROKEADD_NORMAL; } else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { + + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); /* get pointer to destination point */ pt = (tGPspoint *)(gpd->sbuffer); /* store settings */ copy_v2_v2_int(&pt->x, mval); pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */ + pt->strength = 1.0f; pt->time = (float)(curtime - p->inittime); /* if there's stroke for this poly line session add (or replace last) point @@ -433,10 +584,16 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); - + /* if parented change position relative to parent object */ + if (gpl->parent != NULL) { + gp_apply_parent_point(gpl, pts); + } /* copy pressure and time */ pts->pressure = pt->pressure; + pts->strength = pt->strength; pts->time = pt->time; + /* force fill recalc */ + gps->flag |= GP_STROKE_RECALC_CACHES; } /* increment counters */ @@ -534,6 +691,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) bGPDstroke *gps; bGPDspoint *pt; tGPspoint *ptc; + bGPDbrush *brush = p->brush; + ToolSettings *ts = p->scene->toolsettings; int i, totelem; /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ @@ -569,7 +728,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* copy appropriate settings for stroke */ gps->totpoints = totelem; - gps->thickness = p->gpl->thickness; + gps->thickness = brush->thickness; gps->flag = gpd->sbuffer_sflag; gps->inittime = p->inittime; @@ -577,7 +736,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) gps->flag |= GP_STROKE_RECALC_CACHES; /* allocate enough memory for a continuous array for storage points */ - int sublevel = gpl->sublevel; + int sublevel = brush->sublevel; int new_totpoints = gps->totpoints; for (i = 0; i < sublevel; i++) { @@ -600,9 +759,14 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - + /* if parented change position relative to parent object */ + if (gpl->parent != NULL) { + gp_apply_parent_point(gpl, pt); + } /* copy pressure and time */ pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; pt++; @@ -614,9 +778,15 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - + /* if parented change position relative to parent object */ + if (gpl->parent != NULL) { + gp_apply_parent_point(gpl, pt); + } + /* copy pressure and time */ pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; } } @@ -626,9 +796,14 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - + /* if parented change position relative to parent object */ + if (gpl->parent != NULL) { + gp_apply_parent_point(gpl, pt); + } /* copy pressure and time */ pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; } else { @@ -703,6 +878,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* copy pressure and time */ pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; } @@ -716,28 +893,49 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) gp_subdivide_stroke(gps, totpoints); } } - + /* apply randomness to stroke */ + if (brush->draw_random_sub > 0.0f) { + gp_randomize_stroke(gps, brush); + } + /* smooth stroke after subdiv - only if there's something to do * for each iteration, the factor is reduced to get a better smoothing without changing too much * the original stroke */ - if (gpl->draw_smoothfac > 0.0f) { + if (brush->draw_smoothfac > 0.0f) { float reduce = 0.0f; - for (int r = 0; r < gpl->draw_smoothlvl; ++r) { + for (int r = 0; r < brush->draw_smoothlvl; ++r) { for (i = 0; i < gps->totpoints; i++) { /* NOTE: No pressure smoothing, or else we get annoying thickness changes while drawing... */ - gp_smooth_stroke(gps, i, gpl->draw_smoothfac - reduce, false); + gp_smooth_stroke(gps, i, brush->draw_smoothfac - reduce, false); } reduce += 0.25f; // reduce the factor } } - + /* if parented change position relative to parent object */ + if (gpl->parent != NULL) { + gp_apply_parent(gpl, gps); + } + if (depth_arr) MEM_freeN(depth_arr); } + /* Save palette color */ + bGPDpalette *palette = BKE_gpencil_palette_getactive(p->gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + gps->palcolor = palcolor; + strcpy(gps->colorname, palcolor->info); - /* add stroke to frame */ - BLI_addtail(&p->gpf->strokes, gps); + /* add stroke to frame, usually on tail of the listbase, but if on back is enabled the stroke is added on listbase head + * because the drawing order is inverse and the head stroke is the first to draw. This is very useful for artist + * when drawing the background + */ + if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode != GP_PAINTMODE_DRAW_POLY)) { + BLI_addhead(&p->gpf->strokes, gps); + } + else { + BLI_addtail(&p->gpf->strokes, gps); + } gp_stroke_added_enable(p); } @@ -761,12 +959,21 @@ static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, cons (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH)) { RegionView3D *rv3d = p->ar->regiondata; + bGPDlayer *gpl = p->gpl; + const int mval[2] = {x, y}; float mval_3d[3]; - + float fpt[3]; + + float diff_mat[4][4]; + /* calculate difference matrix if parent object */ + ED_gpencil_parent_location(gpl, diff_mat); + if (ED_view3d_autodist_simple(p->ar, mval, mval_3d, 0, NULL)) { const float depth_mval = view3d_point_depth(rv3d, mval_3d); - const float depth_pt = view3d_point_depth(rv3d, &pt->x); + + mul_v3_m4v3(fpt, diff_mat, &pt->x); + const float depth_pt = view3d_point_depth(rv3d, fpt); if (depth_pt > depth_mval) { return true; @@ -804,7 +1011,13 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, int pc1[2] = {0}; int pc2[2] = {0}; int i; - + float diff_mat[4][4]; + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + } + if (gps->totpoints == 0) { /* just free stroke */ if (gps->points) @@ -816,8 +1029,14 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, else if (gps->totpoints == 1) { /* only process if it hasn't been masked out... */ if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) { - gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]); - + if (gpl->parent == NULL) { + gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]); + } + else { + bGPDspoint pt_temp; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]); + } /* do boundbox check first */ if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) { /* only check if point is inside */ @@ -826,7 +1045,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, // XXX: pressure sensitive eraser should apply here too? MEM_freeN(gps->points); if (gps->triangles) - MEM_freeN(gps->triangles); + MEM_freeN(gps->triangles); BLI_freelinkN(&gpf->strokes, gps); } } @@ -836,7 +1055,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* Pressure threshold at which stroke should be culled: Calculated as pressure value * below which we would have invisible strokes */ - const float cull_thresh = (gpl->thickness) ? 1.0f / ((float)gpl->thickness) : 1.0f; + const float cull_thresh = (gps->thickness) ? 1.0f / ((float)gps->thickness) : 1.0f; /* Amount to decrease the pressure of each point with each stroke */ // TODO: Fetch from toolsettings, or compute based on thickness instead? @@ -865,15 +1084,24 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* get points to work with */ pt1 = gps->points + i; pt2 = gps->points + i + 1; - + /* only process if it hasn't been masked out... */ if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) continue; - /* get coordinates of point in screenspace */ - gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]); - gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]); - + if (gpl->parent == NULL) { + gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]); + gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]); + } + else { + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]); + + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]); + } + /* Check that point segment of the boundbox of the eraser stroke */ if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) || ((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1]))) @@ -955,7 +1183,10 @@ static void gp_stroke_doeraser(tGPsdata *p) /* loop over strokes, checking segments for intersections */ for (gps = gpf->strokes.first; gps; gps = gpn) { gpn = gps->next; - + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } /* Not all strokes in the datablock may be valid in the current editor/context * (e.g. 2D space strokes in the 3D view, if the same datablock is shared) */ @@ -994,6 +1225,78 @@ static void gp_session_validatebuffer(tGPsdata *p) p->inittime = 0.0; } +/* create a new palette color */ +static bGPDpalettecolor *gp_create_new_color(bGPDpalette *palette) +{ + bGPDpalettecolor *palcolor; + + palcolor = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); + + return palcolor; +} + +/* initialize a drawing brush */ +static void gp_init_drawing_brush(ToolSettings *ts, tGPsdata *p) +{ + bGPDbrush *brush; + + /* if not exist, create a new one */ + if (BLI_listbase_is_empty(&ts->gp_brushes)) { + /* create new brushes */ + BKE_gpencil_brush_init_presets(ts); + brush = BKE_gpencil_brush_getactive(ts); + } + else { + /* Use the current */ + brush = BKE_gpencil_brush_getactive(ts); + } + /* be sure curves are initializated */ + curvemapping_initialize(brush->cur_sensitivity); + curvemapping_initialize(brush->cur_strength); + curvemapping_initialize(brush->cur_jitter); + + /* asign to temp tGPsdata */ + p->brush = brush; +} + + +/* initialize a paint palette brush and a default color if not exist */ +static void gp_init_palette(tGPsdata *p) +{ + bGPdata *gpd; + bGPDpalette *palette; + bGPDpalettecolor *palcolor; + + gpd = p->gpd; + + /* if not exist, create a new palette */ + if (BLI_listbase_is_empty(&gpd->palettes)) { + /* create new palette */ + palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); + /* now create a default color */ + palcolor = gp_create_new_color(palette); + } + else { + /* Use the current palette and color */ + palette = BKE_gpencil_palette_getactive(gpd); + /* the palette needs one color */ + if (BLI_listbase_is_empty(&palette->colors)) { + palcolor = gp_create_new_color(palette); + } + else { + palcolor = BKE_gpencil_palettecolor_getactive(palette); + } + /* in some situations can be null, so use first */ + if (palcolor == NULL) { + BKE_gpencil_palettecolor_setactive(palette, palette->colors.first); + palcolor = palette->colors.first; + } + } + + /* asign to temp tGPsdata */ + p->palettecolor = palcolor; +} + /* (re)init new painting data */ static bool gp_session_initdata(bContext *C, tGPsdata *p) { @@ -1145,7 +1448,7 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) else { /* if no existing GPencil block exists, add one */ if (*gpd_ptr == NULL) - *gpd_ptr = gpencil_data_addnew("GPencil"); + *gpd_ptr = BKE_gpencil_data_addnew("GPencil"); p->gpd = *gpd_ptr; } @@ -1158,7 +1461,16 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) /* clear out buffer (stored in gp-data), in case something contaminated it */ gp_session_validatebuffer(p); - + /* set brush and create a new one if null */ + gp_init_drawing_brush(ts, p); + /* set palette info and create a new one if null */ + gp_init_palette(p); + /* set palette colors */ + bGPDpalettecolor *palcolor = p->palettecolor; + bGPdata *pdata = p->gpd; + copy_v4_v4(pdata->scolor, palcolor->color); + pdata->sflag = palcolor->flag; + return 1; } @@ -1177,7 +1489,7 @@ static tGPsdata *gp_session_initpaint(bContext *C) * erase size won't get lost */ p->radius = U.gp_eraser; - + /* return context data for running paint operator */ return p; } @@ -1211,9 +1523,9 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode) ToolSettings *ts = scene->toolsettings; /* get active layer (or add a new one if non-existent) */ - p->gpl = gpencil_layer_getactive(p->gpd); + p->gpl = BKE_gpencil_layer_getactive(p->gpd); if (p->gpl == NULL) { - p->gpl = gpencil_layer_addnew(p->gpd, "GP_Layer", true); + p->gpl = BKE_gpencil_layer_addnew(p->gpd, "GP_Layer", true); if (p->custom_color[3]) copy_v3_v3(p->gpl->color, p->custom_color); @@ -1241,7 +1553,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode) /* Add a new frame if needed (and based off the active frame, * as we need some existing strokes to erase) */ - gpl->actframe = gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY); + gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY); /* XXX: we omit GP_FRAME_PAINT here for now, * as it is only really useful for doing @@ -1277,7 +1589,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode) else add_frame_mode = GP_GETFRAME_ADD_NEW; - p->gpf = gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode); + p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode); if (p->gpf == NULL) { p->status = GP_STATUS_ERROR; @@ -1495,7 +1807,6 @@ static bool gpencil_is_tablet_eraser_active(const wmEvent *event) /* ------------------------------- */ - static void gpencil_draw_exit(bContext *C, wmOperator *op) { tGPsdata *p = op->customdata; @@ -1513,7 +1824,7 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op) /* turn off radial brush cursor */ gpencil_draw_toggle_eraser_cursor(C, p, false); } - + /* always store the new eraser size to be used again next time * NOTE: Do this even when not in eraser mode, as eraser may * have been toggled at some point. @@ -1600,7 +1911,7 @@ static void gpencil_draw_status_indicators(tGPsdata *p) break; case GP_PAINTMODE_DRAW: ED_area_headerprint(p->sa, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw | " - "ESC/Enter to end (or click outside this area)")); + "E/ESC/Enter to end (or click outside this area)")); break; case GP_PAINTMODE_DRAW_POLY: ED_area_headerprint(p->sa, IFACE_("Grease Pencil Poly Session: LMB click to place next stroke vertex | " @@ -1691,6 +2002,31 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event) */ p->mval[0] = event->mval[0] + 1; p->mval[1] = event->mval[1] + 1; + + /* verify key status for straight lines */ + if ((event->ctrl > 0) || (event->alt > 0)) { + if (p->straight[0] == 0) { + int dx = abs(p->mval[0] - p->mvalo[0]); + int dy = abs(p->mval[1] - p->mvalo[1]); + if ((dx > 0) || (dy > 0)) { + /* check mouse direction to replace the other coordinate with previous values */ + if (dx >= dy) { + /* horizontal */ + p->straight[0] = 1; + p->straight[1] = p->mval[1]; /* save y */ + } + else { + /* vertical */ + p->straight[0] = 2; + p->straight[1] = p->mval[0]; /* save x */ + } + } + } + } + else { + p->straight[0] = 0; + } + p->curtime = PIL_check_seconds_timer(); /* handle pressure sensitivity (which is supplied by tablets) */ @@ -1725,7 +2061,9 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event) p->mvalo[1] = p->mval[1]; p->opressure = p->pressure; p->inittime = p->ocurtime = p->curtime; - + p->straight[0] = 0; + p->straight[1] = 0; + /* special exception here for too high pressure values on first touch in * windows for some tablets, then we just skip first touch... */ @@ -1733,6 +2071,18 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event) return; } + /* check if alt key is pressed and limit to straight lines */ + if (p->straight[0] != 0) { + if (p->straight[0] == 1) { + /* horizontal */ + p->mval[1] = p->straight[1]; /* replace y */ + } + else { + /* vertical */ + p->mval[0] = p->straight[1]; /* replace x */ + } + } + /* fill in stroke data (not actually used directly by gpencil_draw_apply) */ RNA_collection_add(op->ptr, "stroke", &itemptr); @@ -1855,21 +2205,21 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event if (p->paintmode == GP_PAINTMODE_ERASER) { gpencil_draw_toggle_eraser_cursor(C, p, true); } - /* set cursor * NOTE: This may change later (i.e. intentionally via brush toggle, * or unintentionally if the user scrolls outside the area)... */ gpencil_draw_cursor_set(p); - + /* only start drawing immediately if we're allowed to do so... */ if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { /* hotkey invoked - start drawing */ /* printf("\tGP - set first spot\n"); */ p->status = GP_STATUS_PAINTING; - + /* handle the initial drawing - i.e. for just doing a simple dot */ gpencil_draw_apply_event(op, event); + op->flag |= OP_IS_MODAL_CURSOR_REGION; } else { /* toolbar invoked - don't start drawing yet... */ @@ -1937,6 +2287,28 @@ static void gpencil_stroke_end(wmOperator *op) p->gpf = NULL; } +/* Move last stroke in the listbase to the head to be drawn below all previous strokes in the layer */ +static void gpencil_move_last_stroke_to_back(bContext *C) +{ + /* move last stroke (the polygon) to head of the listbase stroke to draw on back of all previous strokes */ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl, gpl->actframe)) { + return; + } + + bGPDframe *gpf = gpl->actframe; + bGPDstroke *gps = gpf->strokes.last; + if (ELEM(NULL, gps)) { + return; + } + + BLI_remlink(&gpf->strokes, gps); + BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps); +} + /* events handling during interactive drawing part of operator */ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) { @@ -1976,6 +2348,10 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * is essential for ensuring that they can quickly return to that view */ } + else if ((ELEM(event->type, DKEY)) && (event->val == KM_RELEASE)) { + /* enable continuous if release D key in mid drawing */ + p->scene->toolsettings->gpencil_flags |= GP_TOOL_FLAG_PAINTSESSIONS_ON; + } else { estate = OPERATOR_RUNNING_MODAL; } @@ -1986,9 +2362,15 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* exit painting mode (and/or end current stroke) * NOTE: cannot do RIGHTMOUSE (as is standard for canceling) as that would break polyline [#32647] */ - if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY)) { + if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) { /* exit() ends the current stroke before cleaning up */ /* printf("\t\tGP - end of paint op + end of stroke\n"); */ + /* if drawing polygon and enable on back, must move stroke */ + if ((p->scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { + if (p->flags & GP_PAINTFLAG_STROKEADDED) { + gpencil_move_last_stroke_to_back(C); + } + } p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } @@ -2045,6 +2427,12 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } else { /* printf("\t\tGP - end of stroke + op\n"); */ + /* if drawing polygon and enable on back, must move stroke */ + if ((p->scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { + if (p->flags & GP_PAINTFLAG_STROKEADDED) { + gpencil_move_last_stroke_to_back(C); + } + } p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } @@ -2125,6 +2513,12 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * NOTE: Don't eter this case if an error occurred while finding the * region (as above) */ + /* if drawing polygon and enable on back, must move stroke */ + if ((p->scene->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) && (p->paintmode == GP_PAINTMODE_DRAW_POLY)) { + if (p->flags & GP_PAINTFLAG_STROKEADDED) { + gpencil_move_last_stroke_to_back(C); + } + } p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index b6482786b4f..4cb966c6378 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -43,6 +43,7 @@ #include "DNA_gpencil_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "DNA_object_types.h" #include "BKE_context.h" #include "BKE_gpencil.h" @@ -265,7 +266,7 @@ static void gp_select_same_layer(bContext *C) CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0); + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); bGPDstroke *gps; bool found = false; @@ -616,9 +617,10 @@ void GPENCIL_OT_select_less(wmOperatorType *ot) /* NOTE: Code here is adapted (i.e. copied directly) from gpencil_paint.c::gp_stroke_eraser_dostroke() * It would be great to de-duplicate the logic here sometime, but that can wait... */ -static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc, - const int mx, const int my, const int radius, - const bool select, rcti *rect) +static bool gp_stroke_do_circle_sel( + bGPDstroke *gps, GP_SpaceConversion *gsc, + const int mx, const int my, const int radius, + const bool select, rcti *rect, const bool parented, float diff_mat[4][4]) { bGPDspoint *pt1, *pt2; int x0 = 0, y0 = 0, x1 = 0, y1 = 0; @@ -626,7 +628,14 @@ static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc, bool changed = false; if (gps->totpoints == 1) { - gp_point_to_xy(gsc, gps, gps->points, &x0, &y0); + if (!parented) { + gp_point_to_xy(gsc, gps, gps->points, &x0, &y0); + } + else { + bGPDspoint pt_temp; + gp_point_to_parent_space(gps->points, diff_mat, &pt_temp); + gp_point_to_xy(gsc, gps, &pt_temp, &x0, &y0); + } /* do boundbox check first */ if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) { @@ -654,9 +663,18 @@ static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc, /* get points to work with */ pt1 = gps->points + i; pt2 = gps->points + i + 1; - - gp_point_to_xy(gsc, gps, pt1, &x0, &y0); - gp_point_to_xy(gsc, gps, pt2, &x1, &y1); + if (!parented) { + gp_point_to_xy(gsc, gps, pt1, &x0, &y0); + gp_point_to_xy(gsc, gps, pt2, &x1, &y1); + } + else { + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &x0, &y0); + + gp_point_to_parent_space(pt2, diff_mat, &npt); + gp_point_to_xy(gsc, gps, &npt, &x1, &y1); + } /* check that point segment of the boundbox of the selection stroke */ if (((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) || @@ -691,7 +709,7 @@ static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc, } /* Ensure that stroke selection is in sync with its points */ - gpencil_stroke_sync_selection(gps); + BKE_gpencil_stroke_sync_selection(gps); } return changed; @@ -733,12 +751,14 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op) /* find visible strokes, and select if hit */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { - changed |= gp_stroke_do_circle_sel(gps, &gsc, mx, my, radius, select, &rect); + changed |= gp_stroke_do_circle_sel( + gps, &gsc, mx, my, radius, select, &rect, + (gpl->parent != NULL), diff_mat); } - CTX_DATA_END; - + GP_EDITABLE_STROKES_END; + /* updates */ if (changed) { WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); @@ -818,17 +838,25 @@ static int gpencil_border_select_exec(bContext *C, wmOperator *op) WM_operator_properties_border_to_rcti(op, &rect); /* select/deselect points */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { + bGPDspoint *pt; int i; - + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { int x0, y0; - + /* convert point coords to screenspace */ - gp_point_to_xy(&gsc, gps, pt, &x0, &y0); - + if (gpl->parent == NULL) { + gp_point_to_xy(&gsc, gps, pt, &x0, &y0); + } + else { + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0); + } + /* test if in selection rect */ if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&rect, x0, y0)) { if (select) { @@ -837,16 +865,16 @@ static int gpencil_border_select_exec(bContext *C, wmOperator *op) else { pt->flag &= ~GP_SPOINT_SELECT; } - + changed = true; } } - + /* Ensure that stroke selection is in sync with its points */ - gpencil_stroke_sync_selection(gps); + BKE_gpencil_stroke_sync_selection(gps); } - CTX_DATA_END; - + GP_EDITABLE_STROKES_END; + /* updates */ if (changed) { WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); @@ -920,20 +948,26 @@ static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) } /* select/deselect points */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { bGPDspoint *pt; int i; - + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { int x0, y0; - + /* convert point coords to screenspace */ - gp_point_to_xy(&gsc, gps, pt, &x0, &y0); - + if (gpl->parent == NULL) { + gp_point_to_xy(&gsc, gps, pt, &x0, &y0); + } + else { + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0); + } /* test if in lasso boundbox + within the lasso noose */ if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&rect, x0, y0) && - BLI_lasso_is_point_inside(mcords, mcords_tot, x0, y0, INT_MAX)) + BLI_lasso_is_point_inside(mcords, mcords_tot, x0, y0, INT_MAX)) { if (select) { pt->flag |= GP_SPOINT_SELECT; @@ -941,16 +975,16 @@ static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) else { pt->flag &= ~GP_SPOINT_SELECT; } - + changed = true; } } - + /* Ensure that stroke selection is in sync with its points */ - gpencil_stroke_sync_selection(gps); + BKE_gpencil_stroke_sync_selection(gps); } - CTX_DATA_END; - + GP_EDITABLE_STROKES_END; + /* cleanup */ MEM_freeN((void *)mcords); @@ -1020,35 +1054,42 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) /* First Pass: Find stroke point which gets hit */ /* XXX: maybe we should go from the top of the stack down instead... */ - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { bGPDspoint *pt; int i; - + /* firstly, check for hit-point */ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { int xy[2]; - - gp_point_to_xy(&gsc, gps, pt, &xy[0], &xy[1]); - + + if (gpl->parent == NULL) { + gp_point_to_xy(&gsc, gps, pt, &xy[0], &xy[1]); + } + else { + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy(&gsc, gps, &pt2, &xy[0], &xy[1]); + } + /* do boundbox check first */ if (!ELEM(V2D_IS_CLIPPED, xy[0], xy[1])) { const int pt_distance = len_manhattan_v2v2_int(mval, xy); - + /* check if point is inside */ if (pt_distance <= radius_squared) { /* only use this point if it is a better match than the current hit - T44685 */ if (pt_distance < hit_distance) { hit_stroke = gps; - hit_point = pt; + hit_point = pt; hit_distance = pt_distance; } } } } } - CTX_DATA_END; - + GP_EDITABLE_STROKES_END; + /* Abort if nothing hit... */ if (ELEM(NULL, hit_stroke, hit_point)) { return OPERATOR_CANCELLED; @@ -1111,7 +1152,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) hit_point->flag &= ~GP_SPOINT_SELECT; /* ensure that stroke is selected correctly */ - gpencil_stroke_sync_selection(hit_stroke); + BKE_gpencil_stroke_sync_selection(hit_stroke); } } diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index f9b479ca03d..793ed2a07d0 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -100,14 +100,14 @@ int ED_undo_gpencil_step(bContext *C, int step, const char *name) bGPdata *gpd = *gpd_ptr; bGPDlayer *gpl, *gpld; - free_gpencil_layers(&gpd->layers); + BKE_gpencil_free_layers(&gpd->layers); /* copy layers */ BLI_listbase_clear(&gpd->layers); for (gpl = new_gpd->layers.first; gpl; gpl = gpl->next) { /* make a copy of source layer and its data */ - gpld = gpencil_layer_duplicate(gpl); + gpld = BKE_gpencil_layer_duplicate(gpl); BLI_addtail(&gpd->layers, gpld); } } @@ -142,7 +142,7 @@ void gpencil_undo_push(bGPdata *gpd) */ undo_node->gpd->adt = NULL; - BKE_gpencil_free(undo_node->gpd); + BKE_gpencil_free(undo_node->gpd, false); MEM_freeN(undo_node->gpd); BLI_freelinkN(&undo_nodes, undo_node); @@ -153,7 +153,7 @@ void gpencil_undo_push(bGPdata *gpd) /* create new undo node */ undo_node = MEM_callocN(sizeof(bGPundonode), "gpencil undo node"); - undo_node->gpd = gpencil_data_duplicate(G.main, gpd, true); + undo_node->gpd = BKE_gpencil_data_duplicate(G.main, gpd, true); cur_node = undo_node; @@ -170,7 +170,7 @@ void gpencil_undo_finish(void) */ undo_node->gpd->adt = NULL; - BKE_gpencil_free(undo_node->gpd); + BKE_gpencil_free(undo_node->gpd, false); MEM_freeN(undo_node->gpd); undo_node = undo_node->next; diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index d62625baaa4..ed9a591dcbe 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -17,7 +17,7 @@ * * The Original Code is Copyright (C) 2014, Blender Foundation * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -32,9 +32,13 @@ #include <stddef.h> #include <math.h> +#include "MEM_guardedalloc.h" + #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" +#include "BLI_rand.h" #include "DNA_gpencil_types.h" #include "DNA_object_types.h" @@ -46,6 +50,7 @@ #include "BKE_context.h" #include "BKE_gpencil.h" #include "BKE_tracking.h" +#include "BKE_action.h" #include "WM_api.h" @@ -220,7 +225,7 @@ bool ED_gpencil_has_keyframe_v3d(Scene *scene, Object *ob, int cfra) /* just check both for now... */ // XXX: this could get confusing (e.g. if only on the object, but other places don't show this) if (scene->gpd) { - bGPDlayer *gpl = gpencil_layer_getactive(scene->gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(scene->gpd); if (gpl) { if (gpl->actframe) { // XXX: assumes that frame has been fetched already @@ -234,7 +239,7 @@ bool ED_gpencil_has_keyframe_v3d(Scene *scene, Object *ob, int cfra) } if (ob && ob->gpd) { - bGPDlayer *gpl = gpencil_layer_getactive(ob->gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(ob->gpd); if (gpl) { if (gpl->actframe) { // XXX: assumes that frame has been fetched already @@ -264,11 +269,39 @@ int gp_add_poll(bContext *C) int gp_active_layer_poll(bContext *C) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); return (gpl != NULL); } +/* poll callback for checking if there is an active brush */ +int gp_active_brush_poll(bContext *C) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); + + return (brush != NULL); +} + +/* poll callback for checking if there is an active palette */ +int gp_active_palette_poll(bContext *C) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + + return (palette != NULL); +} + +/* poll callback for checking if there is an active palette color */ +int gp_active_palettecolor_poll(bContext *C) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + + return (palcolor != NULL); +} + /* ******************************************************** */ /* Dynamic Enums of GP Layers */ /* NOTE: These include an option to create a new layer and use that... */ @@ -412,6 +445,60 @@ bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps) return ED_gpencil_stroke_can_use_direct(sa, gps); } +/* Check whether given stroke can be edited for the current color */ +bool ED_gpencil_stroke_color_use(const bGPDlayer *gpl, const bGPDstroke *gps) +{ + /* check if the color is editable */ + bGPDpalettecolor *palcolor = gps->palcolor; + if (palcolor != NULL) { + if (palcolor->flag & PC_COLOR_HIDE) + return false; + if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED)) + return false; + } + + return true; +} + +/* Get palette color or create a new one */ +bGPDpalettecolor *ED_gpencil_stroke_getcolor(bGPdata *gpd, bGPDstroke *gps) +{ + bGPDpalette *palette; + bGPDpalettecolor *palcolor; + + if ((gps->palcolor != NULL) && ((gps->flag & GP_STROKE_RECALC_COLOR) == 0)) + return gps->palcolor; + + /* get palette */ + palette = BKE_gpencil_palette_getactive(gpd); + if (palette == NULL) { + palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); + } + /* get color */ + palcolor = BKE_gpencil_palettecolor_getbyname(palette, gps->colorname); + if (palcolor == NULL) { + if (gps->palcolor == NULL) { + palcolor = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); + /* set to a different color */ + ARRAY_SET_ITEMS(palcolor->color, 1.0f, 0.0f, 1.0f, 0.9f); + } + else { + palcolor = BKE_gpencil_palettecolor_addnew(palette, gps->colorname, true); + /* set old color and attributes */ + bGPDpalettecolor *gpscolor = gps->palcolor; + copy_v4_v4(palcolor->color, gpscolor->color); + copy_v4_v4(palcolor->fill, gpscolor->fill); + palcolor->flag = gpscolor->flag; + } + } + + /* clear flag and set pointer */ + gps->flag &= ~GP_STROKE_RECALC_COLOR; + gps->palcolor = palcolor; + + return palcolor; +} + /* ******************************************************** */ /* Space Conversion */ @@ -451,6 +538,50 @@ void gp_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc) } } +/* convert point to parent space */ +void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint *r_pt) +{ + float fpt[3]; + + mul_v3_m4v3(fpt, diff_mat, &pt->x); + copy_v3_v3(&r_pt->x, fpt); +} + +/* Change position relative to parent object */ +void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps) +{ + bGPDspoint *pt; + int i; + + /* undo matrix */ + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + float fpt[3]; + + ED_gpencil_parent_location(gpl, diff_mat); + invert_m4_m4(inverse_diff_mat, diff_mat); + + for (i = 0; i < gps->totpoints; i++) { + pt = &gps->points[i]; + mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); + copy_v3_v3(&pt->x, fpt); + } +} + +/* Change point position relative to parent object */ +void gp_apply_parent_point(bGPDlayer *gpl, bGPDspoint *pt) +{ + /* undo matrix */ + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + float fpt[3]; + + ED_gpencil_parent_location(gpl, diff_mat); + invert_m4_m4(inverse_diff_mat, diff_mat); + + mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); + copy_v3_v3(&pt->x, fpt); +} /* Convert Grease Pencil points to screen-space values * WARNING: This assumes that the caller has already checked whether the stroke in question can be drawn @@ -591,25 +722,107 @@ bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure) madd_v3_v3fl(sco, &pt1->x, average_fac); madd_v3_v3fl(sco, &pt2->x, average_fac); +#if 0 + /* XXX: Disabled because get weird result */ /* do pressure too? */ if (affect_pressure) { pressure += pt1->pressure * average_fac; pressure += pt2->pressure * average_fac; } +#endif } } /* Based on influence factor, blend between original and optimal smoothed coordinate */ interp_v3_v3v3(&pt->x, &pt->x, sco, inf); +#if 0 + /* XXX: Disabled because get weird result */ if (affect_pressure) { pt->pressure = pressure; } +#endif return true; } /** +* Apply smooth for strength to stroke point +* \param gps Stroke to smooth +* \param i Point index +* \param inf Amount of smoothing to apply +*/ +bool gp_smooth_stroke_strength(bGPDstroke *gps, int i, float inf) +{ + bGPDspoint *ptb = &gps->points[i]; + + /* Do nothing if not enough points */ + if (gps->totpoints <= 2) { + return false; + } + + /* Compute theoretical optimal value using distances */ + bGPDspoint *pta, *ptc; + int before = i - 1; + int after = i + 1; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pta = &gps->points[before]; + ptc = &gps->points[after]; + + /* the optimal value is the corresponding to the interpolation of the strength + * at the distance of point b + */ + const float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + const float optimal = (1.0f - fac) * pta->strength + fac * ptc->strength; + + /* Based on influence factor, blend between original and optimal */ + ptb->strength = (1.0f - inf) * ptb->strength + inf * optimal; + + return true; +} + +/** +* Apply smooth for thickness to stroke point (use pressure) +* \param gps Stroke to smooth +* \param i Point index +* \param inf Amount of smoothing to apply +*/ +bool gp_smooth_stroke_thickness(bGPDstroke *gps, int i, float inf) +{ + bGPDspoint *ptb = &gps->points[i]; + + /* Do nothing if not enough points */ + if (gps->totpoints <= 2) { + return false; + } + + /* Compute theoretical optimal value using distances */ + bGPDspoint *pta, *ptc; + int before = i - 1; + int after = i + 1; + + CLAMP_MIN(before, 0); + CLAMP_MAX(after, gps->totpoints - 1); + + pta = &gps->points[before]; + ptc = &gps->points[after]; + + /* the optimal value is the corresponding to the interpolation of the pressure + * at the distance of point b + */ + float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + float optimal = (1.0f - fac) * pta->pressure + fac * ptc->pressure; + + /* Based on influence factor, blend between original and optimal */ + ptb->pressure = (1.0f - inf) * ptb->pressure + inf * optimal; + + return true; +} + +/** * Subdivide a stroke once, by adding a point half way between each pair of existing points * \param gps Stroke data * \param new_totpoints Total number of points (after subdividing) @@ -633,16 +846,99 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints) interp_v3_v3v3(&pt->x, &prev->x, &next->x, 0.5f); pt->pressure = interpf(prev->pressure, next->pressure, 0.5f); - pt->time = interpf(prev->time, next->time, 0.5f); + pt->strength = interpf(prev->strength, next->strength, 0.5f); + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt->time = interpf(prev->time, next->time, 0.5f); } /* Update to new total number of points */ gps->totpoints = new_totpoints; } -/* ******************************************************** */ +/** + * Add randomness to stroke + * \param gps Stroke data + * \param brsuh Brush data + */ +void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush) +{ + bGPDspoint *pt1, *pt2, *pt3; + float v1[3]; + float v2[3]; + if (gps->totpoints < 3) { + return; + } + + /* get two vectors using 3 points */ + pt1 = &gps->points[0]; + pt2 = &gps->points[1]; + pt3 = &gps->points[(int)(gps->totpoints * 0.75)]; + + sub_v3_v3v3(v1, &pt2->x, &pt1->x); + sub_v3_v3v3(v2, &pt3->x, &pt2->x); + normalize_v3(v1); + normalize_v3(v2); + + /* get normal vector to plane created by two vectors */ + float normal[3]; + cross_v3_v3v3(normal, v1, v2); + normalize_v3(normal); + /* get orthogonal vector to plane to rotate random effect */ + float ortho[3]; + cross_v3_v3v3(ortho, v1, normal); + normalize_v3(ortho); + /* Read all points and apply shift vector (first and last point not modified) */ + for (int i = 1; i < gps->totpoints - 1; ++i) { + bGPDspoint *pt = &gps->points[i]; + /* get vector with shift (apply a division because random is too sensitive */ + const float fac = BLI_frand() * (brush->draw_random_sub / 10.0f); + float svec[3]; + copy_v3_v3(svec, ortho); + if (BLI_frand() > 0.5f) { + mul_v3_fl(svec, -fac); + } + else { + mul_v3_fl(svec, fac); + } + + /* apply shift */ + add_v3_v3(&pt->x, svec); + } + +} +/* calculate difference matrix */ +void ED_gpencil_parent_location(bGPDlayer *gpl, float diff_mat[4][4]) +{ + Object *ob = gpl->parent; + if (ob == NULL) { + unit_m4(diff_mat); + return; + } + else { + if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { + mul_m4_m4m4(diff_mat, ob->obmat, gpl->inverse); + return; + } + else if (gpl->partype == PARBONE) { + bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, gpl->parsubstr); + if (pchan) { + float tmp_mat[4][4]; + mul_m4_m4m4(tmp_mat, ob->obmat, pchan->pose_mat); + mul_m4_m4m4(diff_mat, tmp_mat, gpl->inverse); + } + else { + mul_m4_m4m4(diff_mat, ob->obmat, gpl->inverse); /* if bone not found use object (armature) */ + } + return; + } + else { + unit_m4(diff_mat); /* not defined type */ + } + } +} +/* ******************************************************** */ bool ED_gpencil_stroke_minmax( const bGPDstroke *gps, const bool use_select, float r_min[3], float r_max[3]) @@ -659,3 +955,74 @@ bool ED_gpencil_stroke_minmax( } return changed; } +/* Dynamic Enums of GP Brushes */ + +EnumPropertyItem *ED_gpencil_brushes_enum_itemf( + bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), + bool *r_free) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPDbrush *brush; + EnumPropertyItem *item = NULL, item_tmp = { 0 }; + int totitem = 0; + int i = 0; + + if (ELEM(NULL, C, ts)) { + return DummyRNA_DEFAULT_items; + } + + /* Existing brushes */ + for (brush = ts->gp_brushes.first; brush; brush = brush->next, i++) { + item_tmp.identifier = brush->info; + item_tmp.name = brush->info; + item_tmp.value = i; + + if (brush->flag & GP_BRUSH_ACTIVE) + item_tmp.icon = ICON_BRUSH_DATA; + else + item_tmp.icon = ICON_NONE; + + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} +/* Dynamic Enums of GP Palettes */ + +EnumPropertyItem *ED_gpencil_palettes_enum_itemf( + bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), + bool *r_free) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDpalette *palette; + EnumPropertyItem *item = NULL, item_tmp = { 0 }; + int totitem = 0; + int i = 0; + + if (ELEM(NULL, C, gpd)) { + return DummyRNA_DEFAULT_items; + } + + /* Existing palettes */ + for (palette = gpd->palettes.first; palette; palette = palette->next, i++) { + item_tmp.identifier = palette->info; + item_tmp.name = palette->info; + item_tmp.value = i; + + if (palette->flag & PL_PALETTE_ACTIVE) + item_tmp.icon = ICON_COLOR; + else + item_tmp.icon = ICON_NONE; + + RNA_enum_item_add(&item, &totitem, &item_tmp); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} +/* ******************************************************** */ diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 0940f594482..bfd89e90fce 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -156,6 +156,7 @@ typedef enum eAnim_ChannelType { ANIMTYPE_DSMAT, ANIMTYPE_DSLAM, ANIMTYPE_DSCAM, + ANIMTYPE_DSCACHEFILE, ANIMTYPE_DSCUR, ANIMTYPE_DSSKEY, ANIMTYPE_DSWOR, @@ -275,6 +276,7 @@ typedef enum eAnimFilter_Flags { #define FILTER_MAT_OBJD(ma) (CHECK_TYPE_INLINE(ma, Material *), ((ma->flag & MA_DS_EXPAND))) #define FILTER_LAM_OBJD(la) (CHECK_TYPE_INLINE(la, Lamp *), ((la->flag & LA_DS_EXPAND))) #define FILTER_CAM_OBJD(ca) (CHECK_TYPE_INLINE(ca, Camera *), ((ca->flag & CAM_DS_EXPAND))) +#define FILTER_CACHEFILE_OBJD(cf) (CHECK_TYPE_INLINE(cf, CacheFile *), ((cf->flag & CACHEFILE_DS_EXPAND))) #define FILTER_CUR_OBJD(cu) (CHECK_TYPE_INLINE(cu, Curve *), ((cu->flag & CU_DS_EXPAND))) #define FILTER_PART_OBJD(part) (CHECK_TYPE_INLINE(part, ParticleSettings *), ((part->flag & PART_DS_EXPAND))) #define FILTER_MBALL_OBJD(mb) (CHECK_TYPE_INLINE(mb, MetaBall *), ((mb->flag2 & MB_DS_EXPAND))) diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index de5ab80a88f..d526b0841cc 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -41,6 +41,8 @@ struct bGPdata; struct bGPDlayer; struct bGPDframe; struct bGPDstroke; +struct bGPDpalette; +struct bGPDpalettecolor; struct bAnimContext; struct KeyframeEditData; struct PointerRNA; @@ -57,6 +59,7 @@ struct wmKeyConfig; typedef struct tGPspoint { int x, y; /* x and y coordinates of cursor (in relative to area) */ float pressure; /* pressure of tablet at this point */ + float strength; /* pressure of tablet at this point for alpha factor */ float time; /* Time relative to stroke start (used when converting to path) */ } tGPspoint; @@ -86,6 +89,9 @@ bool ED_gpencil_has_keyframe_v3d(struct Scene *scene, struct Object *ob, int cfr bool ED_gpencil_stroke_can_use_direct(const struct ScrArea *sa, const struct bGPDstroke *gps); bool ED_gpencil_stroke_can_use(const struct bContext *C, const struct bGPDstroke *gps); +bool ED_gpencil_stroke_color_use(const struct bGPDlayer *gpl, const struct bGPDstroke *gps); + +struct bGPDpalettecolor *ED_gpencil_stroke_getcolor(struct bGPdata *gpd, struct bGPDstroke *gps); bool ED_gpencil_stroke_minmax( const struct bGPDstroke *gps, const bool use_select, @@ -142,4 +148,10 @@ bool ED_gpencil_anim_copybuf_paste(struct bAnimContext *ac, const short copy_mod int ED_gpencil_session_active(void); int ED_undo_gpencil_step(struct bContext *C, int step, const char *name); +/* ------------ Transformation Utilities ------------ */ + +/* get difference matrix using parent */ +void ED_gpencil_parent_location(struct bGPDlayer *gpl, float diff_mat[4][4]); + + #endif /* __ED_GPENCIL_H__ */ diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index b1f3f012e09..c478a8b17e5 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -34,6 +34,7 @@ struct bAnimContext; struct AnimData; +struct CacheFile; struct FCurve; struct bDopeSheet; struct bAction; @@ -141,6 +142,8 @@ void agroup_to_keylist(struct AnimData *adt, struct bActionGroup *agrp, struct D void action_to_keylist(struct AnimData *adt, struct bAction *act, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks); /* Object */ void ob_to_keylist(struct bDopeSheet *ads, struct Object *ob, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks); +/* Cache File */ +void cachefile_to_keylist(struct bDopeSheet *ads, struct CacheFile *cache_file, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks); /* Scene */ void scene_to_keylist(struct bDopeSheet *ads, struct Scene *sce, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks); /* DopeSheet Summary */ diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index d85b60dcc43..e016e014a1a 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -320,9 +320,9 @@ DEF_ICON(OUTLINER_OB_SPEAKER) DEF_ICON(BLANK123) DEF_ICON(BLANK124) DEF_ICON(BLANK125) - DEF_ICON(BLANK126) - DEF_ICON(BLANK127) #endif +DEF_ICON(RESTRICT_COLOR_OFF) +DEF_ICON(RESTRICT_COLOR_ON) DEF_ICON(RESTRICT_VIEW_OFF) DEF_ICON(RESTRICT_VIEW_ON) DEF_ICON(RESTRICT_SELECT_OFF) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index f77e795adca..1f053c806b0 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -945,6 +945,7 @@ void uiTemplateReportsBanner(uiLayout *layout, struct bContext *C); void uiTemplateKeymapItemProperties(uiLayout *layout, struct PointerRNA *ptr); void uiTemplateComponentMenu(uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *name); void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float *color); +void uiTemplateCacheFile(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname); /* Default UIList class name, keep in sync with its declaration in bl_ui/__init__.py */ #define UI_UL_DEFAULT_CLASS_NAME "UI_UL_list" diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 4107414a240..22a450d2523 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1541,6 +1541,8 @@ int UI_idcode_icon_get(const int idcode) return ICON_BRUSH_DATA; case ID_CA: return ICON_CAMERA_DATA; + case ID_CF: + return ICON_FILE; case ID_CU: return ICON_CURVE_DATA; case ID_GD: diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 58cadf5587a..50dd219b53c 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -32,6 +32,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_cachefile_types.h" #include "DNA_node_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" @@ -325,7 +326,9 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) } else { if (id) { + Main *bmain = CTX_data_main(C); id_single_user(C, id, &template->ptr, template->prop); + DAG_relations_tag_update(bmain); } } } @@ -368,6 +371,7 @@ static const char *template_id_browse_tip(StructRNA *type) case ID_MSK: return N_("Browse Mask to be linked"); case ID_PAL: return N_("Browse Palette Data to be linked"); case ID_PC: return N_("Browse Paint Curve Data to be linked"); + case ID_CF: return N_("Browse Cache Files to be linked"); } } return N_("Browse ID data to be linked"); @@ -872,8 +876,8 @@ static uiLayout *draw_modifier( /* mode enabling buttons */ UI_block_align_begin(block); - /* Softbody not allowed in this situation, enforce! */ - if (((md->type != eModifierType_Softbody && md->type != eModifierType_Collision) || !(ob->pd && ob->pd->deflect)) && + /* Collision and Surface are always enabled, hide buttons! */ + if (((md->type != eModifierType_Collision) || !(ob->pd && ob->pd->deflect)) && (md->type != eModifierType_Surface) ) { uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE); @@ -3848,3 +3852,73 @@ void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float *color) UI_block_align_end(block); } + +/********************************* Cache File *********************************/ + +void uiTemplateCacheFile(uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname) +{ + if (!ptr->data) { + return; + } + + PropertyRNA *prop = RNA_struct_find_property(ptr, propname); + + if (!prop) { + printf("%s: property not found: %s.%s\n", + __func__, RNA_struct_identifier(ptr->type), propname); + return; + } + + if (RNA_property_type(prop) != PROP_POINTER) { + printf("%s: expected pointer property for %s.%s\n", + __func__, RNA_struct_identifier(ptr->type), propname); + return; + } + + PointerRNA fileptr = RNA_property_pointer_get(ptr, prop); + CacheFile *file = fileptr.data; + + uiLayoutSetContextPointer(layout, "edit_cachefile", &fileptr); + + uiTemplateID(layout, C, ptr, propname, NULL, "CACHEFILE_OT_open", NULL); + + if (!file) { + return; + } + + uiLayout *row = uiLayoutRow(layout, false); + uiBlock *block = uiLayoutGetBlock(row); + uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("File Path:"), 0, 19, 145, 19, NULL, 0, 0, 0, 0, ""); + + row = uiLayoutRow(layout, false); + uiLayout *split = uiLayoutSplit(row, 0.0f, false); + row = uiLayoutRow(split, true); + + uiItemR(row, &fileptr, "filepath", 0, "", ICON_NONE); + uiItemO(row, "", ICON_FILE_REFRESH, "cachefile.reload"); + + row = uiLayoutRow(layout, false); + uiItemR(row, &fileptr, "is_sequence", 0, "Is Sequence", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemR(row, &fileptr, "override_frame", 0, "Override Frame", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiLayoutSetEnabled(row, RNA_boolean_get(&fileptr, "override_frame")); + uiItemR(row, &fileptr, "frame", 0, "Frame", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemR(row, &fileptr, "scale", 0, "Scale", ICON_NONE); + + /* TODO: unused for now, so no need to expose. */ +#if 0 + row = uiLayoutRow(layout, false); + uiItemR(row, &fileptr, "forward_axis", 0, "Forward Axis", ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemR(row, &fileptr, "up_axis", 0, "Up Axis", ICON_NONE); +#endif +} diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt index 828cb494eab..b3bbce939a5 100644 --- a/source/blender/editors/io/CMakeLists.txt +++ b/source/blender/editors/io/CMakeLists.txt @@ -28,6 +28,8 @@ set(INC ../../makesrna ../../windowmanager ../../collada + ../../alembic + ../../../../intern/guardedalloc ) set(INC_SYS @@ -35,9 +37,13 @@ set(INC_SYS ) set(SRC + io_alembic.c + io_cache.c io_collada.c io_ops.c + io_alembic.h + io_cache.h io_collada.h io_ops.h ) @@ -46,6 +52,14 @@ if(WITH_OPENCOLLADA) add_definitions(-DWITH_COLLADA) endif() +if(WITH_ALEMBIC) + add_definitions(-DWITH_ALEMBIC) + + if(WITH_ALEMBIC_HDF5) + add_definitions(-DWITH_ALEMBIC_HDF5) + endif() +endif() + if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c new file mode 100644 index 00000000000..7a7c42e501b --- /dev/null +++ b/source/blender/editors/io/io_alembic.c @@ -0,0 +1,458 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#ifdef WITH_ALEMBIC + +/* needed for directory lookup */ +#ifndef WIN32 +# include <dirent.h> +#else +# include "BLI_winstuff.h" +#endif + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_report.h" + +#include "BLI_listbase.h" +#include "BLI_math_vector.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "io_alembic.h" + +#include "ABC_alembic.h" + +static int wm_alembic_export_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + char filepath[FILE_MAX]; + BLI_strncpy(filepath, G.main->name, sizeof(filepath)); + BLI_replace_extension(filepath, sizeof(filepath), ".abc"); + RNA_string_set(op->ptr, "filepath", filepath); + } + + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; + + UNUSED_VARS(event); +} + +static int wm_alembic_export_exec(bContext *C, wmOperator *op) +{ + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + BKE_report(op->reports, RPT_ERROR, "No filename given"); + return OPERATOR_CANCELLED; + } + + char filename[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filename); + + const struct AlembicExportParams params = { + .frame_start = RNA_int_get(op->ptr, "start"), + .frame_end = RNA_int_get(op->ptr, "end"), + + .frame_step_xform = 1.0 / (double)RNA_int_get(op->ptr, "xsamples"), + .frame_step_shape = 1.0 / (double)RNA_int_get(op->ptr, "gsamples"), + + .shutter_open = RNA_float_get(op->ptr, "sh_open"), + .shutter_close = RNA_float_get(op->ptr, "sh_close"), + + .selected_only = RNA_boolean_get(op->ptr, "selected"), + .uvs = RNA_boolean_get(op->ptr, "uvs"), + .normals = RNA_boolean_get(op->ptr, "normals"), + .vcolors = RNA_boolean_get(op->ptr, "vcolors"), + .apply_subdiv = RNA_boolean_get(op->ptr, "apply_subdiv"), + .flatten_hierarchy = RNA_boolean_get(op->ptr, "flatten"), + .visible_layers_only = RNA_boolean_get(op->ptr, "visible_layers_only"), + .renderable_only = RNA_boolean_get(op->ptr, "renderable_only"), + .face_sets = RNA_boolean_get(op->ptr, "face_sets"), + .use_subdiv_schema = RNA_boolean_get(op->ptr, "subdiv_schema"), + .compression_type = RNA_enum_get(op->ptr, "compression_type"), + .packuv = RNA_boolean_get(op->ptr, "packuv"), + + .global_scale = RNA_float_get(op->ptr, "global_scale"), + }; + + ABC_export(CTX_data_scene(C), C, filename, ¶ms); + + return OPERATOR_FINISHED; +} + +static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr) +{ + uiLayout *box = uiLayoutBox(layout); + uiLayout *row; + +#ifdef WITH_ALEMBIC_HDF5 + row = uiLayoutRow(box, false); + uiItemL(row, IFACE_("Archive Options:"), ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "compression_type", 0, NULL, ICON_NONE); +#endif + + box = uiLayoutBox(layout); + row = uiLayoutRow(box, false); + uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "global_scale", 0, NULL, ICON_NONE); + + /* Scene Options */ + box = uiLayoutBox(layout); + row = uiLayoutRow(box, false); + uiItemL(row, IFACE_("Scene Options:"), ICON_SCENE_DATA); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "start", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "end", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "xsamples", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "gsamples", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "sh_open", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "sh_close", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "selected", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "renderable_only", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "visible_layers_only", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "flatten", 0, NULL, ICON_NONE); + + /* Object Data */ + box = uiLayoutBox(layout); + row = uiLayoutRow(box, false); + uiItemL(row, IFACE_("Object Options:"), ICON_OBJECT_DATA); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "uvs", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "packuv", 0, NULL, ICON_NONE); + uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "uvs")); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "normals", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "vcolors", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "face_sets", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "subdiv_schema", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "apply_subdiv", 0, NULL, ICON_NONE); +} + +static void wm_alembic_export_draw(bContext *UNUSED(C), wmOperator *op) +{ + PointerRNA ptr; + + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + ui_alembic_export_settings(op->layout, &ptr); +} + +void WM_OT_alembic_export(wmOperatorType *ot) +{ + ot->name = "Export Alembic Archive"; + ot->idname = "WM_OT_alembic_export"; + + ot->invoke = wm_alembic_export_invoke; + ot->exec = wm_alembic_export_exec; + ot->poll = WM_operator_winactive; + ot->ui = wm_alembic_export_draw; + + WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC, + FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH, + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + + RNA_def_int(ot->srna, "start", 1, INT_MIN, INT_MAX, + "Start Frame", "Start Frame", INT_MIN, INT_MAX); + + RNA_def_int(ot->srna, "end", 1, INT_MIN, INT_MAX, + "End Frame", "End Frame", INT_MIN, INT_MAX); + + RNA_def_int(ot->srna, "xsamples", 1, 1, 128, + "Transform Samples", "Number of times per frame transformations are sampled", 1, 128); + + RNA_def_int(ot->srna, "gsamples", 1, 1, 128, + "Geometry Samples", "Number of times per frame object datas are sampled", 1, 128); + + RNA_def_float(ot->srna, "sh_open", 0.0f, -1.0f, 1.0f, + "Shutter Open", "Time at which the shutter is open", -1.0f, 1.0f); + + RNA_def_float(ot->srna, "sh_close", 1.0f, -1.0f, 1.0f, + "Shutter Close", "Time at which the shutter is closed", -1.0f, 1.0f); + + RNA_def_boolean(ot->srna, "selected", 0, + "Selected Objects Only", "Export only selected objects"); + + RNA_def_boolean(ot->srna, "renderable_only", 1, + "Renderable Objects Only", + "Export only objects marked renderable in the outliner"); + + RNA_def_boolean(ot->srna, "visible_layers_only", 0, + "Visible Layers Only", "Export only objects in visible layers"); + + RNA_def_boolean(ot->srna, "flatten", 0, + "Flatten Hierarchy", + "Do not preserve objects' parent/children relationship"); + + RNA_def_boolean(ot->srna, "uvs", 1, "UVs", "Export UVs"); + + RNA_def_boolean(ot->srna, "packuv", 1, "Pack UV Islands", + "Export UVs with packed island"); + + RNA_def_boolean(ot->srna, "normals", 1, "Normals", "Export normals"); + + RNA_def_boolean(ot->srna, "vcolors", 0, "Vertex colors", "Export vertex colors"); + + RNA_def_boolean(ot->srna, "face_sets", 0, "Face Sets", "Export per face shading group assignments"); + + RNA_def_boolean(ot->srna, "subdiv_schema", 0, + "Use Subdivision Schema", + "Export meshes using Alembic's subdivision schema"); + + RNA_def_boolean(ot->srna, "apply_subdiv", 0, + "Apply Subsurf", "Export subdivision surfaces as meshes"); + + RNA_def_enum(ot->srna, "compression_type", rna_enum_abc_compression_items, + ABC_ARCHIVE_OGAWA, "Compression", ""); + + RNA_def_float(ot->srna, "global_scale", 1.0f, 0.0001f, 1000.0f, "Scale", + "Value by which to enlarge or shrink the objects with respect to the world's origin", + 0.0001f, 1000.0f); +} + +/* ************************************************************************** */ + +/* TODO(kevin): check on de-duplicating all this with code in image_ops.c */ + +typedef struct CacheFrame { + struct CacheFrame *next, *prev; + int framenr; +} CacheFrame; + +static int cmp_frame(const void *a, const void *b) +{ + const CacheFrame *frame_a = a; + const CacheFrame *frame_b = b; + + if (frame_a->framenr < frame_b->framenr) return -1; + if (frame_a->framenr > frame_b->framenr) return 1; + return 0; +} + +static int get_sequence_len(char *filename, int *ofs) +{ + int frame; + int numdigit; + + if (!BLI_path_frame_get(filename, &frame, &numdigit)) { + return 1; + } + + char path[FILE_MAX]; + BLI_split_dir_part(filename, path, FILE_MAX); + + DIR *dir = opendir(path); + + const char *ext = ".abc"; + const char *basename = BLI_path_basename(filename); + const int len = strlen(basename) - (numdigit + strlen(ext)); + + ListBase frames; + BLI_listbase_clear(&frames); + + struct dirent *fname; + while ((fname = readdir(dir)) != NULL) { + /* do we have the right extension? */ + if (!strstr(fname->d_name, ext)) { + continue; + } + + if (!STREQLEN(basename, fname->d_name, len)) { + continue; + } + + CacheFrame *cache_frame = MEM_callocN(sizeof(CacheFrame), "abc_frame"); + + BLI_path_frame_get(fname->d_name, &cache_frame->framenr, &numdigit); + + BLI_addtail(&frames, cache_frame); + } + + closedir(dir); + + BLI_listbase_sort(&frames, cmp_frame); + + CacheFrame *cache_frame = frames.first; + + if (cache_frame) { + int frame_curr = cache_frame->framenr; + (*ofs) = frame_curr; + + while (cache_frame && (cache_frame->framenr == frame_curr)) { + ++frame_curr; + cache_frame = cache_frame->next; + } + + BLI_freelistN(&frames); + + return frame_curr - (*ofs); + } + + return 1; +} + +/* ************************************************************************** */ + +static void ui_alembic_import_settings(uiLayout *layout, PointerRNA *imfptr) +{ + uiLayout *box = uiLayoutBox(layout); + uiLayout *row = uiLayoutRow(box, false); + uiItemL(row, IFACE_("Manual Transform:"), ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "scale", 0, NULL, ICON_NONE); + + box = uiLayoutBox(layout); + row = uiLayoutRow(box, false); + uiItemL(row, IFACE_("Options:"), ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "set_frame_range", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "is_sequence", 0, NULL, ICON_NONE); + + row = uiLayoutRow(box, false); + uiItemR(row, imfptr, "validate_meshes", 0, NULL, ICON_NONE); +} + +static void wm_alembic_import_draw(bContext *UNUSED(C), wmOperator *op) +{ + PointerRNA ptr; + + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + ui_alembic_import_settings(op->layout, &ptr); +} + +static int wm_alembic_import_exec(bContext *C, wmOperator *op) +{ + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + BKE_report(op->reports, RPT_ERROR, "No filename given"); + return OPERATOR_CANCELLED; + } + + char filename[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filename); + + const float scale = RNA_float_get(op->ptr, "scale"); + const bool is_sequence = RNA_boolean_get(op->ptr, "is_sequence"); + const bool set_frame_range = RNA_boolean_get(op->ptr, "set_frame_range"); + const bool validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes"); + + int offset = 0; + int sequence_len = 1; + + if (is_sequence) { + sequence_len = get_sequence_len(filename, &offset); + } + + ABC_import(C, filename, scale, is_sequence, set_frame_range, sequence_len, offset, validate_meshes); + + return OPERATOR_FINISHED; +} + +void WM_OT_alembic_import(wmOperatorType *ot) +{ + ot->name = "Import Alembic Archive"; + ot->idname = "WM_OT_alembic_import"; + + ot->invoke = WM_operator_filesel; + ot->exec = wm_alembic_import_exec; + ot->poll = WM_operator_winactive; + ot->ui = wm_alembic_import_draw; + + WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_ALEMBIC, + FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH, + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + + RNA_def_float(ot->srna, "scale", 1.0f, 0.0001f, 1000.0f, "Scale", + "Value by which to enlarge or shrink the objects with respect to the world's origin", + 0.0001f, 1000.0f); + + RNA_def_boolean(ot->srna, "set_frame_range", true, + "Set Frame Range", + "If checked, update scene's start and end frame to match those of the Alembic archive"); + + RNA_def_boolean(ot->srna, "validate_meshes", 0, + "Validate Meshes", "Check imported mesh objects for invalid data (slow)"); + + RNA_def_boolean(ot->srna, "is_sequence", false, "Is Sequence", + "Set to true if the cache is split into separate files"); +} + +#endif diff --git a/source/blender/editors/io/io_alembic.h b/source/blender/editors/io/io_alembic.h new file mode 100644 index 00000000000..5eefabef4be --- /dev/null +++ b/source/blender/editors/io/io_alembic.h @@ -0,0 +1,37 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#ifndef __IO_ALEMBIC_H__ +#define __IO_ALEMBIC_H__ + +/** \file blender/editors/io/io_alembic.h + * \ingroup editor/io + */ + +struct wmOperatorType; + +void WM_OT_alembic_export(struct wmOperatorType *ot); +void WM_OT_alembic_import(struct wmOperatorType *ot); + +#endif /* __IO_ALEMBIC_H__ */ diff --git a/source/blender/editors/io/io_cache.c b/source/blender/editors/io/io_cache.c new file mode 100644 index 00000000000..d6e2c1ae204 --- /dev/null +++ b/source/blender/editors/io/io_cache.c @@ -0,0 +1,162 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_cachefile_types.h" +#include "DNA_space_types.h" + +#include "BLI_listbase.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "BKE_cachefile.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_report.h" + +#include "RNA_access.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "io_cache.h" + +static void cachefile_init(bContext *C, wmOperator *op) +{ + PropertyPointerRNA *pprop; + + op->customdata = pprop = MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA"); + UI_context_active_but_prop_get_templateID(C, &pprop->ptr, &pprop->prop); +} + +static int cachefile_open_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + char filepath[FILE_MAX]; + BLI_strncpy(filepath, G.main->name, sizeof(filepath)); + BLI_replace_extension(filepath, sizeof(filepath), ".abc"); + RNA_string_set(op->ptr, "filepath", filepath); + } + + cachefile_init(C, op); + + WM_event_add_fileselect(C, op); + + return OPERATOR_RUNNING_MODAL; + + UNUSED_VARS(event); +} + +static void open_cancel(bContext *UNUSED(C), wmOperator *op) +{ + MEM_freeN(op->customdata); + op->customdata = NULL; +} + +static int cachefile_open_exec(bContext *C, wmOperator *op) +{ + if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + BKE_report(op->reports, RPT_ERROR, "No filename given"); + return OPERATOR_CANCELLED; + } + + char filename[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filename); + + Main *bmain = CTX_data_main(C); + + CacheFile *cache_file = BKE_libblock_alloc(bmain, ID_CF, BLI_path_basename(filename)); + BLI_strncpy(cache_file->filepath, filename, FILE_MAX); + BKE_cachefile_reload(bmain, cache_file); + + /* hook into UI */ + PropertyPointerRNA *pprop = op->customdata; + + if (pprop->prop) { + /* when creating new ID blocks, use is already 1, but RNA + * pointer se also increases user, so this compensates it */ + id_us_min(&cache_file->id); + + PointerRNA idptr; + RNA_id_pointer_create(&cache_file->id, &idptr); + RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr); + RNA_property_update(C, &pprop->ptr, pprop->prop); + } + + MEM_freeN(op->customdata); + + return OPERATOR_FINISHED; +} + +void CACHEFILE_OT_open(wmOperatorType *ot) +{ + ot->name = "Open Cache File"; + ot->idname = "CACHEFILE_OT_open"; + + ot->invoke = cachefile_open_invoke; + ot->exec = cachefile_open_exec; + ot->cancel = open_cancel; + + WM_operator_properties_filesel(ot, FILE_TYPE_ALEMBIC | FILE_TYPE_FOLDER, + FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH, + FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); +} + +/* ***************************** Reload Operator **************************** */ + +static int cachefile_reload_exec(bContext *C, wmOperator *op) +{ + CacheFile *cache_file = CTX_data_edit_cachefile(C); + + if (!cache_file) { + return OPERATOR_CANCELLED; + } + + Main *bmain = CTX_data_main(C); + + BLI_listbase_clear(&cache_file->object_paths); + BKE_cachefile_reload(bmain, cache_file); + + return OPERATOR_FINISHED; + + UNUSED_VARS(op); +} + +void CACHEFILE_OT_reload(wmOperatorType *ot) +{ + ot->name = "Refresh Archive"; + ot->description = "Update objects paths list with new data from the archive"; + ot->idname = "CACHEFILE_OT_reload"; + + /* api callbacks */ + ot->exec = cachefile_reload_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/io/io_cache.h b/source/blender/editors/io/io_cache.h new file mode 100644 index 00000000000..ea270c2aba1 --- /dev/null +++ b/source/blender/editors/io/io_cache.h @@ -0,0 +1,37 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +#ifndef __IO_CACHE_H__ +#define __IO_CACHE_H__ + +/** \file blender/editors/io/io_cache.h + * \ingroup editor/io + */ + +struct wmOperatorType; + +void CACHEFILE_OT_open(struct wmOperatorType *ot); +void CACHEFILE_OT_reload(struct wmOperatorType *ot); + +#endif /* __IO_CACHE_H__ */ diff --git a/source/blender/editors/io/io_ops.c b/source/blender/editors/io/io_ops.c index a70a51a60be..d1e933517a9 100644 --- a/source/blender/editors/io/io_ops.c +++ b/source/blender/editors/io/io_ops.c @@ -30,11 +30,18 @@ #include "io_ops.h" /* own include */ +#include "WM_api.h" + #ifdef WITH_COLLADA # include "io_collada.h" -# include "WM_api.h" #endif +#ifdef WITH_ALEMBIC +# include "io_alembic.h" +#endif + +#include "io_cache.h" + void ED_operatortypes_io(void) { #ifdef WITH_COLLADA @@ -42,4 +49,11 @@ void ED_operatortypes_io(void) WM_operatortype_append(WM_OT_collada_export); WM_operatortype_append(WM_OT_collada_import); #endif +#ifdef WITH_ALEMBIC + WM_operatortype_append(WM_OT_alembic_import); + WM_operatortype_append(WM_OT_alembic_export); +#endif + + WM_operatortype_append(CACHEFILE_OT_open); + WM_operatortype_append(CACHEFILE_OT_reload); } diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 1a14fad8650..999d5b278ee 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -3080,7 +3080,7 @@ static void bm_mesh_hflag_flush_vert(BMesh *bm, const char hflag) * \note This could be used for split-by-material for non mesh types. * \note This could take material data from another object or args. */ -static void mesh_separate_material_assign_mat_nr(Object *ob, const short mat_nr) +static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const short mat_nr) { ID *obdata = ob->data; @@ -3116,18 +3116,18 @@ static void mesh_separate_material_assign_mat_nr(Object *ob, const short mat_nr) ma_obdata = NULL; } - BKE_material_clear_id(obdata, true); - BKE_material_resize_object(ob, 1, true); - BKE_material_resize_id(obdata, 1, true); + BKE_material_clear_id(bmain, obdata, true); + BKE_material_resize_object(bmain, ob, 1, true); + BKE_material_resize_id(bmain, obdata, 1, true); ob->mat[0] = ma_ob; ob->matbits[0] = matbit; (*matarar)[0] = ma_obdata; } else { - BKE_material_clear_id(obdata, true); - BKE_material_resize_object(ob, 0, true); - BKE_material_resize_id(obdata, 0, true); + BKE_material_clear_id(bmain, obdata, true); + BKE_material_resize_object(bmain, ob, 0, true); + BKE_material_resize_id(bmain, obdata, 0, true); } } @@ -3162,7 +3162,7 @@ static bool mesh_separate_material(Main *bmain, Scene *scene, Base *base_old, BM /* leave the current object with some materials */ if (tot == bm_old->totface) { - mesh_separate_material_assign_mat_nr(base_old->object, mat_nr); + mesh_separate_material_assign_mat_nr(bmain, base_old->object, mat_nr); /* since we're in editmode, must set faces here */ BM_ITER_MESH (f, &iter, bm_old, BM_FACES_OF_MESH) { @@ -3174,7 +3174,7 @@ static bool mesh_separate_material(Main *bmain, Scene *scene, Base *base_old, BM /* Move selection into a separate object */ base_new = mesh_separate_tagged(bmain, scene, base_old, bm_old); if (base_new) { - mesh_separate_material_assign_mat_nr(base_new->object, mat_nr); + mesh_separate_material_assign_mat_nr(bmain, base_new->object, mat_nr); } result |= (base_new != NULL); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index cc628210e20..a8b0c28599d 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -48,6 +48,7 @@ #include "DNA_scene_types.h" #include "DNA_vfont_types.h" #include "DNA_actuator_types.h" +#include "DNA_gpencil_types.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" @@ -1145,10 +1146,22 @@ static int object_delete_exec(bContext *C, wmOperator *op) } else if (is_indirectly_used && ID_REAL_USERS(base->object) <= 1) { BKE_reportf(op->reports, RPT_WARNING, - "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user", - base->object->id.name + 2, scene->id.name + 2); + "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user", + base->object->id.name + 2, scene->id.name + 2); continue; } + /* remove from Grease Pencil parent */ + for (bGPdata *gpd = bmain->gpencil.first; gpd; gpd = gpd->id.next) { + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if (gpl->parent != NULL) { + Object *ob = gpl->parent; + Object *curob = base->object; + if (ob == curob) { + gpl->parent = NULL; + } + } + } + } /* deselect object -- it could be used in other scenes */ base->object->flag &= ~SELECT; diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index db8a4c1960f..59d78f13ccb 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -416,6 +416,13 @@ static void test_constraint(Object *owner, bPoseChannel *pchan, bConstraint *con if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL)) con->flag |= CONSTRAINT_DISABLE; } + else if (con->type == CONSTRAINT_TYPE_TRANSFORM_CACHE) { + bTransformCacheConstraint *data = con->data; + + if ((data->cache_file == NULL) || (data->object_path[0] == '\0')) { + con->flag |= CONSTRAINT_DISABLE; + } + } /* Check targets for constraints */ if (check_targets && cti && cti->get_constraint_targets) { diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index b3edf1f5e0d..067a5ad2b49 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -48,6 +48,7 @@ #include "DNA_world_types.h" #include "DNA_object_types.h" #include "DNA_vfont_types.h" +#include "DNA_gpencil_types.h" #include "BLI_math.h" #include "BLI_listbase.h" @@ -1381,7 +1382,7 @@ static int move_to_layer_exec(bContext *C, wmOperator *op) /* upper byte is used for local view */ local = base->lay & 0xFF000000; base->lay = lay + local; - base->object->lay = lay; + base->object->lay = base->lay; /* if (base->object->type == OB_LAMP) is_lamp = true; */ } CTX_DATA_END; @@ -1762,6 +1763,17 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const in else { /* copy already clears */ } + /* remap gpencil parenting */ + + if (scene->gpd) { + bGPdata *gpd = scene->gpd; + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if (gpl->parent == ob) { + gpl->parent = obn; + } + } + } + base->flag = obn->flag; id_us_min(&ob->id); @@ -2094,6 +2106,7 @@ void ED_object_single_users(Main *bmain, Scene *scene, const bool full, const bo } BKE_main_id_clear_newpoins(bmain); + DAG_relations_tag_update(bmain); } /******************************* Make Local ***********************************/ diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 992b827113d..85c05ab0e5c 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -43,6 +43,7 @@ #include "DNA_scene_types.h" #include "DNA_object_types.h" +#include "DNA_gpencil_types.h" #include "BKE_camera.h" #include "BKE_context.h" @@ -414,6 +415,99 @@ static void screen_opengl_render_write(OGLRender *oglrender) else printf("OpenGL Render failed to write '%s'\n", name); } +static void addAlphaOverFloat(float dest[4], const float source[4]) +{ + /* d = s + (1-alpha_s)d*/ + float mul; + + mul = 1.0f - source[3]; + + dest[0] = (mul * dest[0]) + source[0]; + dest[1] = (mul * dest[1]) + source[1]; + dest[2] = (mul * dest[2]) + source[2]; + dest[3] = (mul * dest[3]) + source[3]; + +} + +/* add renderlayer and renderpass for each grease pencil layer for using in composition */ +static void add_gpencil_renderpass(OGLRender *oglrender, RenderResult *rr, RenderView *rv) +{ + bGPdata *gpd = oglrender->scene->gpd; + Scene *scene = oglrender->scene; + + /* sanity checks */ + if (gpd == NULL) { + return; + } + if (scene == NULL) { + return; + } + if (BLI_listbase_is_empty(&gpd->layers)) { + return; + } + + /* save old alpha mode */ + short oldalphamode = scene->r.alphamode; + /* set alpha transparent for gp */ + scene->r.alphamode = R_ALPHAPREMUL; + + /* saves layer status */ + short *oldsts = MEM_mallocN(BLI_listbase_count(&gpd->layers) * sizeof(short), "temp_gplayers_flag"); + int i = 0; + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + oldsts[i] = gpl->flag; + ++i; + } + /* loop all layers to create separate render */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* dont draw layer if hidden */ + if (gpl->flag & GP_LAYER_HIDE) + continue; + /* hide all layer except current */ + for (bGPDlayer *gph = gpd->layers.first; gph; gph = gph->next) { + if (gpl != gph) { + gph->flag |= GP_LAYER_HIDE; + } + } + + /* render this gp layer */ + screen_opengl_render_doit(oglrender, rr); + + /* add RendePass composite */ + RenderPass *rp = RE_create_gp_pass(rr, gpl->info, rv->name); + + /* copy image data from rectf */ + float *src = RE_RenderViewGetById(rr, oglrender->view_id)->rectf; + float *dest = rp->rect; + + float *pixSrc, *pixDest; + int x, y, rectx, recty; + rectx = rr->rectx; + recty = rr->recty; + for (y = 0; y < recty; y++) { + for (x = 0; x < rectx; x++) { + pixSrc = src + 4 * (rectx * y + x); + if (pixSrc[3] > 0.0) { + pixDest = dest + 4 * (rectx * y + x); + addAlphaOverFloat(pixDest, pixSrc); + } + } + } + + /* back layer status */ + i = 0; + for (bGPDlayer *gph = gpd->layers.first; gph; gph = gph->next) { + gph->flag = oldsts[i]; + ++i; + } + } + /* free memory */ + MEM_freeN(oldsts); + + /* back default alpha mode */ + scene->r.alphamode = oldalphamode; +} + static void screen_opengl_render_apply(OGLRender *oglrender) { RenderResult *rr; @@ -449,6 +543,9 @@ static void screen_opengl_render_apply(OGLRender *oglrender) BLI_assert(view_id < oglrender->views_len); RE_SetActiveRenderView(oglrender->re, rv->name); oglrender->view_id = view_id; + /* add grease pencil passes */ + add_gpencil_renderpass(oglrender, rr, rv); + /* render composite */ screen_opengl_render_doit(oglrender, rr); } diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index f61ad348501..34c51914027 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -85,7 +85,8 @@ const char *screen_context_dir[] = { "sequences", "selected_sequences", "selected_editable_sequences", /* sequencer */ "gpencil_data", "gpencil_data_owner", /* grease pencil data */ "visible_gpencil_layers", "editable_gpencil_layers", "editable_gpencil_strokes", - "active_gpencil_layer", "active_gpencil_frame", + "active_gpencil_layer", "active_gpencil_frame", "active_gpencil_palette", + "active_gpencil_palettecolor", "active_gpencil_brush", "active_operator", NULL}; @@ -116,7 +117,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "visible_objects") || CTX_data_equals(member, "visible_bases")) { const unsigned int lay = context_layers(sc, scene, sa); - int visible_objects = CTX_data_equals(member, "visible_objects"); + const bool visible_objects = CTX_data_equals(member, "visible_objects"); for (base = scene->base.first; base; base = base->next) { if (((base->object->restrictflag & OB_RESTRICT_VIEW) == 0) && (base->lay & lay)) { @@ -131,7 +132,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "selectable_objects") || CTX_data_equals(member, "selectable_bases")) { const unsigned int lay = context_layers(sc, scene, sa); - int selectable_objects = CTX_data_equals(member, "selectable_objects"); + const bool selectable_objects = CTX_data_equals(member, "selectable_objects"); for (base = scene->base.first; base; base = base->next) { if (base->lay & lay) { @@ -148,7 +149,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "selected_objects") || CTX_data_equals(member, "selected_bases")) { const unsigned int lay = context_layers(sc, scene, sa); - int selected_objects = CTX_data_equals(member, "selected_objects"); + const bool selected_objects = CTX_data_equals(member, "selected_objects"); for (base = scene->base.first; base; base = base->next) { if ((base->flag & SELECT) && (base->lay & lay)) { @@ -163,7 +164,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "selected_editable_objects") || CTX_data_equals(member, "selected_editable_bases")) { const unsigned int lay = context_layers(sc, scene, sa); - int selected_editable_objects = CTX_data_equals(member, "selected_editable_objects"); + const bool selected_editable_objects = CTX_data_equals(member, "selected_editable_objects"); for (base = scene->base.first; base; base = base->next) { if ((base->flag & SELECT) && (base->lay & lay)) { @@ -182,7 +183,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "editable_objects") || CTX_data_equals(member, "editable_bases")) { const unsigned int lay = context_layers(sc, scene, sa); - int editable_objects = CTX_data_equals(member, "editable_objects"); + const bool editable_objects = CTX_data_equals(member, "editable_objects"); /* Visible + Editable, but not necessarily selected */ for (base = scene->base.first; base; base = base->next) { @@ -201,7 +202,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult else if (CTX_data_equals(member, "visible_bones") || CTX_data_equals(member, "editable_bones")) { bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL; EditBone *ebone, *flipbone = NULL; - int editable_bones = CTX_data_equals(member, "editable_bones"); + const bool editable_bones = CTX_data_equals(member, "editable_bones"); if (arm && arm->edbo) { /* Attention: X-Axis Mirroring is also handled here... */ @@ -243,7 +244,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult else if (CTX_data_equals(member, "selected_bones") || CTX_data_equals(member, "selected_editable_bones")) { bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL; EditBone *ebone, *flipbone = NULL; - int selected_editable_bones = CTX_data_equals(member, "selected_editable_bones"); + const bool selected_editable_bones = CTX_data_equals(member, "selected_editable_bones"); if (arm && arm->edbo) { /* Attention: X-Axis Mirroring is also handled here... */ @@ -466,7 +467,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); if (gpd) { - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); if (gpl) { CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl); @@ -474,12 +475,50 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } } } + else if (CTX_data_equals(member, "active_gpencil_palette")) { + /* XXX: see comment for gpencil_data case... */ + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + + if (gpd) { + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + + if (palette) { + CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilPalette, palette); + return 1; + } + } + } + else if (CTX_data_equals(member, "active_gpencil_palettecolor")) { + /* XXX: see comment for gpencil_data case... */ + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + + if (gpd) { + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + + if (palette) { + bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + if (palcolor) { + CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilPaletteColor, palcolor); + return 1; + } + } + } + } + else if (CTX_data_equals(member, "active_gpencil_brush")) { + /* XXX: see comment for gpencil_data case... */ + bGPDbrush *brush = BKE_gpencil_brush_getactive(scene->toolsettings); + + if (brush) { + CTX_data_pointer_set(result, NULL, &RNA_GPencilBrush, brush); + return 1; + } + } else if (CTX_data_equals(member, "active_gpencil_frame")) { /* XXX: see comment for gpencil_data case... */ bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); if (gpd) { - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); if (gpl) { CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilLayer, gpl->actframe); @@ -533,6 +572,11 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult for (gps = gpf->strokes.first; gps; gps = gps->next) { if (ED_gpencil_stroke_can_use_direct(sa, gps)) { + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + CTX_data_list_add(result, &gpd->id, &RNA_GPencilStroke, gps); } } diff --git a/source/blender/editors/space_action/action_buttons.c b/source/blender/editors/space_action/action_buttons.c index 063a477d2b6..bfc808f6d83 100644 --- a/source/blender/editors/space_action/action_buttons.c +++ b/source/blender/editors/space_action/action_buttons.c @@ -122,7 +122,7 @@ void ACTION_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; ot->idname = "ACTION_OT_properties"; - ot->description = "Toggle display properties panel"; + ot->description = "Toggle the properties region visibility"; ot->exec = action_properties_toggle_exec; ot->poll = ED_operator_action_active; diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 55b087c40e7..f8db35e2311 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -750,7 +750,7 @@ static void insert_gpencil_keys(bAnimContext *ac, short mode) /* insert gp frames */ for (ale = anim_data.first; ale; ale = ale->next) { bGPDlayer *gpl = (bGPDlayer *)ale->data; - gpencil_layer_getframe(gpl, CFRA, add_frame_mode); + BKE_gpencil_layer_getframe(gpl, CFRA, add_frame_mode); } ANIM_animdata_update(ac, &anim_data); diff --git a/source/blender/editors/space_clip/clip_intern.h b/source/blender/editors/space_clip/clip_intern.h index 2a5d959bb84..13dcd405ee6 100644 --- a/source/blender/editors/space_clip/clip_intern.h +++ b/source/blender/editors/space_clip/clip_intern.h @@ -185,7 +185,10 @@ void CLIP_OT_detect_features(struct wmOperatorType *ot); void CLIP_OT_stabilize_2d_add(struct wmOperatorType *ot); void CLIP_OT_stabilize_2d_remove(struct wmOperatorType *ot); void CLIP_OT_stabilize_2d_select(struct wmOperatorType *ot); -void CLIP_OT_stabilize_2d_set_rotation(struct wmOperatorType *ot); + +void CLIP_OT_stabilize_2d_rotation_add(struct wmOperatorType *ot); +void CLIP_OT_stabilize_2d_rotation_remove(struct wmOperatorType *ot); +void CLIP_OT_stabilize_2d_rotation_select(struct wmOperatorType *ot); void CLIP_OT_clean_tracks(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_clip/clip_toolbar.c b/source/blender/editors/space_clip/clip_toolbar.c index d31d7f53b30..b042bbe8fea 100644 --- a/source/blender/editors/space_clip/clip_toolbar.c +++ b/source/blender/editors/space_clip/clip_toolbar.c @@ -106,7 +106,7 @@ void CLIP_OT_properties(wmOperatorType *ot) { /* identifiers */ ot->name = "Properties"; - ot->description = "Toggle clip properties panel"; + ot->description = "Toggle the properties region visibility"; ot->idname = "CLIP_OT_properties"; /* api callbacks */ diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c index 5964e9a898b..547c2fba66f 100644 --- a/source/blender/editors/space_clip/clip_utils.c +++ b/source/blender/editors/space_clip/clip_utils.c @@ -175,21 +175,14 @@ void clip_graph_tracking_iterate(SpaceClip *sc, bool selected_only, bool include void clip_delete_track(bContext *C, MovieClip *clip, MovieTrackingTrack *track) { MovieTracking *tracking = &clip->tracking; - MovieTrackingStabilization *stab = &tracking->stabilization; MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking); ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); - bool has_bundle = false, update_stab = false; + bool has_bundle = false; char track_name_escaped[MAX_NAME], prefix[MAX_NAME * 2]; if (track == act_track) tracking->act_track = NULL; - if (track == stab->rot_track) { - stab->rot_track = NULL; - - update_stab = true; - } - /* handle reconstruction display in 3d viewport */ if (track->flag & TRACK_HAS_BUNDLE) has_bundle = true; @@ -207,8 +200,7 @@ void clip_delete_track(bContext *C, MovieClip *clip, MovieTrackingTrack *track) WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip); - if (update_stab) { - tracking->stabilization.ok = false; + if (track->flag & (TRACK_USE_2D_STAB | TRACK_USE_2D_STAB_ROT)) { WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); } diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 396d71f0a20..4f653bab682 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -225,18 +225,6 @@ static void clip_scopes_check_gpencil_change(ScrArea *sa) } } -static void clip_stabilization_tag_refresh(ScrArea *sa) -{ - SpaceClip *sc = (SpaceClip *) sa->spacedata.first; - MovieClip *clip = ED_space_clip_get_clip(sc); - - if (clip) { - MovieTrackingStabilization *stab = &clip->tracking.stabilization; - - stab->ok = false; - } -} - /* ******************** default callbacks for clip space ***************** */ static SpaceLink *clip_new(const bContext *C) @@ -368,7 +356,6 @@ static void clip_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn) case NA_REMOVED: case NA_EDITED: case NA_EVALUATED: - clip_stabilization_tag_refresh(sa); /* fall-through */ case NA_SELECTED: @@ -412,7 +399,6 @@ static void clip_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn) case NC_SPACE: if (wmn->data == ND_SPACE_CLIP) { clip_scopes_tag_refresh(sa); - clip_stabilization_tag_refresh(sa); ED_area_tag_redraw(sa); } break; @@ -457,7 +443,7 @@ static void clip_operatortypes(void) /* navigation */ WM_operatortype_append(CLIP_OT_frame_jump); - /* foorage */ + /* set optical center to frame center */ WM_operatortype_append(CLIP_OT_set_center_principal); /* selection */ @@ -505,7 +491,9 @@ static void clip_operatortypes(void) WM_operatortype_append(CLIP_OT_stabilize_2d_add); WM_operatortype_append(CLIP_OT_stabilize_2d_remove); WM_operatortype_append(CLIP_OT_stabilize_2d_select); - WM_operatortype_append(CLIP_OT_stabilize_2d_set_rotation); + WM_operatortype_append(CLIP_OT_stabilize_2d_rotation_add); + WM_operatortype_append(CLIP_OT_stabilize_2d_rotation_remove); + WM_operatortype_append(CLIP_OT_stabilize_2d_rotation_select); /* clean-up */ WM_operatortype_append(CLIP_OT_clear_track_path); diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index 61bfa5b315b..d28cbe5fb1d 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -1509,8 +1509,10 @@ static int join_tracks_exec(bContext *C, wmOperator *op) SpaceClip *sc = CTX_wm_space_clip(C); MovieClip *clip = ED_space_clip_get_clip(sc); MovieTracking *tracking = &clip->tracking; + MovieTrackingStabilization *stab = &tracking->stabilization; ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking); + bool update_stabilization = false; MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking); if (act_track == NULL) { @@ -1528,8 +1530,23 @@ static int join_tracks_exec(bContext *C, wmOperator *op) if (TRACK_VIEW_SELECTED(sc, track) && track != act_track) { BKE_tracking_tracks_join(tracking, act_track, track); - if (tracking->stabilization.rot_track == track) { - tracking->stabilization.rot_track = act_track; + if (track->flag & TRACK_USE_2D_STAB) { + update_stabilization = true; + if ((act_track->flag & TRACK_USE_2D_STAB) == 0) { + act_track->flag |= TRACK_USE_2D_STAB; + } else { + stab->tot_track--; + } + BLI_assert(0 <= stab->tot_track); + } + if (track->flag & TRACK_USE_2D_STAB_ROT) { + update_stabilization = true; + if ((act_track->flag & TRACK_USE_2D_STAB_ROT) == 0) { + act_track->flag |= TRACK_USE_2D_STAB_ROT; + } else { + stab->tot_rot_track--; + } + BLI_assert(0 <= stab->tot_rot_track); } for (MovieTrackingPlaneTrack *plane_track = plane_tracks_base->first; @@ -1551,6 +1568,10 @@ static int join_tracks_exec(bContext *C, wmOperator *op) } } + if (update_stabilization) { + WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); + } + GSetIterator gs_iter; int framenr = ED_space_clip_get_clip_frame_number(sc); GSET_ITER (gs_iter, point_tracks) { diff --git a/source/blender/editors/space_clip/tracking_ops_stabilize.c b/source/blender/editors/space_clip/tracking_ops_stabilize.c index 8d6173e1cea..35b1aead343 100644 --- a/source/blender/editors/space_clip/tracking_ops_stabilize.c +++ b/source/blender/editors/space_clip/tracking_ops_stabilize.c @@ -84,7 +84,6 @@ static int stabilize_2d_add_exec(bContext *C, wmOperator *UNUSED(op)) } if (update) { - stab->ok = 0; DAG_id_tag_update(&clip->id, 0); WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); } @@ -96,7 +95,7 @@ void CLIP_OT_stabilize_2d_add(wmOperatorType *ot) { /* identifiers */ ot->name = "Add Stabilization Tracks"; - ot->description = "Add selected tracks to 2D stabilization tool"; + ot->description = "Add selected tracks to 2D translation stabilization"; ot->idname = "CLIP_OT_stabilize_2d_add"; /* api callbacks */ @@ -139,7 +138,6 @@ static int stabilize_2d_remove_exec(bContext *C, wmOperator *UNUSED(op)) } if (update) { - stab->ok = 0; DAG_id_tag_update(&clip->id, 0); WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); } @@ -151,7 +149,7 @@ void CLIP_OT_stabilize_2d_remove(wmOperatorType *ot) { /* identifiers */ ot->name = "Remove Stabilization Track"; - ot->description = "Remove selected track from stabilization"; + ot->description = "Remove selected track from translation stabilization"; ot->idname = "CLIP_OT_stabilize_2d_remove"; /* api callbacks */ @@ -193,7 +191,7 @@ void CLIP_OT_stabilize_2d_select(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Stabilization Tracks"; - ot->description = "Select tracks which are used for stabilization"; + ot->description = "Select tracks which are used for translation stabilization"; ot->idname = "CLIP_OT_stabilize_2d_select"; /* api callbacks */ @@ -204,20 +202,85 @@ void CLIP_OT_stabilize_2d_select(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/***************** set 2d stabilization rotation track operator ****************/ +/********************** add 2d stabilization tracks for rotation operator ****************/ -static int stabilize_2d_set_rotation_exec(bContext *C, wmOperator *UNUSED(op)) +static int stabilize_2d_rotation_add_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceClip *sc = CTX_wm_space_clip(C); MovieClip *clip = ED_space_clip_get_clip(sc); MovieTracking *tracking = &clip->tracking; - MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking); + ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); + MovieTrackingStabilization *stab = &tracking->stabilization; + + bool update = false; + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + if (TRACK_VIEW_SELECTED(sc, track) && + (track->flag & TRACK_USE_2D_STAB_ROT) == 0) + { + track->flag |= TRACK_USE_2D_STAB_ROT; + stab->tot_rot_track++; + update = true; + } + } + + if (update) { + DAG_id_tag_update(&clip->id, 0); + WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); + } + + return OPERATOR_FINISHED; +} + +void CLIP_OT_stabilize_2d_rotation_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Stabilization Rotation Tracks"; + ot->description = "Add selected tracks to 2D rotation stabilization"; + ot->idname = "CLIP_OT_stabilize_2d_rotation_add"; + + /* api callbacks */ + ot->exec = stabilize_2d_rotation_add_exec; + ot->poll = stabilize_2d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/********************** remove 2d stabilization tracks for rotation operator *************/ + +static int stabilize_2d_rotation_remove_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + MovieTracking *tracking = &clip->tracking; + MovieTrackingStabilization *stab = &tracking->stabilization; + ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); + int a = 0; + bool update = false; - if (act_track != NULL) { - MovieTrackingStabilization *stab = &tracking->stabilization; - stab->rot_track = act_track; - stab->ok = 0; + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + if (track->flag & TRACK_USE_2D_STAB_ROT) { + if (a == stab->act_rot_track) { + track->flag &= ~TRACK_USE_2D_STAB_ROT; + stab->act_rot_track--; + stab->tot_rot_track--; + if (stab->act_rot_track < 0) { + stab->act_rot_track = 0; + } + update = true; + break; + } + a++; + } + } + if (update) { DAG_id_tag_update(&clip->id, 0); WM_event_add_notifier(C, NC_MOVIECLIP | ND_DISPLAY, clip); } @@ -225,18 +288,60 @@ static int stabilize_2d_set_rotation_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -void CLIP_OT_stabilize_2d_set_rotation(wmOperatorType *ot) +void CLIP_OT_stabilize_2d_rotation_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Stabilization Rotation Track"; + ot->description = "Remove selected track from rotation stabilization"; + ot->idname = "CLIP_OT_stabilize_2d_rotation_remove"; + + /* api callbacks */ + ot->exec = stabilize_2d_rotation_remove_exec; + ot->poll = stabilize_2d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/********************** select 2d stabilization rotation tracks operator *****************/ + +static int stabilize_2d_rotation_select_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + MovieClip *clip = ED_space_clip_get_clip(sc); + MovieTracking *tracking = &clip->tracking; + ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); + bool update = false; + + for (MovieTrackingTrack *track = tracksbase->first; + track != NULL; + track = track->next) + { + if (track->flag & TRACK_USE_2D_STAB_ROT) { + BKE_tracking_track_flag_set(track, TRACK_AREA_ALL, SELECT); + update = true; + } + } + + if (update) { + WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, clip); + } + + return OPERATOR_FINISHED; +} + +void CLIP_OT_stabilize_2d_rotation_select(wmOperatorType *ot) { /* identifiers */ - ot->name = "Set Rotation Track"; - ot->description = "Use active track to compensate rotation when " - "doing 2D stabilization"; - ot->idname = "CLIP_OT_stabilize_2d_set_rotation"; + ot->name = "Select Stabilization Rotation Tracks"; + ot->description = "Select tracks which are used for rotation stabilization"; + ot->idname = "CLIP_OT_stabilize_2d_rotation_select"; /* api callbacks */ - ot->exec = stabilize_2d_set_rotation_exec; + ot->exec = stabilize_2d_rotation_select_exec; ot->poll = stabilize_2d_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 5e9eb1f9207..b6e4991bf52 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -920,6 +920,8 @@ static int filelist_geticon_ex( return ICON_FILE_BLANK; else if (typeflag & FILE_TYPE_COLLADA) return ICON_FILE_BLANK; + else if (typeflag & FILE_TYPE_ALEMBIC) + return ICON_FILE_BLANK; else if (typeflag & FILE_TYPE_TEXT) return ICON_FILE_TEXT; else if (typeflag & FILE_TYPE_BLENDERLIB) { @@ -1952,6 +1954,9 @@ int ED_path_extension_type(const char *path) else if (BLI_testextensie(path, ".dae")) { return FILE_TYPE_COLLADA; } + else if (BLI_testextensie(path, ".abc")) { + return FILE_TYPE_ALEMBIC; + } else if (BLI_testextensie_array(path, imb_ext_image) || (G.have_quicktime && BLI_testextensie_array(path, imb_ext_image_qt))) { @@ -2004,6 +2009,8 @@ int ED_file_extension_icon(const char *path) return ICON_FILE_BLANK; case FILE_TYPE_COLLADA: return ICON_FILE_BLANK; + case FILE_TYPE_ALEMBIC: + return ICON_FILE_BLANK; case FILE_TYPE_TEXT: return ICON_FILE_TEXT; default: diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index ab33d452d3c..1a558d40560 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -185,6 +185,8 @@ short ED_fileselect_set_params(SpaceFile *sfile) params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_BTX : 0; if ((prop = RNA_struct_find_property(op->ptr, "filter_collada"))) params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_COLLADA : 0; + if ((prop = RNA_struct_find_property(op->ptr, "filter_alembic"))) + params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_ALEMBIC : 0; if ((prop = RNA_struct_find_property(op->ptr, "filter_glob"))) { /* Protection against pyscripts not setting proper size limit... */ char *tmp = RNA_property_string_get_alloc( @@ -213,7 +215,7 @@ short ED_fileselect_set_params(SpaceFile *sfile) FILTER_ID_GR | FILTER_ID_IM | FILTER_ID_LA | FILTER_ID_LS | FILTER_ID_LT | FILTER_ID_MA | FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | FILTER_ID_OB | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | FILTER_ID_SO | - FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO; + FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF; if (U.uiflag & USER_HIDE_DOT) { params->flag |= FILE_HIDE_DOT; diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index 4cbf04f9d42..516814b63b4 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -1067,7 +1067,7 @@ void GRAPH_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; ot->idname = "GRAPH_OT_properties"; - ot->description = "Toggle display properties panel"; + ot->description = "Toggle the properties region visibility"; ot->exec = graph_properties_toggle_exec; ot->poll = ED_operator_graphedit_active; diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 2582ba4be8d..b35d1b2c777 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -687,7 +687,7 @@ static void graph_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID return; } - if ((ID *)sgraph->ads->filter_grp == old_id) { + if (sgraph->ads && (ID *)sgraph->ads->filter_grp == old_id) { sgraph->ads->filter_grp = (Group *)new_id; } } diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 38a54ade367..b9d98dfe794 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -1330,7 +1330,7 @@ void IMAGE_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; ot->idname = "IMAGE_OT_properties"; - ot->description = "Toggle display properties panel"; + ot->description = "Toggle the properties region visibility"; ot->exec = image_properties_toggle_exec; ot->poll = ED_operator_image_active; diff --git a/source/blender/editors/space_logic/logic_buttons.c b/source/blender/editors/space_logic/logic_buttons.c index 12c7ef3d3ec..e5eee21ed08 100644 --- a/source/blender/editors/space_logic/logic_buttons.c +++ b/source/blender/editors/space_logic/logic_buttons.c @@ -63,7 +63,7 @@ static int logic_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op)) void LOGIC_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; - ot->description = "Toggle display properties panel"; + ot->description = "Toggle the properties region visibility"; ot->idname = "LOGIC_OT_properties"; ot->exec = logic_properties_toggle_exec; diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index cbdc476bee6..3243579f7d0 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -131,6 +131,7 @@ bool nla_panel_context(const bContext *C, PointerRNA *adt_ptr, PointerRNA *nlt_p case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: + case ANIMTYPE_DSCACHEFILE: case ANIMTYPE_DSCUR: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: @@ -567,7 +568,7 @@ void NLA_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; ot->idname = "NLA_OT_properties"; - ot->description = "Toggle display properties panel"; + ot->description = "Toggle the properties region visibility"; ot->exec = nla_properties_toggle_exec; ot->poll = ED_operator_nla_active; diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 9e73e03a664..4f30c049d9d 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -170,6 +170,7 @@ static int mouse_nla_channels(bContext *C, bAnimContext *ac, float x, int channe case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: + case ANIMTYPE_DSCACHEFILE: case ANIMTYPE_DSCUR: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index ddbd07616bc..7b08b8368ba 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -1338,7 +1338,7 @@ static void node_composit_buts_renderlayers(uiLayout *layout, bContext *C, Point scn_ptr = RNA_pointer_get(ptr, "scene"); RNA_string_get(&scn_ptr, "name", scene_name); - + WM_operator_properties_create_ptr(&op_ptr, ot); RNA_string_set(&op_ptr, "layer", layer_name); RNA_string_set(&op_ptr, "scene", scene_name); diff --git a/source/blender/editors/space_node/node_buttons.c b/source/blender/editors/space_node/node_buttons.c index 52b0292b9a3..f0567924edd 100644 --- a/source/blender/editors/space_node/node_buttons.c +++ b/source/blender/editors/space_node/node_buttons.c @@ -222,7 +222,7 @@ static int node_properties_poll(bContext *C) void NODE_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; - ot->description = "Toggles the properties panel display"; + ot->description = "Toggle the properties region visibility"; ot->idname = "NODE_OT_properties"; ot->exec = node_properties_toggle_exec; diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index ea3869ef387..5f592431558 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -276,25 +276,16 @@ static bNodeSocket *best_socket_input(bNodeTree *ntree, bNode *node, int num, in return NULL; } -static int snode_autoconnect_input(SpaceNode *snode, bNode *node_fr, bNodeSocket *sock_fr, bNode *node_to, bNodeSocket *sock_to, int replace) +static bool snode_autoconnect_input(SpaceNode *snode, bNode *node_fr, bNodeSocket *sock_fr, bNode *node_to, bNodeSocket *sock_to, int replace) { bNodeTree *ntree = snode->edittree; - bNodeLink *link; /* then we can connect */ if (replace) nodeRemSocketLinks(ntree, sock_to); - link = nodeAddLink(ntree, node_fr, sock_fr, node_to, sock_to); - /* validate the new link */ - ntreeUpdateTree(G.main, ntree); - if (!(link->flag & NODE_LINK_VALID)) { - nodeRemLink(ntree, link); - return 0; - } - - snode_update(snode, node_to); - return 1; + nodeAddLink(ntree, node_fr, sock_fr, node_to, sock_to); + return true; } static void snode_autoconnect(SpaceNode *snode, const bool allow_multiple, const bool replace) diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index ae34a118992..d4553b650c5 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -1002,7 +1002,7 @@ static void tselem_draw_icon_uibut(struct DrawIconArg *arg, int icon) } -static void tselem_draw_gp_icon_uibut(struct DrawIconArg *arg, ID *id, bGPDlayer *gpl) +static void UNUSED_FUNCTION(tselem_draw_gp_icon_uibut)(struct DrawIconArg *arg, ID *id, bGPDlayer *gpl) { /* restrict column clip - skip it for now... */ if (arg->x >= arg->xmax) { @@ -1173,6 +1173,8 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto UI_icon_draw(x, y, ICON_MOD_TRIANGULATE); break; case eModifierType_MeshCache: UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */ + case eModifierType_MeshSequenceCache: + UI_icon_draw(x, y, ICON_MOD_MESHDEFORM); break; /* XXX, needs own icon */ case eModifierType_Wireframe: UI_icon_draw(x, y, ICON_MOD_WIREFRAME); break; case eModifierType_LaplacianDeform: @@ -1233,9 +1235,12 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto else UI_icon_draw(x, y, RNA_struct_ui_icon(te->rnaptr.type)); break; + /* Removed the icons from outliner. Need a better structure with Layers, Palettes and Colors */ +#if 0 case TSE_GP_LAYER: tselem_draw_gp_icon_uibut(&arg, tselem->id, te->directdata); break; +#endif default: UI_icon_draw(x, y, ICON_DOT); break; } diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index d2666cd0b6d..ca037cb20cc 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -62,7 +62,7 @@ typedef struct TreeElement { #define TREESTORE_ID_TYPE(_id) \ (ELEM(GS((_id)->name), ID_SCE, ID_LI, ID_OB, ID_ME, ID_CU, ID_MB, ID_NT, ID_MA, ID_TE, ID_IM, ID_LT, ID_LA, ID_CA) || \ ELEM(GS((_id)->name), ID_KE, ID_WO, ID_SPK, ID_GR, ID_AR, ID_AC, ID_BR, ID_PA, ID_GD, ID_LS) || \ - ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO)) /* Only in 'blendfile' mode ... :/ */ + ELEM(GS((_id)->name), ID_SCR, ID_WM, ID_TXT, ID_VF, ID_SO, ID_CF)) /* Only in 'blendfile' mode ... :/ */ /* TreeElement->flag */ #define TE_ACTIVE 1 diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index b22e6595caf..96bab3d5c1e 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -38,6 +38,7 @@ #include "DNA_armature_types.h" #include "DNA_constraint_types.h" #include "DNA_camera_types.h" +#include "DNA_cachefile_types.h" #include "DNA_gpencil_types.h" #include "DNA_group_types.h" #include "DNA_key_types.h" @@ -746,6 +747,16 @@ static void outliner_add_id_contents(SpaceOops *soops, TreeElement *te, TreeStor outliner_add_element(soops, &te->subtree, ca, te, TSE_ANIM_DATA, 0); break; } + case ID_CF: + { + CacheFile *cache_file = (CacheFile *)id; + + if (outliner_animdata_test(cache_file->adt)) { + outliner_add_element(soops, &te->subtree, cache_file, te, TSE_ANIM_DATA, 0); + } + + break; + } case ID_LA: { Lamp *la = (Lamp *)id; diff --git a/source/blender/editors/space_sequencer/sequencer_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c index 86d3fcbe1ac..b33a26db728 100644 --- a/source/blender/editors/space_sequencer/sequencer_buttons.c +++ b/source/blender/editors/space_sequencer/sequencer_buttons.c @@ -94,7 +94,7 @@ void SEQUENCER_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; ot->idname = "SEQUENCER_OT_properties"; - ot->description = "Open sequencer properties panel"; + ot->description = "Toggle the properties region visibility"; ot->exec = sequencer_properties_toggle_exec; ot->poll = ED_operator_sequencer_active; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index adb7cf4940c..e1768e4aedc 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -471,7 +471,7 @@ static void draw_seq_handle(View2D *v2d, Sequence *seq, const float handsize_cla } /* draw info text on a sequence strip */ -static void draw_seq_text(View2D *v2d, Sequence *seq, float x1, float x2, float y1, float y2, const unsigned char background_col[3]) +static void draw_seq_text(View2D *v2d, SpaceSeq *sseq, Sequence *seq, float x1, float x2, float y1, float y2, const unsigned char background_col[3]) { rctf rect; char str[32 + FILE_MAX]; @@ -540,7 +540,12 @@ static void draw_seq_text(View2D *v2d, Sequence *seq, float x1, float x2, float name, seq->len); } else if (seq->type == SEQ_TYPE_SOUND_RAM) { - if (seq->sound) { + /* If a waveform is drawn, we don't want to overlay it with text, + * as it would make both hard to read. */ + if ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM)) { + str[0] = 0; + str_len = 0; + } else if (seq->sound) { str_len = BLI_snprintf(str, sizeof(str), "%s: %s | %d", name, seq->sound->name, seq->len); } @@ -870,7 +875,7 @@ static void draw_seq_strip(const bContext *C, SpaceSeq *sseq, Scene *scene, AReg /* nice text here would require changing the view matrix for texture text */ if ((x2 - x1) / pixelx > 32) { - draw_seq_text(v2d, seq, x1, x2, y1, y2, background_col); + draw_seq_text(v2d, sseq, seq, x1, x2, y1, y2, background_col); } } diff --git a/source/blender/editors/space_text/text_header.c b/source/blender/editors/space_text/text_header.c index 91665f1a598..e06a5b5474e 100644 --- a/source/blender/editors/space_text/text_header.c +++ b/source/blender/editors/space_text/text_header.c @@ -92,7 +92,7 @@ void TEXT_OT_properties(wmOperatorType *ot) { /* identifiers */ ot->name = "Properties"; - ot->description = "Toggle text properties panel"; + ot->description = "Toggle the properties region visibility"; ot->idname = "TEXT_OT_properties"; /* api callbacks */ diff --git a/source/blender/editors/space_time/space_time.c b/source/blender/editors/space_time/space_time.c index 525d42a1965..021c4a54b0a 100644 --- a/source/blender/editors/space_time/space_time.c +++ b/source/blender/editors/space_time/space_time.c @@ -32,7 +32,10 @@ #include <string.h> #include <stdio.h> +#include "DNA_cachefile_types.h" +#include "DNA_constraint_types.h" #include "DNA_gpencil_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -42,7 +45,10 @@ #include "BLI_dlrbTree.h" #include "BLI_utildefines.h" +#include "BKE_constraint.h" #include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_modifier.h" #include "BKE_screen.h" #include "BKE_pointcache.h" @@ -320,6 +326,9 @@ static void time_draw_idblock_keyframes(View2D *v2d, ID *id, short onlysel) case ID_GD: gpencil_to_keylist(&ads, (bGPdata *)id, &keys); break; + case ID_CF: + cachefile_to_keylist(&ads, (CacheFile *)id, &keys, NULL); + break; } /* build linked-list for searching */ @@ -344,6 +353,56 @@ static void time_draw_idblock_keyframes(View2D *v2d, ID *id, short onlysel) BLI_dlrbTree_free(&keys); } +static void time_draw_caches_keyframes(Main *bmain, Scene *scene, View2D *v2d, bool onlysel) +{ + CacheFile *cache_file; + + for (cache_file = bmain->cachefiles.first; + cache_file; + cache_file = cache_file->id.next) + { + cache_file->draw_flag &= ~CACHEFILE_KEYFRAME_DRAWN; + } + + for (Base *base = scene->base.first; base; base = base->next) { + Object *ob = base->object; + + ModifierData *md = modifiers_findByType(ob, eModifierType_MeshSequenceCache); + + if (md) { + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + + cache_file = mcmd->cache_file; + + if (!cache_file || (cache_file->draw_flag & CACHEFILE_KEYFRAME_DRAWN) != 0) { + continue; + } + + cache_file->draw_flag |= CACHEFILE_KEYFRAME_DRAWN; + + time_draw_idblock_keyframes(v2d, (ID *)cache_file, onlysel); + } + + for (bConstraint *con = ob->constraints.first; con; con = con->next) { + if (con->type != CONSTRAINT_TYPE_TRANSFORM_CACHE) { + continue; + } + + bTransformCacheConstraint *data = con->data; + + cache_file = data->cache_file; + + if (!cache_file || (cache_file->draw_flag & CACHEFILE_KEYFRAME_DRAWN) != 0) { + continue; + } + + cache_file->draw_flag |= CACHEFILE_KEYFRAME_DRAWN; + + time_draw_idblock_keyframes(v2d, (ID *)cache_file, onlysel); + } + } +} + /* draw keyframe lines for timeline */ static void time_draw_keyframes(const bContext *C, ARegion *ar) { @@ -354,7 +413,11 @@ static void time_draw_keyframes(const bContext *C, ARegion *ar) /* set this for all keyframe lines once and for all */ glLineWidth(1.0); - + + /* draw cache files keyframes (if available) */ + UI_ThemeColor(TH_TIME_KEYFRAME); + time_draw_caches_keyframes(CTX_data_main(C), scene, v2d, onlysel); + /* draw grease pencil keyframes (if available) */ UI_ThemeColor(TH_TIME_GP_KEYFRAME); if (scene->gpd) { diff --git a/source/blender/editors/space_view3d/drawvolume.c b/source/blender/editors/space_view3d/drawvolume.c index e93d840eddd..8dbc2788744 100644 --- a/source/blender/editors/space_view3d/drawvolume.c +++ b/source/blender/editors/space_view3d/drawvolume.c @@ -379,11 +379,13 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, /* setup buffer and draw */ - int gl_depth = 0, gl_blend = 0; + int gl_depth = 0, gl_blend = 0, gl_depth_write = 0; glGetBooleanv(GL_BLEND, (GLboolean *)&gl_blend); glGetBooleanv(GL_DEPTH_TEST, (GLboolean *)&gl_depth); + glGetBooleanv(GL_DEPTH_WRITEMASK, (GLboolean *)&gl_depth_write); glEnable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); @@ -422,6 +424,8 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, GPU_shader_unbind(); + glDepthMask(gl_depth_write); + if (!gl_blend) { glDisable(GL_BLEND); } diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index c52d1238163..b9c8c98b62f 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -1206,7 +1206,7 @@ static int view3d_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op)) void VIEW3D_OT_properties(wmOperatorType *ot) { ot->name = "Properties"; - ot->description = "Toggles the properties panel display"; + ot->description = "Toggle the properties region visibility"; ot->idname = "VIEW3D_OT_properties"; ot->exec = view3d_properties_toggle_exec; diff --git a/source/blender/editors/space_view3d/view3d_ruler.c b/source/blender/editors/space_view3d/view3d_ruler.c index f46608b7d5e..37b068e3e49 100644 --- a/source/blender/editors/space_view3d/view3d_ruler.c +++ b/source/blender/editors/space_view3d/view3d_ruler.c @@ -302,18 +302,18 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) bool changed = false; if (scene->gpd == NULL) { - scene->gpd = gpencil_data_addnew("GPencil"); + scene->gpd = BKE_gpencil_data_addnew("GPencil"); } gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info)); if (gpl == NULL) { - gpl = gpencil_layer_addnew(scene->gpd, ruler_name, false); + gpl = BKE_gpencil_layer_addnew(scene->gpd, ruler_name, false); gpl->thickness = 1; gpl->flag |= GP_LAYER_HIDE; } - gpf = gpencil_layer_getframe(gpl, CFRA, true); - free_gpencil_strokes(gpf); + gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true); + BKE_gpencil_free_strokes(gpf); for (ruler_item = ruler_info->items.first; ruler_item; ruler_item = ruler_item->next) { bGPDspoint *pt; @@ -327,6 +327,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) for (j = 0; j < 3; j++) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; + pt->strength = 1.0f; pt++; } } @@ -336,6 +337,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) for (j = 0; j < 3; j += 2) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; + pt->strength = 1.0f; pt++; } } @@ -358,7 +360,7 @@ static bool view3d_ruler_from_gpencil(bContext *C, RulerInfo *ruler_info) gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info)); if (gpl) { bGPDframe *gpf; - gpf = gpencil_layer_getframe(gpl, CFRA, false); + gpf = BKE_gpencil_layer_getframe(gpl, CFRA, false); if (gpf) { bGPDstroke *gps; for (gps = gpf->strokes.first; gps; gps = gps->next) { diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 1376b6bf4da..9c266890d6d 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -3292,7 +3292,7 @@ static void posttrans_gpd_clean(bGPdata *gpd) for (gpf = gpl->frames.first; gpf; gpf = gpfn) { gpfn = gpf->next; if (gpfn && gpf->framenum == gpfn->framenum) { - gpencil_layer_delframe(gpl, gpf); + BKE_gpencil_layer_delframe(gpl, gpf); } } } @@ -7687,7 +7687,11 @@ static void createTransGPencil(bContext *C, TransInfo *t) if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; } - + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } + if (is_prop_edit) { /* Proportional Editing... */ if (is_prop_edit_connected) { @@ -7735,14 +7739,27 @@ static void createTransGPencil(bContext *C, TransInfo *t) if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { bGPDframe *gpf = gpl->actframe; bGPDstroke *gps; + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + /* undo matrix */ + invert_m4_m4(inverse_diff_mat, diff_mat); + } - /* Make a new frame to work on if the layer's frame and the current scene frame don't match up + /* Make a new frame to work on if the layer's frame and the current scene frame don't match up * - This is useful when animating as it saves that "uh-oh" moment when you realize you've * spent too much time editing the wrong frame... */ // XXX: should this be allowed when framelock is enabled? if (gpf->framenum != cfra) { - gpf = gpencil_frame_addcopy(gpl, cfra); + gpf = BKE_gpencil_frame_addcopy(gpl, cfra); + /* in some weird situations (framelock enabled) return NULL */ + if (gpf == NULL) { + continue; + } } /* Loop over strokes, adding TransData for points as needed... */ @@ -7755,7 +7772,10 @@ static void createTransGPencil(bContext *C, TransInfo *t) if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; } - + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + continue; + } /* What we need to include depends on proportional editing settings... */ if (is_prop_edit) { if (is_prop_edit_connected) { @@ -7824,9 +7844,18 @@ static void createTransGPencil(bContext *C, TransInfo *t) /* screenspace */ td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ; - copy_m3_m4(td->smtx, t->persmat); - copy_m3_m4(td->mtx, t->persinv); - unit_m3(td->axismtx); + /* apply parent transformations */ + if (gpl->parent == NULL) { + copy_m3_m4(td->smtx, t->persmat); + copy_m3_m4(td->mtx, t->persinv); + unit_m3(td->axismtx); + } + else { + /* apply matrix transformation relative to parent */ + copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */ + copy_m3_m4(td->mtx, diff_mat); /* display position */ + copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */ + } } else { /* configure 2D dataspace points so that they don't play up... */ @@ -7835,9 +7864,18 @@ static void createTransGPencil(bContext *C, TransInfo *t) // XXX: matrices may need to be different? } - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); - unit_m3(td->axismtx); // XXX? + /* apply parent transformations */ + if (gpl->parent == NULL) { + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); + unit_m3(td->axismtx); // XXX? + } + else { + /* apply matrix transformation relative to parent */ + copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */ + copy_m3_m4(td->mtx, diff_mat); /* display position */ + copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */ + } } /* Triangulation must be calculated again, so save the stroke for recalc function */ td->extra = gps; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 728b10f5e6f..f78a23be7b8 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -975,7 +975,9 @@ static void recalcData_gpencil_strokes(TransInfo *t) TransData *td = t->data; for (int i = 0; i < t->total; i++, td++) { bGPDstroke *gps = td->extra; - gps->flag |= GP_STROKE_RECALC_CACHES; + if (gps != NULL) { + gps->flag |= GP_STROKE_RECALC_CACHES; + } } } diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c index 309ad22e31c..075f311db72 100644 --- a/source/blender/editors/transform/transform_manipulator.c +++ b/source/blender/editors/transform/transform_manipulator.c @@ -58,6 +58,7 @@ #include "BKE_pointcache.h" #include "BKE_editmesh.h" #include "BKE_lattice.h" +#include "BKE_gpencil.h" #include "BIF_gl.h" @@ -68,6 +69,7 @@ #include "ED_curve.h" #include "ED_particle.h" #include "ED_view3d.h" +#include "ED_gpencil.h" #include "UI_resources.h" @@ -288,24 +290,49 @@ static int calc_manipulator_stats(const bContext *C) zero_v3(scene->twcent); if (is_gp_edit) { - CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) - { - /* we're only interested in selected points here... */ - if (gps->flag & GP_STROKE_SELECT) { - bGPDspoint *pt; - int i; - - /* Change selection status of all points, then make the stroke match */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - calc_tw_center(scene, &pt->x); - totsel++; + float diff_mat[4][4]; + float fpt[3]; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + } + + for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + + /* we're only interested in selected points here... */ + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + int i; + + /* Change selection status of all points, then make the stroke match */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + if (gpl->parent == NULL) { + calc_tw_center(scene, &pt->x); + totsel++; + } + else { + mul_v3_m4v3(fpt, diff_mat, &pt->x); + calc_tw_center(scene, fpt); + totsel++; + } + } + } } } } } - CTX_DATA_END; - + + /* selection center */ if (totsel) { mul_v3_fl(scene->twcent, 1.0f / (float)totsel); /* centroid! */ diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp index ea5a55731c3..1f5e2b63bfa 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp @@ -807,6 +807,7 @@ void BlenderFileLoader::insertShapeNode(ObjectInstanceRen *obi, int id) // sets the id of the rep rep->setId(Id(id, 0)); rep->setName(obi->ob->id.name + 2); + rep->setLibraryPath(obi->ob->id.lib ? obi->ob->id.lib->name : NULL); const BBox<Vec3r> bbox = BBox<Vec3r>(Vec3r(ls.minBBox[0], ls.minBBox[1], ls.minBBox[2]), Vec3r(ls.maxBBox[0], ls.maxBBox[1], ls.maxBBox[2])); diff --git a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp index 253bf278478..f2f53159fcf 100644 --- a/source/blender/freestyle/intern/python/BPy_ViewShape.cpp +++ b/source/blender/freestyle/intern/python/BPy_ViewShape.cpp @@ -296,6 +296,19 @@ static PyObject *ViewShape_name_get(BPy_ViewShape *self, void *UNUSED(closure)) return PyUnicode_FromString(self->vs->getName()); } +PyDoc_STRVAR(ViewShape_library_path_doc, +"The library path of the ViewShape.\n" +"\n" +":type: str, or None if the ViewShape is not part of a library"); + +static PyObject *ViewShape_library_path_get(BPy_ViewShape *self, void *UNUSED(closure)) +{ + const char *name = self->vs->getLibraryPath(); + if (!name) + Py_RETURN_NONE; + return PyUnicode_FromString(name); +} + PyDoc_STRVAR(ViewShape_id_doc, "The Id of this ViewShape.\n" "\n" @@ -313,6 +326,7 @@ static PyGetSetDef BPy_ViewShape_getseters[] = { (char *)ViewShape_vertices_doc, NULL}, {(char *)"edges", (getter)ViewShape_edges_get, (setter)ViewShape_edges_set, (char *)ViewShape_edges_doc, NULL}, {(char *)"name", (getter)ViewShape_name_get, (setter)NULL, (char *)ViewShape_name_doc, NULL}, + {(char *)"library_path", (getter)ViewShape_library_path_get, (setter)NULL, (char *)ViewShape_library_path_doc, NULL}, {(char *)"id", (getter)ViewShape_id_get, (setter)NULL, (char *)ViewShape_id_doc, NULL}, {NULL, NULL, NULL, NULL, NULL} /* Sentinel */ }; diff --git a/source/blender/freestyle/intern/scene_graph/Rep.h b/source/blender/freestyle/intern/scene_graph/Rep.h index c5036cdb153..773eb2d3278 100644 --- a/source/blender/freestyle/intern/scene_graph/Rep.h +++ b/source/blender/freestyle/intern/scene_graph/Rep.h @@ -48,6 +48,8 @@ public: inline Rep() : BaseObject() { _Id = 0; + _Name = 0; + _LibraryPath = 0; _FrsMaterial = 0; } @@ -55,6 +57,7 @@ public: { _Id = iBrother._Id; _Name = iBrother._Name; + _LibraryPath = iBrother._LibraryPath; if (0 == iBrother._FrsMaterial) _FrsMaterial = 0; else @@ -68,6 +71,7 @@ public: std::swap(_BBox, ioOther._BBox); std::swap(_Id, ioOther._Id); std::swap(_Name, ioOther._Name); + std::swap(_LibraryPath, ioOther._LibraryPath); std::swap(_FrsMaterial, ioOther._FrsMaterial); } @@ -76,6 +80,7 @@ public: if (&iBrother != this) { _Id = iBrother._Id; _Name = iBrother._Name; + _LibraryPath = iBrother._LibraryPath; if (0 == iBrother._FrsMaterial) { _FrsMaterial = 0; } @@ -132,6 +137,11 @@ public: return _Name; } + inline const char *getLibraryPath() const + { + return _LibraryPath; + } + inline const FrsMaterial *frs_material() const { return _FrsMaterial; @@ -153,7 +163,12 @@ public: _Name = name; } - inline void setFrsMaterial(const FrsMaterial& iMaterial) + inline void setLibraryPath(const char *path) + { + _LibraryPath = path; + } + + inline void setFrsMaterial(const FrsMaterial& iMaterial) { _FrsMaterial = new FrsMaterial(iMaterial); } @@ -162,6 +177,7 @@ private: BBox<Vec3f> _BBox; Id _Id; const char *_Name; + const char *_LibraryPath; FrsMaterial *_FrsMaterial; }; diff --git a/source/blender/freestyle/intern/view_map/BoxGrid.cpp b/source/blender/freestyle/intern/view_map/BoxGrid.cpp index ae22a26ec9b..34fa2b14379 100644 --- a/source/blender/freestyle/intern/view_map/BoxGrid.cpp +++ b/source/blender/freestyle/intern/view_map/BoxGrid.cpp @@ -77,11 +77,11 @@ BoxGrid::Iterator::Iterator (BoxGrid& grid, Vec3r& center, real /*epsilon*/) // Find target cell _cell = grid.findCell(_target); #if BOX_GRID_LOGGING - if (G.debug & G_DEBUG_FREESTYLE) { - cout << "Searching for occluders of edge centered at " << _target << " in cell [" << - 1_cell->boundary[0] << ", " << _cell->boundary[1] << ", " << _cell->boundary[2] << - ", " << _cell->boundary[3] << "] (" << _cell->faces.size() << " occluders)" << endl; - } + if (G.debug & G_DEBUG_FREESTYLE) { + cout << "Searching for occluders of edge centered at " << _target << " in cell [" << + 1_cell->boundary[0] << ", " << _cell->boundary[1] << ", " << _cell->boundary[2] << + ", " << _cell->boundary[3] << "] (" << _cell->faces.size() << " occluders)" << endl; + } #endif // Set iterator diff --git a/source/blender/freestyle/intern/view_map/Silhouette.h b/source/blender/freestyle/intern/view_map/Silhouette.h index b9924e6ad95..9d373107bfa 100644 --- a/source/blender/freestyle/intern/view_map/Silhouette.h +++ b/source/blender/freestyle/intern/view_map/Silhouette.h @@ -1416,6 +1416,7 @@ private: vector<FEdge*> _edgesList; // list of all edges Id _Id; const char *_Name; + const char *_LibraryPath; BBox<Vec3r> _BBox; vector<FrsMaterial> _FrsMaterials; @@ -1436,6 +1437,7 @@ public: _importance = 0.0f; _ViewShape = NULL; _Name = NULL; + _LibraryPath = NULL; } /*! Copy constructor */ @@ -1444,6 +1446,7 @@ public: userdata = NULL; _Id = iBrother._Id; _Name = iBrother._Name; + _LibraryPath = iBrother._LibraryPath; _BBox = iBrother.bbox(); _FrsMaterials = iBrother._FrsMaterials; _importance = iBrother._importance; @@ -1893,6 +1896,12 @@ public: return _Name; } + /*! Returns the library path of the Shape. */ + inline const char *getLibraryPath() const + { + return _LibraryPath; + } + /* Modififers */ /*! Sets the Id of the shape.*/ inline void setId(Id id) @@ -1906,6 +1915,12 @@ public: _Name = name; } + /*! Sets the library path of the shape.*/ + inline void setLibraryPath(const char *path) + { + _LibraryPath = path; + } + /*! Sets the list of materials for the shape */ inline void setFrsMaterials(const vector<FrsMaterial>& iMaterials) { diff --git a/source/blender/freestyle/intern/view_map/ViewMap.h b/source/blender/freestyle/intern/view_map/ViewMap.h index 74297e1dbfd..8b73c8aac3a 100644 --- a/source/blender/freestyle/intern/view_map/ViewMap.h +++ b/source/blender/freestyle/intern/view_map/ViewMap.h @@ -1565,12 +1565,18 @@ public: return _SShape->getId(); } - /*! Returns the ViewShape id. */ + /*! Returns the ViewShape name. */ inline const char *getName() const { return _SShape->getName(); } + /*! Returns the ViewShape library path. */ + inline const char *getLibraryPath() const + { + return _SShape->getLibraryPath(); + } + /* modifiers */ /*! Sets the SShape on top of which the ViewShape is built. */ inline void setSShape(SShape *iSShape) diff --git a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp index 9ca021475b2..77beb1d97d9 100644 --- a/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp +++ b/source/blender/freestyle/intern/view_map/ViewMapBuilder.cpp @@ -1204,6 +1204,7 @@ void ViewMapBuilder::computeInitialViewEdges(WingedEdge& we) psShape = new SShape; psShape->setId((*it)->GetId()); psShape->setName((*it)->getName()); + psShape->setLibraryPath((*it)->getLibraryPath()); psShape->setFrsMaterials((*it)->frs_materials()); // FIXME // create the view shape diff --git a/source/blender/freestyle/intern/winged_edge/WEdge.cpp b/source/blender/freestyle/intern/winged_edge/WEdge.cpp index 99aa2d22239..7bec5ba1d6e 100644 --- a/source/blender/freestyle/intern/winged_edge/WEdge.cpp +++ b/source/blender/freestyle/intern/winged_edge/WEdge.cpp @@ -471,6 +471,7 @@ WShape::WShape(WShape& iBrother) { _Id = iBrother.GetId(); _Name = iBrother._Name; + _LibraryPath = iBrother._LibraryPath; _FrsMaterials = iBrother._FrsMaterials; #if 0 _meanEdgeSize = iBrother._meanEdgeSize; diff --git a/source/blender/freestyle/intern/winged_edge/WEdge.h b/source/blender/freestyle/intern/winged_edge/WEdge.h index 8001342775b..14109fba843 100644 --- a/source/blender/freestyle/intern/winged_edge/WEdge.h +++ b/source/blender/freestyle/intern/winged_edge/WEdge.h @@ -1025,6 +1025,7 @@ protected: vector<WFace *> _FaceList; int _Id; const char *_Name; + const char *_LibraryPath; static unsigned _SceneCurrentId; #if 0 Vec3f _min; @@ -1043,6 +1044,8 @@ public: #endif _Id = _SceneCurrentId; _SceneCurrentId++; + _Name = 0; + _LibraryPath = 0; } /*! copy constructor */ @@ -1127,6 +1130,11 @@ public: return _Name; } + inline const char *getLibraryPath() const + { + return _LibraryPath; + } + /*! modifiers */ static inline void setCurrentId(const unsigned id) { @@ -1176,6 +1184,11 @@ public: _Name = name; } + inline void setLibraryPath(const char *path) + { + _LibraryPath = path; + } + /*! designed to build a specialized WFace for use in MakeFace */ virtual WFace *instanciateFace() const { diff --git a/source/blender/freestyle/intern/winged_edge/WXEdgeBuilder.cpp b/source/blender/freestyle/intern/winged_edge/WXEdgeBuilder.cpp index 78773a9680d..dfdeedef2e1 100644 --- a/source/blender/freestyle/intern/winged_edge/WXEdgeBuilder.cpp +++ b/source/blender/freestyle/intern/winged_edge/WXEdgeBuilder.cpp @@ -42,6 +42,7 @@ void WXEdgeBuilder::visitIndexedFaceSet(IndexedFaceSet& ifs) } shape->setId(ifs.getId().getFirst()); shape->setName(ifs.getName()); + shape->setLibraryPath(ifs.getLibraryPath()); //ifs.setId(shape->GetId()); } diff --git a/source/blender/gpu/GPU_debug.h b/source/blender/gpu/GPU_debug.h index 2c1728bfff1..61b2bc591ce 100644 --- a/source/blender/gpu/GPU_debug.h +++ b/source/blender/gpu/GPU_debug.h @@ -34,8 +34,6 @@ #include "GPU_glew.h" -#include "BLI_utildefines.h" - #ifdef __cplusplus extern "C" { #endif @@ -62,7 +60,7 @@ void GPU_assert_no_gl_errors(const char *file, int line, const char *str); /* inserts a debug marker message for the debug context messaging system */ -void GPU_string_marker(size_t size, const char *str); +void GPU_string_marker(const char *str); #ifdef __cplusplus } diff --git a/source/blender/gpu/intern/gpu_debug.c b/source/blender/gpu/intern/gpu_debug.c index be9285727fe..d632e767ca9 100644 --- a/source/blender/gpu/intern/gpu_debug.c +++ b/source/blender/gpu/intern/gpu_debug.c @@ -29,7 +29,9 @@ * \ingroup gpu */ +#include "BLI_utildefines.h" #include "BLI_sys_types.h" +#include "BLI_system.h" #include "BKE_global.h" @@ -159,17 +161,73 @@ const char *gpuErrorString(GLenum err) #endif +static const char* source_name(GLenum source) +{ + switch (source) { + case GL_DEBUG_SOURCE_API: return "API"; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: return "window system"; + case GL_DEBUG_SOURCE_SHADER_COMPILER: return "shader compiler"; + case GL_DEBUG_SOURCE_THIRD_PARTY: return "3rd party"; + case GL_DEBUG_SOURCE_APPLICATION: return "application"; + case GL_DEBUG_SOURCE_OTHER: return "other"; + default: return "???"; + } +} + +static const char* message_type_name(GLenum message) +{ + switch (message) { + case GL_DEBUG_TYPE_ERROR: return "error"; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "deprecated behavior"; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "undefined behavior"; + case GL_DEBUG_TYPE_PORTABILITY: return "portability"; + case GL_DEBUG_TYPE_PERFORMANCE: return "performance"; + case GL_DEBUG_TYPE_OTHER: return "other"; + case GL_DEBUG_TYPE_MARKER: return "marker"; /* KHR has this, ARB does not */ + default: return "???"; + } +} + +static const char* category_name_amd(GLenum category) +{ + switch (category) { + case GL_DEBUG_CATEGORY_API_ERROR_AMD: return "API error"; + case GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD: return "window system"; + case GL_DEBUG_CATEGORY_DEPRECATION_AMD: return "deprecated behavior"; + case GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD: return "undefined behavior"; + case GL_DEBUG_CATEGORY_PERFORMANCE_AMD: return "performance"; + case GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD: return "shader compiler"; + case GL_DEBUG_CATEGORY_APPLICATION_AMD: return "application"; + case GL_DEBUG_CATEGORY_OTHER_AMD: return "other"; + default: return "???"; + } +} + + static void APIENTRY gpu_debug_proc( GLenum source, GLenum type, GLuint UNUSED(id), - GLenum UNUSED(severity), GLsizei UNUSED(length), + GLenum severity, GLsizei UNUSED(length), const GLchar *message, const GLvoid *UNUSED(userParm)) { - if (source == GL_DEBUG_SOURCE_API && type == GL_DEBUG_TYPE_ERROR) { - fprintf(stderr, "GL: %s\n", message); - fflush(stderr); + if (type == GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR) { + /* Blender 2.7x uses OpenGL 2.1, we don't care if features are deprecated */ + return; } - else if (G.debug_value == 20) { - fprintf(stderr, "GL: %s\n", message); + + bool backtrace = false; + + switch (severity) { + case GL_DEBUG_SEVERITY_HIGH: + backtrace = true; + /* fall through */ + case GL_DEBUG_SEVERITY_MEDIUM: + case GL_DEBUG_SEVERITY_LOW: + case GL_DEBUG_SEVERITY_NOTIFICATION: /* KHR has this, ARB does not */ + fprintf(stderr, "GL %s %s: %s\n", source_name(source), message_type_name(type), message); + } + + if (backtrace) { + BLI_system_backtrace(stderr); fflush(stderr); } } @@ -177,11 +235,30 @@ static void APIENTRY gpu_debug_proc( #ifndef GLEW_ES_ONLY static void APIENTRY gpu_debug_proc_amd( - GLuint UNUSED(id), GLenum UNUSED(category), - GLenum UNUSED(severity), GLsizei UNUSED(length), + GLuint UNUSED(id), GLenum category, + GLenum severity, GLsizei UNUSED(length), const GLchar *message, GLvoid *UNUSED(userParm)) { - fprintf(stderr, "GL: %s\n", message); + if (category == GL_DEBUG_CATEGORY_DEPRECATION_AMD) { + /* Blender 2.7x uses OpenGL 2.1, we don't care if features are deprecated */ + return; + } + + bool backtrace = false; + + switch (severity) { + case GL_DEBUG_SEVERITY_HIGH: + backtrace = true; + /* fall through */ + case GL_DEBUG_SEVERITY_MEDIUM: + case GL_DEBUG_SEVERITY_LOW: + fprintf(stderr, "GL %s: %s\n", category_name_amd(category), message); + } + + if (backtrace) { + BLI_system_backtrace(stderr); + fflush(stderr); + } } #endif @@ -194,36 +271,44 @@ void gpu_debug_init(void) #if !defined(WITH_GLEW_ES) && !defined(GLEW_ES_ONLY) if (GLEW_VERSION_4_3) { + fprintf(stderr, "Using OpenGL 4.3 debug facilities\n"); glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallback((GLDEBUGPROC)gpu_debug_proc, mxGetCurrentContext()); glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); - GPU_string_marker(sizeof(success), success); + GPU_string_marker(success); return; } #endif if (GLEW_KHR_debug) { #ifndef GLEW_ES_ONLY + fprintf(stderr, "Using KHR_debug extension\n"); + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallback((GLDEBUGPROC)gpu_debug_proc, mxGetCurrentContext()); glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); - GPU_string_marker(sizeof(success), success); + GPU_string_marker(success); #endif return; } #ifndef GLEW_ES_ONLY if (GLEW_ARB_debug_output) { + fprintf(stderr, "Using ARB_debug_output extension\n"); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallbackARB((GLDEBUGPROCARB)gpu_debug_proc, mxGetCurrentContext()); glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); - GPU_string_marker(sizeof(success), success); + GPU_string_marker(success); return; } if (GLEW_AMD_debug_output) { + fprintf(stderr, "Using AMD_debug_output extension\n"); glDebugMessageCallbackAMD(gpu_debug_proc_amd, mxGetCurrentContext()); glDebugMessageEnableAMD(GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); - GPU_string_marker(sizeof(success), success); + GPU_string_marker(success); return; } @@ -271,14 +356,14 @@ void gpu_debug_exit(void) return; } -void GPU_string_marker(size_t length, const char *buf) +void GPU_string_marker(const char *buf) { #ifndef WITH_GLEW_ES #ifndef GLEW_ES_ONLY if (GLEW_VERSION_4_3) { glDebugMessageInsert( GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 0, - GL_DEBUG_SEVERITY_NOTIFICATION, length, buf); + GL_DEBUG_SEVERITY_NOTIFICATION, -1, buf); return; } @@ -289,7 +374,7 @@ void GPU_string_marker(size_t length, const char *buf) #ifndef GLEW_ES_ONLY glDebugMessageInsert( GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 0, - GL_DEBUG_SEVERITY_NOTIFICATION, length, buf); + GL_DEBUG_SEVERITY_NOTIFICATION, -1, buf); #endif return; } @@ -298,7 +383,7 @@ void GPU_string_marker(size_t length, const char *buf) if (GLEW_ARB_debug_output) { glDebugMessageInsertARB( GL_DEBUG_SOURCE_APPLICATION_ARB, GL_DEBUG_TYPE_OTHER_ARB, 0, - GL_DEBUG_SEVERITY_LOW_ARB, length, buf); + GL_DEBUG_SEVERITY_LOW_ARB, -1, buf); return; } @@ -306,19 +391,17 @@ void GPU_string_marker(size_t length, const char *buf) if (GLEW_AMD_debug_output) { glDebugMessageInsertAMD( GL_DEBUG_CATEGORY_APPLICATION_AMD, GL_DEBUG_SEVERITY_LOW_AMD, 0, - length, buf); + 0, buf); return; } if (GLEW_GREMEDY_string_marker) { - glStringMarkerGREMEDY(length, buf); + glStringMarkerGREMEDY(0, buf); return; } #endif - - return; } void GPU_print_error_debug(const char *str) diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.c index 827c52c9a5f..54f0003c086 100644 --- a/source/blender/gpu/intern/gpu_texture.c +++ b/source/blender/gpu/intern/gpu_texture.c @@ -55,7 +55,8 @@ struct GPUTexture { int number; /* number for multitexture binding */ int refcount; /* reference count */ GLenum target; /* GL_TEXTURE_* */ - GLenum target_base; /* same as target, (but no multisample) */ + GLenum target_base; /* same as target, (but no multisample) + * use it for unbinding */ GLuint bindcode; /* opengl identifier for texture */ int fromblender; /* we got the texture from Blender */ @@ -374,6 +375,9 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget GLint bindcode = GPU_verify_image(ima, iuser, textarget, 0, 0, mipmap, is_data); GPU_update_image_time(ima, time); + /* see GPUInput::textarget: it can take two values - GL_TEXTURE_2D and GL_TEXTURE_CUBE_MAP + * these values are correct for glDisable, so textarget can be safely used in + * GPU_texture_bind/GPU_texture_unbind through tex->target_base */ if (textarget == GL_TEXTURE_2D) gputt = TEXTARGET_TEXTURE_2D; else @@ -390,7 +394,7 @@ GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, int textarget tex->number = -1; tex->refcount = 1; tex->target = textarget; - tex->target_base = GL_TEXTURE_2D; + tex->target_base = textarget; tex->fromblender = 1; ima->gputexture[gputt] = tex; @@ -626,11 +630,11 @@ void GPU_texture_bind(GPUTexture *tex, int number) GLenum arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + number); if (number != 0) glActiveTexture(arbnumber); if (tex->bindcode != 0) { - glBindTexture(tex->target, tex->bindcode); + glBindTexture(tex->target_base, tex->bindcode); } else - GPU_invalid_tex_bind(tex->target); - glEnable(tex->target); + GPU_invalid_tex_bind(tex->target_base); + glEnable(tex->target_base); if (number != 0) glActiveTexture(GL_TEXTURE0); tex->number = number; @@ -652,8 +656,6 @@ void GPU_texture_unbind(GPUTexture *tex) GLenum arbnumber = (GLenum)((GLuint)GL_TEXTURE0 + tex->number); if (tex->number != 0) glActiveTexture(arbnumber); - glBindTexture(tex->target, 0); - glDisable(tex->target); glBindTexture(tex->target_base, 0); glDisable(tex->target_base); if (tex->number != 0) glActiveTexture(GL_TEXTURE0); diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 07df94ee332..5c1bfc229da 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -247,6 +247,7 @@ typedef enum ID_Type { ID_LS = MAKE_ID2('L', 'S'), /* FreestyleLineStyle */ ID_PAL = MAKE_ID2('P', 'L'), /* Palette */ ID_PC = MAKE_ID2('P', 'C'), /* PaintCurve */ + ID_CF = MAKE_ID2('C', 'F'), /* CacheFile */ } ID_Type; /* Only used as 'placeholder' in .blend files for directly linked datablocks. */ @@ -377,6 +378,47 @@ enum { FILTER_ID_VF = (1 << 25), FILTER_ID_WO = (1 << 26), FILTER_ID_PA = (1 << 27), + FILTER_ID_CF = (1 << 28), +}; + +/* IMPORTANT: this enum matches the order currently use in set_lisbasepointers, + * keep them in sync! */ +enum { + INDEX_ID_LI = 0, + INDEX_ID_IP, + INDEX_ID_AC, + INDEX_ID_KE, + INDEX_ID_GD, + INDEX_ID_NT, + INDEX_ID_IM, + INDEX_ID_TE, + INDEX_ID_MA, + INDEX_ID_VF, + INDEX_ID_AR, + INDEX_ID_CF, + INDEX_ID_ME, + INDEX_ID_CU, + INDEX_ID_MB, + INDEX_ID_LT, + INDEX_ID_LA, + INDEX_ID_CA, + INDEX_ID_TXT, + INDEX_ID_SO, + INDEX_ID_GR, + INDEX_ID_PAL, + INDEX_ID_PC, + INDEX_ID_BR, + INDEX_ID_PA, + INDEX_ID_SPK, + INDEX_ID_WO, + INDEX_ID_MC, + INDEX_ID_SCR, + INDEX_ID_OB, + INDEX_ID_LS, + INDEX_ID_SCE, + INDEX_ID_WM, + INDEX_ID_MSK, + INDEX_ID_NULL, }; #ifdef __cplusplus diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 9a19606d6c8..f3df9090d41 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -694,7 +694,9 @@ typedef enum eAnimEdit_Context { /* dopesheet (default) */ SACTCONT_DOPESHEET = 3, /* mask */ - SACTCONT_MASK = 4 + SACTCONT_MASK = 4, + /* cache file */ + SACTCONT_CACHEFILE = 5, } eAnimEdit_Context; /* SpaceAction AutoSnap Settings (also used by other Animation Editors) */ diff --git a/source/blender/makesdna/DNA_cachefile_types.h b/source/blender/makesdna/DNA_cachefile_types.h new file mode 100644 index 00000000000..1cda0233aa8 --- /dev/null +++ b/source/blender/makesdna/DNA_cachefile_types.h @@ -0,0 +1,84 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file DNA_cachefile_types.h + * \ingroup DNA + */ + +#ifndef __DNA_CACHEFILE_TYPES_H__ +#define __DNA_CACHEFILE_TYPES_H__ + +#include "DNA_ID.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* CacheFile::flag */ +enum { + CACHEFILE_DS_EXPAND = (1 << 0), +}; + +/* CacheFile::draw_flag */ +enum { + CACHEFILE_KEYFRAME_DRAWN = (1 << 0), +}; + +typedef struct AlembicObjectPath { + struct AlembicObjectPath *next, *prev; + + char path[1024]; /* 1024 = FILE_MAX, might use PATH_MAX in the future. */ +} AlembicObjectPath; + +typedef struct CacheFile { + ID id; + struct AnimData *adt; + + struct AbcArchiveHandle *handle; + + /* Paths of the objects inside of the Alembic archive referenced by this + * CacheFile. */ + ListBase object_paths; + + char filepath[1024]; /* 1024 = FILE_MAX */ + + char is_sequence; + char forward_axis; + char up_axis; + char override_frame; + + float scale; + float frame; /* The frame/time to lookup in the cache file. */ + + short flag; /* Animation flag. */ + short draw_flag; +} CacheFile; + +#ifdef __cplusplus +} +#endif + +#endif /* __DNA_CACHEFILE_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 5fcd374b21f..fc4e7de73f5 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -458,6 +458,12 @@ typedef struct bObjectSolverConstraint { struct Object *camera; } bObjectSolverConstraint; +/* Transform matrix cache constraint */ +typedef struct bTransformCacheConstraint { + struct CacheFile *cache_file; + char object_path[1024]; /* FILE_MAX */ +} bTransformCacheConstraint; + /* ------------------------------------------ */ /* bConstraint->type @@ -494,6 +500,7 @@ typedef enum eBConstraint_Types { CONSTRAINT_TYPE_FOLLOWTRACK = 26, /* Follow Track Constraint */ CONSTRAINT_TYPE_CAMERASOLVER = 27, /* Camera Solver Constraint */ CONSTRAINT_TYPE_OBJECTSOLVER = 28, /* Object Solver Constraint */ + CONSTRAINT_TYPE_TRANSFORM_CACHE = 29, /* Transform Cache Constraint */ /* NOTE: no constraints are allowed to be added after this */ NUM_CONSTRAINT_TYPES diff --git a/source/blender/makesdna/DNA_genfile.h b/source/blender/makesdna/DNA_genfile.h index a2981c0aa76..9e9ab974b01 100644 --- a/source/blender/makesdna/DNA_genfile.h +++ b/source/blender/makesdna/DNA_genfile.h @@ -100,6 +100,7 @@ void *DNA_struct_reconstruct( int DNA_elem_array_size(const char *str); int DNA_elem_offset(struct SDNA *sdna, const char *stype, const char *vartype, const char *name); +bool DNA_struct_find(const struct SDNA *sdna, const char *stype); bool DNA_struct_elem_find(const struct SDNA *sdna, const char *stype, const char *vartype, const char *name); diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 41f53f9f51c..773d203bdb3 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -18,7 +18,7 @@ * The Original Code is Copyright (C) 2008, Blender Foundation. * This is a new part of Blender * - * Contributor(s): Joshua Leung + * Contributor(s): Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -32,9 +32,10 @@ #include "DNA_listBase.h" #include "DNA_ID.h" +#include "DNA_brush_types.h" struct AnimData; - +struct CurveMapping; /* Grease-Pencil Annotations - 'Stroke Point' * -> Coordinates may either be 2d or 3d depending on settings at the time @@ -44,6 +45,7 @@ struct AnimData; typedef struct bGPDspoint { float x, y, z; /* co-ordinates of point (usually 2d, but can be 3d as well) */ float pressure; /* pressure of input device (from 0 to 1) at this point */ + float strength; /* color strength (used for alpha factor) */ float time; /* seconds since start of stroke */ int flag; /* additional options (NOTE: can shrink this field down later if needed) */ } bGPDspoint; @@ -65,24 +67,113 @@ typedef struct bGPDtriangle { int v1, v2, v3; /* indices for tesselated triangle used for GP Fill */ } bGPDtriangle; +/* GP brush (used for new strokes) */ +typedef struct bGPDbrush { + struct bGPDbrush *next, *prev; + + char info[64]; /* Brush name. Must be unique. */ + short thickness; /* thickness to apply to strokes */ + short flag; + float draw_smoothfac; /* amount of smoothing to apply to newly created strokes */ + short draw_smoothlvl; /* number of times to apply smooth factor to new strokes */ + short sublevel; /* number of times to subdivide new strokes */ + + float draw_sensitivity; /* amount of sensivity to apply to newly created strokes */ + float draw_strength; /* amount of alpha strength to apply to newly created strokes */ + float draw_jitter; /* amount of jitter to apply to newly created strokes */ + float draw_angle; /* angle when the brush has full thickness */ + float draw_angle_factor; /* factor to apply when angle change (only 90 degrees) */ + float draw_random_press; /* factor of randomness for sensitivity and strength */ + float draw_random_sub; /* factor of randomness for subdivision */ + struct CurveMapping *cur_sensitivity; + struct CurveMapping *cur_strength; + struct CurveMapping *cur_jitter; +} bGPDbrush; + +/* bGPDbrush->flag */ +typedef enum eGPDbrush_Flag { + /* brush is active */ + GP_BRUSH_ACTIVE = (1 << 0), + /* brush use pressure */ + GP_BRUSH_USE_PRESSURE = (1 << 1), + /* brush use pressure for alpha factor */ + GP_BRUSH_USE_STENGTH_PRESSURE = (1 << 2), + /* brush use pressure for alpha factor */ + GP_BRUSH_USE_JITTER_PRESSURE = (1 << 3), + /* brush use random for pressure */ + GP_BRUSH_USE_RANDOM_PRESSURE = (1 << 4), + /* brush use random for strength */ + GP_BRUSH_USE_RANDOM_STRENGTH = (1 << 5) +} eGPDbrush_Flag; + +/* color of palettes */ +typedef struct bGPDpalettecolor { + struct bGPDpalettecolor *next, *prev; + char info[64]; /* Color name. Must be unique. */ + float color[4]; + float fill[4]; /* color that should be used for drawing "fills" for strokes */ + short flag; /* settings for palette color */ + char pad[6]; /* padding for compiler alignment error */ +} bGPDpalettecolor; + +/* bGPDpalettecolor->flag */ +typedef enum eGPDpalettecolor_Flag { + /* color is active */ + PC_COLOR_ACTIVE = (1 << 0), + /* don't display color */ + PC_COLOR_HIDE = (1 << 1), + /* protected from further editing */ + PC_COLOR_LOCKED = (1 << 2), + /* do onion skinning */ + PC_COLOR_ONIONSKIN = (1 << 3), + /* "volumetric" strokes (i.e. GLU Quadric discs in 3D) */ + PC_COLOR_VOLUMETRIC = (1 << 4), + /* Use High quality fill */ + PC_COLOR_HQ_FILL = (1 << 5) +} eGPDpalettecolor_Flag; + +/* palette of colors */ +typedef struct bGPDpalette { + struct bGPDpalette *next, *prev; + + /* pointer to individual colours */ + ListBase colors; + char info[64]; /* Palette name. Must be unique. */ + + short flag; + char pad[6]; /* padding for compiler alignment error */ +} bGPDpalette; + +/* bGPDpalette->flag */ +typedef enum eGPDpalette_Flag { + /* palette is active */ + PL_PALETTE_ACTIVE = (1 << 0) +} eGPDpalette_Flag; + /* Grease-Pencil Annotations - 'Stroke' * -> A stroke represents a (simplified version) of the curve * drawn by the user in one 'mousedown'->'mouseup' operation */ typedef struct bGPDstroke { struct bGPDstroke *next, *prev; + bGPDspoint *points; /* array of data-points for stroke */ - void *pad; /* keep 4 pointers at the beginning, padding for 'inittime' is tricky 64/32bit */ - int totpoints; /* number of data-points in array */ - - short thickness; /* thickness of stroke (currently not used) */ - short flag; /* various settings about this stroke */ - bGPDtriangle *triangles;/* tesselated triangles for GP Fill */ + int totpoints; /* number of data-points in array */ int tot_triangles; /* number of triangles in array */ - int pad1, *pad2; + + short thickness; /* thickness of stroke */ + short flag, pad[2]; /* various settings about this stroke */ double inittime; /* Init time of stroke */ + /* The pointer to color is only used during drawing, but not saved + * colorname is the join with the palette, but when draw, the pointer is update if the value is NULL + * to speed up the drawing + */ + char colorname[128]; /* color name */ + bGPDpalettecolor *palcolor; /* current palette color */ + /* temporary layer name only used during copy/paste to put the stroke in the original layer */ + char tmp_layerinfo[128]; } bGPDstroke; /* bGPDstroke->flag */ @@ -97,6 +188,10 @@ typedef enum eGPDstroke_Flag { GP_STROKE_SELECT = (1 << 3), /* Recalculate triangulation for high quality fill (when true, force a new recalc) */ GP_STROKE_RECALC_CACHES = (1 << 4), + /* Recalculate the color pointer using the name as index (true force a new recalc) */ + GP_STROKE_RECALC_COLOR = (1 << 5), + /* Flag used to indicate that stroke is closed and draw edge between last and first point */ + GP_STROKE_CYCLIC = (1 << 7), /* only for use with stroke-buffer (while drawing eraser) */ GP_STROKE_ERASER = (1 << 15) } eGPDstroke_Flag; @@ -139,16 +234,18 @@ typedef struct bGPDlayer { float gcolor_prev[3]; /* optional color for ghosts before the active frame */ float gcolor_next[3]; /* optional color for ghosts after the active frame */ - float color[4]; /* color that should be used to draw all the strokes in this layer */ - float fill[4]; /* color that should be used for drawing "fills" for strokes */ + float color[4]; /* Color for strokes in layers (replaced by palettecolor). Only used for ruler (which uses GPencil internally) */ + float fill[4]; /* Fill color for strokes in layers. Not used and replaced by palettecolor fill */ char info[128]; /* optional reference info about this layer (i.e. "director's comments, 12/3") * this is used for the name of the layer too and kept unique. */ - float draw_smoothfac; /* amount of smoothing to apply to newly created strokes */ - short draw_smoothlvl; /* number of times to apply smooth factor to new strokes */ - short sublevel; /* number of times to subdivide new strokes */ - short pad[4]; /* padding for compiler error */ + struct Object *parent; /* parent object */ + float inverse[4][4]; /* inverse matrix (only used if parented) */ + char parsubstr[64]; /* String describing subobject info, MAX_ID_NAME-2 */ + short partype, pad; + float tintcolor[4]; /* Color used to tint layer, alpha value is used as factor */ + float opacity; /* Opacity of the layer */ } bGPDlayer; /* bGPDlayer->flag */ @@ -176,7 +273,9 @@ typedef enum eGPDlayer_Flag { /* "volumetric" strokes (i.e. GLU Quadric discs in 3D) */ GP_LAYER_VOLUMETRIC = (1 << 10), /* Use high quality fill (instead of buggy legacy OpenGL Fill) */ - GP_LAYER_HQ_FILL = (1 << 11) + GP_LAYER_HQ_FILL = (1 << 11), + /* Unlock color */ + GP_LAYER_UNLOCK_COLOR = (1 << 12) } eGPDlayer_Flag; /* Grease-Pencil Annotations - 'DataBlock' */ @@ -187,7 +286,7 @@ typedef struct bGPdata { /* saved Grease-Pencil data */ ListBase layers; /* bGPDlayers */ int flag; /* settings for this datablock */ - + /* not-saved stroke buffer data (only used during paint-session) * - buffer must be initialized before use, but freed after * whole paint operation is over @@ -195,6 +294,12 @@ typedef struct bGPdata { short sbuffer_size; /* number of elements currently in cache */ short sbuffer_sflag; /* flags for stroke that cache represents */ void *sbuffer; /* stroke buffer (can hold GP_STROKE_BUFFER_MAX) */ + float scolor[4]; /* buffer color using palettes */ + char pad[6]; /* padding for compiler alignment error */ + short sflag; /* settings for palette color */ + + /* saved palettes */ + ListBase palettes; } bGPdata; /* bGPdata->flag */ @@ -229,7 +334,9 @@ typedef enum eGPdata_Flag { GP_DATA_STROKE_EDITMODE = (1 << 8), /* Convenience/cache flag to make it easier to quickly toggle onion skinning on/off */ - GP_DATA_SHOW_ONIONSKINS = (1 << 9) + GP_DATA_SHOW_ONIONSKINS = (1 << 9), + /* Draw a green and red point to indicate start and end of the stroke */ + GP_DATA_SHOW_DIRECTION = (1 << 10) } eGPdata_Flag; #endif /* __DNA_GPENCIL_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index bbc8edf4344..0424dc98a25 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -85,6 +85,7 @@ typedef enum ModifierType { eModifierType_DataTransfer = 49, eModifierType_NormalEdit = 50, eModifierType_CorrectiveSmooth = 51, + eModifierType_MeshSequenceCache = 52, NUM_MODIFIER_TYPES } ModifierType; @@ -1541,4 +1542,25 @@ enum { MOD_NORMALEDIT_MIX_MUL = 3, }; +typedef struct MeshSeqCacheModifierData { + ModifierData modifier; + + struct CacheFile *cache_file; + char object_path[1024]; /* 1024 = FILE_MAX */ + + char read_flag; + char pad[7]; +} MeshSeqCacheModifierData; + +/* MeshSeqCacheModifierData.read_flag */ +enum { + MOD_MESHSEQ_READ_VERT = (1 << 0), + MOD_MESHSEQ_READ_POLY = (1 << 1), + MOD_MESHSEQ_READ_UV = (1 << 2), + MOD_MESHSEQ_READ_COLOR = (1 << 3), +}; + +#define MOD_MESHSEQ_READ_ALL \ + (MOD_MESHSEQ_READ_VERT | MOD_MESHSEQ_READ_POLY | MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR) + #endif /* __DNA_MODIFIER_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index c7578a19e0c..a4934cc1f24 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -62,6 +62,7 @@ struct AnimData; struct Editing; struct SceneStats; struct bGPdata; +struct bGPDbrush; struct MovieClip; struct ColorSpace; @@ -1117,12 +1118,12 @@ typedef enum eGP_EditBrush_Types { GP_EDITBRUSH_TYPE_SUBDIVIDE = 7, GP_EDITBRUSH_TYPE_SIMPLIFY = 8, GP_EDITBRUSH_TYPE_CLONE = 9, - + GP_EDITBRUSH_TYPE_STRENGTH = 10, + /* !!! Update GP_EditBrush_Data brush[###]; below !!! */ TOT_GP_EDITBRUSH_TYPES } eGP_EditBrush_Types; - /* Settings for a GPencil Stroke Sculpting Brush */ typedef struct GP_EditBrush_Data { short size; /* radius of brush */ @@ -1148,17 +1149,26 @@ typedef enum eGP_EditBrush_Flag { /* GPencil Stroke Sculpting Settings */ typedef struct GP_BrushEdit_Settings { - GP_EditBrush_Data brush[10]; /* TOT_GP_EDITBRUSH_TYPES */ + GP_EditBrush_Data brush[11]; /* TOT_GP_EDITBRUSH_TYPES */ void *paintcursor; /* runtime */ int brushtype; /* eGP_EditBrush_Types */ int flag; /* eGP_BrushEdit_SettingsFlag */ + char pad[4]; + float alpha; /* alpha factor for selection color */ } GP_BrushEdit_Settings; /* GP_BrushEdit_Settings.flag */ typedef enum eGP_BrushEdit_SettingsFlag { /* only affect selected points */ - GP_BRUSHEDIT_FLAG_SELECT_MASK = (1 << 0) + GP_BRUSHEDIT_FLAG_SELECT_MASK = (1 << 0), + /* apply brush to position */ + GP_BRUSHEDIT_FLAG_APPLY_POSITION = (1 << 1), + /* apply brush to strength */ + GP_BRUSHEDIT_FLAG_APPLY_STRENGTH = (1 << 2), + /* apply brush to thickness */ + GP_BRUSHEDIT_FLAG_APPLY_THICKNESS = (1 << 3) + } eGP_BrushEdit_SettingsFlag; /* *************************************************************** */ @@ -1378,6 +1388,9 @@ typedef struct ToolSettings { /* Grease Pencil Sculpt */ struct GP_BrushEdit_Settings gp_sculpt; + /* Grease Pencil Drawing Brushes (bGPDbrush) */ + ListBase gp_brushes; + /* Image Paint (8 byttse aligned please!) */ struct ImagePaintSettings imapaint; @@ -2075,6 +2088,8 @@ typedef enum eGPencil_Flags { GP_TOOL_FLAG_PAINTSESSIONS_ON = (1 << 0), /* When creating new frames, the last frame gets used as the basis for the new one */ GP_TOOL_FLAG_RETAIN_LAST = (1 << 1), + /* Add the strokes below all strokes in the layer */ + GP_TOOL_FLAG_PAINT_ONBACK = (1 << 2) } eGPencil_Flags; /* toolsettings->gpencil_src */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 4ce0f369ebd..41188c2412f 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -738,6 +738,7 @@ typedef enum eFileSel_File_Types { FILE_TYPE_COLLADA = (1 << 13), FILE_TYPE_OPERATOR = (1 << 14), /* from filter_glob operator property */ FILE_TYPE_APPLICATIONBUNDLE = (1 << 15), + FILE_TYPE_ALEMBIC = (1 << 16), FILE_TYPE_DIR = (1 << 30), /* An FS directory (i.e. S_ISDIR on its path is true). */ FILE_TYPE_BLENDERLIB = (1 << 31), diff --git a/source/blender/makesdna/DNA_tracking_types.h b/source/blender/makesdna/DNA_tracking_types.h index 9888b735b8b..42b72c1ff93 100644 --- a/source/blender/makesdna/DNA_tracking_types.h +++ b/source/blender/makesdna/DNA_tracking_types.h @@ -158,7 +158,10 @@ typedef struct MovieTrackingTrack { * Used to prevent jumps of the camera when tracks are appearing or * disappearing. */ - float weight, pad; + float weight; + + /* track weight especially for 2D stabilization */ + float weight_stab; } MovieTrackingTrack; typedef struct MovieTrackingPlaneMarker { @@ -250,19 +253,24 @@ typedef struct MovieTrackingSettings { typedef struct MovieTrackingStabilization { int flag; - int tot_track, act_track; /* total number and index of active track in list */ + int tot_track, act_track; /* total number of translation tracks and index of active track in list */ + int tot_rot_track, act_rot_track; /* total number of rotation tracks and index of active track in list */ /* 2d stabilization */ float maxscale; /* max auto-scale factor */ - MovieTrackingTrack *rot_track; /* track used to stabilize rotation */ + MovieTrackingTrack *rot_track DNA_DEPRECATED; /* use TRACK_USE_2D_STAB_ROT on individual tracks instead */ + + int anchor_frame; /* reference point to anchor stabilization offset */ + float target_pos[2]; /* expected target position of frame after raw stabilization, will be subtracted */ + float target_rot; /* expected target rotation of frame after raw stabilization, will be compensated */ + float scale; /* zoom factor known to be present on original footage. Also used for autoscale */ float locinf, scaleinf, rotinf; /* influence on location, scale and rotation */ int filter; /* filter used for pixel interpolation */ - /* some pre-computing run-time variables */ - int ok; /* are precomputed values and scaled buf relevant? */ - float scale; /* autoscale factor */ + /* initialization and run-time data */ + int ok DNA_DEPRECATED; /* Without effect now, we initialize on every frame. Formerly used for caching of init values */ } MovieTrackingStabilization; typedef struct MovieTrackingReconstruction { @@ -386,7 +394,8 @@ enum { TRACK_USE_2D_STAB = (1 << 8), TRACK_PREVIEW_GRAYSCALE = (1 << 9), TRACK_DOPE_SEL = (1 << 10), - TRACK_PREVIEW_ALPHA = (1 << 11) + TRACK_PREVIEW_ALPHA = (1 << 11), + TRACK_USE_2D_STAB_ROT = (1 << 12) }; /* MovieTrackingTrack->motion_model */ @@ -452,7 +461,9 @@ enum { enum { TRACKING_2D_STABILIZATION = (1 << 0), TRACKING_AUTOSCALE = (1 << 1), - TRACKING_STABILIZE_ROTATION = (1 << 2) + TRACKING_STABILIZE_ROTATION = (1 << 2), + TRACKING_STABILIZE_SCALE = (1 << 3), + TRACKING_SHOW_STAB_TRACKS = (1 << 5) }; /* MovieTrackingStrabilization->filter */ diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c index 6a41591e051..96085a79eff 100644 --- a/source/blender/makesdna/intern/dna_genfile.c +++ b/source/blender/makesdna/intern/dna_genfile.c @@ -1294,6 +1294,11 @@ int DNA_elem_offset(SDNA *sdna, const char *stype, const char *vartype, const ch return (int)((intptr_t)cp); } +bool DNA_struct_find(const SDNA *sdna, const char *stype) +{ + return DNA_struct_find_nr(sdna, stype) != -1; +} + bool DNA_struct_elem_find(const SDNA *sdna, const char *stype, const char *vartype, const char *name) { const int SDNAnr = DNA_struct_find_nr(sdna, stype); diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index b78299316e1..2cea8715a65 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -129,6 +129,7 @@ static const char *includefiles[] = { "DNA_rigidbody_types.h", "DNA_freestyle_types.h", "DNA_linestyle_types.h", + "DNA_cachefile_types.h", /* see comment above before editing! */ /* empty string to indicate end of includefiles */ @@ -1340,4 +1341,5 @@ int main(int argc, char **argv) #include "DNA_rigidbody_types.h" #include "DNA_freestyle_types.h" #include "DNA_linestyle_types.h" +#include "DNA_cachefile_types.h" /* end of list */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index e884d769afe..9cbe132282f 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -95,6 +95,8 @@ extern StructRNA RNA_Brush; extern StructRNA RNA_BrushTextureSlot; extern StructRNA RNA_BuildModifier; extern StructRNA RNA_MeshCacheModifier; +extern StructRNA RNA_MeshSequenceCacheModifier; +extern StructRNA RNA_CacheFile; extern StructRNA RNA_Camera; extern StructRNA RNA_CastModifier; extern StructRNA RNA_ChildOfConstraint; @@ -257,6 +259,9 @@ extern StructRNA RNA_FreestyleSettings; extern StructRNA RNA_Function; extern StructRNA RNA_GPencilFrame; extern StructRNA RNA_GPencilLayer; +extern StructRNA RNA_GPencilPalette; +extern StructRNA RNA_GPencilPaletteColor; +extern StructRNA RNA_GPencilBrush; extern StructRNA RNA_GPencilStroke; extern StructRNA RNA_GPencilStrokePoint; extern StructRNA RNA_GPencilSculptSettings; @@ -417,6 +422,7 @@ extern StructRNA RNA_MovieClipSequence; extern StructRNA RNA_MovieTracking; extern StructRNA RNA_MovieTrackingObject; extern StructRNA RNA_MovieTrackingTrack; +extern StructRNA RNA_MovieTrackingStabilization; extern StructRNA RNA_MulticamSequence; extern StructRNA RNA_MultiresModifier; extern StructRNA RNA_MusgraveTexture; diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index 7ae3d552916..1c9b3593d17 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -198,6 +198,8 @@ extern EnumPropertyItem rna_enum_dt_mix_mode_items[]; extern EnumPropertyItem rna_enum_dt_layers_select_src_items[]; extern EnumPropertyItem rna_enum_dt_layers_select_dst_items[]; +extern EnumPropertyItem rna_enum_abc_compression_items[]; + /* API calls */ int rna_node_tree_type_to_enum(struct bNodeTreeType *typeinfo); diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 7bfac9d0605..44e99bd2995 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -38,6 +38,7 @@ set(DEFSRC rna_armature.c rna_boid.c rna_brush.c + rna_cachefile.c rna_camera.c rna_cloth.c rna_color.c @@ -290,6 +291,13 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +if(WITH_ALEMBIC) + list(APPEND INC + ../../alembic + ) + add_definitions(-DWITH_ALEMBIC) +endif() + if(WITH_BULLET) list(APPEND INC ../../../../intern/rigidbody diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index f9a46409aea..569c1ee5f3f 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -50,7 +50,7 @@ #ifndef NDEBUG void BLI_system_backtrace(FILE *fp) { - (void)fp; + (void)fp; } #endif @@ -3301,6 +3301,7 @@ static RNAProcessItem PROCESS_ITEMS[] = { {"rna_armature.c", "rna_armature_api.c", RNA_def_armature}, {"rna_boid.c", NULL, RNA_def_boid}, {"rna_brush.c", NULL, RNA_def_brush}, + {"rna_cachefile.c", NULL, RNA_def_cachefile}, {"rna_camera.c", "rna_camera_api.c", RNA_def_camera}, {"rna_cloth.c", NULL, RNA_def_cloth}, {"rna_color.c", NULL, RNA_def_color}, diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 8ad6713192a..ab124b361f1 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -52,6 +52,7 @@ EnumPropertyItem rna_enum_id_type_items[] = { {ID_AR, "ARMATURE", ICON_ARMATURE_DATA, "Armature", ""}, {ID_BR, "BRUSH", ICON_BRUSH_DATA, "Brush", ""}, {ID_CA, "CAMERA", ICON_CAMERA_DATA, "Camera", ""}, + {ID_CF, "CACHEFILE", ICON_FILE, "Cache File", ""}, {ID_CU, "CURVE", ICON_CURVE_DATA, "Curve", ""}, {ID_VF, "FONT", ICON_FONT_DATA, "Font", ""}, {ID_GD, "GREASEPENCIL", ICON_GREASEPENCIL, "Grease Pencil", ""}, @@ -139,6 +140,7 @@ short RNA_type_to_ID_code(StructRNA *type) if (RNA_struct_is_a(type, &RNA_Action)) return ID_AC; if (RNA_struct_is_a(type, &RNA_Armature)) return ID_AR; if (RNA_struct_is_a(type, &RNA_Brush)) return ID_BR; + if (RNA_struct_is_a(type, &RNA_CacheFile)) return ID_CF; if (RNA_struct_is_a(type, &RNA_Camera)) return ID_CA; if (RNA_struct_is_a(type, &RNA_Curve)) return ID_CU; if (RNA_struct_is_a(type, &RNA_GreasePencil)) return ID_GD; @@ -179,6 +181,7 @@ StructRNA *ID_code_to_RNA_type(short idcode) case ID_AR: return &RNA_Armature; case ID_BR: return &RNA_Brush; case ID_CA: return &RNA_Camera; + case ID_CF: return &RNA_CacheFile; case ID_CU: return &RNA_Curve; case ID_GD: return &RNA_GreasePencil; case ID_GR: return &RNA_Group; @@ -383,15 +386,15 @@ int rna_IDMaterials_assign_int(PointerRNA *ptr, int key, const PointerRNA *assig } } -static void rna_IDMaterials_append_id(ID *id, Material *ma) +static void rna_IDMaterials_append_id(ID *id, Main *bmain, Material *ma) { - BKE_material_append_id(id, ma); + BKE_material_append_id(bmain, id, ma); WM_main_add_notifier(NC_OBJECT | ND_DRAW, id); WM_main_add_notifier(NC_OBJECT | ND_OB_SHADING, id); } -static Material *rna_IDMaterials_pop_id(ID *id, ReportList *reports, int index_i, int remove_material_slot) +static Material *rna_IDMaterials_pop_id(ID *id, Main *bmain, ReportList *reports, int index_i, int remove_material_slot) { Material *ma; short *totcol = give_totcolp_id(id); @@ -405,7 +408,7 @@ static Material *rna_IDMaterials_pop_id(ID *id, ReportList *reports, int index_i return NULL; } - ma = BKE_material_pop_id(id, index_i, remove_material_slot); + ma = BKE_material_pop_id(bmain, id, index_i, remove_material_slot); if (*totcol == totcol_orig) { BKE_report(reports, RPT_ERROR, "No material to removed"); @@ -421,7 +424,7 @@ static Material *rna_IDMaterials_pop_id(ID *id, ReportList *reports, int index_i static void rna_IDMaterials_clear_id(ID *id, int remove_material_slot) { - BKE_material_clear_id(id, remove_material_slot); + BKE_material_clear_id(G.main, id, remove_material_slot); DAG_id_tag_update(id, OB_RECALC_DATA); WM_main_add_notifier(NC_OBJECT | ND_DRAW, id); @@ -817,12 +820,13 @@ static void rna_def_ID_materials(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "ID Materials", "Collection of materials"); func = RNA_def_function(srna, "append", "rna_IDMaterials_append_id"); + RNA_def_function_flag(func, FUNC_USE_MAIN); RNA_def_function_ui_description(func, "Add a new material to the data block"); parm = RNA_def_pointer(func, "material", "Material", "", "Material to add"); RNA_def_property_flag(parm, PROP_REQUIRED); func = RNA_def_function(srna, "pop", "rna_IDMaterials_pop_id"); - RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_MAIN); RNA_def_function_ui_description(func, "Remove a material from the data block"); parm = RNA_def_int(func, "index", -1, -MAXMAT, MAXMAT, "", "Index of material to remove", 0, MAXMAT); RNA_def_boolean(func, "update_data", 0, "", "Update data by re-adjusting the material slots assigned"); diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c new file mode 100644 index 00000000000..7249ebd5feb --- /dev/null +++ b/source/blender/makesrna/intern/rna_cachefile.c @@ -0,0 +1,169 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "DNA_cachefile_types.h" +#include "DNA_scene_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "rna_internal.h" + +#ifdef RNA_RUNTIME + +#include "BKE_cachefile.h" +#include "BKE_depsgraph.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" +#include "WM_types.h" + +#ifdef WITH_ALEMBIC +# include "../../../alembic/ABC_alembic.h" +#endif + +static void rna_CacheFile_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + CacheFile *cache_file = (CacheFile *)ptr->data; + + DAG_id_tag_update(&cache_file->id, 0); + WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL); + + UNUSED_VARS(bmain, scene); +} + +static void rna_CacheFile_update_handle(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + CacheFile *cache_file = ptr->data; + + BKE_cachefile_reload(bmain, cache_file); + + rna_CacheFile_update(bmain, scene, ptr); +} + +static void rna_CacheFile_object_paths_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + CacheFile *cache_file = (CacheFile *)ptr->data; + rna_iterator_listbase_begin(iter, &cache_file->object_paths, NULL); +} + +#else + +/* cachefile.object_paths */ +static void rna_def_alembic_object_path(BlenderRNA *brna) +{ + StructRNA *srna = RNA_def_struct(brna, "AlembicObjectPath", NULL); + RNA_def_struct_sdna(srna, "AlembicObjectPath"); + RNA_def_struct_ui_text(srna, "Object Path", "Path of an object inside of an Alembic archive"); + RNA_def_struct_ui_icon(srna, ICON_NONE); + + PropertyRNA *prop = RNA_def_property(srna, "path", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Path", "Object path"); + RNA_def_struct_name_property(srna, prop); +} + +/* cachefile.object_paths */ +static void rna_def_cachefile_object_paths(BlenderRNA *brna, PropertyRNA *cprop) +{ + RNA_def_property_srna(cprop, "AlembicObjectPaths"); + StructRNA *srna = RNA_def_struct(brna, "AlembicObjectPaths", NULL); + RNA_def_struct_sdna(srna, "CacheFile"); + RNA_def_struct_ui_text(srna, "Object Paths", "Collection of object paths"); +} + +static void rna_def_cachefile(BlenderRNA *brna) +{ + StructRNA *srna = RNA_def_struct(brna, "CacheFile", "ID"); + RNA_def_struct_sdna(srna, "CacheFile"); + RNA_def_struct_ui_text(srna, "CacheFile", ""); + RNA_def_struct_ui_icon(srna, ICON_FILE); + + PropertyRNA *prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH); + RNA_def_property_ui_text(prop, "File Path", "Path to external displacements file"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update_handle"); + + prop = RNA_def_property(srna, "is_sequence", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text(prop, "Sequence", "Whether the cache is separated in a series of files"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + + /* ----------------- For Scene time ------------------- */ + + prop = RNA_def_property(srna, "override_frame", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_ui_text(prop, "Override Frame", + "Whether to use a custom frame for looking up data in the cache file," + " instead of using the current scene frame"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + + prop = RNA_def_property(srna, "frame", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "frame"); + RNA_def_property_range(prop, -MAXFRAME, MAXFRAME); + RNA_def_property_ui_text(prop, "Frame", "The time to use for looking up the data in the cache file," + " or to determine which file to use in a file sequence"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + + /* ----------------- Axis Conversion ----------------- */ + + prop = RNA_def_property(srna, "forward_axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "forward_axis"); + RNA_def_property_enum_items(prop, rna_enum_object_axis_items); + RNA_def_property_ui_text(prop, "Forward", ""); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + + prop = RNA_def_property(srna, "up_axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "up_axis"); + RNA_def_property_enum_items(prop, rna_enum_object_axis_items); + RNA_def_property_ui_text(prop, "Up", ""); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + + prop = RNA_def_property(srna, "scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "scale"); + RNA_def_property_range(prop, 0.0001f, 1000.0f); + RNA_def_property_ui_text(prop, "Scale", "Value by which to enlarge or shrink the object with respect to the world's origin" + " (only applicable through a Transform Cache constraint)"); + RNA_def_property_update(prop, 0, "rna_CacheFile_update"); + + /* object paths */ + prop = RNA_def_property(srna, "object_paths", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "object_paths", NULL); + RNA_def_property_collection_funcs(prop, "rna_CacheFile_object_paths_begin", "rna_iterator_listbase_next", + "rna_iterator_listbase_end", "rna_iterator_listbase_get", + NULL, NULL, NULL, NULL); + RNA_def_property_struct_type(prop, "AlembicObjectPath"); + RNA_def_property_srna(prop, "AlembicObjectPaths"); + RNA_def_property_ui_text(prop, "Object Paths", "Paths of the objects inside the Alembic archive"); + rna_def_cachefile_object_paths(brna, prop); + + rna_def_animdata_common(srna); +} + +void RNA_def_cachefile(BlenderRNA *brna) +{ + rna_def_cachefile(brna); + rna_def_alembic_object_path(brna); +} + +#endif diff --git a/source/blender/makesrna/intern/rna_cloth.c b/source/blender/makesrna/intern/rna_cloth.c index 781e44c9ed6..d8bcbc2cc72 100644 --- a/source/blender/makesrna/intern/rna_cloth.c +++ b/source/blender/makesrna/intern/rna_cloth.c @@ -56,6 +56,12 @@ static void rna_cloth_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerR WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob); } +static void rna_cloth_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + DAG_relations_tag_update(bmain); + rna_cloth_update(bmain, scene, ptr); +} + static void rna_cloth_pinning_changed(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { Object *ob = (Object *)ptr->id.data; @@ -68,6 +74,16 @@ static void rna_cloth_pinning_changed(Main *UNUSED(bmain), Scene *UNUSED(scene), WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob); } +static void rna_ClothSettings_bending_set(struct PointerRNA *ptr, float value) +{ + ClothSimSettings *settings = (ClothSimSettings *)ptr->data; + + settings->bending = value; + + /* check for max clipping */ + if (value > settings->max_bend) + settings->max_bend = value; +} static void rna_ClothSettings_max_bend_set(struct PointerRNA *ptr, float value) { @@ -80,6 +96,17 @@ static void rna_ClothSettings_max_bend_set(struct PointerRNA *ptr, float value) settings->max_bend = value; } +static void rna_ClothSettings_structural_set(struct PointerRNA *ptr, float value) +{ + ClothSimSettings *settings = (ClothSimSettings *)ptr->data; + + settings->structural = value; + + /* check for max clipping */ + if (value > settings->max_struct) + settings->max_struct = value; +} + static void rna_ClothSettings_max_struct_set(struct PointerRNA *ptr, float value) { ClothSimSettings *settings = (ClothSimSettings *)ptr->data; @@ -493,6 +520,7 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "structural_stiffness", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "structural"); RNA_def_property_range(prop, 0.0f, 10000.0f); + RNA_def_property_float_funcs(prop, NULL, "rna_ClothSettings_structural_set", NULL); RNA_def_property_ui_text(prop, "Structural Stiffness", "Overall stiffness of structure"); RNA_def_property_update(prop, 0, "rna_cloth_update"); @@ -521,6 +549,7 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "bending_stiffness", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "bending"); RNA_def_property_range(prop, 0.0f, 10000.0f); + RNA_def_property_float_funcs(prop, NULL, "rna_ClothSettings_bending_set", NULL); RNA_def_property_ui_text(prop, "Bending Stiffness", "Wrinkle coefficient (higher = less smaller but more big wrinkles)"); RNA_def_property_update(prop, 0, "rna_cloth_update"); @@ -706,7 +735,7 @@ static void rna_def_cloth_collision_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "group", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Collision Group", "Limit colliders to this Group"); - RNA_def_property_update(prop, 0, "rna_cloth_update"); + RNA_def_property_update(prop, 0, "rna_cloth_dependency_update"); prop = RNA_def_property(srna, "vertex_group_self_collisions", PROP_STRING, PROP_NONE); RNA_def_property_string_funcs(prop, "rna_CollSettings_selfcol_vgroup_get", "rna_CollSettings_selfcol_vgroup_length", diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 98560bf3452..db3f76f3cfc 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -72,6 +72,8 @@ EnumPropertyItem rna_enum_constraint_type_items[] = { "Compensate for scaling one axis by applying suitable scaling to the other two axes"}, {CONSTRAINT_TYPE_TRANSFORM, "TRANSFORM", ICON_CONSTRAINT_DATA, "Transformation", "Use one transform property from target to control another (or same) property on owner"}, + {CONSTRAINT_TYPE_TRANSFORM_CACHE, "TRANSFORM_CACHE", ICON_CONSTRAINT_DATA, "Transform Cache", + "Look up the transformation matrix from an external file"}, {0, "", 0, N_("Tracking"), ""}, {CONSTRAINT_TYPE_CLAMPTO, "CLAMP_TO", ICON_CONSTRAINT_DATA, "Clamp To", "Restrict movements to lie along a curve by remapping location along curve's longest axis"}, @@ -214,6 +216,8 @@ static StructRNA *rna_ConstraintType_refine(struct PointerRNA *ptr) return &RNA_CameraSolverConstraint; case CONSTRAINT_TYPE_OBJECTSOLVER: return &RNA_ObjectSolverConstraint; + case CONSTRAINT_TYPE_TRANSFORM_CACHE: + return &RNA_TransformCacheConstraint; default: return &RNA_UnknownType; } @@ -2571,6 +2575,27 @@ static void rna_def_constraint_object_solver(BlenderRNA *brna) "rna_Constraint_cameraObject_poll"); } +static void rna_def_constraint_transform_cache(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "TransformCacheConstraint", "Constraint"); + RNA_def_struct_ui_text(srna, "Transform Cache Constraint", "Look up transformation from an external file"); + RNA_def_struct_sdna_from(srna, "bTransformCacheConstraint", "data"); + + prop = RNA_def_property(srna, "cache_file", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "cache_file"); + RNA_def_property_struct_type(prop, "CacheFile"); + RNA_def_property_ui_text(prop, "Cache File", ""); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_update(prop, 0, "rna_Constraint_dependency_update"); + + prop = RNA_def_property(srna, "object_path", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Object Path", "Path to the object in the Alembic archive used to lookup the transform matrix"); + RNA_def_property_update(prop, 0, "rna_Constraint_update"); +} + /* base struct for constraints */ void RNA_def_constraint(BlenderRNA *brna) { @@ -2687,6 +2712,7 @@ void RNA_def_constraint(BlenderRNA *brna) rna_def_constraint_follow_track(brna); rna_def_constraint_camera_solver(brna); rna_def_constraint_object_solver(brna); + rna_def_constraint_transform_cache(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 52c04bec743..9476c964b40 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -15,7 +15,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * Contributor(s): Blender Foundation (2009), Joshua Leung + * Contributor(s): Blender Foundation (2009), Joshua Leung, Antonio Vazquez * * ***** END GPL LICENSE BLOCK ***** */ @@ -41,6 +41,17 @@ #include "rna_internal.h" #include "WM_types.h" +#include "DNA_object_types.h" +#include "ED_gpencil.h" + +/* parent type */ +static EnumPropertyItem parent_type_items[] = { + {PAROBJECT, "OBJECT", 0, "Object", "The layer is parented to an object"}, + {PARSKEL, "ARMATURE", 0, "Armature", ""}, + {PARBONE, "BONE", 0, "Bone", "The layer is parented to a bone"}, + {0, NULL, 0, NULL, NULL} +}; + #ifdef RNA_RUNTIME @@ -49,8 +60,7 @@ #include "WM_api.h" #include "BKE_gpencil.h" - -#include "DNA_object_types.h" +#include "BKE_action.h" static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) @@ -90,6 +100,16 @@ static void rna_GPencil_onion_skinning_update(Main *bmain, Scene *scene, Pointer rna_GPencil_update(bmain, scene, ptr); } +static void rna_GPencil_stroke_colorname_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + bGPDstroke *gps = (bGPDstroke *)ptr->data; + gps->flag |= GP_STROKE_RECALC_COLOR; + gps->palcolor = NULL; + + /* Now do standard updates... */ + rna_GPencil_update(bmain, scene, ptr); +} + static char *rna_GPencilLayer_path(PointerRNA *ptr) { bGPDlayer *gpl = (bGPDlayer *)ptr->data; @@ -123,38 +143,140 @@ static void rna_GPencilLayer_line_width_range(PointerRNA *ptr, int *min, int *ma * it's relatively hard to test for that. So, for now, only volumetric strokes * get to be larger... */ + + /* From GP v2 this value is used to increase or decrease the thickness of the stroke */ if (gpl->flag & GP_LAYER_VOLUMETRIC) { - *min = 1; + *min = -300; *max = 300; - *softmin = 1; + *softmin = -100; *softmax = 100; } else { - *min = 1; + *min = -10; *max = 10; - *softmin = 1; + *softmin = -10; *softmax = 10; } } -static int rna_GPencilLayer_is_stroke_visible_get(PointerRNA *ptr) +/* set parent */ +static void set_parent(bGPDlayer *gpl, Object *par, const int type, const char *substr) +{ + if (type == PAROBJECT) { + invert_m4_m4(gpl->inverse, par->obmat); + gpl->parent = par; + gpl->partype |= PAROBJECT; + gpl->parsubstr[0] = 0; + } + else if (type == PARSKEL) { + invert_m4_m4(gpl->inverse, par->obmat); + gpl->parent = par; + gpl->partype |= PARSKEL; + gpl->parsubstr[0] = 0; + } + else if (type == PARBONE) { + bPoseChannel *pchan = BKE_pose_channel_find_name(par->pose, substr); + if (pchan) { + float tmp_mat[4][4]; + mul_m4_m4m4(tmp_mat, par->obmat, pchan->pose_mat); + + invert_m4_m4(gpl->inverse, tmp_mat); + gpl->parent = par; + gpl->partype |= PARBONE; + BLI_strncpy(gpl->parsubstr, substr, sizeof(gpl->parsubstr)); + } + } +} + +/* set parent object and inverse matrix */ +static void rna_GPencilLayer_parent_set(PointerRNA *ptr, PointerRNA value) { - /* see drawgpencil.c -> gp_draw_data_layers() for more details - * about this limit for showing/not showing - */ bGPDlayer *gpl = (bGPDlayer *)ptr->data; - return (gpl->color[3] > GPENCIL_ALPHA_OPACITY_THRESH); + Object *par = (Object *)value.data; + + if (par != NULL) { + set_parent(gpl, par, gpl->partype, gpl->parsubstr); + } + else { + /* keep strokes in the same place, so apply current transformation */ + if (gpl->parent != NULL) { + bGPDspoint *pt; + int i; + float diff_mat[4][4]; + /* calculate difference matrix */ + ED_gpencil_parent_location(gpl, diff_mat); + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + mul_m4_v3(diff_mat, &pt->x); + } + } + } + } + /* clear parent */ + gpl->parent = NULL; + } } -static int rna_GPencilLayer_is_fill_visible_get(PointerRNA *ptr) +/* set parent type */ +static void rna_GPencilLayer_parent_type_set(PointerRNA *ptr, int value) +{ + bGPDlayer *gpl = (bGPDlayer *)ptr->data; + Object *par = gpl->parent; + gpl->partype = value; + + if (par != NULL) { + set_parent(gpl, par, value, gpl->parsubstr); + } +} + +/* set parent bone */ +static void rna_GPencilLayer_parent_bone_set(PointerRNA *ptr, const char *value) +{ + bGPDlayer *gpl = (bGPDlayer *)ptr->data; + + Object *par = gpl->parent; + gpl->partype = PARBONE; + + if (par != NULL) { + set_parent(gpl, par, gpl->partype, value); + } +} + + +/* parent types enum */ +static EnumPropertyItem *rna_Object_parent_type_itemf( + bContext *UNUSED(C), PointerRNA *ptr, + PropertyRNA *UNUSED(prop), bool *r_free) +{ + bGPDlayer *gpl = (bGPDlayer *)ptr->data; + EnumPropertyItem *item = NULL; + int totitem = 0; + + RNA_enum_items_add_value(&item, &totitem, parent_type_items, PAROBJECT); + + if (gpl->parent) { + Object *par = gpl->parent; + + if (par->type == OB_ARMATURE) { + /* special hack: prevents this being overrided */ + RNA_enum_items_add_value(&item, &totitem, &parent_type_items[1], PARSKEL); + RNA_enum_items_add_value(&item, &totitem, parent_type_items, PARBONE); + } + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + +static int rna_GPencilLayer_is_parented_get(PointerRNA *ptr) { - /* see drawgpencil.c -> gp_draw_data_layers() for more details - * about this limit for showing/not showing - */ bGPDlayer *gpl = (bGPDlayer *)ptr->data; - return (gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH); + return (gpl->parent != NULL); } static PointerRNA rna_GPencil_active_layer_get(PointerRNA *ptr) @@ -201,7 +323,7 @@ static void rna_GPencil_active_layer_set(PointerRNA *ptr, PointerRNA value) static int rna_GPencil_active_layer_index_get(PointerRNA *ptr) { bGPdata *gpd = (bGPdata *)ptr->id.data; - bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); return BLI_findindex(&gpd->layers, gpl); } @@ -211,7 +333,7 @@ static void rna_GPencil_active_layer_index_set(PointerRNA *ptr, int value) bGPdata *gpd = (bGPdata *)ptr->id.data; bGPDlayer *gpl = BLI_findlink(&gpd->layers, value); - gpencil_layer_setactive(gpd, gpl); + BKE_gpencil_layer_setactive(gpd, gpl); } static void rna_GPencil_active_layer_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) @@ -244,7 +366,7 @@ static void rna_GPencil_use_onion_skinning_set(PointerRNA *ptr, const int value) /* set new value */ if (value) { /* enable on active layer (it's the one that's most likely to be of interest right now) */ - gpl = gpencil_layer_getactive(gpd); + gpl = BKE_gpencil_layer_getactive(gpd); if (gpl) { gpl->flag |= GP_LAYER_ONIONSKIN; } @@ -313,7 +435,7 @@ static void rna_GPencil_stroke_point_select_set(PointerRNA *ptr, const int value pt->flag &= ~GP_SPOINT_SELECT; /* Check if the stroke should be selected or not... */ - gpencil_stroke_sync_selection(gps); + BKE_gpencil_stroke_sync_selection(gps); } } @@ -357,10 +479,14 @@ static void rna_GPencil_stroke_point_pop(bGPDstroke *stroke, ReportList *reports WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); } -static bGPDstroke *rna_GPencil_stroke_new(bGPDframe *frame) +static bGPDstroke *rna_GPencil_stroke_new(bGPDframe *frame, const char *colorname) { bGPDstroke *stroke = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); - + if (colorname) { + strcpy(stroke->colorname, colorname); + } + stroke->palcolor = NULL; + stroke->flag |= GP_STROKE_RECALC_COLOR; BLI_addtail(&frame->strokes, stroke); return stroke; @@ -410,7 +536,7 @@ static bGPDframe *rna_GPencil_frame_new(bGPDlayer *layer, ReportList *reports, i return NULL; } - frame = gpencil_frame_addnew(layer, frame_number); + frame = BKE_gpencil_frame_addnew(layer, frame_number); WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); @@ -425,7 +551,7 @@ static void rna_GPencil_frame_remove(bGPDlayer *layer, ReportList *reports, Poin return; } - gpencil_layer_delframe(layer, frame); + BKE_gpencil_layer_delframe(layer, frame); RNA_POINTER_INVALIDATE(frame_ptr); WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); @@ -433,7 +559,7 @@ static void rna_GPencil_frame_remove(bGPDlayer *layer, ReportList *reports, Poin static bGPDframe *rna_GPencil_frame_copy(bGPDlayer *layer, bGPDframe *src) { - bGPDframe *frame = gpencil_frame_duplicate(src); + bGPDframe *frame = BKE_gpencil_frame_duplicate(src); while (BKE_gpencil_layer_find_frame(layer, frame->framenum)) { frame->framenum++; @@ -448,7 +574,7 @@ static bGPDframe *rna_GPencil_frame_copy(bGPDlayer *layer, bGPDframe *src) static bGPDlayer *rna_GPencil_layer_new(bGPdata *gpd, const char *name, int setactive) { - bGPDlayer *gl = gpencil_layer_addnew(gpd, name, setactive != 0); + bGPDlayer *gl = BKE_gpencil_layer_addnew(gpd, name, setactive != 0); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -463,7 +589,7 @@ static void rna_GPencil_layer_remove(bGPdata *gpd, ReportList *reports, PointerR return; } - gpencil_layer_delete(gpd, layer); + BKE_gpencil_layer_delete(gpd, layer); RNA_POINTER_INVALIDATE(layer_ptr); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -471,25 +597,258 @@ static void rna_GPencil_layer_remove(bGPdata *gpd, ReportList *reports, PointerR static void rna_GPencil_frame_clear(bGPDframe *frame) { - free_gpencil_strokes(frame); + BKE_gpencil_free_strokes(frame); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } static void rna_GPencil_layer_clear(bGPDlayer *layer) { - free_gpencil_frames(layer); + BKE_gpencil_free_frames(layer); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } static void rna_GPencil_clear(bGPdata *gpd) { - free_gpencil_layers(&gpd->layers); + BKE_gpencil_free_layers(&gpd->layers); WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } +/* Palettes */ +static bGPDpalette *rna_GPencil_palette_new(bGPdata *gpd, const char *name, int setactive) +{ + bGPDpalette *palette = BKE_gpencil_palette_addnew(gpd, name, setactive != 0); + + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return palette; +} + +static void rna_GPencil_palette_remove(bGPdata *gpd, ReportList *reports, PointerRNA *palette_ptr) +{ + bGPDpalette *palette = palette_ptr->data; + if (BLI_findindex(&gpd->palettes, palette) == -1) { + BKE_report(reports, RPT_ERROR, "Palette not found in grease pencil data"); + return; + } + + BKE_gpencil_palette_delete(gpd, palette); + RNA_POINTER_INVALIDATE(palette_ptr); + + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); +} + +static PointerRNA rna_GPencil_active_palette_get(PointerRNA *ptr) +{ + bGPdata *gpd = ptr->id.data; + + if (GS(gpd->id.name) == ID_GD) { /* why would this ever be not GD */ + bGPDpalette *palette; + + for (palette = gpd->palettes.first; palette; palette = palette->next) { + if (palette->flag & PL_PALETTE_ACTIVE) { + break; + } + } + + if (palette) { + return rna_pointer_inherit_refine(ptr, &RNA_GPencilPalette, palette); + } + } + + return rna_pointer_inherit_refine(ptr, NULL, NULL); +} + +static void rna_GPencil_active_palette_set(PointerRNA *ptr, PointerRNA value) +{ + bGPdata *gpd = ptr->id.data; + + if (GS(gpd->id.name) == ID_GD) { /* why would this ever be not GD */ + bGPDpalette *palette; + + for (palette = gpd->palettes.first; palette; palette = palette->next) { + if (palette == value.data) { + palette->flag |= PL_PALETTE_ACTIVE; + } + else { + palette->flag &= ~PL_PALETTE_ACTIVE; + } + } + /* force color recalc */ + BKE_gpencil_palette_change_strokes(gpd); + + WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); + } +} + +static int rna_GPencilPalette_index_get(PointerRNA *ptr) +{ + bGPdata *gpd = (bGPdata *)ptr->id.data; + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + + return BLI_findindex(&gpd->palettes, palette); +} + +static void rna_GPencilPalette_index_set(PointerRNA *ptr, int value) +{ + bGPdata *gpd = (bGPdata *)ptr->id.data; + bGPDpalette *palette = BLI_findlink(&gpd->palettes, value); + + BKE_gpencil_palette_setactive(gpd, palette); + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); +} + +static void rna_GPencilPalette_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) +{ + bGPdata *gpd = (bGPdata *)ptr->id.data; + + *min = 0; + *max = max_ii(0, BLI_listbase_count(&gpd->palettes) - 1); + + *softmin = *min; + *softmax = *max; +} + +/* Palette colors */ +static bGPDpalettecolor *rna_GPencilPalette_color_new(bGPDpalette *palette) +{ + bGPDpalettecolor *color = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); + + return color; +} + +static void rna_GPencilPalette_color_remove(bGPDpalette *palette, ReportList *reports, PointerRNA *color_ptr) +{ + bGPDpalettecolor *color = color_ptr->data; + + if (BLI_findindex(&palette->colors, color) == -1) { + BKE_reportf(reports, RPT_ERROR, "Palette '%s' does not contain color given", palette->info + 2); + return; + } + + BKE_gpencil_palettecolor_delete(palette, color); + RNA_POINTER_INVALIDATE(color_ptr); + + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); +} + +static PointerRNA rna_GPencilPalette_active_color_get(PointerRNA *ptr) +{ + bGPDpalette *palette = (bGPDpalette *)ptr->data; + bGPDpalettecolor *color; + + for (color = palette->colors.first; color; color = color->next) { + if (color->flag & PC_COLOR_ACTIVE) { + break; + } + } + + if (color) { + return rna_pointer_inherit_refine(ptr, &RNA_GPencilPaletteColor, color); + } + + return rna_pointer_inherit_refine(ptr, NULL, NULL); +} + +static void rna_GPencilPalette_active_color_set(PointerRNA *ptr, PointerRNA value) +{ + bGPDpalette *palette = (bGPDpalette *)ptr->data; + bGPDpalettecolor *color = value.data; + + BKE_gpencil_palettecolor_setactive(palette, color); +} + +static void rna_GPencilPalette_info_set(PointerRNA *ptr, const char *value) +{ + bGPdata *gpd = ptr->id.data; + bGPDpalette *palette = ptr->data; + + /* copy the new name into the name slot */ + BLI_strncpy_utf8(palette->info, value, sizeof(palette->info)); + + BLI_uniquename(&gpd->palettes, palette, DATA_("GP_Palette"), '.', offsetof(bGPDpalette, info), + sizeof(palette->info)); +} + +static char *rna_GPencilPalette_color_path(PointerRNA *ptr) +{ + bGPdata *gpd = ptr->id.data; + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = ptr->data; + + char name_palette[sizeof(palette->info) * 2]; + char name_color[sizeof(palcolor->info) * 2]; + + BLI_strescape(name_palette, palette->info, sizeof(name_palette)); + BLI_strescape(name_color, palcolor->info, sizeof(name_color)); + + return BLI_sprintfN("palettes[\"%s\"].colors[\"%s\"]", name_palette, name_color); +} + +static void rna_GPencilPaletteColor_info_set(PointerRNA *ptr, const char *value) +{ + bGPdata *gpd = ptr->id.data; + bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + bGPDpalettecolor *palcolor = ptr->data; + + /* rename all strokes */ + BKE_gpencil_palettecolor_changename(gpd, palcolor->info, value); + + /* copy the new name into the name slot */ + BLI_strncpy_utf8(palcolor->info, value, sizeof(palcolor->info)); + BLI_uniquename(&palette->colors, palcolor, DATA_("Color"), '.', offsetof(bGPDpalettecolor, info), + sizeof(palcolor->info)); +} + +static void rna_GPencilStrokeColor_info_set(PointerRNA *ptr, const char *value) +{ + bGPDstroke *gps = ptr->data; + + /* copy the new name into the name slot */ + BLI_strncpy_utf8(gps->colorname, value, sizeof(gps->colorname)); +} + + +static int rna_GPencilPaletteColor_is_stroke_visible_get(PointerRNA *ptr) +{ + bGPDpalettecolor *pcolor = (bGPDpalettecolor *)ptr->data; + return (pcolor->color[3] > GPENCIL_ALPHA_OPACITY_THRESH); +} + +static int rna_GPencilPaletteColor_is_fill_visible_get(PointerRNA *ptr) +{ + bGPDpalettecolor *pcolor = (bGPDpalettecolor *)ptr->data; + return (pcolor->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH); +} + +static int rna_GPencilPaletteColor_index_get(PointerRNA *ptr) +{ + bGPDpalette *palette = (bGPDpalette *)ptr->data; + bGPDpalettecolor *pcolor = BKE_gpencil_palettecolor_getactive(palette); + + return BLI_findindex(&palette->colors, pcolor); +} + +static void rna_GPencilPaletteColor_index_set(PointerRNA *ptr, int value) +{ + bGPDpalette *palette = (bGPDpalette *)ptr->data; + bGPDpalettecolor *pcolor = BLI_findlink(&palette->colors, value); + BKE_gpencil_palettecolor_setactive(palette, pcolor); +} + +static void rna_GPencilPaletteColor_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) +{ + bGPDpalette *palette = (bGPDpalette *)ptr->data; + + *min = 0; + *max = max_ii(0, BLI_listbase_count(&palette->colors) - 1); + + *softmin = *min; + *softmax = *max; +} + #else static void rna_def_gpencil_stroke_point(BlenderRNA *brna) @@ -513,6 +872,12 @@ static void rna_def_gpencil_stroke_point(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Pressure", "Pressure of tablet at point when drawing it"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "strength"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Strength", "Color intensity (alpha factor)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_SPOINT_SELECT); RNA_def_property_boolean_funcs(prop, NULL, "rna_GPencil_stroke_point_select_set"); @@ -542,6 +907,35 @@ static void rna_def_gpencil_stroke_points_api(BlenderRNA *brna, PropertyRNA *cpr RNA_def_int(func, "index", -1, INT_MIN, INT_MAX, "Index", "point index", INT_MIN, INT_MAX); } +/* This information is read only and it can be used by add-ons */ +static void rna_def_gpencil_triangle(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GPencilTriangle", NULL); + RNA_def_struct_sdna(srna, "bGPDtriangle"); + RNA_def_struct_ui_text(srna, "Triangle", "Triangulation data for HQ fill"); + + /* point v1 */ + prop = RNA_def_property(srna, "v1", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "v1"); + RNA_def_property_ui_text(prop, "v1", "First triangle vertice index"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + /* point v2 */ + prop = RNA_def_property(srna, "v2", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "v2"); + RNA_def_property_ui_text(prop, "v2", "Second triangle vertice index"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + /* point v3 */ + prop = RNA_def_property(srna, "v3", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "v3"); + RNA_def_property_ui_text(prop, "v3", "Third triangle vertice index"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); +} + static void rna_def_gpencil_stroke(BlenderRNA *brna) { StructRNA *srna; @@ -566,6 +960,19 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Stroke Points", "Stroke data points"); rna_def_gpencil_stroke_points_api(brna, prop); + /* Triangles */ + prop = RNA_def_property(srna, "triangles", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "triangles", "tot_triangles"); + RNA_def_property_struct_type(prop, "GPencilTriangle"); + RNA_def_property_ui_text(prop, "Triangles", "Triangulation data for HQ fill"); + + /* Color */ + prop = RNA_def_property(srna, "color", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "GPencilPaletteColor"); + RNA_def_property_pointer_sdna(prop, NULL, "palcolor"); + RNA_def_property_ui_text(prop, "Palette Color", "Color from palette used in Stroke"); + RNA_def_property_update(prop, 0, "rna_GPencil_update"); + /* Settings */ prop = RNA_def_property(srna, "draw_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); @@ -578,6 +985,27 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna) RNA_def_property_boolean_funcs(prop, NULL, "rna_GPencil_stroke_select_set"); RNA_def_property_ui_text(prop, "Select", "Stroke is selected for viewport editing"); RNA_def_property_update(prop, 0, "rna_GPencil_update"); + + /* Color Name */ + prop = RNA_def_property(srna, "colorname", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilStrokeColor_info_set"); + RNA_def_property_ui_text(prop, "Color Name", "Palette color name"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_colorname_update"); + + /* Cyclic: Draw a line from end to start point */ + prop = RNA_def_property(srna, "draw_cyclic", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_STROKE_CYCLIC); + RNA_def_property_ui_text(prop, "Cyclic", "Enable cyclic drawing, closing the stroke"); + RNA_def_property_update(prop, 0, "rna_GPencil_update"); + + /* Line Thickness */ + prop = RNA_def_property(srna, "line_width", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "thickness"); + RNA_def_property_range(prop, 1, 300); + RNA_def_property_ui_range(prop, 1, 10, 1, 0); + RNA_def_property_ui_text(prop, "Thickness", "Thickness of stroke (in pixels)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + } static void rna_def_gpencil_strokes_api(BlenderRNA *brna, PropertyRNA *cprop) @@ -594,6 +1022,7 @@ static void rna_def_gpencil_strokes_api(BlenderRNA *brna, PropertyRNA *cprop) func = RNA_def_function(srna, "new", "rna_GPencil_stroke_new"); RNA_def_function_ui_description(func, "Add a new grease pencil stroke"); + parm = RNA_def_string(func, "colorname", 0, MAX_NAME, "Color", "Name of the color"); parm = RNA_def_pointer(func, "stroke", "GPencilStroke", "", "The newly created stroke"); RNA_def_function_return(func, parm); @@ -721,45 +1150,33 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Volumetric Strokes", "Draw strokes as a series of circular blobs, resulting in a volumetric effect"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - /* Use High Quality Fill */ - prop = RNA_def_property(srna, "use_hq_fill", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_HQ_FILL); - RNA_def_property_ui_text(prop, "High Quality Fill", "Fill strokes using high quality method to avoid glitches (slower fps during animation playback)"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* Stroke Drawing Color */ - prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_array(prop, 3); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Color", "Color for all strokes in this layer"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - prop = RNA_def_property(srna, "alpha", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "color[3]"); + prop = RNA_def_property(srna, "opacity", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "opacity"); RNA_def_property_range(prop, 0.0, 1.0f); RNA_def_property_ui_text(prop, "Opacity", "Layer Opacity"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - /* Fill Drawing Color */ - prop = RNA_def_property(srna, "fill_color", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "fill"); + /* Tint Color */ + prop = RNA_def_property(srna, "tint_color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, NULL, "tintcolor"); RNA_def_property_array(prop, 3); RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_text(prop, "Fill Color", "Color for filling region bounded by each stroke"); + RNA_def_property_ui_text(prop, "Tint Color", "Color for tinting stroke colors"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - prop = RNA_def_property(srna, "fill_alpha", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "fill[3]"); + /* Tint factor */ + prop = RNA_def_property(srna, "tint_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "tintcolor[3]"); RNA_def_property_range(prop, 0.0, 1.0f); - RNA_def_property_ui_text(prop, "Fill Opacity", "Opacity for filling region bounded by each stroke"); + RNA_def_property_ui_text(prop, "Tint Factor", "Factor of tinting color"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - /* Line Thickness */ - prop = RNA_def_property(srna, "line_width", PROP_INT, PROP_PIXEL); + /* Line Thickness change */ + prop = RNA_def_property(srna, "line_change", PROP_INT, PROP_PIXEL); RNA_def_property_int_sdna(prop, NULL, "thickness"); //RNA_def_property_range(prop, 1, 10); /* 10 px limit comes from Windows OpenGL limits for natively-drawn strokes */ RNA_def_property_int_funcs(prop, NULL, NULL, "rna_GPencilLayer_line_width_range"); - RNA_def_property_ui_text(prop, "Thickness", "Thickness of strokes (in pixels)"); + RNA_def_property_ui_text(prop, "Thickness", "Thickness change to apply current strokes (in pixels)"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); /* Onion-Skinning */ @@ -803,31 +1220,6 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "After Color", "Base color for ghosts after the active frame"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - /* Smoothing factor for new strokes */ - prop = RNA_def_property(srna, "pen_smooth_factor", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "draw_smoothfac"); - RNA_def_property_range(prop, 0.0, 2.0f); - RNA_def_property_ui_text(prop, "Smooth", - "Amount of smoothing to apply to newly created strokes, to reduce jitter/noise"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* Iterations of the Smoothing factor */ - prop = RNA_def_property(srna, "pen_smooth_steps", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "draw_smoothlvl"); - RNA_def_property_range(prop, 1, 3); - RNA_def_property_ui_text(prop, "Iterations", - "Number of times to smooth newly created strokes " - "(smoothing strength is halved on each successive round of smoothing)"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* Subdivision level for new strokes */ - prop = RNA_def_property(srna, "pen_subdivision_steps", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "sublevel"); - RNA_def_property_range(prop, 0, 3); - RNA_def_property_ui_text(prop, "Subdivision Steps", - "Number of times to subdivide newly created strokes, for less jagged strokes"); - RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - /* Flags */ prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_HIDE); @@ -847,6 +1239,15 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Frame Locked", "Lock current frame displayed by layer"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* Unlock colors */ + prop = RNA_def_property(srna, "unlock_color", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LAYER_UNLOCK_COLOR); + RNA_def_property_ui_icon(prop, ICON_RESTRICT_COLOR_OFF, 1); + RNA_def_property_ui_text(prop, "Unlock color", "Unprotect colors selected from further editing " + "and/or frame changes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* expose as layers.active */ #if 0 prop = RNA_def_property(srna, "active", PROP_BOOLEAN, PROP_NONE); @@ -873,18 +1274,42 @@ static void rna_def_gpencil_layer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "X Ray", "Make the layer draw in front of objects"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); - - /* Read-only state props (for simpler UI code) */ - prop = RNA_def_property(srna, "is_stroke_visible", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs(prop, "rna_GPencilLayer_is_stroke_visible_get", NULL); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Is Stroke Visible", "True when opacity of stroke is set high enough to be visible"); - - prop = RNA_def_property(srna, "is_fill_visible", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_funcs(prop, "rna_GPencilLayer_is_fill_visible_get", NULL); + /* Parent object */ + prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_funcs(prop, NULL, "rna_GPencilLayer_parent_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_ui_text(prop, "Parent", "Parent Object"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* parent type */ + prop = RNA_def_property(srna, "parent_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "partype"); + RNA_def_property_enum_items(prop, parent_type_items); + RNA_def_property_enum_funcs(prop, NULL, "rna_GPencilLayer_parent_type_set", "rna_Object_parent_type_itemf"); + RNA_def_property_ui_text(prop, "Parent Type", "Type of parent relation"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* parent bone */ + prop = RNA_def_property(srna, "parent_bone", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "parsubstr"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilLayer_parent_bone_set"); + RNA_def_property_ui_text(prop, "Parent Bone", "Name of parent bone in case of a bone parenting relation"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* matrix */ + prop = RNA_def_property(srna, "matrix_inverse", PROP_FLOAT, PROP_MATRIX); + RNA_def_property_float_sdna(prop, NULL, "inverse"); + RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "MatrixInverse", "Parent inverse transformation matrix"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* read only parented flag */ + prop = RNA_def_property(srna, "is_parented", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_GPencilLayer_is_parented_get", NULL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Is Fill Visible", "True when opacity of fill is set high enough to be visible"); - + RNA_def_property_ui_text(prop, "Is Parented", "True when the layer parent object is set"); + /* Layers API */ func = RNA_def_function(srna, "clear", "rna_GPencil_layer_clear"); RNA_def_function_ui_description(func, "Remove all the grease pencil layer data"); @@ -933,6 +1358,208 @@ static void rna_def_gpencil_layers_api(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_ui_text(prop, "Active Layer Index", "Index of active grease pencil layer"); } +static void rna_def_gpencil_palettecolor(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GPencilPaletteColor", NULL); + RNA_def_struct_sdna(srna, "bGPDpalettecolor"); + RNA_def_struct_ui_text(srna, "Grease Pencil Palette color", "Collection of related colors"); + RNA_def_struct_path_func(srna, "rna_GPencilPalette_color_path"); + + /* Stroke Drawing Color */ + prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, NULL, "color"); + RNA_def_property_array(prop, 3); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Color", "Color for strokes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "alpha", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "color[3]"); + RNA_def_property_range(prop, 0.0, 1.0f); + RNA_def_property_ui_text(prop, "Opacity", "Color Opacity"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Name */ + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "info"); + RNA_def_property_ui_text(prop, "Name", "Color name"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilPaletteColor_info_set"); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Fill Drawing Color */ + prop = RNA_def_property(srna, "fill_color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, NULL, "fill"); + RNA_def_property_array(prop, 3); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Fill Color", "Color for filling region bounded by each stroke"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Fill alpha */ + prop = RNA_def_property(srna, "fill_alpha", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "fill[3]"); + RNA_def_property_range(prop, 0.0, 1.0f); + RNA_def_property_ui_text(prop, "Fill Opacity", "Opacity for filling region bounded by each stroke"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Flags */ + prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_HIDE); + RNA_def_property_ui_icon(prop, ICON_RESTRICT_VIEW_OFF, 1); + RNA_def_property_ui_text(prop, "Hide", "Set color Visibility"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "lock", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_LOCKED); + RNA_def_property_ui_icon(prop, ICON_UNLOCKED, 1); + RNA_def_property_ui_text(prop, "Locked", "Protect color from further editing and/or frame changes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + prop = RNA_def_property(srna, "ghost", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_ONIONSKIN); + RNA_def_property_ui_icon(prop, ICON_GHOST_ENABLED, 0); + RNA_def_property_ui_text(prop, "Ghost", "Display the color in onion skinning"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Draw Style */ + prop = RNA_def_property(srna, "use_volumetric_strokes", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_VOLUMETRIC); + RNA_def_property_ui_text(prop, "Volumetric Strokes", "Draw strokes as a series of circular blobs, resulting in " + "a volumetric effect"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Use High quality fill */ + prop = RNA_def_property(srna, "use_hq_fill", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", PC_COLOR_HQ_FILL); + RNA_def_property_ui_text(prop, "High Quality Fill", "Fill strokes using high quality to avoid glitches " + "(slower fps during animation play)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Read-only state props (for simpler UI code) */ + prop = RNA_def_property(srna, "is_stroke_visible", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_GPencilPaletteColor_is_stroke_visible_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Is Stroke Visible", "True when opacity of stroke is set high enough to be visible"); + + prop = RNA_def_property(srna, "is_fill_visible", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_GPencilPaletteColor_is_fill_visible_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Is Fill Visible", "True when opacity of fill is set high enough to be visible"); +} + +/* palette colors api */ +static void rna_def_gpencil_palettecolors_api(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + PropertyRNA *prop; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "GPencilPaletteColors"); + srna = RNA_def_struct(brna, "GPencilPaletteColors", NULL); + RNA_def_struct_sdna(srna, "bGPDpalette"); + RNA_def_struct_ui_text(srna, "Palette colors", "Collection of palette colors"); + + func = RNA_def_function(srna, "new", "rna_GPencilPalette_color_new"); + RNA_def_function_ui_description(func, "Add a new color to the palette"); + parm = RNA_def_pointer(func, "color", "GPencilPaletteColor", "", "The newly created color"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_GPencilPalette_color_remove"); + RNA_def_function_ui_description(func, "Remove a color from the palette"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "color", "GPencilPaletteColor", "", "The color to remove"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR); + RNA_def_property_clear_flag(parm, PROP_THICK_WRAP); + + prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "GPencilPaletteColor"); + RNA_def_property_pointer_funcs(prop, "rna_GPencilPalette_active_color_get", "rna_GPencilPalette_active_color_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Active Palette Color", "Current active color"); + + prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_funcs(prop, + "rna_GPencilPaletteColor_index_get", + "rna_GPencilPaletteColor_index_set", + "rna_GPencilPaletteColor_index_range"); + RNA_def_property_ui_text(prop, "Active color Index", "Index of active palette color"); +} + +static void rna_def_gpencil_palette(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GPencilPalette", NULL); + RNA_def_struct_sdna(srna, "bGPDpalette"); + RNA_def_struct_ui_text(srna, "Grease Pencil Palette", "Collection of related palettes"); + RNA_def_struct_ui_icon(srna, ICON_COLOR); + + /* Name */ + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "info"); + RNA_def_property_ui_text(prop, "Name", "Palette name"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilPalette_info_set"); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + + /* Colors */ + prop = RNA_def_property(srna, "colors", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "colors", NULL); + RNA_def_property_struct_type(prop, "GPencilPaletteColor"); + RNA_def_property_ui_text(prop, "Colors", "Colors of the palette"); + rna_def_gpencil_palettecolors_api(brna, prop); + +} + +static void rna_def_gpencil_palettes_api(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + PropertyRNA *prop; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "GreasePencilPalettes"); + srna = RNA_def_struct(brna, "GreasePencilPalettes", NULL); + RNA_def_struct_sdna(srna, "bGPdata"); + RNA_def_struct_ui_text(srna, "Grease Pencil Palettes", "Collection of grease pencil palettes"); + + func = RNA_def_function(srna, "new", "rna_GPencil_palette_new"); + RNA_def_function_ui_description(func, "Add a new grease pencil palette"); + parm = RNA_def_string(func, "name", "GPencilPalette", MAX_NAME, "Name", "Name of the palette"); + RNA_def_property_flag(parm, PROP_REQUIRED); + RNA_def_boolean(func, "set_active", 0, "Set Active", "Activate the newly created palette"); + parm = RNA_def_pointer(func, "palette", "GPencilPalette", "", "The newly created palette"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_GPencil_palette_remove"); + RNA_def_function_ui_description(func, "Remove a grease pencil palette"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "palette", "GPencilPalette", "", "The palette to remove"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR); + RNA_def_property_clear_flag(parm, PROP_THICK_WRAP); + + prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "GPencilPalette"); + RNA_def_property_pointer_funcs(prop, "rna_GPencil_active_palette_get", "rna_GPencil_active_palette_set", + NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Active Palette", "Current active palette"); + + prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_funcs(prop, + "rna_GPencilPalette_index_get", + "rna_GPencilPalette_index_set", + "rna_GPencilPalette_index_range"); + RNA_def_property_ui_text(prop, "Active Palette Index", "Index of active palette"); +} + static void rna_def_gpencil_data(BlenderRNA *brna) { StructRNA *srna; @@ -951,6 +1578,13 @@ static void rna_def_gpencil_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Layers", ""); rna_def_gpencil_layers_api(brna, prop); + /* Palettes */ + prop = RNA_def_property(srna, "palettes", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "palettes", NULL); + RNA_def_property_struct_type(prop, "GPencilPalette"); + RNA_def_property_ui_text(prop, "Palettes", ""); + rna_def_gpencil_palettes_api(brna, prop); + /* Animation Data */ rna_def_animdata_common(srna); @@ -967,6 +1601,12 @@ static void rna_def_gpencil_data(BlenderRNA *brna) "Show ghosts of the frames before and after the current frame, toggle to enable on active layer or disable all"); RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + prop = RNA_def_property(srna, "show_stroke_direction", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_SHOW_DIRECTION); + RNA_def_property_ui_text(prop, "Show Direction", "Show stroke drawing direction with a bigger green dot (start) " + "and smaller red dot (end) points"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update"); + /* API Functions */ func = RNA_def_function(srna, "clear", "rna_GPencil_clear"); RNA_def_function_ui_description(func, "Remove all the grease pencil data"); @@ -980,8 +1620,12 @@ void RNA_def_gpencil(BlenderRNA *brna) rna_def_gpencil_layer(brna); rna_def_gpencil_frame(brna); + rna_def_gpencil_triangle(brna); rna_def_gpencil_stroke(brna); rna_def_gpencil_stroke_point(brna); + + rna_def_gpencil_palette(brna); + rna_def_gpencil_palettecolor(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 161e19f581c..364aa9ba939 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -133,6 +133,7 @@ void RNA_def_armature(struct BlenderRNA *brna); void RNA_def_actuator(struct BlenderRNA *brna); void RNA_def_boid(struct BlenderRNA *brna); void RNA_def_brush(struct BlenderRNA *brna); +void RNA_def_cachefile(struct BlenderRNA *brna); void RNA_def_camera(struct BlenderRNA *brna); void RNA_def_cloth(struct BlenderRNA *brna); void RNA_def_color(struct BlenderRNA *brna); @@ -332,6 +333,7 @@ void RNA_def_main_gpencil(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_movieclips(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_masks(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_linestyles(BlenderRNA *brna, PropertyRNA *cprop); +void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop); /* ID Properties */ diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c index e0538d1c05b..3392d0d9b3a 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -281,6 +281,12 @@ static void rna_Main_linestyle_begin(CollectionPropertyIterator *iter, PointerRN rna_iterator_listbase_begin(iter, &bmain->linestyle, NULL); } +static void rna_Main_cachefiles_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + Main *bmain = (Main *)ptr->data; + rna_iterator_listbase_begin(iter, &bmain->cachefiles, NULL); +} + static void rna_Main_version_get(PointerRNA *ptr, int *value) { Main *bmain = (Main *)ptr->data; @@ -354,6 +360,7 @@ void RNA_def_main(BlenderRNA *brna) {"movieclips", "MovieClip", "rna_Main_movieclips_begin", "Movie Clips", "Movie Clip datablocks", RNA_def_main_movieclips}, {"masks", "Mask", "rna_Main_masks_begin", "Masks", "Masks datablocks", RNA_def_main_masks}, {"linestyles", "FreestyleLineStyle", "rna_Main_linestyle_begin", "Line Styles", "Line Style datablocks", RNA_def_main_linestyles}, + {"cache_files", "CacheFile", "rna_Main_cachefiles_begin", "Cache Files", "Cache Files datablocks", RNA_def_main_cachefiles}, {NULL, NULL, NULL, NULL, NULL, NULL} }; diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 0083207efd0..7c627e72fd5 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -535,6 +535,7 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(gpencil, gpencil, ID_GD) RNA_MAIN_ID_TAG_FUNCS_DEF(movieclips, movieclip, ID_MC) RNA_MAIN_ID_TAG_FUNCS_DEF(masks, mask, ID_MSK) RNA_MAIN_ID_TAG_FUNCS_DEF(linestyle, linestyle, ID_LS) +RNA_MAIN_ID_TAG_FUNCS_DEF(cachefiles, cachefiles, ID_CF) #undef RNA_MAIN_ID_TAG_FUNCS_DEF @@ -1548,6 +1549,21 @@ void RNA_def_main_palettes(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_boolean_funcs(prop, "rna_Main_palettes_is_updated_get", NULL); } +void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop) +{ + RNA_def_property_srna(cprop, "BlendDataCacheFiles"); + StructRNA *srna = RNA_def_struct(brna, "BlendDataCacheFiles", NULL); + RNA_def_struct_sdna(srna, "Main"); + RNA_def_struct_ui_text(srna, "Main Cache Files", "Collection of cache files"); + + FunctionRNA *func = RNA_def_function(srna, "tag", "rna_Main_cachefiles_tag"); + PropertyRNA *parm = RNA_def_boolean(func, "value", 0, "Value", ""); + RNA_def_property_flag(parm, PROP_REQUIRED); + + PropertyRNA *prop = RNA_def_property(srna, "is_updated", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_boolean_funcs(prop, "rna_Main_cachefiles_is_updated_get", NULL); +} void RNA_def_main_gpencil(BlenderRNA *brna, PropertyRNA *cprop) { StructRNA *srna; @@ -1564,7 +1580,7 @@ void RNA_def_main_gpencil(BlenderRNA *brna, PropertyRNA *cprop) parm = RNA_def_boolean(func, "value", 0, "Value", ""); RNA_def_property_flag(parm, PROP_REQUIRED); - func = RNA_def_function(srna, "new", "gpencil_data_addnew"); + func = RNA_def_function(srna, "new", "BKE_gpencil_data_addnew"); RNA_def_function_flag(func, FUNC_NO_SELF); parm = RNA_def_string(func, "name", "GreasePencil", 0, "", "New name for the data-block"); RNA_def_property_flag(parm, PROP_REQUIRED); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index a23ef6eaa82..0b55c19c374 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -30,6 +30,7 @@ #include <stdlib.h> #include "DNA_armature_types.h" +#include "DNA_cachefile_types.h" #include "DNA_mesh_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" @@ -65,6 +66,7 @@ EnumPropertyItem rna_enum_object_modifier_type_items[] = { {0, "", 0, N_("Modify"), ""}, {eModifierType_DataTransfer, "DATA_TRANSFER", ICON_MOD_DATA_TRANSFER, "Data Transfer", ""}, {eModifierType_MeshCache, "MESH_CACHE", ICON_MOD_MESHDEFORM, "Mesh Cache", ""}, + {eModifierType_MeshSequenceCache, "MESH_SEQUENCE_CACHE", ICON_MOD_MESHDEFORM, "Mesh Sequence Cache", ""}, {eModifierType_NormalEdit, "NORMAL_EDIT", ICON_MOD_NORMALEDIT, "Normal Edit", ""}, {eModifierType_UVProject, "UV_PROJECT", ICON_MOD_UVPROJECT, "UV Project", ""}, {eModifierType_UVWarp, "UV_WARP", ICON_MOD_UVPROJECT, "UV Warp", ""}, @@ -281,6 +283,7 @@ EnumPropertyItem rna_enum_axis_flag_xyz_items[] = { #include "DNA_curve_types.h" #include "DNA_smoke_types.h" +#include "BKE_cachefile.h" #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_library.h" @@ -288,6 +291,10 @@ EnumPropertyItem rna_enum_axis_flag_xyz_items[] = { #include "BKE_object.h" #include "BKE_particle.h" +#ifdef WITH_ALEMBIC +# include "ABC_alembic.h" +#endif + static void rna_UVProject_projectors_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { UVProjectModifierData *uvp = (UVProjectModifierData *)ptr->data; @@ -399,6 +406,8 @@ static StructRNA *rna_Modifier_refine(struct PointerRNA *ptr) return &RNA_NormalEditModifier; case eModifierType_CorrectiveSmooth: return &RNA_CorrectiveSmoothModifier; + case eModifierType_MeshSequenceCache: + return &RNA_MeshSequenceCacheModifier; /* Default */ case eModifierType_None: case eModifierType_ShapeKey: @@ -4216,6 +4225,42 @@ static void rna_def_modifier_meshcache(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Modifier_update"); } +static void rna_def_modifier_meshseqcache(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "MeshSequenceCacheModifier", "Modifier"); + RNA_def_struct_ui_text(srna, "Cache Modifier", "Cache Mesh"); + RNA_def_struct_sdna(srna, "MeshSeqCacheModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ + + prop = RNA_def_property(srna, "cache_file", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "cache_file"); + RNA_def_property_struct_type(prop, "CacheFile"); + RNA_def_property_ui_text(prop, "Cache File", ""); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "object_path", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Object Path", "Path to the object in the Alembic archive used to lookup geometric data"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + static EnumPropertyItem read_flag_items[] = { + {MOD_MESHSEQ_READ_VERT, "VERT", 0, "Vertex", ""}, + {MOD_MESHSEQ_READ_POLY, "POLY", 0, "Faces", ""}, + {MOD_MESHSEQ_READ_UV, "UV", 0, "UV", ""}, + {MOD_MESHSEQ_READ_COLOR, "COLOR", 0, "Color", ""}, + {0, NULL, 0, NULL, NULL} + }; + + prop = RNA_def_property(srna, "read_data", PROP_ENUM, PROP_NONE); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_property_enum_sdna(prop, NULL, "read_flag"); + RNA_def_property_enum_items(prop, read_flag_items); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); +} + static void rna_def_modifier_laplaciandeform(BlenderRNA *brna) { StructRNA *srna; @@ -4745,6 +4790,7 @@ void RNA_def_modifier(BlenderRNA *brna) rna_def_modifier_wireframe(brna); rna_def_modifier_datatransfer(brna); rna_def_modifier_normaledit(brna); + rna_def_modifier_meshseqcache(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index f05813043ca..1d89f7535c4 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -730,6 +730,11 @@ static void rna_softbody_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Point WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob); } +static void rna_softbody_dependency_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + DAG_relations_tag_update(bmain); + rna_softbody_update(bmain, scene, ptr); +} static EnumPropertyItem *rna_Effector_shape_itemf(bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *UNUSED(r_free)) @@ -1378,7 +1383,7 @@ static void rna_def_field(BlenderRNA *brna) prop = RNA_def_property(srna, "use_absorption", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PFIELD_VISIBILITY); RNA_def_property_ui_text(prop, "Absorption", "Force gets absorbed by collision objects"); - RNA_def_property_update(prop, 0, "rna_FieldSettings_update"); + RNA_def_property_update(prop, 0, "rna_FieldSettings_dependency_update"); prop = RNA_def_property(srna, "use_multiple_springs", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PFIELD_MULTIPLE_SPRINGS); @@ -1855,7 +1860,7 @@ static void rna_def_softbody(BlenderRNA *brna) prop = RNA_def_property(srna, "collision_group", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Collision Group", "Limit colliders to this Group"); - RNA_def_property_update(prop, 0, "rna_softbody_update"); + RNA_def_property_update(prop, 0, "rna_softbody_dependency_update"); prop = RNA_def_property(srna, "effector_weights", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "effector_weights"); diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index eabf41cb332..5e3fa4b467d 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -630,6 +630,12 @@ static void rna_Particle_reset(Main *bmain, Scene *scene, PointerRNA *ptr) particle_recalc(bmain, scene, ptr, PSYS_RECALC_RESET); } +static void rna_Particle_reset_dependency(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + DAG_relations_tag_update(bmain); + rna_Particle_reset(bmain, scene, ptr); +} + static void rna_Particle_change_type(Main *bmain, Scene *scene, PointerRNA *ptr) { particle_recalc(bmain, scene, ptr, PSYS_RECALC_RESET | PSYS_RECALC_TYPE); @@ -2744,7 +2750,7 @@ static void rna_def_particle_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "collision_group", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Collision Group", "Limit colliders to this Group"); - RNA_def_property_update(prop, 0, "rna_Particle_reset"); + RNA_def_property_update(prop, 0, "rna_Particle_reset_dependency"); /* global physical properties */ prop = RNA_def_property(srna, "drag_factor", PROP_FLOAT, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index ed90f146f4d..156c327f97c 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -35,6 +35,7 @@ #include "DNA_linestyle_types.h" #include "DNA_userdef_types.h" #include "DNA_world_types.h" +#include "DNA_gpencil_types.h" #include "IMB_imbuf_types.h" @@ -437,6 +438,7 @@ EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = { #include "BKE_sequencer.h" #include "BKE_animsys.h" #include "BKE_freestyle.h" +#include "BKE_gpencil.h" #include "ED_info.h" #include "ED_node.h" @@ -449,6 +451,108 @@ EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = { #include "FRS_freestyle.h" #endif +/* Grease pencil Drawing Brushes */ +static bGPDbrush *rna_GPencil_brush_new(ToolSettings *ts, const char *name, int setactive) +{ + bGPDbrush *brush = BKE_gpencil_brush_addnew(ts, name, setactive != 0); + + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return brush; +} + +static void rna_GPencil_brush_remove(ToolSettings *ts, ReportList *reports, PointerRNA *brush_ptr) +{ + bGPDbrush *brush = brush_ptr->data; + if (BLI_findindex(&ts->gp_brushes, brush) == -1) { + BKE_report(reports, RPT_ERROR, "Brush not found in grease pencil data"); + return; + } + + BKE_gpencil_brush_delete(ts, brush); + RNA_POINTER_INVALIDATE(brush_ptr); + + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); +} + +static PointerRNA rna_GPencilBrushes_active_get(PointerRNA *ptr) +{ + ToolSettings *ts = (ToolSettings *) ptr->data; + + bGPDbrush *brush; + + for (brush = ts->gp_brushes.first; brush; brush = brush->next) { + if (brush->flag & GP_BRUSH_ACTIVE) { + break; + } + } + + if (brush) { + return rna_pointer_inherit_refine(ptr, &RNA_GPencilBrush, brush); + } + + return rna_pointer_inherit_refine(ptr, NULL, NULL); +} + +static void rna_GPencilBrushes_active_set(PointerRNA *ptr, PointerRNA value) +{ + ToolSettings *ts = (ToolSettings *) ptr->data; + + bGPDbrush *brush; + + for (brush = ts->gp_brushes.first; brush; brush = brush->next) { + if (brush == value.data) { + brush->flag |= GP_BRUSH_ACTIVE; + } + else { + brush->flag &= ~GP_BRUSH_ACTIVE; + } + } + WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); +} + +static int rna_GPencilBrushes_index_get(PointerRNA *ptr) +{ + ToolSettings *ts = (ToolSettings *) ptr->data; + bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); + + return BLI_findindex(&ts->gp_brushes, brush); +} + +static void rna_GPencilBrushes_index_set(PointerRNA *ptr, int value) +{ + ToolSettings *ts = (ToolSettings *) ptr->data; + + bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, value); + + BKE_gpencil_brush_setactive(ts, brush); + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); +} + +static void rna_GPencilBrushes_index_range(PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) +{ + ToolSettings *ts = (ToolSettings *) ptr->data; + + *min = 0; + *max = max_ii(0, BLI_listbase_count(&ts->gp_brushes) - 1); + + *softmin = *min; + *softmax = *max; +} + +static void rna_GPencilBrush_name_set(PointerRNA *ptr, const char *value) +{ + ToolSettings *ts = ((Scene *) ptr->id.data)->toolsettings; + bGPDbrush *brush = ptr->data; + + /* copy the new name into the name slot */ + BLI_strncpy_utf8(brush->info, value, sizeof(brush->info)); + + BLI_uniquename(&ts->gp_brushes, brush, DATA_("GP_Brush"), '.', offsetof(bGPDbrush, info), sizeof(brush->info)); +} + +/* ----------------- end of Grease pencil drawing brushes ------------*/ + static void rna_SpaceImageEditor_uv_sculpt_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr)) { ED_space_image_uv_sculpt_update(bmain->wm.first, scene); @@ -1991,6 +2095,203 @@ static int rna_gpu_is_hq_supported_get(PointerRNA *UNUSED(ptr)) #else +/* Grease Pencil Drawing Brushes */ +static void rna_def_gpencil_brush(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GPencilBrush", NULL); + RNA_def_struct_sdna(srna, "bGPDbrush"); + RNA_def_struct_ui_text(srna, "Grease Pencil Brush", + "Collection of brushes being used to control the line style of new strokes"); + RNA_def_struct_ui_icon(srna, ICON_BRUSH_DATA); + + /* Name */ + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "info"); + RNA_def_property_ui_text(prop, "Name", "Brush name"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_GPencilBrush_name_set"); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Line Thickness */ + prop = RNA_def_property(srna, "line_width", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "thickness"); + RNA_def_property_range(prop, 1, 300); + RNA_def_property_ui_range(prop, 1, 10, 1, 0); + RNA_def_property_ui_text(prop, "Thickness", "Thickness of strokes (in pixels)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Sensitivity factor for new strokes */ + prop = RNA_def_property(srna, "pen_sensitivity_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_sensitivity"); + RNA_def_property_range(prop, 0.1f, 3.0f); + RNA_def_property_ui_text(prop, "Sensitivity", "Pressure sensitivity factor for new strokes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Strength factor for new strokes */ + prop = RNA_def_property(srna, "strength", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_strength"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Strength", "Color strength for new strokes (affect alpha factor of color)"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Jitter factor for new strokes */ + prop = RNA_def_property(srna, "jitter", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_jitter"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Jitter", "Jitter factor for new strokes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Randomnes factor for sensitivity and strength */ + prop = RNA_def_property(srna, "random_press", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_random_press"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Randomness", "Randomness factor for pressure and strength in new strokes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Randomnes factor for subdivision */ + prop = RNA_def_property(srna, "random_subdiv", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_random_sub"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Random Subdivision", "Randomness factor for new strokes after subdivision"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Angle when brush is full size */ + prop = RNA_def_property(srna, "angle", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "draw_angle"); + RNA_def_property_range(prop, 0.0f, M_PI_2); + RNA_def_property_ui_text(prop, "Angle", "Angle of drawing when brush has full size"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Factor to change brush size depending of angle */ + prop = RNA_def_property(srna, "angle_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_angle_factor"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Angle Factor", "Factor to apply when the brush rotate of its full size"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Smoothing factor for new strokes */ + prop = RNA_def_property(srna, "pen_smooth_factor", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "draw_smoothfac"); + RNA_def_property_range(prop, 0.0, 2.0f); + RNA_def_property_ui_text(prop, "Smooth", "Amount of smoothing to apply to newly created strokes, to " + "reduce jitter/noise"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Iterations of the Smoothing factor */ + prop = RNA_def_property(srna, "pen_smooth_steps", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "draw_smoothlvl"); + RNA_def_property_range(prop, 1, 3); + RNA_def_property_ui_text(prop, "Iterations", + "Number of times to smooth newly created strokes " + "[+ reason/effect of using higher values of this property]"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Subdivision level for new strokes */ + prop = RNA_def_property(srna, "pen_subdivision_steps", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "sublevel"); + RNA_def_property_range(prop, 0, 3); + RNA_def_property_ui_text(prop, "Subdivision Steps", + "Number of times to subdivide newly created strokes, for less jagged strokes"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Curves for pressure */ + prop = RNA_def_property(srna, "curve_sensitivity", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "cur_sensitivity"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Curve Sensitivity", "Curve used for the sensitivity"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "curve_strength", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "cur_strength"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Curve Strength", "Curve used for the strength"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "curve_jitter", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "cur_jitter"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Curve Jitter", "Curve used for the jitter effect"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + /* Flags */ + prop = RNA_def_property(srna, "use_pressure", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_PRESSURE); + RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); + RNA_def_property_ui_text(prop, "Use Pressure", "Use tablet pressure"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "use_strength_pressure", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_STENGTH_PRESSURE); + RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); + RNA_def_property_ui_text(prop, "Use Pressure Strength", "Use tablet pressure for color strength"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "use_jitter_pressure", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_JITTER_PRESSURE); + RNA_def_property_ui_icon(prop, ICON_STYLUS_PRESSURE, 0); + RNA_def_property_ui_text(prop, "Use Pressure Jitter", "Use tablet pressure for jitter"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "use_random_pressure", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_RANDOM_PRESSURE); + RNA_def_property_ui_icon(prop, ICON_PARTICLES, 0); + RNA_def_property_ui_text(prop, "Random Pressure", "Use random value for pressure"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + + prop = RNA_def_property(srna, "use_random_strength", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_RANDOM_STRENGTH); + RNA_def_property_ui_icon(prop, ICON_PARTICLES, 0); + RNA_def_property_ui_text(prop, "Random Strength", "Use random value for strength"); + RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL); + +} + +/* Grease Pencil Drawing Brushes API */ +static void rna_def_gpencil_brushes(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + PropertyRNA *prop; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "GreasePencilBrushes"); + srna = RNA_def_struct(brna, "GreasePencilBrushes", NULL); + RNA_def_struct_sdna(srna, "ToolSettings"); + RNA_def_struct_ui_text(srna, "Grease Pencil Brushes", "Collection of grease pencil brushes"); + + func = RNA_def_function(srna, "new", "rna_GPencil_brush_new"); + RNA_def_function_ui_description(func, "Add a new grease pencil brush"); + parm = RNA_def_string(func, "name", "GPencilBrush", MAX_NAME, "Name", "Name of the brush"); + RNA_def_property_flag(parm, PROP_REQUIRED); + RNA_def_boolean(func, "set_active", 0, "Set Active", "Set the newly created brush to the active brush"); + parm = RNA_def_pointer(func, "palette", "GPencilBrush", "", "The newly created brush"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_GPencil_brush_remove"); + RNA_def_function_ui_description(func, "Remove a grease pencil brush"); + RNA_def_function_flag(func, FUNC_USE_REPORTS); + parm = RNA_def_pointer(func, "brush", "GPencilBrush", "", "The brush to remove"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR); + RNA_def_property_clear_flag(parm, PROP_THICK_WRAP); + + prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "GPencilBrush"); + RNA_def_property_pointer_funcs(prop, "rna_GPencilBrushes_active_get", "rna_GPencilBrushes_active_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Active Brush", "Current active brush"); + + prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_funcs(prop, + "rna_GPencilBrushes_index_get", + "rna_GPencilBrushes_index_set", + "rna_GPencilBrushes_index_range"); + RNA_def_property_ui_text(prop, "Active brush Index", "Index of active brush"); +} + static void rna_def_transform_orientation(BlenderRNA *brna) { StructRNA *srna; @@ -2311,6 +2612,12 @@ static void rna_def_tool_settings(BlenderRNA *brna) "are included as the basis for the new one"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "use_gpencil_draw_onback", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "gpencil_flags", GP_TOOL_FLAG_PAINT_ONBACK); + RNA_def_property_ui_text(prop, "Draw Strokes on Back", + "When draw new strokes, the new stroke is drawn below of all strokes in the layer"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "grease_pencil_source", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_src"); RNA_def_property_enum_items(prop, gpencil_source_3d_items); @@ -2322,7 +2629,14 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "gp_sculpt"); RNA_def_property_struct_type(prop, "GPencilSculptSettings"); RNA_def_property_ui_text(prop, "Grease Pencil Sculpt", ""); - + + /* Grease Pencil - Drawing brushes */ + prop = RNA_def_property(srna, "gpencil_brushes", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "gp_brushes", NULL); + RNA_def_property_struct_type(prop, "GPencilBrush"); + RNA_def_property_ui_text(prop, "Grease Pencil Brushes", "Grease Pencil drawing brushes"); + rna_def_gpencil_brushes(brna, prop); + /* Grease Pencil - 3D View Stroke Placement */ prop = RNA_def_property(srna, "gpencil_stroke_placement_view3d", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "gpencil_v3d_align"); @@ -6839,6 +7153,7 @@ void RNA_def_scene(BlenderRNA *brna) /* *** Non-Animated *** */ RNA_define_animate_sdna(false); rna_def_tool_settings(brna); + rna_def_gpencil_brush(brna); rna_def_unified_paint_settings(brna); rna_def_curve_paint_settings(brna); rna_def_statvis(brna); diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index 1d3537dc9a0..ed51f0cffb0 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -37,6 +37,7 @@ #include "BLI_path_util.h" #include "RNA_define.h" +#include "RNA_enum_types.h" #include "DNA_anim_types.h" #include "DNA_object_types.h" @@ -44,6 +45,18 @@ #include "rna_internal.h" /* own include */ +#ifdef WITH_ALEMBIC +# include "../../alembic/ABC_alembic.h" +#endif + +EnumPropertyItem rna_enum_abc_compression_items[] = { +#ifdef WITH_ALEMBIC + { ABC_ARCHIVE_OGAWA, "OGAWA", 0, "Ogawa", "" }, + { ABC_ARCHIVE_HDF5, "HDF5", 0, "HDF5", "" }, +#endif + { 0, NULL, 0, NULL, NULL } +}; + #ifdef RNA_RUNTIME #include "BKE_animsys.h" @@ -173,6 +186,73 @@ static void rna_Scene_ray_cast( } } +#ifdef WITH_ALEMBIC + +static void rna_Scene_alembic_export( + Scene *scene, + bContext *C, + const char *filepath, + int frame_start, + int frame_end, + int xform_samples, + int geom_samples, + float shutter_open, + float shutter_close, + int selected_only, + int uvs, + int normals, + int vcolors, + int apply_subdiv, + int flatten_hierarchy, + int visible_layers_only, + int renderable_only, + int face_sets, + int use_subdiv_schema, + int compression_type, + int packuv, + float scale) +{ +/* We have to enable allow_threads, because we may change scene frame number + * during export. */ +#ifdef WITH_PYTHON + BPy_BEGIN_ALLOW_THREADS; +#endif + + const struct AlembicExportParams params = { + .frame_start = frame_start, + .frame_end = frame_end, + + .frame_step_xform = 1.0 / (double)xform_samples, + .frame_step_shape = 1.0 / (double)geom_samples, + + .shutter_open = shutter_open, + .shutter_close = shutter_close, + + .selected_only = selected_only, + .uvs = uvs, + .normals = normals, + .vcolors = vcolors, + .apply_subdiv = apply_subdiv, + .flatten_hierarchy = flatten_hierarchy, + .visible_layers_only = visible_layers_only, + .renderable_only = renderable_only, + .face_sets = face_sets, + .use_subdiv_schema = use_subdiv_schema, + .compression_type = compression_type, + .packuv = packuv, + + .global_scale = scale, + }; + + ABC_export(scene, C, filepath, ¶ms); + +#ifdef WITH_PYTHON + BPy_END_ALLOW_THREADS; +#endif +} + +#endif + #ifdef WITH_COLLADA /* don't remove this, as COLLADA exporting cannot be done through operators in render() callback. */ #include "../../collada/collada.h" @@ -296,6 +376,37 @@ void RNA_api_scene(StructRNA *srna) RNA_def_function_ui_description(func, "Export to collada file"); #endif + +#ifdef WITH_ALEMBIC + func = RNA_def_function(srna, "alembic_export", "rna_Scene_alembic_export"); + RNA_def_function_ui_description(func, "Export to Alembic file"); + + parm = RNA_def_string(func, "filepath", NULL, FILE_MAX, "File Path", "File path to write Alembic file"); + RNA_def_property_flag(parm, PROP_REQUIRED); + RNA_def_property_subtype(parm, PROP_FILEPATH); /* allow non utf8 */ + + RNA_def_int(func, "frame_start", 1, INT_MIN, INT_MAX, "Start", "Start Frame", INT_MIN, INT_MAX); + RNA_def_int(func, "frame_end", 1, INT_MIN, INT_MAX, "End", "End Frame", INT_MIN, INT_MAX); + RNA_def_int(func, "xform_samples", 1, 1, 128, "Xform samples", "Transform samples per frame", 1, 128); + RNA_def_int(func, "geom_samples", 1, 1, 128, "Geom samples", "Geometry samples per frame", 1, 128); + RNA_def_float(func, "shutter_open", 0.0f, -1.0f, 1.0f, "Shutter open", "", -1.0f, 1.0f); + RNA_def_float(func, "shutter_close", 1.0f, -1.0f, 1.0f, "Shutter close", "", -1.0f, 1.0f); + RNA_def_boolean(func, "selected_only" , 0, "Selected only", "Export only selected objects"); + RNA_def_boolean(func, "uvs" , 1, "UVs", "Export UVs"); + RNA_def_boolean(func, "normals" , 1, "Normals", "Export cormals"); + RNA_def_boolean(func, "vcolors" , 0, "Vertex colors", "Export vertex colors"); + RNA_def_boolean(func, "apply_subdiv" , 1, "Subsurfs as meshes", "Export subdivision surfaces as meshes"); + RNA_def_boolean(func, "flatten" , 0, "Flatten hierarchy", "Flatten hierarchy"); + RNA_def_boolean(func, "visible_layers_only" , 0, "Visible layers only", "Export only objects in visible layers"); + RNA_def_boolean(func, "renderable_only" , 0, "Renderable objects only", "Export only objects marked renderable in the outliner"); + RNA_def_boolean(func, "face_sets" , 0, "Facesets", "Export face sets"); + RNA_def_boolean(func, "subdiv_schema", 0, "Use Alembic subdivision Schema", "Use Alembic subdivision Schema"); + RNA_def_enum(func, "compression_type", rna_enum_abc_compression_items, 0, "Compression", ""); + RNA_def_boolean(func, "packuv" , 0, "Export with packed UV islands", "Export with packed UV islands"); + RNA_def_float(func, "scale", 1.0f, 0.0001f, 1000.0f, "Scale", "Value by which to enlarge or shrink the objects with respect to the world's origin", 0.0001f, 1000.0f); + + RNA_def_function_flag(func, FUNC_USE_CONTEXT); +#endif } diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 90215bc883f..acde2e0957e 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -63,7 +63,8 @@ static EnumPropertyItem particle_edit_hair_brush_items[] = { EnumPropertyItem rna_enum_gpencil_sculpt_brush_items[] = { {GP_EDITBRUSH_TYPE_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth stroke points"}, {GP_EDITBRUSH_TYPE_THICKNESS, "THICKNESS", 0, "Thickness", "Adjust thickness of strokes"}, - {GP_EDITBRUSH_TYPE_GRAB, "GRAB", 0, "Grab", "Translate the set of points initially within the brush circle"}, + { GP_EDITBRUSH_TYPE_STRENGTH, "STRENGTH", 0, "Strength", "Adjust color strength of strokes" }, + { GP_EDITBRUSH_TYPE_GRAB, "GRAB", 0, "Grab", "Translate the set of points initially within the brush circle" }, {GP_EDITBRUSH_TYPE_PUSH, "PUSH", 0, "Push", "Move points out of the way, as if combing them"}, {GP_EDITBRUSH_TYPE_TWIST, "TWIST", 0, "Twist", "Rotate points around the midpoint of the brush"}, {GP_EDITBRUSH_TYPE_PINCH, "PINCH", 0, "Pinch", "Pull points towards the midpoint of the brush"}, @@ -71,7 +72,7 @@ EnumPropertyItem rna_enum_gpencil_sculpt_brush_items[] = { //{GP_EDITBRUSH_TYPE_SUBDIVIDE, "SUBDIVIDE", 0, "Subdivide", "Increase point density for higher resolution strokes when zoomed in"}, //{GP_EDITBRUSH_TYPE_SIMPLIFY, "SIMPLIFY", 0, "Simplify", "Reduce density of stroke points"}, {GP_EDITBRUSH_TYPE_CLONE, "CLONE", 0, "Clone", "Paste copies of the strokes stored on the clipboard"}, - {0, NULL, 0, NULL, NULL} + { 0, NULL, 0, NULL, NULL } }; EnumPropertyItem rna_enum_symmetrize_direction_items[] = { @@ -100,6 +101,11 @@ EnumPropertyItem rna_enum_symmetrize_direction_items[] = { #include "ED_particle.h" +static void rna_GPencil_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *UNUSED(ptr)) +{ + WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); +} + static EnumPropertyItem particle_edit_disconnected_hair_brush_items[] = { {PE_BRUSH_NONE, "NONE", 0, "None", "Don't use any brush"}, {PE_BRUSH_COMB, "COMB", 0, "Comb", "Comb hairs"}, @@ -1016,6 +1022,27 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna) RNA_def_property_ui_icon(prop, ICON_VERTEXSEL, 0); // FIXME: this needs a custom icon RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "affect_position", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_APPLY_POSITION); + RNA_def_property_ui_text(prop, "Affect position", "The brush affects the position of the point"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "affect_strength", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_APPLY_STRENGTH); + RNA_def_property_ui_text(prop, "Affect strength", "The brush affects the color strength of the point"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "affect_thickness", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_APPLY_THICKNESS); + RNA_def_property_ui_text(prop, "Affect thickness", "The brush affects the thickness of the point"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "selection_alpha", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "alpha"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Alpha", "Alpha value for selected vertices"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_GPencil_update"); + /* brush */ srna = RNA_def_struct(brna, "GPencilSculptBrush", NULL); RNA_def_struct_sdna(srna, "GP_EditBrush_Data"); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 4818fe0cd35..1c8d4a4c818 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -3355,6 +3355,7 @@ static void rna_def_space_dopesheet(BlenderRNA *brna) {SACTCONT_SHAPEKEY, "SHAPEKEY", ICON_SHAPEKEY_DATA, "Shape Key Editor", "Edit keyframes in active object's Shape Keys action"}, {SACTCONT_GPENCIL, "GPENCIL", ICON_GREASEPENCIL, "Grease Pencil", "Edit timings for all Grease Pencil sketches in file"}, {SACTCONT_MASK, "MASK", ICON_MOD_MASK, "Mask", "Edit timings for Mask Editor splines"}, + {SACTCONT_CACHEFILE, "CACHEFILE", ICON_FILE, "Cache File", "Edit timings for Cache File data-blocks"}, {0, NULL, 0, NULL, NULL} }; @@ -3799,6 +3800,7 @@ static void rna_def_fileselect_params(BlenderRNA *brna) {FILTER_ID_AR, "ARMATURE", ICON_ARMATURE_DATA, "Armatures", "Show/hide Armature data-blocks"}, {FILTER_ID_BR, "BRUSH", ICON_BRUSH_DATA, "Brushes", "Show/hide Brushes data-blocks"}, {FILTER_ID_CA, "CAMERA", ICON_CAMERA_DATA, "Cameras", "Show/hide Camera data-blocks"}, + {FILTER_ID_CF, "CACHEFILE", ICON_FILE, "Cache Files", "Show/hide Cache File data-blocks"}, {FILTER_ID_CU, "CURVE", ICON_CURVE_DATA, "Curves", "Show/hide Curve data-blocks"}, {FILTER_ID_GD, "GREASE_PENCIL", ICON_GREASEPENCIL, "Grease Pencil", "Show/hide Grease pencil data-blocks"}, {FILTER_ID_GR, "GROUP", ICON_GROUP, "Groups", "Show/hide Group data-blocks"}, @@ -3844,7 +3846,7 @@ static void rna_def_fileselect_params(BlenderRNA *brna) "IMAGE", ICON_IMAGE_DATA, "Images & Sounds", "Show/hide images, movie clips, sounds and masks"}, {FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_SPK | FILTER_ID_WO, "ENVIRONMENT", ICON_WORLD_DATA, "Environment", "Show/hide worlds, lamps, cameras and speakers"}, - {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | FILTER_ID_VF, + {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_CF, "MISC", ICON_GREASEPENCIL, "Miscellaneous", "Show/hide other data types"}, {0, NULL, 0, NULL, NULL} }; diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c index 2564bdb800f..0591c65d484 100644 --- a/source/blender/makesrna/intern/rna_tracking.c +++ b/source/blender/makesrna/intern/rna_tracking.c @@ -394,6 +394,16 @@ static int rna_track_2d_stabilization(CollectionPropertyIterator *UNUSED(iter), return 0; } +static int rna_track_2d_stabilization_rotation(CollectionPropertyIterator *UNUSED(iter), void *data) +{ + MovieTrackingTrack *track = (MovieTrackingTrack *)data; + + if ((track->flag & TRACK_USE_2D_STAB_ROT) == 0) + return 1; + + return 0; +} + static void rna_tracking_stabTracks_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { MovieClip *clip = (MovieClip *)ptr->id.data; @@ -421,23 +431,36 @@ static void rna_tracking_stabTracks_active_index_range(PointerRNA *ptr, int *min *max = max_ii(0, clip->tracking.stabilization.tot_track - 1); } -static void rna_tracking_resetIntrinsics(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_tracking_stabRotTracks_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { MovieClip *clip = (MovieClip *)ptr->id.data; - MovieTracking *tracking = &clip->tracking; + rna_iterator_listbase_begin(iter, &clip->tracking.tracks, rna_track_2d_stabilization_rotation); +} - if (tracking->camera.intrinsics) { - BKE_tracking_distortion_free(tracking->camera.intrinsics); - tracking->camera.intrinsics = NULL; - } +static int rna_tracking_stabRotTracks_active_index_get(PointerRNA *ptr) +{ + MovieClip *clip = (MovieClip *)ptr->id.data; + return clip->tracking.stabilization.act_rot_track; } -static void rna_tracking_flushUpdate(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) +static void rna_tracking_stabRotTracks_active_index_set(PointerRNA *ptr, int value) +{ + MovieClip *clip = (MovieClip *)ptr->id.data; + clip->tracking.stabilization.act_rot_track = value; +} + +static void rna_tracking_stabRotTracks_active_index_range(PointerRNA *ptr, int *min, int *max, + int *UNUSED(softmin), int *UNUSED(softmax)) { MovieClip *clip = (MovieClip *)ptr->id.data; - MovieTrackingStabilization *stab = &clip->tracking.stabilization; - stab->ok = 0; + *min = 0; + *max = max_ii(0, clip->tracking.stabilization.tot_rot_track - 1); +} + +static void rna_tracking_flushUpdate(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) +{ + MovieClip *clip = (MovieClip *)ptr->id.data; nodeUpdateID(scene->nodetree, &clip->id); @@ -446,6 +469,17 @@ static void rna_tracking_flushUpdate(Main *UNUSED(bmain), Scene *scene, PointerR DAG_id_tag_update(&clip->id, 0); } +static void rna_tracking_resetIntrinsics(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + MovieClip *clip = (MovieClip *)ptr->id.data; + MovieTracking *tracking = &clip->tracking; + + if (tracking->camera.intrinsics) { + BKE_tracking_distortion_free(tracking->camera.intrinsics); + tracking->camera.intrinsics = NULL; + } +} + static void rna_trackingObject_tracks_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { MovieTrackingObject *object = (MovieTrackingObject *)ptr->data; @@ -1495,6 +1529,12 @@ static void rna_def_trackingTrack(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "Weight", "Influence of this track on a final solution"); + /* weight_stab */ + prop = RNA_def_property(srna, "weight_stab", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "weight_stab"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Stab Weight", "Influence of this track on 2D stabilization"); + /* offset */ prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_TRANSLATION); RNA_def_property_array(prop, 2); @@ -1634,15 +1674,15 @@ static void rna_def_trackingStabilization(BlenderRNA *brna) PropertyRNA *prop; static EnumPropertyItem filter_items[] = { - {TRACKING_FILTER_NEAREST, "NEAREST", 0, "Nearest", ""}, - {TRACKING_FILTER_BILINEAR, "BILINEAR", 0, "Bilinear", ""}, - {TRACKING_FILTER_BICUBIC, "BICUBIC", 0, "Bicubic", ""}, + {TRACKING_FILTER_NEAREST, "NEAREST", 0, "Nearest", "No interpolation; use nearest neighbor pixel"}, + {TRACKING_FILTER_BILINEAR, "BILINEAR", 0, "Bilinear", "Simple interpolation between adjacent pixels"}, + {TRACKING_FILTER_BICUBIC, "BICUBIC", 0, "Bicubic", "High quality pixel interpolation"}, {0, NULL, 0, NULL, NULL} }; srna = RNA_def_struct(brna, "MovieTrackingStabilization", NULL); RNA_def_struct_path_func(srna, "rna_trackingStabilization_path"); - RNA_def_struct_ui_text(srna, "Movie tracking stabilization data", "Match-moving stabilization data for tracking"); + RNA_def_struct_ui_text(srna, "Movie tracking stabilization data", "2D stabilization based on tracking markers"); /* 2d stabilization */ prop = RNA_def_property(srna, "use_2d_stabilization", PROP_BOOLEAN, PROP_NONE); @@ -1651,22 +1691,29 @@ static void rna_def_trackingStabilization(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Use 2D stabilization", "Use 2D stabilization for footage"); RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); + /* use_stabilize_rotation */ + prop = RNA_def_property(srna, "use_stabilize_rotation", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", TRACKING_STABILIZE_ROTATION); + RNA_def_property_ui_text(prop, "Stabilize Rotation", "Stabilize detected rotation around center of frame"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); + + /* use_stabilize_scale */ + prop = RNA_def_property(srna, "use_stabilize_scale", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", TRACKING_STABILIZE_SCALE); + RNA_def_property_ui_text(prop, "Stabilize Scale", "Compensate any scale changes relative to center of rotation"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); + /* tracks */ prop = RNA_def_property(srna, "tracks", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_funcs(prop, "rna_tracking_stabTracks_begin", "rna_iterator_listbase_next", "rna_iterator_listbase_end", "rna_iterator_listbase_get", NULL, NULL, NULL, NULL); RNA_def_property_struct_type(prop, "MovieTrackingTrack"); - RNA_def_property_ui_text(prop, "Tracks", "Collection of tracks used for stabilization"); + RNA_def_property_ui_text(prop, "Translation Tracks", "Collection of tracks used for 2D stabilization (translation)"); RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); - /* rotation track */ - prop = RNA_def_property(srna, "rotation_track", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "rot_track"); - RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Rotation Track", "Track used to compensate rotation"); - RNA_def_property_update(prop, NC_MOVIECLIP | NA_EDITED, "rna_tracking_flushUpdate"); - /* active track index */ prop = RNA_def_property(srna, "active_track_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "act_track"); @@ -1674,7 +1721,57 @@ static void rna_def_trackingStabilization(BlenderRNA *brna) RNA_def_property_int_funcs(prop, "rna_tracking_stabTracks_active_index_get", "rna_tracking_stabTracks_active_index_set", "rna_tracking_stabTracks_active_index_range"); - RNA_def_property_ui_text(prop, "Active Track Index", "Index of active track in stabilization tracks list"); + RNA_def_property_ui_text(prop, "Active Track Index", "Index of active track in translation stabilization tracks list"); + + /* tracks used for rotation stabilization */ + prop = RNA_def_property(srna, "rotation_tracks", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_funcs(prop, "rna_tracking_stabRotTracks_begin", "rna_iterator_listbase_next", + "rna_iterator_listbase_end", "rna_iterator_listbase_get", + NULL, NULL, NULL, NULL); + RNA_def_property_struct_type(prop, "MovieTrackingTrack"); + RNA_def_property_ui_text(prop, "Rotation Tracks", "Collection of tracks used for 2D stabilization (translation)"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); + + /* active rotation track index */ + prop = RNA_def_property(srna, "active_rotation_track_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "act_rot_track"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_int_funcs(prop, "rna_tracking_stabRotTracks_active_index_get", + "rna_tracking_stabRotTracks_active_index_set", + "rna_tracking_stabRotTracks_active_index_range"); + RNA_def_property_ui_text(prop, "Active Rotation Track Index", "Index of active track in rotation stabilization tracks list"); + + /* anchor frame */ + prop = RNA_def_property(srna, "anchor_frame", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "anchor_frame"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_range(prop, MINFRAME, MAXFRAME); + RNA_def_property_ui_text(prop, "Anchor Frame", "Reference point to anchor stabilization (other frames will be adjusted relative to this frame's position)"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); + + /* target position */ + prop = RNA_def_property(srna, "target_pos", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_array(prop, 2); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, 3); /* increment in steps of 0.01 and show 3 digit after point */ + RNA_def_property_float_sdna(prop, NULL, "target_pos"); + RNA_def_property_ui_text(prop, "Expected Position", "Known relative offset of original shot, will be subtracted; e.g. for panning shot, can be animated"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL); + + /* target rotation */ + prop = RNA_def_property(srna, "target_rot", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "target_rot"); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, -FLT_MAX, FLT_MAX, 1, 3); + RNA_def_property_ui_text(prop, "Expected Rotation", "Rotation present on original shot, will be compensated; e.g. for deliberate tilting"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, NULL); + + /* target scale */ + prop = RNA_def_property(srna, "target_zoom", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "scale"); + RNA_def_property_range(prop, FLT_EPSILON, 100.0f); + RNA_def_property_ui_range(prop, 0.1f, 10.0f, 1, 3); /* increment in steps of 0.01. Show 3 digit after point */ + RNA_def_property_ui_text(prop, "Expected Zoom", "Explicitly scale resulting frame to compensate zoom of original shot"); + RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); /* autoscale */ prop = RNA_def_property(srna, "use_autoscale", PROP_BOOLEAN, PROP_NONE); @@ -1705,13 +1802,6 @@ static void rna_def_trackingStabilization(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Scale Influence", "Influence of stabilization algorithm on footage scale"); RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); - /* use_stabilize_rotation */ - prop = RNA_def_property(srna, "use_stabilize_rotation", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", TRACKING_STABILIZE_ROTATION); - RNA_def_property_ui_text(prop, "Stabilize Rotation", "Stabilize horizon line on the shot"); - RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); - /* influence_rotation */ prop = RNA_def_property(srna, "influence_rotation", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "rotinf"); @@ -1723,8 +1813,15 @@ static void rna_def_trackingStabilization(BlenderRNA *brna) prop = RNA_def_property(srna, "filter_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "filter"); RNA_def_property_enum_items(prop, filter_items); - RNA_def_property_ui_text(prop, "Filter", "Method to use to filter stabilization"); + RNA_def_property_ui_text(prop, "Interpolate", "Interpolation to use for sub-pixel shifts and rotations due to stabilization"); RNA_def_property_update(prop, NC_MOVIECLIP | ND_DISPLAY, "rna_tracking_flushUpdate"); + + /* UI display : show participating tracks */ + prop = RNA_def_property(srna, "show_tracks_expanded", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", TRACKING_SHOW_STAB_TRACKS); + RNA_def_property_ui_text(prop, "Show Tracks", "Show UI list of tracks participating in stabilization"); + RNA_def_property_ui_icon(prop, ICON_TRIA_RIGHT, 1); } static void rna_def_reconstructedCamera(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index 80777f57811..a751c414d83 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -912,6 +912,11 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_function_ui_description(func, "Node Socket Icon"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); RNA_def_float_array(func, "color", 4, node_socket_color_default, 0.0f, 1.0f, "Color", "", 0.0f, 1.0f); + + func = RNA_def_function(srna, "template_cache_file", "uiTemplateCacheFile"); + RNA_def_function_ui_description(func, "Item(s). User interface for selecting cache files and their source paths"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + api_ui_item_rna_common(func); } #endif diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 0de7676e8f8..b8ebb375a48 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -73,6 +73,7 @@ set(SRC intern/MOD_meshcache_pc2.c intern/MOD_meshcache_util.c intern/MOD_meshdeform.c + intern/MOD_meshsequencecache.c intern/MOD_mirror.c intern/MOD_multires.c intern/MOD_none.c @@ -112,6 +113,13 @@ set(SRC intern/MOD_weightvg_util.h ) +if(WITH_ALEMBIC) + add_definitions(-DWITH_ALEMBIC) + list(APPEND INC + ../alembic + ) +endif() + if(WITH_MOD_BOOLEAN) add_definitions(-DWITH_MOD_BOOLEAN) list(APPEND SRC diff --git a/source/blender/modifiers/MOD_modifiertypes.h b/source/blender/modifiers/MOD_modifiertypes.h index a5d96759952..4c881445893 100644 --- a/source/blender/modifiers/MOD_modifiertypes.h +++ b/source/blender/modifiers/MOD_modifiertypes.h @@ -84,6 +84,7 @@ extern ModifierTypeInfo modifierType_Wireframe; extern ModifierTypeInfo modifierType_DataTransfer; extern ModifierTypeInfo modifierType_NormalEdit; extern ModifierTypeInfo modifierType_CorrectiveSmooth; +extern ModifierTypeInfo modifierType_MeshSequenceCache; /* MOD_util.c */ void modifier_type_init(ModifierTypeInfo *types[]); diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index 6cc2f097be8..d15a6fcb1c8 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -123,19 +123,11 @@ static void updateDepgraph(ModifierData *md, DagForest *forest, { ClothModifierData *clmd = (ClothModifierData *) md; - Base *base; - if (clmd) { - for (base = scene->base.first; base; base = base->next) { - Object *ob1 = base->object; - if (ob1 != ob) { - CollisionModifierData *coll_clmd = (CollisionModifierData *)modifiers_findByType(ob1, eModifierType_Collision); - if (coll_clmd) { - DagNode *curNode = dag_get_node(forest, ob1); - dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Cloth Collision"); - } - } - } + /* Actual code uses get_collisionobjects */ + dag_add_collision_relations(forest, scene, ob, obNode, clmd->coll_parms->group, ob->lay|scene->lay, eModifierType_Collision, NULL, true, "Cloth Collision"); + + dag_add_forcefield_relations(forest, scene, ob, obNode, clmd->sim_parms->effector_weights, true, 0, "Cloth Field"); } } @@ -147,16 +139,10 @@ static void updateDepsgraph(ModifierData *md, { ClothModifierData *clmd = (ClothModifierData *)md; if (clmd != NULL) { - Base *base; - for (base = scene->base.first; base; base = base->next) { - Object *ob1 = base->object; - if (ob1 != ob) { - CollisionModifierData *coll_clmd = (CollisionModifierData *)modifiers_findByType(ob1, eModifierType_Collision); - if (coll_clmd) { - DEG_add_object_relation(node, ob1, DEG_OB_COMP_TRANSFORM, "Cloth Modifier"); - } - } - } + /* Actual code uses get_collisionobjects */ + DEG_add_collision_relations(node, scene, ob, clmd->coll_parms->group, ob->lay|scene->lay, eModifierType_Collision, NULL, true, "Cloth Collision"); + + DEG_add_forcefield_relations(node, scene, ob, clmd->sim_parms->effector_weights, true, 0, "Cloth Field"); } } diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c index edf959f42c6..bde20e56748 100644 --- a/source/blender/modifiers/intern/MOD_dynamicpaint.c +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -114,6 +114,11 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, return dm; } +static bool is_brush_cb(Object *UNUSED(ob), ModifierData *pmd) +{ + return ((DynamicPaintModifierData*)pmd)->brush != NULL; +} + static void updateDepgraph(ModifierData *md, DagForest *forest, struct Main *UNUSED(bmain), struct Scene *scene, @@ -124,16 +129,13 @@ static void updateDepgraph(ModifierData *md, DagForest *forest, /* add relation from canvases to all brush objects */ if (pmd && pmd->canvas) { - Base *base = scene->base.first; - - for (; base; base = base->next) { - DynamicPaintModifierData *pmd2 = - (DynamicPaintModifierData *)modifiers_findByType(base->object, eModifierType_DynamicPaint); - - if (pmd2 && pmd2->brush && ob != base->object) { - DagNode *brushNode = dag_get_node(forest, base->object); - dag_add_relation(forest, brushNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Dynamic Paint Brush"); + for (DynamicPaintSurface *surface = pmd->canvas->surfaces.first; surface; surface = surface->next) { + if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) { + dag_add_forcefield_relations(forest, scene, ob, obNode, surface->effector_weights, true, 0, "Dynamic Paint Field"); } + + /* Actual code uses custom loop over group/scene without layer checks in dynamicPaint_doStep */ + dag_add_collision_relations(forest, scene, ob, obNode, surface->brush_group, -1, eModifierType_DynamicPaint, is_brush_cb, false, "Dynamic Paint Brush"); } } } @@ -147,13 +149,13 @@ static void updateDepsgraph(ModifierData *md, DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; /* Add relation from canvases to all brush objects. */ if (pmd->canvas != NULL) { - Base *base = scene->base.first; - for (; base; base = base->next) { - DynamicPaintModifierData *pmd2 = - (DynamicPaintModifierData *)modifiers_findByType(base->object, eModifierType_DynamicPaint); - if (pmd2 && pmd2->brush && ob != base->object) { - DEG_add_object_relation(node, base->object, DEG_OB_COMP_TRANSFORM, "Dynamic Paint Brush"); + for (DynamicPaintSurface *surface = pmd->canvas->surfaces.first; surface; surface = surface->next) { + if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) { + DEG_add_forcefield_relations(node, scene, ob, surface->effector_weights, true, 0, "Dynamic Paint Field"); } + + /* Actual code uses custom loop over group/scene without layer checks in dynamicPaint_doStep */ + DEG_add_collision_relations(node, scene, ob, surface->brush_group, -1, eModifierType_DynamicPaint, is_brush_cb, false, "Dynamic Paint Brush"); } } } diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c new file mode 100644 index 00000000000..355ac9563dd --- /dev/null +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -0,0 +1,197 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/modifiers/intern/MOD_meshsequencecache.c + * \ingroup modifiers + */ + +#include "DNA_cachefile_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_cachefile.h" +#include "BKE_DerivedMesh.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_library_query.h" +#include "BKE_scene.h" + +#include "depsgraph_private.h" +#include "DEG_depsgraph_build.h" + +#include "MOD_modifiertypes.h" + +#ifdef WITH_ALEMBIC +# include "ABC_alembic.h" +#endif + +static void initData(ModifierData *md) +{ + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; + + mcmd->cache_file = NULL; + mcmd->object_path[0] = '\0'; + mcmd->read_flag = MOD_MESHSEQ_READ_ALL; +} + +static void copyData(ModifierData *md, ModifierData *target) +{ +#if 0 + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *)md; +#endif + MeshSeqCacheModifierData *tmcmd = (MeshSeqCacheModifierData *)target; + + modifier_copyData_generic(md, target); + + if (tmcmd->cache_file) { + id_us_plus(&tmcmd->cache_file->id); + } +} + +static void freeData(ModifierData *md) +{ + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md; + + if (mcmd->cache_file) { + id_us_min(&mcmd->cache_file->id); + } +} + +static bool isDisabled(ModifierData *md, int UNUSED(useRenderParams)) +{ + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md; + + /* leave it up to the modifier to check the file is valid on calculation */ + return (mcmd->cache_file == NULL) || (mcmd->object_path[0] == '\0'); +} + +static DerivedMesh *applyModifier(ModifierData *md, Object *ob, + DerivedMesh *dm, + ModifierApplyFlag flag) +{ +#ifdef WITH_ALEMBIC + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md; + + Scene *scene = md->scene; + const float frame = BKE_scene_frame_get(scene); + const float time = BKE_cachefile_time_offset(mcmd->cache_file, frame, FPS); + + const char *err_str = NULL; + + CacheFile *cache_file = mcmd->cache_file; + + BKE_cachefile_ensure_handle(G.main, cache_file); + + DerivedMesh *result = ABC_read_mesh(cache_file->handle, + ob, + dm, + mcmd->object_path, + time, + &err_str, + mcmd->read_flag); + + if (err_str) { + modifier_setError(md, "%s", err_str); + } + + return result ? result : dm; + UNUSED_VARS(flag); +#else + return dm; + UNUSED_VARS(md, ob, flag); +#endif +} + +static bool dependsOnTime(ModifierData *md) +{ + UNUSED_VARS(md); + return true; +} + +static void foreachIDLink(ModifierData *md, Object *ob, + IDWalkFunc walk, void *userData) +{ + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md; + + walk(userData, ob, (ID **)&mcmd->cache_file, IDWALK_USER); +} + + +static void updateDepgraph(ModifierData *md, DagForest *forest, + struct Main *bmain, + struct Scene *scene, + Object *ob, DagNode *obNode) +{ + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md; + + if (mcmd->cache_file != NULL) { + DagNode *curNode = dag_get_node(forest, mcmd->cache_file); + + dag_add_relation(forest, curNode, obNode, + DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Cache File Modifier"); + } + + UNUSED_VARS(bmain, scene, ob); +} + +static void updateDepsgraph(ModifierData *md, + struct Main *bmain, + struct Scene *scene, + Object *ob, + struct DepsNodeHandle *node) +{ + MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md; + + if (mcmd->cache_file != NULL) { + DEG_add_object_cache_relation(node, mcmd->cache_file, DEG_OB_COMP_CACHE, "Mesh Cache File"); + } + + UNUSED_VARS(bmain, scene, ob); +} + +ModifierTypeInfo modifierType_MeshSequenceCache = { + /* name */ "Mesh Sequence Cache", + /* structName */ "MeshSeqCacheModifierData", + /* structSize */ sizeof(MeshSeqCacheModifierData), + /* type */ eModifierTypeType_Constructive, + /* flags */ eModifierTypeFlag_AcceptsMesh | + eModifierTypeFlag_AcceptsCVs, + /* copyData */ copyData, + /* deformVerts */ NULL, + /* deformMatrices */ NULL, + /* deformVertsEM */ NULL, + /* deformMatricesEM */ NULL, + /* applyModifier */ applyModifier, + /* applyModifierEM */ NULL, + /* initData */ initData, + /* requiredDataMask */ NULL, + /* freeData */ freeData, + /* isDisabled */ isDisabled, + /* updateDepgraph */ updateDepgraph, + /* updateDepsgraph */ updateDepsgraph, + /* dependsOnTime */ dependsOnTime, + /* dependsOnNormals */ NULL, + /* foreachObjectLink */ NULL, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ NULL, +}; diff --git a/source/blender/modifiers/intern/MOD_smoke.c b/source/blender/modifiers/intern/MOD_smoke.c index 237d4cc6718..f04d7432a8f 100644 --- a/source/blender/modifiers/intern/MOD_smoke.c +++ b/source/blender/modifiers/intern/MOD_smoke.c @@ -117,219 +117,48 @@ static bool dependsOnTime(ModifierData *UNUSED(md)) return true; } -static void update_depsgraph_flow_coll_object(DagForest *forest, - DagNode *obNode, - Object *object2) +static bool is_flow_cb(Object *UNUSED(ob), ModifierData *md) { - SmokeModifierData *smd; - if ((object2->id.tag & LIB_TAG_DOIT) == 0) { - return; - } - object2->id.tag &= ~LIB_TAG_DOIT; - smd = (SmokeModifierData *)modifiers_findByType(object2, eModifierType_Smoke); - if (smd && (((smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow) || - ((smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll))) - { - DagNode *curNode = dag_get_node(forest, object2); - dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Smoke Flow/Coll"); - } - if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) { - GroupObject *go; - for (go = object2->dup_group->gobject.first; - go != NULL; - go = go->next) - { - if (go->ob == NULL) { - continue; - } - update_depsgraph_flow_coll_object(forest, obNode, go->ob); - } - } + SmokeModifierData *smd = (SmokeModifierData *) md; + return (smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow; } -static void update_depsgraph_field_source_object(DagForest *forest, - DagNode *obNode, - Object *object, - Object *object2) +static bool is_coll_cb(Object *UNUSED(ob), ModifierData *md) { - if ((object2->id.tag & LIB_TAG_DOIT) == 0) { - return; - } - object2->id.tag &= ~LIB_TAG_DOIT; - if (object2->pd && object2->pd->forcefield == PFIELD_SMOKEFLOW && object2->pd->f_source == object) { - DagNode *node2 = dag_get_node(forest, object2); - dag_add_relation(forest, obNode, node2, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Field Source Object"); - } - if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) { - GroupObject *go; - for (go = object2->dup_group->gobject.first; - go != NULL; - go = go->next) - { - if (go->ob == NULL) { - continue; - } - update_depsgraph_field_source_object(forest, obNode, object, go->ob); - } - } + SmokeModifierData *smd = (SmokeModifierData *) md; + return (smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll; } static void updateDepgraph(ModifierData *md, DagForest *forest, - struct Main *bmain, + struct Main *UNUSED(bmain), struct Scene *scene, struct Object *ob, DagNode *obNode) { SmokeModifierData *smd = (SmokeModifierData *) md; - Base *base; if (smd && (smd->type & MOD_SMOKE_TYPE_DOMAIN) && smd->domain) { - if (smd->domain->fluid_group || smd->domain->coll_group) { - GroupObject *go = NULL; - - if (smd->domain->fluid_group) - for (go = smd->domain->fluid_group->gobject.first; go; go = go->next) { - if (go->ob) { - SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke); - - /* check for initialized smoke object */ - if (smd2 && (smd2->type & MOD_SMOKE_TYPE_FLOW) && smd2->flow) { - DagNode *curNode = dag_get_node(forest, go->ob); - dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Smoke Flow"); - } - } - } - - if (smd->domain->coll_group) - for (go = smd->domain->coll_group->gobject.first; go; go = go->next) { - if (go->ob) { - SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke); - - /* check for initialized smoke object */ - if (smd2 && (smd2->type & MOD_SMOKE_TYPE_COLL) && smd2->coll) { - DagNode *curNode = dag_get_node(forest, go->ob); - dag_add_relation(forest, curNode, obNode, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Smoke Coll"); - } - } - } - } - else { - BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true); - base = scene->base.first; - for (; base; base = base->next) { - update_depsgraph_flow_coll_object(forest, obNode, base->object); - } - } - /* add relation to all "smoke flow" force fields */ - base = scene->base.first; - BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true); - for (; base; base = base->next) { - update_depsgraph_field_source_object(forest, obNode, ob, base->object); - } - } -} + /* Actual code uses get_collisionobjects */ + dag_add_collision_relations(forest, scene, ob, obNode, smd->domain->fluid_group, ob->lay|scene->lay, eModifierType_Smoke, is_flow_cb, true, "Smoke Flow"); + dag_add_collision_relations(forest, scene, ob, obNode, smd->domain->coll_group, ob->lay|scene->lay, eModifierType_Smoke, is_coll_cb, true, "Smoke Coll"); -static void update_depsgraph_flow_coll_object_new(struct DepsNodeHandle *node, - Object *object2) -{ - SmokeModifierData *smd; - if ((object2->id.tag & LIB_TAG_DOIT) == 0) { - return; - } - object2->id.tag &= ~LIB_TAG_DOIT; - smd = (SmokeModifierData *)modifiers_findByType(object2, eModifierType_Smoke); - if (smd && (((smd->type & MOD_SMOKE_TYPE_FLOW) && smd->flow) || - ((smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll))) - { - DEG_add_object_relation(node, object2, DEG_OB_COMP_TRANSFORM, "Smoke Flow/Coll"); - DEG_add_object_relation(node, object2, DEG_OB_COMP_GEOMETRY, "Smoke Flow/Coll"); - } - if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) { - GroupObject *go; - for (go = object2->dup_group->gobject.first; - go != NULL; - go = go->next) - { - if (go->ob == NULL) { - continue; - } - update_depsgraph_flow_coll_object_new(node, go->ob); - } - } -} - -static void update_depsgraph_field_source_object_new(struct DepsNodeHandle *node, - Object *object, - Object *object2) -{ - if ((object2->id.tag & LIB_TAG_DOIT) == 0) { - return; - } - object2->id.tag &= ~LIB_TAG_DOIT; - if (object2->pd && object2->pd->forcefield == PFIELD_SMOKEFLOW && object2->pd->f_source == object) { - DEG_add_object_relation(node, object2, DEG_OB_COMP_TRANSFORM, "Field Source Object"); - DEG_add_object_relation(node, object2, DEG_OB_COMP_GEOMETRY, "Field Source Object"); - } - if ((object2->transflag & OB_DUPLIGROUP) && object2->dup_group) { - GroupObject *go; - for (go = object2->dup_group->gobject.first; - go != NULL; - go = go->next) - { - if (go->ob == NULL) { - continue; - } - update_depsgraph_field_source_object_new(node, object, go->ob); - } + dag_add_forcefield_relations(forest, scene, ob, obNode, smd->domain->effector_weights, true, PFIELD_SMOKEFLOW, "Smoke Force Field"); } } static void updateDepsgraph(ModifierData *md, - struct Main *bmain, + struct Main *UNUSED(bmain), struct Scene *scene, Object *ob, struct DepsNodeHandle *node) { SmokeModifierData *smd = (SmokeModifierData *)md; - Base *base; + if (smd && (smd->type & MOD_SMOKE_TYPE_DOMAIN) && smd->domain) { - if (smd->domain->fluid_group || smd->domain->coll_group) { - GroupObject *go = NULL; - if (smd->domain->fluid_group != NULL) { - for (go = smd->domain->fluid_group->gobject.first; go; go = go->next) { - if (go->ob != NULL) { - SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke); - /* Check for initialized smoke object. */ - if (smd2 && (smd2->type & MOD_SMOKE_TYPE_FLOW) && smd2->flow) { - DEG_add_object_relation(node, go->ob, DEG_OB_COMP_TRANSFORM, "Smoke Flow"); - } - } - } - } - if (smd->domain->coll_group != NULL) { - for (go = smd->domain->coll_group->gobject.first; go; go = go->next) { - if (go->ob != NULL) { - SmokeModifierData *smd2 = (SmokeModifierData *)modifiers_findByType(go->ob, eModifierType_Smoke); - /* Check for initialized smoke object. */ - if (smd2 && (smd2->type & MOD_SMOKE_TYPE_COLL) && smd2->coll) { - DEG_add_object_relation(node, go->ob, DEG_OB_COMP_TRANSFORM, "Smoke Coll"); - } - } - } - } - } - else { - BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true); - base = scene->base.first; - for (; base; base = base->next) { - update_depsgraph_flow_coll_object_new(node, base->object); - } - } - /* add relation to all "smoke flow" force fields */ - base = scene->base.first; - BKE_main_id_tag_listbase(&bmain->object, LIB_TAG_DOIT, true); - for (; base; base = base->next) { - update_depsgraph_field_source_object_new(node, ob, base->object); - } + /* Actual code uses get_collisionobjects */ + DEG_add_collision_relations(node, scene, ob, smd->domain->fluid_group, ob->lay|scene->lay, eModifierType_Smoke, is_flow_cb, true, "Smoke Flow"); + DEG_add_collision_relations(node, scene, ob, smd->domain->coll_group, ob->lay|scene->lay, eModifierType_Smoke, is_coll_cb, true, "Smoke Coll"); + + DEG_add_forcefield_relations(node, scene, ob, smd->domain->effector_weights, true, PFIELD_SMOKEFLOW, "Smoke Force Field"); } } diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index 98a1412d0c6..17adc7f1520 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -35,6 +35,7 @@ #include <stdio.h> #include "DNA_scene_types.h" +#include "DNA_object_force.h" #include "BLI_utildefines.h" @@ -42,6 +43,9 @@ #include "BKE_particle.h" #include "BKE_softbody.h" +#include "depsgraph_private.h" +#include "DEG_depsgraph_build.h" + #include "MOD_modifiertypes.h" static void deformVerts(ModifierData *md, Object *ob, @@ -58,6 +62,31 @@ static bool dependsOnTime(ModifierData *UNUSED(md)) return true; } +static void updateDepgraph(ModifierData *UNUSED(md), DagForest *forest, + struct Main *UNUSED(bmain), + Scene *scene, Object *ob, DagNode *obNode) +{ + if (ob->soft) { + /* Actual code uses ccd_build_deflector_hash */ + dag_add_collision_relations(forest, scene, ob, obNode, ob->soft->collision_group, ob->lay, eModifierType_Collision, NULL, false, "Softbody Collision"); + + dag_add_forcefield_relations(forest, scene, ob, obNode, ob->soft->effector_weights, true, 0, "Softbody Field"); + } +} + +static void updateDepsgraph(ModifierData *UNUSED(md), + struct Main *UNUSED(bmain), + struct Scene *scene, + Object *ob, + struct DepsNodeHandle *node) +{ + if (ob->soft) { + /* Actual code uses ccd_build_deflector_hash */ + DEG_add_collision_relations(node, scene, ob, ob->soft->collision_group, ob->lay, eModifierType_Collision, NULL, false, "Softbody Collision"); + + DEG_add_forcefield_relations(node, scene, ob, ob->soft->effector_weights, true, 0, "Softbody Field"); + } +} ModifierTypeInfo modifierType_Softbody = { /* name */ "Softbody", @@ -80,8 +109,8 @@ ModifierTypeInfo modifierType_Softbody = { /* requiredDataMask */ NULL, /* freeData */ NULL, /* isDisabled */ NULL, - /* updateDepgraph */ NULL, - /* updateDepsgraph */ NULL, + /* updateDepgraph */ updateDepgraph, + /* updateDepsgraph */ updateDepsgraph, /* dependsOnTime */ dependsOnTime, /* dependsOnNormals */ NULL, /* foreachObjectLink */ NULL, diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index f9291fb077f..93414562ccf 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -286,5 +286,6 @@ void modifier_type_init(ModifierTypeInfo *types[]) INIT_TYPE(DataTransfer); INIT_TYPE(NormalEdit); INIT_TYPE(CorrectiveSmooth); + INIT_TYPE(MeshSequenceCache); #undef INIT_TYPE } diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 2715d2c5992..038c1e7eb10 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -49,6 +49,7 @@ set(SRC gpu_offscreen.c bpy.c bpy_app.c + bpy_app_alembic.c bpy_app_build_options.c bpy_app_ffmpeg.c bpy_app_handlers.c @@ -82,6 +83,7 @@ set(SRC gpu.h bpy.h bpy_app.h + bpy_app_alembic.h bpy_app_build_options.h bpy_app_ffmpeg.h bpy_app_handlers.h @@ -264,6 +266,10 @@ if(WITH_OPENCOLLADA) add_definitions(-DWITH_COLLADA) endif() +if(WITH_ALEMBIC) + add_definitions(-DWITH_ALEMBIC) +endif() + if(WITH_OPENCOLORIO) add_definitions(-DWITH_OCIO) endif() @@ -275,6 +281,13 @@ if(WITH_OPENVDB) ) endif() +if(WITH_ALEMBIC) + add_definitions(-DWITH_ALEMBIC) + list(APPEND INC + ../../alembic + ) +endif() + if(WITH_OPENIMAGEIO) add_definitions(-DWITH_OPENIMAGEIO) list(APPEND INC diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 727d980b182..78cb537cccd 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -33,6 +33,7 @@ #include "bpy_app.h" +#include "bpy_app_alembic.h" #include "bpy_app_ffmpeg.h" #include "bpy_app_ocio.h" #include "bpy_app_oiio.h" @@ -104,6 +105,7 @@ static PyStructSequence_Field app_info_fields[] = { {(char *)"build_system", (char *)"Build system used"}, /* submodules */ + {(char *)"alembic", (char *)"Alembic library information backend"}, {(char *)"ffmpeg", (char *)"FFmpeg library information backend"}, {(char *)"ocio", (char *)"OpenColorIO library information backend"}, {(char *)"oiio", (char *)"OpenImageIO library information backend"}, @@ -182,6 +184,7 @@ static PyObject *make_app_info(void) SetBytesItem("Unknown"); #endif + SetObjItem(BPY_app_alembic_struct()); SetObjItem(BPY_app_ffmpeg_struct()); SetObjItem(BPY_app_ocio_struct()); SetObjItem(BPY_app_oiio_struct()); diff --git a/source/blender/python/intern/bpy_app_alembic.c b/source/blender/python/intern/bpy_app_alembic.c new file mode 100644 index 00000000000..90e6a02b418 --- /dev/null +++ b/source/blender/python/intern/bpy_app_alembic.c @@ -0,0 +1,113 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/intern/bpy_app_alembic.c + * \ingroup pythonintern + */ + +#include <Python.h> +#include "BLI_utildefines.h" + +#include "bpy_app_alembic.h" + +#ifdef WITH_ALEMBIC +# include "ABC_alembic.h" +#endif + +static PyTypeObject BlenderAppABCType; + +static PyStructSequence_Field app_alembic_info_fields[] = { + {(char *)"supported", (char *)"Boolean, True when Blender is built with Alembic support"}, + {(char *)"version", (char *)"The Alembic version as a tuple of 3 numbers"}, + {(char *)"version_string", (char *)"The Alembic version formatted as a string"}, + {NULL} +}; + +static PyStructSequence_Desc app_alembic_info_desc = { + (char *)"bpy.app.alembic", /* name */ + (char *)"This module contains information about Alembic blender is linked against", /* doc */ + app_alembic_info_fields, /* fields */ + ARRAY_SIZE(app_alembic_info_fields) - 1 +}; + +static PyObject *make_alembic_info(void) +{ + PyObject *alembic_info = PyStructSequence_New(&BlenderAppABCType); + + if (alembic_info == NULL) { + return NULL; + } + + int pos = 0; + +#ifndef WITH_ALEMBIC +# define SetStrItem(str) \ + PyStructSequence_SET_ITEM(alembic_info, pos++, PyUnicode_FromString(str)) +#endif + +#define SetObjItem(obj) \ + PyStructSequence_SET_ITEM(alembic_info, pos++, obj) + +#ifdef WITH_ALEMBIC + const int curversion = ABC_get_version(); + const int major = curversion / 10000; + const int minor = (curversion / 100) - (major * 100); + const int patch = curversion - ((curversion / 100 ) * 100); + + SetObjItem(PyBool_FromLong(1)); + SetObjItem(Py_BuildValue("(iii)", major, minor, patch)); + SetObjItem(PyUnicode_FromFormat("%2d, %2d, %2d", major, minor, patch)); +#else + SetObjItem(PyBool_FromLong(0)); + SetObjItem(Py_BuildValue("(iii)", 0, 0, 0)); + SetStrItem("Unknown"); +#endif + + if (PyErr_Occurred()) { + Py_CLEAR(alembic_info); + return NULL; + } + +#undef SetStrItem +#undef SetObjItem + + return alembic_info; +} + +PyObject *BPY_app_alembic_struct(void) +{ + PyStructSequence_InitType(&BlenderAppABCType, &app_alembic_info_desc); + + PyObject *ret = make_alembic_info(); + + /* prevent user from creating new instances */ + BlenderAppABCType.tp_init = NULL; + BlenderAppABCType.tp_new = NULL; + BlenderAppABCType.tp_hash = (hashfunc)_Py_HashPointer; /* without this we can't do set(sys.modules) [#29635] */ + + return ret; +} diff --git a/source/blender/python/intern/bpy_app_alembic.h b/source/blender/python/intern/bpy_app_alembic.h new file mode 100644 index 00000000000..8cc647a77df --- /dev/null +++ b/source/blender/python/intern/bpy_app_alembic.h @@ -0,0 +1,38 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/intern/bpy_app_alembic.h + * \ingroup pythonintern + */ + +#ifndef __BPY_APP_ALEMBIC_H__ +#define __BPY_APP_ALEMBIC_H__ + +PyObject *BPY_app_alembic_struct(void); + +#endif /* __BPY_APP_ALEMBIC_H__ */ + diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c index 4c186aab100..a6b98567a9a 100644 --- a/source/blender/python/intern/bpy_app_build_options.c +++ b/source/blender/python/intern/bpy_app_build_options.c @@ -69,6 +69,7 @@ static PyStructSequence_Field app_builtopts_info_fields[] = { {(char *)"player", NULL}, {(char *)"openmp", NULL}, {(char *)"openvdb", NULL}, + {(char *)"alembic", NULL}, {NULL} }; @@ -303,6 +304,12 @@ static PyObject *make_builtopts_info(void) SetObjIncref(Py_False); #endif +#ifdef WITH_ALEMBIC + SetObjIncref(Py_True); +#else + SetObjIncref(Py_False); +#endif + #undef SetObjIncref return builtopts_info; diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h index 39f62f9fc33..509ad6f3515 100644 --- a/source/blender/render/extern/include/RE_pipeline.h +++ b/source/blender/render/extern/include/RE_pipeline.h @@ -238,6 +238,9 @@ void RE_render_result_rect_from_ibuf( struct RenderLayer *RE_GetRenderLayer(struct RenderResult *rr, const char *name); float *RE_RenderLayerGetPass(volatile struct RenderLayer *rl, int passtype, const char *viewname); +/* add passes for grease pencil */ +struct RenderPass *RE_create_gp_pass(struct RenderResult *rr, const char *layername, const char *viewname); + /* obligatory initialize call, disprect is optional */ void RE_InitState(struct Render *re, struct Render *source, struct RenderData *rd, struct SceneRenderLayer *srl, diff --git a/source/blender/render/intern/include/render_result.h b/source/blender/render/intern/include/render_result.h index 2619ac76d59..0c4f4e20325 100644 --- a/source/blender/render/intern/include/render_result.h +++ b/source/blender/render/intern/include/render_result.h @@ -83,6 +83,9 @@ void render_result_save_empty_result_tiles(struct Render *re); void render_result_exr_file_begin(struct Render *re); void render_result_exr_file_end(struct Render *re); +/* render pass wrapper for gpencil */ +struct RenderPass *gp_add_pass(struct RenderResult *rr, struct RenderLayer *rl, int channels, int passtype, const char *viewname); + void render_result_exr_file_merge(struct RenderResult *rr, struct RenderResult *rrpart, const char *viewname); void render_result_exr_file_path(struct Scene *scene, const char *layname, int sample, char *filepath); diff --git a/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp b/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp index 02a49fc3c8f..724a809077e 100644 --- a/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp +++ b/source/blender/render/intern/raytrace/rayobject_rtbuild.cpp @@ -42,6 +42,10 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#if __cplusplus >= 201103L +using std::isfinite; +#endif + static bool selected_node(RTBuilder::Object *node) { return node->selected; diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 1ee905c596c..ec629aa2863 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -2785,6 +2785,7 @@ static void do_render_all_options(Render *re) /* ensure no images are in memory from previous animated sequences */ BKE_image_all_free_anim_ibufs(re->r.cfra); + BKE_sequencer_all_free_anim_ibufs(re->r.cfra); if (RE_engine_render(re, 1)) { /* in this case external render overrides all */ @@ -4018,3 +4019,31 @@ RenderPass *RE_pass_find_by_type(volatile RenderLayer *rl, int passtype, const c } return rp; } + +/* create a renderlayer and renderpass for grease pencil layer */ +RenderPass *RE_create_gp_pass(RenderResult *rr, const char *layername, const char *viewname) +{ + RenderLayer *rl = BLI_findstring(&rr->layers, layername, offsetof(RenderLayer, name)); + /* only create render layer if not exist */ + if (!rl) { + rl = MEM_callocN(sizeof(RenderLayer), layername); + BLI_addtail(&rr->layers, rl); + BLI_strncpy(rl->name, layername, sizeof(rl->name)); + rl->lay = 0; + rl->layflag = SCE_LAY_SOLID; + rl->passflag = SCE_PASS_COMBINED; + rl->rectx = rr->rectx; + rl->recty = rr->recty; + } + + /* clear previous pass if exist or the new image will be over previous one*/ + RenderPass *rp = RE_pass_find_by_type(rl, SCE_PASS_COMBINED, viewname); + if (rp) { + if (rp->rect) { + MEM_freeN(rp->rect); + } + BLI_freelinkN(&rl->passes, rp); + } + /* create a totally new pass */ + return gp_add_pass(rr, rl, 4, SCE_PASS_COMBINED, viewname); +} diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c index bddd84c45d7..6ea46af6f7e 100644 --- a/source/blender/render/intern/source/render_result.c +++ b/source/blender/render/intern/source/render_result.c @@ -540,6 +540,11 @@ static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int return rpass; } +/* wrapper called from render_opengl */ +RenderPass *gp_add_pass(RenderResult *rr, RenderLayer *rl, int channels, int passtype, const char *viewname) +{ + return render_layer_add_pass(rr, rl, channels, passtype, viewname); +} #ifdef WITH_CYCLES_DEBUG const char *RE_debug_pass_name_get(int debug_type) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 493fe82ff80..69905fc296b 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -441,6 +441,7 @@ enum { WM_JOB_TYPE_SEQ_BUILD_PREVIEW, WM_JOB_TYPE_POINTCACHE, WM_JOB_TYPE_DPAINT_BAKE, + WM_JOB_TYPE_ALEMBIC, /* add as needed, screencast, seq proxy build * if having hard coded values is a problem */ }; diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c index 7ec2aea73e1..18836f34c99 100644 --- a/source/blender/windowmanager/intern/wm_operator_props.c +++ b/source/blender/windowmanager/intern/wm_operator_props.c @@ -97,6 +97,8 @@ void WM_operator_properties_filesel( RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "filter_collada", (filter & FILE_TYPE_COLLADA) != 0, "Filter COLLADA files", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "filter_alembic", (filter & FILE_TYPE_ALEMBIC) != 0, "Filter Alembic files", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "filter_folder", (filter & FILE_TYPE_FOLDER) != 0, "Filter folders", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, "filter_blenlib", (filter & FILE_TYPE_BLENDERLIB) != 0, "Filter Blender IDs", ""); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 42f6585f152..2d43c47679d 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -69,6 +69,8 @@ #include "ED_screen.h" #include "ED_fileselect.h" +#include "UI_interface.h" + #include "PIL_time.h" #include "GPU_draw.h" @@ -1194,14 +1196,30 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr break; } case GHOST_kEventNativeResolutionChange: - // printf("change, pixel size %f\n", GHOST_GetNativePixelSize(win->ghostwin)); - + { + // only update if the actual pixel size changes + float prev_pixelsize = U.pixelsize; U.pixelsize = wm_window_pixelsize(win); - BKE_blender_userdef_refresh(); - WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); - WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL); + + if (U.pixelsize != prev_pixelsize) { + BKE_blender_userdef_refresh(); + + // close all popups since they are positioned with the pixel + // size baked in and it's difficult to correct them + wmWindow *oldWindow = CTX_wm_window(C); + CTX_wm_window_set(C, win); + UI_popup_handlers_remove_all(C, &win->modalhandlers); + CTX_wm_window_set(C, oldWindow); + + wm_window_make_drawable(wm, win); + wm_draw_window_clear(win); + + WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); + WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL); + } break; + } case GHOST_kEventTrackpad: { GHOST_TEventTrackpadData *pd = data; diff --git a/source/blenderplayer/CMakeLists.txt b/source/blenderplayer/CMakeLists.txt index ca841954b16..2748de0e7dd 100644 --- a/source/blenderplayer/CMakeLists.txt +++ b/source/blenderplayer/CMakeLists.txt @@ -233,6 +233,10 @@ endif() list(APPEND BLENDER_SORTED_LIBS bf_intern_openvdb) endif() + if(WITH_ALEMBIC) + list(APPEND BLENDER_SORTED_LIBS bf_alembic) + endif() + foreach(SORTLIB ${BLENDER_SORTED_LIBS}) set(REMLIB ${SORTLIB}) foreach(SEARCHLIB ${BLENDER_LINK_LIBS}) diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index 6c0bbc8e01f..cdfd326d986 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -45,6 +45,7 @@ struct ARegion; struct ARegionType; struct BMEditMesh; struct Base; +struct bContext; struct BoundBox; struct Brush; struct CSG_FaceIteratorDescriptor; @@ -111,6 +112,7 @@ struct bConstraint; struct bConstraintOb; struct bConstraintTarget; struct bContextDataResult; +struct bGPDlayer; struct bNode; struct bNodeType; struct bNodeSocket; @@ -153,6 +155,7 @@ struct wmWindowManager; #include "../blender/editors/include/ED_clip.h" #include "../blender/editors/include/ED_curve.h" #include "../blender/editors/include/ED_fileselect.h" +#include "../blender/editors/include/ED_gpencil.h" #include "../blender/editors/include/ED_image.h" #include "../blender/editors/include/ED_info.h" #include "../blender/editors/include/ED_keyframes_edit.h" @@ -320,6 +323,19 @@ void WM_cursor_modal_restore(struct wmWindow *win) RET_NONE void WM_cursor_time(struct wmWindow *win, int nr) RET_NONE void WM_cursor_warp(struct wmWindow *win, int x, int y) RET_NONE +struct wmJob *WM_jobs_get(struct wmWindowManager *wm, struct wmWindow *win, void *owner, const char *name, int flag, int job_type) RET_NULL +void WM_jobs_customdata_set(struct wmJob *job, void *customdata, void (*free)(void *)) RET_NONE +void WM_jobs_timer(struct wmJob *job, double timestep, unsigned int note, unsigned int endnote) RET_NONE + +void WM_jobs_callbacks(struct wmJob *job, + void (*startjob)(void *, short *, short *, float *), + void (*initjob)(void *), + void (*update)(void *), + void (*endjob)(void *)) RET_NONE + +void WM_jobs_start(struct wmWindowManager *wm, struct wmJob *job) RET_NONE +void WM_report(ReportType type, const char *message) RET_NONE + void WM_ndof_deadzone_set(float deadzone) RET_NONE void WM_uilisttype_init(void) RET_NONE @@ -350,6 +366,7 @@ void *ED_region_draw_cb_activate(struct ARegionType *art, void(*draw)(const stru void *ED_region_draw_cb_customdata(void *handle) RET_ZERO /* XXX This one looks wrong also */ void ED_region_draw_cb_exit(struct ARegionType *art, void *handle) RET_NONE void ED_area_headerprint(struct ScrArea *sa, const char *str) RET_NONE +void ED_gpencil_parent_location(struct bGPDlayer *gpl, float diff_mat[4][4]) RET_NONE void UI_view2d_region_to_view(struct View2D *v2d, float x, float y, float *viewx, float *viewy) RET_NONE bool UI_view2d_view_to_region_clip(struct View2D *v2d, float x, float y, int *regionx, int *regiony) RET_ZERO void UI_view2d_view_to_region(struct View2D *v2d, float x, float y, int *regionx, int *region_y) RET_NONE @@ -620,6 +637,7 @@ void uiTemplateComponentMenu(struct uiLayout *layout, struct PointerRNA *ptr, co void uiTemplateNodeSocket(struct uiLayout *layout, struct bContext *C, float *color) RET_NONE void uiTemplatePalette(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname, int color) RET_NONE void uiTemplateImageStereo3d(struct uiLayout *layout, struct PointerRNA *stereo3d_format_ptr) RET_NONE +void uiTemplateCacheFile(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname) RET_NONE /* rna render */ struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) RET_NULL diff --git a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp index d033afacc08..edbbf93bf7a 100644 --- a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp +++ b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp @@ -61,6 +61,7 @@ extern "C" #include "DNA_scene_types.h" #include "DNA_userdef_types.h" +#include "DNA_genfile.h" #include "BLO_readfile.h" #include "BLO_runtime.h" @@ -492,6 +493,8 @@ int main( // freeing up GPU_Textures works correctly. BLI_threadapi_init(); + DNA_sdna_current_init(); + RNA_init(); init_nodesystem(); |