diff options
author | Julian Eisel <eiseljulian@gmail.com> | 2016-09-08 13:16:41 +0300 |
---|---|---|
committer | Julian Eisel <eiseljulian@gmail.com> | 2016-09-08 13:16:41 +0300 |
commit | 788c75ed879fb25a35c0a0e9245df7aeaae2f4ab (patch) | |
tree | 3b1c315078e257e0401904860f9ec90fe60390ee | |
parent | 1ffd9714b930dbef8059403d69650476f625f554 (diff) | |
parent | 45f833c21dcfad4d1d7045987bb8b69d6aab05be (diff) |
Merge branch 'master' into viewport_bvh_selectviewport_bvh_select
139 files changed, 2528 insertions, 636 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index d7276f4417a..352712e5ca9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -220,6 +220,8 @@ if(${CMAKE_VERSION} VERSION_LESS 2.8.8) endif() set(BUILDINFO_OVERRIDE_DATE "" CACHE STRING "Use instead of the current date for reproducable builds (empty string disables this option)") set(BUILDINFO_OVERRIDE_TIME "" CACHE STRING "Use instead of the current time for reproducable builds (empty string disables this option)") +set(CPACK_OVERRIDE_PACKAGENAME "" CACHE STRING "Use instead of the standard packagename (empty string disables this option)") +mark_as_advanced(CPACK_OVERRIDE_PACKAGENAME) mark_as_advanced(BUILDINFO_OVERRIDE_DATE) mark_as_advanced(BUILDINFO_OVERRIDE_TIME) @@ -512,6 +514,15 @@ mark_as_advanced(WITH_LEGACY_DEPSGRAPH) option(WITH_WINDOWS_FIND_MODULES "Use find_package to locate libraries" OFF) mark_as_advanced(WITH_WINDOWS_FIND_MODULES) +option(WITH_WINDOWS_CODESIGN "Use signtool to sign the final binary." OFF) +mark_as_advanced(WITH_WINDOWS_CODESIGN) + +set(WINDOWS_CODESIGN_PFX CACHE FILEPATH "Path to pfx file to use for codesigning.") +mark_as_advanced(WINDOWS_CODESIGN_PFX) + +set(WINDOWS_CODESIGN_PFX_PASSWORD CACHE STRING "password for pfx file used for codesigning.") +mark_as_advanced(WINDOWS_CODESIGN_PFX_PASSWORD) + # avoid using again option_defaults_clear() diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index dc8b158f5c0..fabb35c539e 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -1578,3 +1578,26 @@ macro(openmp_delayload endif(WITH_OPENMP) endif(MSVC) endmacro() + +MACRO(WINDOWS_SIGN_TARGET target) + if (WITH_WINDOWS_CODESIGN) + if (!SIGNTOOL_EXE) + error("Codesigning is enabled, but signtool is not found") + else() + if (WINDOWS_CODESIGN_PFX_PASSWORD) + set(CODESIGNPASSWORD /p ${WINDOWS_CODESIGN_PFX_PASSWORD}) + else() + if ($ENV{PFXPASSWORD}) + set(CODESIGNPASSWORD /p $ENV{PFXPASSWORD}) + else() + message( FATAL_ERROR "WITH_WINDOWS_CODESIGN is on but WINDOWS_CODESIGN_PFX_PASSWORD not set, and environment variable PFXPASSWORD not found, unable to sign code.") + endif() + endif() + add_custom_command(TARGET ${target} + POST_BUILD + COMMAND ${SIGNTOOL_EXE} sign /f ${WINDOWS_CODESIGN_PFX} ${CODESIGNPASSWORD} $<TARGET_FILE:${target}> + VERBATIM + ) + endif() + endif() +ENDMACRO()
\ No newline at end of file diff --git a/build_files/cmake/packaging.cmake b/build_files/cmake/packaging.cmake index bc1d64f7494..1563331387f 100644 --- a/build_files/cmake/packaging.cmake +++ b/build_files/cmake/packaging.cmake @@ -48,7 +48,11 @@ else(MSVC) set(PACKAGE_ARCH ${CMAKE_SYSTEM_PROCESSOR}) endif() -set(CPACK_PACKAGE_FILE_NAME ${PROJECT_NAME_LOWER}-${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}-git${CPACK_DATE}.${BUILD_REV}-${PACKAGE_ARCH}) +if (CPACK_OVERRIDE_PACKAGENAME) + set(CPACK_PACKAGE_FILE_NAME ${CPACK_OVERRIDE_PACKAGENAME}-${PACKAGE_ARCH}) +else() + set(CPACK_PACKAGE_FILE_NAME ${PROJECT_NAME_LOWER}-${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}-git${CPACK_DATE}.${BUILD_REV}-${PACKAGE_ARCH}) +endif() if(CMAKE_SYSTEM_NAME MATCHES "Linux") # RPM packages diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index 0e0dc382ca3..1ec9b00d4ca 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -245,7 +245,7 @@ if(WITH_BOOST) set(Boost_USE_STATIC_LIBS ON) endif() set(Boost_USE_MULTITHREADED ON) - set(__boost_packages filesystem regex system thread date_time) + set(__boost_packages filesystem regex thread date_time) if(WITH_CYCLES_OSL) if(NOT (${OSL_LIBRARY_VERSION_MAJOR} EQUAL "1" AND ${OSL_LIBRARY_VERSION_MINOR} LESS "6")) list(APPEND __boost_packages wave) @@ -261,6 +261,7 @@ if(WITH_BOOST) if(WITH_OPENVDB) list(APPEND __boost_packages iostreams) endif() + list(APPEND __boost_packages system) find_package(Boost 1.48 COMPONENTS ${__boost_packages}) if(NOT Boost_FOUND) # try to find non-multithreaded if -mt not found, this flag diff --git a/build_files/cmake/platform/platform_win32_msvc.cmake b/build_files/cmake/platform/platform_win32_msvc.cmake index eaa6e41c9c3..2772944214b 100644 --- a/build_files/cmake/platform/platform_win32_msvc.cmake +++ b/build_files/cmake/platform/platform_win32_msvc.cmake @@ -471,3 +471,15 @@ endif() # used in many places so include globally, like OpenGL blender_include_dirs_sys("${PTHREADS_INCLUDE_DIRS}") + +#find signtool +SET(ProgramFilesX86_NAME "ProgramFiles(x86)") #env dislikes the ( ) +find_program(SIGNTOOL_EXE signtool +HINTS + "$ENV{${ProgramFilesX86_NAME}}/Windows Kits/10/bin/x86/" + "$ENV{ProgramFiles}/Windows Kits/10/bin/x86/" + "$ENV{${ProgramFilesX86_NAME}}/Windows Kits/8.1/bin/x86/" + "$ENV{ProgramFiles}/Windows Kits/8.1/bin/x86/" + "$ENV{${ProgramFilesX86_NAME}}/Windows Kits/8.0/bin/x86/" + "$ENV{ProgramFiles}/Windows Kits/8.0/bin/x86/" +) diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index b34d4dfe6f1..9834cda43bc 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Blender # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "V2.7x" +PROJECT_NUMBER = "V2.8x" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/doc/python_api/sphinx_doc_update.py b/doc/python_api/sphinx_doc_update.py new file mode 100755 index 00000000000..c93f1676d52 --- /dev/null +++ b/doc/python_api/sphinx_doc_update.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 + +# ##### 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): Bastien Montagne +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +""" +This is a helper script to generate Blender Python API documentation (using Sphinx), and update server data using rsync. + +You'll need to specify your user login and password, obviously. + +Example usage: + + ./sphinx_doc_update.py --mirror ../../../docs/remote_api_backup/ --source ../.. --blender ../../../build_cmake/bin/blender --user foobar --password barfoo + +""" + +import os +import shutil +import subprocess +import sys +import tempfile +import zipfile + + +DEFAULT_RSYNC_SERVER = "www.blender.org" +DEFAULT_RSYNC_ROOT = "/api/" +DEFAULT_SYMLINK_ROOT = "/data/www/vhosts/www.blender.org/api" + + +def argparse_create(): + import argparse + global __doc__ + + # When --help or no args are given, print this help + usage_text = __doc__ + + parser = argparse.ArgumentParser(description=usage_text, + formatter_class=argparse.RawDescriptionHelpFormatter) + + parser.add_argument( + "--mirror", dest="mirror_dir", + metavar='PATH', required=True, + help="Path to local rsync mirror of api doc server") + parser.add_argument( + "--source", dest="source_dir", + metavar='PATH', required=True, + help="Path to Blender git repository") + parser.add_argument( + "--blender", dest="blender", + metavar='PATH', required=True, + help="Path to Blender executable") + parser.add_argument( + "--rsync-server", dest="rsync_server", default=DEFAULT_RSYNC_SERVER, + metavar='RSYNCSERVER', type=str, required=False, + help=("rsync server address")) + parser.add_argument( + "--rsync-root", dest="rsync_root", default=DEFAULT_RSYNC_ROOT, + metavar='RSYNCROOT', type=str, required=False, + help=("Root path of API doc on rsync server")) + parser.add_argument( + "--user", dest="user", + metavar='USER', type=str, required=True, + help=("User to login on rsync server")) + parser.add_argument( + "--password", dest="password", + metavar='PASSWORD', type=str, required=True, + help=("Password to login on rsync server")) + + return parser + + +def main(): + # ---------- + # Parse Args + + args = argparse_create().parse_args() + + rsync_base = "rsync://%s@%s:%s" % (args.user, args.rsync_server, args.rsync_root) + + # I) Update local mirror using rsync. + rsync_mirror_cmd = ("rsync", "--delete-after", "-avzz", rsync_base, args.mirror_dir) + subprocess.run(rsync_mirror_cmd, env=dict(os.environ, RSYNC_PASSWORD=args.password)) + + with tempfile.TemporaryDirectory() as tmp_dir: + # II) Generate doc source in temp dir. + doc_gen_cmd = (args.blender, "--background", "-noaudio", "--factory-startup", "--python-exit-code", "1", + "--python", "%s/doc/python_api/sphinx_doc_gen.py" % args.source_dir, "--", + "--output", tmp_dir) + subprocess.run(doc_gen_cmd) + + # III) Get Blender version info. + blenver = "" + getver_file = os.path.join(tmp_dir, "blendver.txt") + getver_script = ("" + "import sys, bpy\n" + "with open(sys.argv[-1], 'w') as f:\n" + " f.write('%d_%d%s_release' % (bpy.app.version[0], bpy.app.version[1], bpy.app.version_char)\n" + " if bpy.app.version_cycle in {'rc', 'release'} else '%d_%d_%d' % bpy.app.version)\n") + get_ver_cmd = (args.blender, "--background", "-noaudio", "--factory-startup", "--python-exit-code", "1", + "--python-expr", getver_script, "--", getver_file) + subprocess.run(get_ver_cmd) + with open(getver_file) as f: + blenver = f.read() + os.remove(getver_file) + + # IV) Build doc. + curr_dir = os.getcwd() + os.chdir(tmp_dir) + sphinx_cmd = ("sphinx-build", "-b", "html", "sphinx-in", "sphinx-out") + subprocess.run(sphinx_cmd) + shutil.rmtree(os.path.join("sphinx-out", ".doctrees")) + os.chdir(curr_dir) + + # V) Cleanup existing matching dir in server mirror (if any), and copy new doc. + api_name = "blender_python_api_%s" % blenver + api_dir = os.path.join(args.mirror_dir, api_name) + if os.path.exists(api_dir): + shutil.rmtree(api_dir) + os.rename(os.path.join(tmp_dir, "sphinx-out"), api_dir) + + # VI) Create zip archive. + zip_name = "blender_python_reference_%s" % blenver + zip_path = os.path.join(args.mirror_dir, zip_name) + with zipfile.ZipFile(zip_path, 'w') as zf: + for de in os.scandir(api_dir): + zf.write(de.path, arcname=os.path.join(zip_name, de.name)) + os.rename(zip_path, os.path.join(api_dir, "%s.zip" % zip_name)) + + # VII) Create symlinks and html redirects. + #~ os.symlink(os.path.join(DEFAULT_SYMLINK_ROOT, api_name, "contents.html"), os.path.join(api_dir, "index.html")) + os.symlink("./contents.html", os.path.join(api_dir, "index.html")) + if blenver.endswith("release"): + symlink = os.path.join(args.mirror_dir, "blender_python_api_current") + os.remove(symlink) + os.symlink("./%s" % api_name, symlink) + with open(os.path.join(args.mirror_dir, "250PythonDoc/index.html"), 'w') as f: + f.write("<html><head><title>Redirecting...</title><meta http-equiv=\"REFRESH\"" + "content=\"0;url=../%s/\"></head><body>Redirecting...</body></html>" % api_name) + else: + symlink = os.path.join(args.mirror_dir, "blender_python_api_master") + os.remove(symlink) + os.symlink("./%s" % api_name, symlink) + with open(os.path.join(args.mirror_dir, "blender_python_api/index.html"), 'w') as f: + f.write("<html><head><title>Redirecting...</title><meta http-equiv=\"REFRESH\"" + "content=\"0;url=../%s/\"></head><body>Redirecting...</body></html>" % api_name) + + # VIII) Upload (first do a dry-run so user can ensure everything is OK). + print("Doc generated in local mirror %s, please check it before uploading " + "(hit [Enter] to continue, [Ctrl-C] to exit):" % api_dir) + sys.stdin.read(1) + + rsync_mirror_cmd = ("rsync", "--dry-run", "--delete-after", "-avzz", args.mirror_dir, rsync_base) + subprocess.run(rsync_mirror_cmd, env=dict(os.environ, RSYNC_PASSWORD=args.password)) + + print("Rsync upload simulated, please check every thing is OK (hit [Enter] to continue, [Ctrl-C] to exit):") + sys.stdin.read(1) + + rsync_mirror_cmd = ("rsync", "--delete-after", "-avzz", args.mirror_dir, rsync_base) + subprocess.run(rsync_mirror_cmd, env=dict(os.environ, RSYNC_PASSWORD=args.password)) + + +if __name__ == "__main__": + main() diff --git a/extern/bullet2/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp b/extern/bullet2/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp index 2c362778210..c1da9f36cfb 100644 --- a/extern/bullet2/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp +++ b/extern/bullet2/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp @@ -151,8 +151,8 @@ static btScalar EdgeSeparation(const btBox2dShape* poly1, const btTransform& xf1 int index = 0; btScalar minDot = BT_LARGE_FLOAT; - if( count2 > 0 ) - index = (int) normal1.minDot( vertices2, count2, minDot); + if( count2 > 0 ) + index = (int) normal1.minDot( vertices2, count2, minDot); btVector3 v1 = b2Mul(xf1, vertices1[edge1]); btVector3 v2 = b2Mul(xf2, vertices2[index]); @@ -174,9 +174,9 @@ static btScalar FindMaxSeparation(int* edgeIndex, // Find edge normal on poly1 that has the largest projection onto d. int edge = 0; - btScalar maxDot; - if( count1 > 0 ) - edge = (int) dLocal1.maxDot( normals1, count1, maxDot); + btScalar maxDot; + if( count1 > 0 ) + edge = (int) dLocal1.maxDot( normals1, count1, maxDot); // Get the separation for the edge normal. btScalar s = EdgeSeparation(poly1, xf1, edge, poly2, xf2); diff --git a/extern/bullet2/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp b/extern/bullet2/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp index a80c438d12a..589a7f55f2b 100644 --- a/extern/bullet2/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp +++ b/extern/bullet2/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp @@ -232,8 +232,8 @@ void btCompoundCollisionAlgorithm::processCollision (const btCollisionObjectWrap m_compoundShapeRevision = compoundShape->getUpdateRevision(); } - if (m_childCollisionAlgorithms.size()==0) - return; + if (m_childCollisionAlgorithms.size()==0) + return; const btDbvt* tree = compoundShape->getDynamicAabbTree(); //use a dynamic aabb tree to cull potential child-overlaps diff --git a/extern/curve_fit_nd/intern/curve_fit_cubic_refit.c b/extern/curve_fit_nd/intern/curve_fit_cubic_refit.c index b51535bab10..d22d042bff5 100644 --- a/extern/curve_fit_nd/intern/curve_fit_cubic_refit.c +++ b/extern/curve_fit_nd/intern/curve_fit_cubic_refit.c @@ -1048,7 +1048,6 @@ int curve_fit_cubic_to_points_refit_db( { const uint knots_len = points_len; struct Knot *knots = malloc(sizeof(Knot) * knots_len); - knots[0].next = NULL; #ifndef USE_CORNER_DETECT (void)r_corner_index_array; @@ -1085,7 +1084,6 @@ int curve_fit_cubic_to_points_refit_db( knots[i].heap_node = NULL; knots[i].index = i; - knots[i].index = i; knots[i].can_remove = true; knots[i].is_removed = false; knots[i].is_corner = false; @@ -1155,8 +1153,8 @@ int curve_fit_cubic_to_points_refit_db( add_vn_vnvn(k->tan[0], tan_prev, tan_next, dims); normalize_vn(k->tan[0], dims); copy_vnvn(k->tan[1], k->tan[0], dims); - k->handles[0] = len_prev / 3; - k->handles[1] = len_next / 3; + k->handles[0] = len_prev / 3; + k->handles[1] = len_next / -3; } #else if (knots_len < 2) { @@ -1185,8 +1183,8 @@ int curve_fit_cubic_to_points_refit_db( add_vn_vnvn(k->tan[0], tan_prev, tan_next, dims); normalize_vn(k->tan[0], dims); copy_vnvn(k->tan[1], k->tan[0], dims); - k->handles[0] = len_prev / 3; - k->handles[1] = len_next / 3; + k->handles[0] = len_prev / 3; + k->handles[1] = len_next / -3; copy_vnvn(tan_prev, tan_next, dims); len_prev = len_next; @@ -1201,8 +1199,8 @@ int curve_fit_cubic_to_points_refit_db( tan_prev, &points[0 * dims], &points[1 * dims], dims); copy_vnvn(knots[0].tan[0], tan_prev, dims); copy_vnvn(knots[0].tan[1], tan_prev, dims); - knots[0].handles[0] = len_prev / 3; - knots[0].handles[1] = len_prev / 3; + knots[0].handles[0] = len_prev / 3; + knots[0].handles[1] = len_prev / -3; for (uint i_curr = 1, i_next = 2; i_next < knots_len; i_curr = i_next++) { struct Knot *k = &knots[i_curr]; @@ -1215,8 +1213,8 @@ int curve_fit_cubic_to_points_refit_db( add_vn_vnvn(k->tan[0], tan_prev, tan_next, dims); normalize_vn(k->tan[0], dims); copy_vnvn(k->tan[1], k->tan[0], dims); - k->handles[0] = len_prev / 3; - k->handles[1] = len_next / 3; + k->handles[0] = len_prev / 3; + k->handles[1] = len_next / -3; copy_vnvn(tan_prev, tan_next, dims); len_prev = len_next; @@ -1224,8 +1222,8 @@ int curve_fit_cubic_to_points_refit_db( copy_vnvn(knots[knots_len - 1].tan[0], tan_next, dims); copy_vnvn(knots[knots_len - 1].tan[1], tan_next, dims); - knots[knots_len - 1].handles[0] = len_next / 3; - knots[knots_len - 1].handles[1] = len_next / 3; + knots[knots_len - 1].handles[0] = len_next / 3; + knots[knots_len - 1].handles[1] = len_next / -3; } #endif } diff --git a/intern/cycles/blender/blender_particles.cpp b/intern/cycles/blender/blender_particles.cpp index b9876cd604f..dd2900a8d5b 100644 --- a/intern/cycles/blender/blender_particles.cpp +++ b/intern/cycles/blender/blender_particles.cpp @@ -36,6 +36,8 @@ bool BlenderSync::sync_dupli_particle(BL::Object& b_ob, if(!b_psys) return false; + object->hide_on_missing_motion = true; + /* test if we need particle data */ if(!object->mesh->need_attribute(scene, ATTR_STD_PARTICLE)) return false; diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 9317bfbb703..3c37bc0b045 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -113,6 +113,7 @@ set(SRC_SVM_HEADERS svm/svm.h svm/svm_attribute.h svm/svm_blackbody.h + svm/svm_bump.h svm/svm_camera.h svm/svm_closure.h svm/svm_convert.h diff --git a/intern/cycles/kernel/geom/geom_volume.h b/intern/cycles/kernel/geom/geom_volume.h index efe540a8518..fd97a63efb5 100644 --- a/intern/cycles/kernel/geom/geom_volume.h +++ b/intern/cycles/kernel/geom/geom_volume.h @@ -69,7 +69,7 @@ ccl_device float volume_attribute_float(KernelGlobals *kg, const ShaderData *sd, # if __CUDA_ARCH__ >= 300 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); + float4 r = make_float4(f, f, f, 1.0f); # else float4 r = volume_image_texture_3d(desc.offset, P.x, P.y, P.z); # endif diff --git a/intern/cycles/kernel/kernel_compat_cpu.h b/intern/cycles/kernel/kernel_compat_cpu.h index 3775934f293..7b30df04550 100644 --- a/intern/cycles/kernel/kernel_compat_cpu.h +++ b/intern/cycles/kernel/kernel_compat_cpu.h @@ -112,7 +112,7 @@ template<typename T> struct texture_image { ccl_always_inline float4 read(uchar r) { float f = r*(1.0f/255.0f); - return make_float4(f, f, f, 1.0); + return make_float4(f, f, f, 1.0f); } ccl_always_inline float4 read(float r) diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index deebf87c75f..0646148f6a0 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -714,20 +714,21 @@ enum ShaderDataFlag { SD_VOLUME_MIS = (1 << 19), /* use multiple importance sampling */ SD_VOLUME_CUBIC = (1 << 20), /* use cubic interpolation for voxels */ SD_HAS_BUMP = (1 << 21), /* has data connected to the displacement input */ + SD_HAS_DISPLACEMENT = (1 << 22), /* has true displacement */ SD_SHADER_FLAGS = (SD_USE_MIS|SD_HAS_TRANSPARENT_SHADOW|SD_HAS_VOLUME| SD_HAS_ONLY_VOLUME|SD_HETEROGENEOUS_VOLUME| SD_HAS_BSSRDF_BUMP|SD_VOLUME_EQUIANGULAR|SD_VOLUME_MIS| - SD_VOLUME_CUBIC|SD_HAS_BUMP), + SD_VOLUME_CUBIC|SD_HAS_BUMP|SD_HAS_DISPLACEMENT), /* object flags */ - SD_HOLDOUT_MASK = (1 << 22), /* holdout for camera rays */ - SD_OBJECT_MOTION = (1 << 23), /* has object motion blur */ - SD_TRANSFORM_APPLIED = (1 << 24), /* vertices have transform applied */ - SD_NEGATIVE_SCALE_APPLIED = (1 << 25), /* vertices have negative scale applied */ - SD_OBJECT_HAS_VOLUME = (1 << 26), /* object has a volume shader */ - SD_OBJECT_INTERSECTS_VOLUME = (1 << 27), /* object intersects AABB of an object with volume shader */ - SD_OBJECT_HAS_VERTEX_MOTION = (1 << 28), /* has position for motion vertices */ + SD_HOLDOUT_MASK = (1 << 23), /* holdout for camera rays */ + SD_OBJECT_MOTION = (1 << 24), /* has object motion blur */ + SD_TRANSFORM_APPLIED = (1 << 25), /* vertices have transform applied */ + SD_NEGATIVE_SCALE_APPLIED = (1 << 26), /* vertices have negative scale applied */ + SD_OBJECT_HAS_VOLUME = (1 << 27), /* object has a volume shader */ + SD_OBJECT_INTERSECTS_VOLUME = (1 << 28), /* object intersects AABB of an object with volume shader */ + SD_OBJECT_HAS_VERTEX_MOTION = (1 << 29), /* has position for motion vertices */ SD_OBJECT_FLAGS = (SD_HOLDOUT_MASK|SD_OBJECT_MOTION|SD_TRANSFORM_APPLIED| SD_NEGATIVE_SCALE_APPLIED|SD_OBJECT_HAS_VOLUME| diff --git a/intern/cycles/kernel/osl/osl_globals.h b/intern/cycles/kernel/osl/osl_globals.h index 8353c4e434b..65cb7ecc6b4 100644 --- a/intern/cycles/kernel/osl/osl_globals.h +++ b/intern/cycles/kernel/osl/osl_globals.h @@ -54,6 +54,7 @@ struct OSLGlobals { vector<OSL::ShaderGroupRef> surface_state; vector<OSL::ShaderGroupRef> volume_state; vector<OSL::ShaderGroupRef> displacement_state; + vector<OSL::ShaderGroupRef> bump_state; OSL::ShaderGroupRef background_state; /* attributes */ diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index 153ebad6cd2..f61a9ec0fb1 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -93,6 +93,7 @@ ustring OSLRenderServices::u_geom_numpolyvertices("geom:numpolyvertices"); ustring OSLRenderServices::u_geom_trianglevertices("geom:trianglevertices"); ustring OSLRenderServices::u_geom_polyvertices("geom:polyvertices"); ustring OSLRenderServices::u_geom_name("geom:name"); +ustring OSLRenderServices::u_geom_undisplaced("geom:undisplaced"); ustring OSLRenderServices::u_is_smooth("geom:is_smooth"); #ifdef __HAIR__ ustring OSLRenderServices::u_is_curve("geom:is_curve"); @@ -127,8 +128,10 @@ OSLRenderServices::OSLRenderServices() OSLRenderServices::~OSLRenderServices() { - VLOG(2) << "OSL texture system stats:\n" - << osl_ts->getstats(); + if(osl_ts) { + VLOG(2) << "OSL texture system stats:\n" + << osl_ts->getstats(); + } #ifdef WITH_PTEX ptex_cache->release(); #endif diff --git a/intern/cycles/kernel/osl/osl_services.h b/intern/cycles/kernel/osl/osl_services.h index 2701abb483c..0f2e02c62b0 100644 --- a/intern/cycles/kernel/osl/osl_services.h +++ b/intern/cycles/kernel/osl/osl_services.h @@ -158,6 +158,7 @@ public: static ustring u_geom_trianglevertices; static ustring u_geom_polyvertices; static ustring u_geom_name; + static ustring u_geom_undisplaced; static ustring u_is_smooth; static ustring u_is_curve; static ustring u_curve_thickness; diff --git a/intern/cycles/kernel/osl/osl_shader.cpp b/intern/cycles/kernel/osl/osl_shader.cpp index 20dd167708c..0d762bbdb38 100644 --- a/intern/cycles/kernel/osl/osl_shader.cpp +++ b/intern/cycles/kernel/osl/osl_shader.cpp @@ -184,6 +184,48 @@ void OSLShader::eval_surface(KernelGlobals *kg, ShaderData *sd, PathState *state OSL::ShadingContext *octx = tdata->context[(int)ctx]; int shader = sd->shader & SHADER_MASK; + /* automatic bump shader */ + if(kg->osl->bump_state[shader]) { + /* save state */ + float3 P = sd->P; + float3 dPdx = sd->dP.dx; + float3 dPdy = sd->dP.dy; + + /* set state as if undisplaced */ + if(sd->flag & SD_HAS_DISPLACEMENT) { + float data[9]; + bool found = kg->osl->services->get_attribute(sd, true, OSLRenderServices::u_empty, TypeDesc::TypeVector, + OSLRenderServices::u_geom_undisplaced, data); + (void)found; + assert(found); + + memcpy(&sd->P, data, sizeof(float)*3); + memcpy(&sd->dP.dx, data+3, sizeof(float)*3); + memcpy(&sd->dP.dy, data+6, sizeof(float)*3); + + object_position_transform(kg, sd, &sd->P); + object_dir_transform(kg, sd, &sd->dP.dx); + object_dir_transform(kg, sd, &sd->dP.dy); + + globals->P = TO_VEC3(sd->P); + globals->dPdx = TO_VEC3(sd->dP.dx); + globals->dPdy = TO_VEC3(sd->dP.dy); + } + + /* execute bump shader */ + ss->execute(octx, *(kg->osl->bump_state[shader]), *globals); + + /* reset state */ + sd->P = P; + sd->dP.dx = dPdx; + sd->dP.dy = dPdy; + + globals->P = TO_VEC3(P); + globals->dPdx = TO_VEC3(dPdx); + globals->dPdy = TO_VEC3(dPdy); + } + + /* surface shader */ if(kg->osl->surface_state[shader]) { ss->execute(octx, *(kg->osl->surface_state[shader]), *globals); } diff --git a/intern/cycles/kernel/shaders/node_normal_map.osl b/intern/cycles/kernel/shaders/node_normal_map.osl index f95e9fcfe3c..41bcac4fb10 100644 --- a/intern/cycles/kernel/shaders/node_normal_map.osl +++ b/intern/cycles/kernel/shaders/node_normal_map.osl @@ -26,6 +26,7 @@ shader node_normal_map( output normal Normal = NormalIn) { color mcolor = 2.0 * color(Color[0] - 0.5, Color[1] - 0.5, Color[2] - 0.5); + int is_backfacing = backfacing(); if (space == "tangent") { vector tangent; @@ -34,9 +35,15 @@ shader node_normal_map( float is_smooth; getattribute("geom:is_smooth", is_smooth); - if (!is_smooth) + if (!is_smooth) { ninterp = normalize(transform("world", "object", Ng)); + /* the normal is already inverted, which is too soon for the math here */ + if (is_backfacing) { + ninterp = -ninterp; + } + } + // get _unnormalized_ interpolated normal and tangent if (getattribute(attr_name, tangent) && getattribute(attr_sign_name, tangent_sign) && @@ -73,7 +80,12 @@ shader node_normal_map( Normal = normalize(vector(mcolor)); } - + + /* invert normal for backfacing polygons */ + if (is_backfacing) { + Normal = -Normal; + } + if (Strength != 1.0) Normal = normalize(NormalIn + (Normal - NormalIn) * max(Strength, 0.0)); } diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 502994e71f1..9ca8917d327 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -181,6 +181,7 @@ CCL_NAMESPACE_END #include "svm_brick.h" #include "svm_vector_transform.h" #include "svm_voxel.h" +#include "svm_bump.h" CCL_NAMESPACE_BEGIN @@ -294,6 +295,14 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, ccl_a case NODE_CLOSURE_SET_NORMAL: svm_node_set_normal(kg, sd, stack, node.y, node.z); break; +# if NODES_FEATURE(NODE_FEATURE_BUMP_STATE) + case NODE_ENTER_BUMP_EVAL: + svm_node_enter_bump_eval(kg, sd, stack, node.y); + break; + case NODE_LEAVE_BUMP_EVAL: + svm_node_leave_bump_eval(kg, sd, stack, node.y); + break; +# endif /* NODES_FEATURE(NODE_FEATURE_BUMP_STATE) */ # endif /* NODES_FEATURE(NODE_FEATURE_BUMP) */ case NODE_HSV: svm_node_hsv(kg, sd, stack, node, &offset); diff --git a/intern/cycles/kernel/svm/svm_bump.h b/intern/cycles/kernel/svm/svm_bump.h new file mode 100644 index 00000000000..04a8c7b64e5 --- /dev/null +++ b/intern/cycles/kernel/svm/svm_bump.h @@ -0,0 +1,54 @@ +/* + * 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. + */ + +CCL_NAMESPACE_BEGIN + +/* Bump Eval Nodes */ + +ccl_device void svm_node_enter_bump_eval(KernelGlobals *kg, ShaderData *sd, float *stack, uint offset) +{ + /* save state */ + stack_store_float3(stack, offset+0, ccl_fetch(sd, P)); + stack_store_float3(stack, offset+3, ccl_fetch(sd, dP).dx); + stack_store_float3(stack, offset+6, ccl_fetch(sd, dP).dy); + + /* set state as if undisplaced */ + const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_POSITION_UNDISPLACED); + + if(desc.offset != ATTR_STD_NOT_FOUND) { + float3 P, dPdx, dPdy; + P = primitive_attribute_float3(kg, sd, desc, &dPdx, &dPdy); + + object_position_transform(kg, sd, &P); + object_dir_transform(kg, sd, &dPdx); + object_dir_transform(kg, sd, &dPdy); + + ccl_fetch(sd, P) = P; + ccl_fetch(sd, dP).dx = dPdx; + ccl_fetch(sd, dP).dy = dPdy; + } +} + +ccl_device void svm_node_leave_bump_eval(KernelGlobals *kg, ShaderData *sd, float *stack, uint offset) +{ + /* restore state */ + ccl_fetch(sd, P) = stack_load_float3(stack, offset+0); + ccl_fetch(sd, dP).dx = stack_load_float3(stack, offset+3); + ccl_fetch(sd, dP).dy = stack_load_float3(stack, offset+6); +} + +CCL_NAMESPACE_END + diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/svm_image.h index 5d02be1fa2f..378ce650129 100644 --- a/intern/cycles/kernel/svm/svm_image.h +++ b/intern/cycles/kernel/svm/svm_image.h @@ -296,7 +296,7 @@ ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y, /* float, byte and half */ else { float f = kernel_tex_image_interp_float(tex, x, y); - r = make_float4(f, f, f, 1.0); + r = make_float4(f, f, f, 1.0f); } # endif #endif diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/svm_tex_coord.h index 01dede3fff5..6ea2539c543 100644 --- a/intern/cycles/kernel/svm/svm_tex_coord.h +++ b/intern/cycles/kernel/svm/svm_tex_coord.h @@ -277,6 +277,7 @@ ccl_device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *st float3 color = stack_load_float3(stack, color_offset); color = 2.0f*make_float3(color.x - 0.5f, color.y - 0.5f, color.z - 0.5f); + bool is_backfacing = (ccl_fetch(sd, flag) & SD_BACKFACING) != 0; float3 N; if(space == NODE_NORMAL_MAP_TANGENT) { @@ -306,6 +307,12 @@ ccl_device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *st } else { normal = ccl_fetch(sd, Ng); + + /* the normal is already inverted, which is too soon for the math here */ + if(is_backfacing) { + normal = -normal; + } + object_inverse_normal_transform(kg, sd, &normal); } @@ -332,6 +339,11 @@ ccl_device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *st N = safe_normalize(N); } + /* invert normal for backfacing polygons */ + if(is_backfacing) { + N = -N; + } + float strength = stack_load_float(stack, strength_offset); if(strength != 1.0f) { diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index 51083c31708..5adf7d34f7f 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -26,6 +26,8 @@ CCL_NAMESPACE_BEGIN /* SVM stack offsets with this value indicate that it's not on the stack */ #define SVM_STACK_INVALID 255 +#define SVM_BUMP_EVAL_STATE_SIZE 9 + /* Nodes */ /* Known frequencies of used nodes, used for selective nodes compilation @@ -45,11 +47,12 @@ CCL_NAMESPACE_BEGIN #define NODE_FEATURE_VOLUME (1 << 0) #define NODE_FEATURE_HAIR (1 << 1) #define NODE_FEATURE_BUMP (1 << 2) +#define NODE_FEATURE_BUMP_STATE (1 << 3) /* TODO(sergey): Consider using something like ((uint)(-1)). * Need to check carefully operand types around usage of this * define first. */ -#define NODE_FEATURE_ALL (NODE_FEATURE_VOLUME|NODE_FEATURE_HAIR|NODE_FEATURE_BUMP) +#define NODE_FEATURE_ALL (NODE_FEATURE_VOLUME|NODE_FEATURE_HAIR|NODE_FEATURE_BUMP|NODE_FEATURE_BUMP_STATE) typedef enum ShaderNodeType { NODE_END = 0, @@ -127,6 +130,8 @@ typedef enum ShaderNodeType { NODE_HAIR_INFO, NODE_UVMAP, NODE_TEX_VOXEL, + NODE_ENTER_BUMP_EVAL, + NODE_LEAVE_BUMP_EVAL, } ShaderNodeType; typedef enum NodeAttributeType { @@ -374,7 +379,8 @@ typedef enum NodeTexVoxelSpace { typedef enum ShaderType { SHADER_TYPE_SURFACE, SHADER_TYPE_VOLUME, - SHADER_TYPE_DISPLACEMENT + SHADER_TYPE_DISPLACEMENT, + SHADER_TYPE_BUMP, } ShaderType; /* Closure */ diff --git a/intern/cycles/kernel/svm/svm_voxel.h b/intern/cycles/kernel/svm/svm_voxel.h index d2cc2c3730e..f54f4e8e888 100644 --- a/intern/cycles/kernel/svm/svm_voxel.h +++ b/intern/cycles/kernel/svm/svm_voxel.h @@ -50,7 +50,7 @@ ccl_device void svm_node_tex_voxel(KernelGlobals *kg, r = kernel_tex_image_interp_3d_float4(tex, co.x, co.y, co.z); else { float f = kernel_tex_image_interp_3d_float(tex, co.x, co.y, co.z); - r = make_float4(f, f, f, 1.0); + r = make_float4(f, f, f, 1.0f); } # else /* __CUDA_ARCH__ >= 300 */ r = volume_image_texture_3d(id, co.x, co.y, co.z); diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp index 6e795ef896a..57256ceecd3 100644 --- a/intern/cycles/render/graph.cpp +++ b/intern/cycles/render/graph.cpp @@ -856,27 +856,8 @@ void ShaderGraph::bump_from_displacement() /* connect the bump out to the set normal in: */ connect(bump->output("Normal"), set_normal->input("Direction")); - /* connect bump output to normal input nodes that aren't set yet. actually - * this will only set the normal input to the geometry node that we created - * and connected to all other normal inputs already. */ - foreach(ShaderNode *node, nodes) { - /* Don't connect normal to the bump node we're coming from, - * otherwise it'll be a cycle in graph. - */ - if(node == bump) { - continue; - } - foreach(ShaderInput *input, node->inputs) { - if(!input->link && (input->flags() & SocketType::LINK_NORMAL)) - connect(set_normal->output("Normal"), input); - } - } - - /* for displacement bump, clear the normal input in case the above loop - * connected the setnormal out to the bump normalin */ - ShaderInput *bump_normal_in = bump->input("Normal"); - if(bump_normal_in) - bump_normal_in->link = NULL; + /* connect to output node */ + connect(set_normal->output("Normal"), output()->input("Normal")); /* finally, add the copied nodes to the graph. we can't do this earlier * because we would create dependency cycles in the above loop */ diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index 4cd77f8c6e1..787e5cf07b2 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -177,6 +177,16 @@ LightManager::~LightManager() { } +bool LightManager::has_background_light(Scene *scene) +{ + foreach(Light *light, scene->lights) { + if(light->type == LIGHT_BACKGROUND) { + return true; + } + } + return false; +} + void LightManager::disable_ineffective_light(Device *device, Scene *scene) { /* Make all lights enabled by default, and perform some preliminary checks diff --git a/intern/cycles/render/light.h b/intern/cycles/render/light.h index 745caa96159..040a672937d 100644 --- a/intern/cycles/render/light.h +++ b/intern/cycles/render/light.h @@ -91,6 +91,9 @@ public: void tag_update(Scene *scene); + /* Check whether there is a background light. */ + bool has_background_light(Scene *scene); + protected: /* Optimization: disable light which is either unsupported or * which doesn't contribute to the scene or which is only used for MIS diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index 35d1a441975..2d297c33446 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -567,10 +567,11 @@ void Mesh::add_vertex_normals() for(size_t i = 0; i < subd_faces.size(); i++) { SubdFace& face = subd_faces[i]; + float3 fN = face.normal(this); for(size_t j = 0; j < face.num_corners; j++) { size_t corner = subd_face_corners[face.start_corner+j]; - vN[corner] += verts[corner]; + vN[corner] += fN; } } @@ -583,6 +584,28 @@ void Mesh::add_vertex_normals() } } +void Mesh::add_undisplaced() +{ + AttributeSet& attrs = (subdivision_type == SUBDIVISION_NONE) ? attributes : subd_attributes; + + /* don't compute if already there */ + if(attrs.find(ATTR_STD_POSITION_UNDISPLACED)) { + return; + } + + /* get attribute */ + Attribute *attr = attrs.add(ATTR_STD_POSITION_UNDISPLACED); + attr->flags |= ATTR_SUBDIVIDED; + + float3 *data = attr->data_float3(); + + /* copy verts */ + size_t size = attr->buffer_size(this, (subdivision_type == SUBDIVISION_NONE) ? ATTR_PRIM_TRIANGLE : ATTR_PRIM_SUBD); + if(size) { + memcpy(data, verts.data(), size); + } +} + void Mesh::pack_normals(Scene *scene, uint *tri_shader, float4 *vnormal) { Attribute *attr_vN = attributes.find(ATTR_STD_VERTEX_NORMAL); @@ -609,7 +632,7 @@ void Mesh::pack_normals(Scene *scene, uint *tri_shader, float4 *vnormal) last_smooth = smooth[i]; Shader *shader = (last_shader < used_shaders.size()) ? used_shaders[last_shader] : scene->default_surface; - shader_id = scene->shader_manager->get_shader_id(shader, this, last_smooth); + shader_id = scene->shader_manager->get_shader_id(shader, last_smooth); } tri_shader[i] = shader_id; @@ -678,7 +701,7 @@ void Mesh::pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, s int shader_id = curve_shader[i]; Shader *shader = (shader_id < used_shaders.size()) ? used_shaders[shader_id] : scene->default_surface; - shader_id = scene->shader_manager->get_shader_id(shader, this, false); + shader_id = scene->shader_manager->get_shader_id(shader, false); curve_data[i] = make_float4( __int_as_float(curve.first_key + curvekey_offset), @@ -1627,10 +1650,10 @@ void MeshManager::device_update_displacement_images(Device *device, foreach(Mesh *mesh, scene->meshes) { if(mesh->need_update) { foreach(Shader *shader, mesh->used_shaders) { - if(shader->graph_bump == NULL) { + if(!shader->has_displacement || shader->displacement_method == DISPLACE_BUMP) { continue; } - foreach(ShaderNode* node, shader->graph_bump->nodes) { + foreach(ShaderNode* node, shader->graph->nodes) { if(node->special_type != SHADER_SPECIAL_TYPE_IMAGE_SLOT) { continue; } @@ -1681,6 +1704,10 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen mesh->add_face_normals(); mesh->add_vertex_normals(); + if(mesh->need_attribute(scene, ATTR_STD_POSITION_UNDISPLACED)) { + mesh->add_undisplaced(); + } + if(progress.get_cancel()) return; } } diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index a77e296ea4a..c0310f45840 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -215,6 +215,7 @@ public: void compute_bounds(); void add_face_normals(); void add_vertex_normals(); + void add_undisplaced(); void pack_normals(Scene *scene, uint *shader, float4 *vnormal); void pack_verts(const vector<uint>& tri_prim_index, diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index b0eb2395adf..fd349f4b7e8 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -770,6 +770,7 @@ class CameraNode : public ShaderNode { public: SHADER_NODE_CLASS(CameraNode) bool has_spatial_varying() { return true; } + virtual int get_group() { return NODE_GROUP_LEVEL_2; } }; class FresnelNode : public ShaderNode { diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index db44327e24c..d8f3ce58505 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -45,6 +45,7 @@ NODE_DEFINE(Object) SOCKET_UINT(random_id, "Random ID", 0); SOCKET_INT(pass_id, "Pass ID", 0); SOCKET_BOOLEAN(use_holdout, "Use Holdout", false); + SOCKET_BOOLEAN(hide_on_missing_motion, "Hide on Missing Motion", false); SOCKET_POINT(dupli_generated, "Dupli Generated", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_POINT2(dupli_uv, "Dupli UV", make_float2(0.0f, 0.0f)); @@ -72,28 +73,41 @@ void Object::compute_bounds(bool motion_blur) BoundBox mbounds = mesh->bounds; if(motion_blur && use_motion) { - if(motion.pre == transform_empty() || - motion.post == transform_empty()) { + MotionTransform mtfm = motion; + + if(hide_on_missing_motion) { /* 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; + if(mtfm.pre == transform_empty() || + mtfm.post == transform_empty()) { + bounds = BoundBox::empty; + return; + } + } + + /* In case of missing motion information for previous/next frame, + * assume there is no motion. */ + if(mtfm.pre == transform_empty()) { + mtfm.pre = tfm; + } + if(mtfm.post == transform_empty()) { + mtfm.post = tfm; } - else { - DecompMotionTransform decomp; - transform_motion_decompose(&decomp, &motion, &tfm); - bounds = BoundBox::empty; + DecompMotionTransform decomp; + transform_motion_decompose(&decomp, &mtfm, &tfm); - /* 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; + bounds = BoundBox::empty; - transform_motion_interpolate(&ttfm, &decomp, t); - bounds.grow(mbounds.transformed(&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)); } } else { @@ -345,28 +359,27 @@ void ObjectManager::device_update_object_transform(UpdateObejctTransformState *s * comes with deformed position in object space, or if we transform * the shading point in world space. */ - Transform mtfm_pre = ob->motion.pre; - Transform mtfm_post = ob->motion.post; + MotionTransform mtfm = ob->motion; /* 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.pre == transform_empty()) { + mtfm.pre = ob->tfm; } - if(!ob->use_motion || mtfm_post == transform_empty()) { - mtfm_post = 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; + mtfm.pre = mtfm.pre * itfm; + mtfm.post = mtfm.post * itfm; } else { flag |= SD_OBJECT_HAS_VERTEX_MOTION; } - memcpy(&objects_vector[object_index*OBJECT_VECTOR_SIZE+0], &mtfm_pre, sizeof(float4)*3); - memcpy(&objects_vector[object_index*OBJECT_VECTOR_SIZE+3], &mtfm_post, sizeof(float4)*3); + memcpy(&objects_vector[object_index*OBJECT_VECTOR_SIZE+0], &mtfm.pre, sizeof(float4)*3); + memcpy(&objects_vector[object_index*OBJECT_VECTOR_SIZE+3], &mtfm.post, sizeof(float4)*3); } #ifdef __OBJECT_MOTION__ else if(state->need_motion == Scene::MOTION_BLUR) { diff --git a/intern/cycles/render/object.h b/intern/cycles/render/object.h index 2e5837f672f..7e306fab2a8 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/render/object.h @@ -51,6 +51,7 @@ public: uint visibility; MotionTransform motion; bool use_motion; + bool hide_on_missing_motion; bool use_holdout; float3 dupli_generated; diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index 1a6ae5f9277..f83aab47e84 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -609,7 +609,7 @@ bool OSLCompiler::node_skip_input(ShaderNode *node, ShaderInput *input) return true; if(input->name() == "Displacement" && current_type != SHADER_TYPE_DISPLACEMENT) return true; - if(input->name() == "Normal") + if(input->name() == "Normal" && current_type != SHADER_TYPE_BUMP) return true; } else if(node->special_type == SHADER_SPECIAL_TYPE_BUMP) { @@ -684,6 +684,8 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath) ss->Shader("surface", name, id(node).c_str()); else if(current_type == SHADER_TYPE_DISPLACEMENT) ss->Shader("displacement", name, id(node).c_str()); + else if(current_type == SHADER_TYPE_BUMP) + ss->Shader("displacement", name, id(node).c_str()); else assert(0); @@ -1055,6 +1057,12 @@ OSL::ShaderGroupRef OSLCompiler::compile_type(Shader *shader, ShaderGraph *graph generate_nodes(dependencies); output->compile(*this); } + else if(type == SHADER_TYPE_BUMP) { + /* generate bump shader */ + find_dependencies(dependencies, output->input("Normal")); + generate_nodes(dependencies); + output->compile(*this); + } else if(type == SHADER_TYPE_VOLUME) { /* generate volume shader */ find_dependencies(dependencies, output->input("Volume")); @@ -1116,10 +1124,10 @@ void OSLCompiler::compile(Scene *scene, OSLGlobals *og, Shader *shader) if(shader->used && graph && output->input("Surface")->link) { shader->osl_surface_ref = compile_type(shader, shader->graph, SHADER_TYPE_SURFACE); - if(shader->graph_bump) - shader->osl_surface_bump_ref = compile_type(shader, shader->graph_bump, SHADER_TYPE_SURFACE); + if(shader->graph_bump && shader->displacement_method != DISPLACE_TRUE) + shader->osl_surface_bump_ref = compile_type(shader, shader->graph_bump, SHADER_TYPE_BUMP); else - shader->osl_surface_bump_ref = shader->osl_surface_ref; + shader->osl_surface_bump_ref = OSL::ShaderGroupRef(); shader->has_surface = true; } @@ -1147,13 +1155,9 @@ void OSLCompiler::compile(Scene *scene, OSLGlobals *og, Shader *shader) /* push state to array for lookup */ og->surface_state.push_back(shader->osl_surface_ref); - og->surface_state.push_back(shader->osl_surface_bump_ref); - - og->volume_state.push_back(shader->osl_volume_ref); og->volume_state.push_back(shader->osl_volume_ref); - - og->displacement_state.push_back(shader->osl_displacement_ref); og->displacement_state.push_back(shader->osl_displacement_ref); + og->bump_state.push_back(shader->osl_surface_bump_ref); } #else diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index d000cca5a45..70e1443be2c 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -221,6 +221,16 @@ void Shader::tag_update(Scene *scene) if(use_mis && has_surface_emission) scene->light_manager->need_update = true; + /* Special handle of background MIS light for now: for some reason it + * has use_mis set to false. We are quite close to release now, so + * better to be safe. + */ + if(this == scene->default_background && + scene->light_manager->has_background_light(scene)) + { + scene->light_manager->need_update = true; + } + /* quick detection of which kind of shaders we have to avoid loading * e.g. surface attributes when there is only a volume shader. this could * be more fine grained but it's better than nothing */ @@ -240,6 +250,10 @@ void Shader::tag_update(Scene *scene) attributes.clear(); foreach(ShaderNode *node, graph->nodes) node->attributes(this, &attributes); + + if(has_displacement && displacement_method == DISPLACE_BOTH) { + attributes.add(ATTR_STD_POSITION_UNDISPLACED); + } /* compare if the attributes changed, mesh manager will check * need_update_attributes, update the relevant meshes and clear it. */ @@ -312,14 +326,11 @@ uint ShaderManager::get_attribute_id(AttributeStandard std) return (uint)std; } -int ShaderManager::get_shader_id(Shader *shader, Mesh *mesh, bool smooth) +int ShaderManager::get_shader_id(Shader *shader, bool smooth) { /* get a shader id to pass to the kernel */ - int id = shader->id*2; - - /* index depends bump since this setting is not in the shader */ - if(mesh && shader->displacement_method != DISPLACE_TRUE) - id += 1; + int id = shader->id; + /* smooth flag */ if(smooth) id |= SHADER_SMOOTH_NORMAL; @@ -368,7 +379,7 @@ void ShaderManager::device_update_common(Device *device, if(scene->shaders.size() == 0) return; - uint shader_flag_size = scene->shaders.size()*4; + uint shader_flag_size = scene->shaders.size()*2; uint *shader_flag = dscene->shader_flag.resize(shader_flag_size); uint i = 0; bool has_volumes = false; @@ -406,15 +417,14 @@ void ShaderManager::device_update_common(Device *device, flag |= SD_VOLUME_CUBIC; if(shader->graph_bump) flag |= SD_HAS_BUMP; - - /* regular shader */ - shader_flag[i++] = flag; - shader_flag[i++] = shader->pass_id; + if(shader->displacement_method != DISPLACE_BUMP) + flag |= SD_HAS_DISPLACEMENT; /* shader with bump mapping */ - if(shader->graph_bump) + if(shader->displacement_method != DISPLACE_TRUE && shader->graph_bump) flag |= SD_HAS_BSSRDF_BUMP; + /* regular shader */ shader_flag[i++] = flag; shader_flag[i++] = shader->pass_id; @@ -551,6 +561,9 @@ void ShaderManager::get_requested_features(Scene *scene, ShaderNode *output_node = shader->graph->output(); if(output_node->input("Displacement")->link != NULL) { requested_features->nodes_features |= NODE_FEATURE_BUMP; + if(shader->displacement_method == DISPLACE_BOTH && requested_features->experimental) { + requested_features->nodes_features |= NODE_FEATURE_BUMP_STATE; + } } /* On top of volume nodes, also check if we need volume sampling because * e.g. an Emission node would slip through the NODE_FEATURE_VOLUME check */ diff --git a/intern/cycles/render/shader.h b/intern/cycles/render/shader.h index 060ad7056bc..696e22bc3c9 100644 --- a/intern/cycles/render/shader.h +++ b/intern/cycles/render/shader.h @@ -173,7 +173,7 @@ public: uint get_attribute_id(AttributeStandard std); /* get shader id for mesh faces */ - int get_shader_id(Shader *shader, Mesh *mesh = NULL, bool smooth = false); + int get_shader_id(Shader *shader, bool smooth = false); /* add default shaders to scene, to use as default for things that don't * have any shader assigned explicitly */ diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp index 1a166885e2b..352bed8f0f2 100644 --- a/intern/cycles/render/svm.cpp +++ b/intern/cycles/render/svm.cpp @@ -63,7 +63,6 @@ void SVMShaderManager::device_update(Device *device, DeviceScene *dscene, Scene for(i = 0; i < scene->shaders.size(); i++) { svm_nodes.push_back(make_int4(NODE_SHADER_JUMP, 0, 0, 0)); - svm_nodes.push_back(make_int4(NODE_SHADER_JUMP, 0, 0, 0)); } foreach(Shader *shader, scene->shaders) { @@ -147,9 +146,8 @@ int SVMCompiler::stack_size(SocketType::Type type) return size; } -int SVMCompiler::stack_find_offset(SocketType::Type type) +int SVMCompiler::stack_find_offset(int size) { - int size = stack_size(type); int offset = -1; /* find free space in stack & mark as used */ @@ -176,6 +174,11 @@ int SVMCompiler::stack_find_offset(SocketType::Type type) return 0; } +int SVMCompiler::stack_find_offset(SocketType::Type type) +{ + return stack_find_offset(stack_size(type)); +} + void SVMCompiler::stack_clear_offset(SocketType::Type type, int offset) { int size = stack_size(type); @@ -648,6 +651,9 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty case SHADER_TYPE_DISPLACEMENT: clin = node->input("Displacement"); break; + case SHADER_TYPE_BUMP: + clin = node->input("Normal"); + break; default: assert(0); break; @@ -664,6 +670,14 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty output->stack_offset = SVM_STACK_INVALID; } + /* for the bump shader we need add a node to store the shader state */ + bool need_bump_state = (type == SHADER_TYPE_BUMP) && (shader->displacement_method == DISPLACE_BOTH); + int bump_state_offset = SVM_STACK_INVALID; + if(need_bump_state) { + bump_state_offset = stack_find_offset(SVM_BUMP_EVAL_STATE_SIZE); + add_node(NODE_ENTER_BUMP_EVAL, bump_state_offset); + } + if(shader->used) { if(clin->link) { bool generate = false; @@ -681,6 +695,9 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty generate = true; shader->has_displacement = true; break; + case SHADER_TYPE_BUMP: /* generate bump shader */ + generate = true; + break; default: break; } @@ -697,13 +714,21 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty node->compile(*this); } + /* add node to restore state after bump shader has finished */ + if(need_bump_state) { + add_node(NODE_LEAVE_BUMP_EVAL, bump_state_offset); + } + /* if compile failed, generate empty shader */ if(compile_failed) { svm_nodes.clear(); compile_failed = false; } - add_node(NODE_END, 0, 0, 0); + /* for bump shaders we fall thru to the surface shader, but if this is any other kind of shader it ends here */ + if(type != SHADER_TYPE_BUMP) { + add_node(NODE_END, 0, 0, 0); + } } void SVMCompiler::compile(Scene *scene, @@ -753,19 +778,22 @@ void SVMCompiler::compile(Scene *scene, shader->has_object_dependency = false; shader->has_integrator_dependency = false; + /* generate bump shader */ + if(shader->displacement_method != DISPLACE_TRUE && shader->graph_bump) { + scoped_timer timer((summary != NULL)? &summary->time_generate_bump: NULL); + compile_type(shader, shader->graph_bump, SHADER_TYPE_BUMP); + global_svm_nodes[index].y = global_svm_nodes.size(); + global_svm_nodes.insert(global_svm_nodes.end(), svm_nodes.begin(), svm_nodes.end()); + } + /* generate surface shader */ { scoped_timer timer((summary != NULL)? &summary->time_generate_surface: NULL); compile_type(shader, shader->graph, SHADER_TYPE_SURFACE); - global_svm_nodes[index*2 + 0].y = global_svm_nodes.size(); - global_svm_nodes[index*2 + 1].y = global_svm_nodes.size(); - global_svm_nodes.insert(global_svm_nodes.end(), svm_nodes.begin(), svm_nodes.end()); - } - - if(shader->graph_bump) { - scoped_timer timer((summary != NULL)? &summary->time_generate_bump: NULL); - compile_type(shader, shader->graph_bump, SHADER_TYPE_SURFACE); - global_svm_nodes[index*2 + 1].y = global_svm_nodes.size(); + /* only set jump offset if there's no bump shader, as the bump shader will fall thru to this one if it exists */ + if(shader->displacement_method == DISPLACE_TRUE || !shader->graph_bump) { + global_svm_nodes[index].y = global_svm_nodes.size(); + } global_svm_nodes.insert(global_svm_nodes.end(), svm_nodes.begin(), svm_nodes.end()); } @@ -773,8 +801,7 @@ void SVMCompiler::compile(Scene *scene, { scoped_timer timer((summary != NULL)? &summary->time_generate_volume: NULL); compile_type(shader, shader->graph, SHADER_TYPE_VOLUME); - global_svm_nodes[index*2 + 0].z = global_svm_nodes.size(); - global_svm_nodes[index*2 + 1].z = global_svm_nodes.size(); + global_svm_nodes[index].z = global_svm_nodes.size(); global_svm_nodes.insert(global_svm_nodes.end(), svm_nodes.begin(), svm_nodes.end()); } @@ -782,8 +809,7 @@ void SVMCompiler::compile(Scene *scene, { scoped_timer timer((summary != NULL)? &summary->time_generate_displacement: NULL); compile_type(shader, shader->graph, SHADER_TYPE_DISPLACEMENT); - global_svm_nodes[index*2 + 0].w = global_svm_nodes.size(); - global_svm_nodes[index*2 + 1].w = global_svm_nodes.size(); + global_svm_nodes[index].w = global_svm_nodes.size(); global_svm_nodes.insert(global_svm_nodes.end(), svm_nodes.begin(), svm_nodes.end()); } diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h index e14d57d7601..99e91ca0c3e 100644 --- a/intern/cycles/render/svm.h +++ b/intern/cycles/render/svm.h @@ -99,6 +99,7 @@ public: int stack_assign(ShaderInput *input); int stack_assign_if_linked(ShaderInput *input); int stack_assign_if_linked(ShaderOutput *output); + int stack_find_offset(int size); int stack_find_offset(SocketType::Type type); void stack_clear_offset(SocketType::Type type, int offset); void stack_link(ShaderInput *input, ShaderOutput *output); diff --git a/intern/cycles/util/util_static_assert.h b/intern/cycles/util/util_static_assert.h index 1b945705145..033d85e8ec6 100644 --- a/intern/cycles/util/util_static_assert.h +++ b/intern/cycles/util/util_static_assert.h @@ -25,6 +25,10 @@ CCL_NAMESPACE_BEGIN #ifndef __KERNEL_GPU__ # if (__cplusplus > 199711L) || (defined(_MSC_VER) && _MSC_VER >= 1800) /* C++11 has built-in static_assert() */ +# elif defined(static_assert) +/* Some platforms might have static_assert() defined even tho their + * C++ support wouldn't be declared to be C++11. + */ # else /* C++11 or MSVC2015 */ template <bool Test> class StaticAssertFailure; template <> class StaticAssertFailure<true> {}; diff --git a/intern/elbeem/intern/controlparticles.cpp b/intern/elbeem/intern/controlparticles.cpp index 526fac0cc6b..f6329ad4b67 100644 --- a/intern/elbeem/intern/controlparticles.cpp +++ b/intern/elbeem/intern/controlparticles.cpp @@ -1316,7 +1316,8 @@ void ControlParticles::finishControl(std::vector<ControlForces> &forces, LbmFloa if(cvweight>1.) { cvweight = 1.; } // thus cvweight is in the range of 0..influenceVelocity, currently not normalized by numCParts cvweight *= ivel; - if(cvweight<0.) cvweight=0.; if(cvweight>1.) cvweight=1.; + if(cvweight<0.) cvweight=0.; + if(cvweight>1.) cvweight=1.; // LBM, FIXME todo use relaxation factor //pvel = (cvel*0.5 * cvweight) + (pvel * (1.0-cvweight)); forces[i].weightVel = cvweight; diff --git a/intern/elbeem/intern/isosurface.h b/intern/elbeem/intern/isosurface.h index 15b923866d3..30fd9adfee1 100644 --- a/intern/elbeem/intern/isosurface.h +++ b/intern/elbeem/intern/isosurface.h @@ -67,8 +67,10 @@ class IsoSurface : /*! set # of subdivisions, this has to be done before init! */ void setSubdivs(int s) { if(mInitDone) errFatal("IsoSurface::setSubdivs","Changing subdivs after init!", SIMWORLD_INITERROR); - if(s<1) s=1; if(s>10) s=10; - mSubdivs = s; } + if(s<1) s=1; + if(s>10) s=10; + mSubdivs = s; + } int getSubdivs() { return mSubdivs;} /*! set full edge settings, this has to be done before init! */ void setUseFulledgeArrays(bool set) { diff --git a/intern/ghost/intern/GHOST_ContextWGL.cpp b/intern/ghost/intern/GHOST_ContextWGL.cpp index abce3ea6588..64ee692797b 100644 --- a/intern/ghost/intern/GHOST_ContextWGL.cpp +++ b/intern/ghost/intern/GHOST_ContextWGL.cpp @@ -183,6 +183,7 @@ static int weight_pixel_format(PIXELFORMATDESCRIPTOR &pfd, PIXELFORMATDESCRIPTOR !(pfd.dwFlags & PFD_DOUBLEBUFFER) || /* Blender _needs_ this */ !(pfd.iPixelType == PFD_TYPE_RGBA) || (pfd.cDepthBits < 16) || + (pfd.cColorBits > 32) || /* 64 bit formats disable aero */ (pfd.dwFlags & PFD_GENERIC_FORMAT)) /* no software renderers */ { return 0; diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 727bc9a01fb..9a2dcfc9687 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -870,18 +870,25 @@ GHOST_SystemX11::processEvent(XEvent *xe) * * To address this, we: * - * - Try to get a 'number' key_sym using XLookupKeysym (with or without shift modifier). + * - Try to get a 'number' key_sym using XLookupKeysym (with virtual shift modifier), + * in a very restrictive set of cases. * - Fallback to XLookupString to get a key_sym from active user-defined keymap. * - * Note that this enforces users to use an ascii-compatible keymap with Blender - but at least it gives - * predictable and consistent results. + * Note that: + * - This effectively 'lock' main number keys to always output number events (except when using alt-gr). + * - This enforces users to use an ascii-compatible keymap with Blender - but at least it gives + * predictable and consistent results. * * Also, note that nothing in XLib sources [1] makes it obvious why those two functions give different * key_sym results... * * [1] http://cgit.freedesktop.org/xorg/lib/libX11/tree/src/KeyBind.c */ - if ((xke->keycode >= 10 && xke->keycode < 20)) { + /* Mode_switch 'modifier' is AltGr - when this one or Shift are enabled, we do not want to apply + * that 'forced number' hack. */ + const unsigned int mode_switch_mask = XkbKeysymToModifiers(xke->display, XK_Mode_switch); + const unsigned int number_hack_forbidden_kmods_mask = mode_switch_mask | ShiftMask; + if ((xke->keycode >= 10 && xke->keycode < 20) && ((xke->state & number_hack_forbidden_kmods_mask) == 0)) { key_sym = XLookupKeysym(xke, ShiftMask); if (!((key_sym >= XK_0) && (key_sym <= XK_9))) { key_sym = XLookupKeysym(xke, 0); @@ -895,8 +902,29 @@ GHOST_SystemX11::processEvent(XEvent *xe) ascii = '\0'; } - if ((gkey = convertXKey(key_sym)) == GHOST_kKeyUnknown) { - gkey = convertXKey(key_sym_str); + /* Only allow a very limited set of keys from XLookupKeysym, all others we take from XLookupString... */ + gkey = convertXKey(key_sym); + switch (gkey) { + case GHOST_kKeyRightAlt: + case GHOST_kKeyLeftAlt: + case GHOST_kKeyRightShift: + case GHOST_kKeyLeftShift: + case GHOST_kKeyRightControl: + case GHOST_kKeyLeftControl: + case GHOST_kKeyOS: + case GHOST_kKey0: + case GHOST_kKey1: + case GHOST_kKey2: + case GHOST_kKey3: + case GHOST_kKey4: + case GHOST_kKey5: + case GHOST_kKey6: + case GHOST_kKey7: + case GHOST_kKey8: + case GHOST_kKey9: + break; + default: + gkey = convertXKey(key_sym_str); } #else /* In keyboards like latin ones, @@ -1578,6 +1606,7 @@ convertXKey(KeySym key) switch (key) { GXMAP(type, XK_BackSpace, GHOST_kKeyBackSpace); GXMAP(type, XK_Tab, GHOST_kKeyTab); + GXMAP(type, XK_ISO_Left_Tab, GHOST_kKeyTab); GXMAP(type, XK_Return, GHOST_kKeyEnter); GXMAP(type, XK_Escape, GHOST_kKeyEsc); GXMAP(type, XK_space, GHOST_kKeySpace); diff --git a/intern/itasc/ConstraintSet.cpp b/intern/itasc/ConstraintSet.cpp index e21cd36e4c8..0baf580ca68 100644 --- a/intern/itasc/ConstraintSet.cpp +++ b/intern/itasc/ConstraintSet.cpp @@ -145,27 +145,29 @@ bool ConstraintSet::closeLoop(){ //toggle=!toggle; //svd_boost_Macie(Jf,U,S,V,B,temp,1e-3*threshold,toggle); int ret = KDL::svd_eigen_HH(m_Jf,m_U,m_S,m_V,m_temp); - if(ret<0) - return false; + if(ret<0) + return false; // the reference point and frame of the jacobian is the base frame // m_externalPose-m_internalPose is the twist to extend the end effector // to get the required pose => change the reference point to the base frame Twist twist_delta(diff(m_internalPose,m_externalPose)); twist_delta=twist_delta.RefPoint(-m_internalPose.p); - for(unsigned int i=0;i<6;i++) - m_tdelta(i)=twist_delta(i); - //TODO: use damping in constraintset inversion? - for(unsigned int i=0;i<6;i++) - if(m_S(i)<m_threshold){ + for(unsigned int i=0;i<6;i++) + m_tdelta(i)=twist_delta(i); + //TODO: use damping in constraintset inversion? + for(unsigned int i=0;i<6;i++) { + if(m_S(i)<m_threshold){ m_B.row(i).setConstant(0.0); - }else - m_B.row(i) = m_U.col(i)/m_S(i); + } else { + m_B.row(i) = m_U.col(i)/m_S(i); + } + } - m_Jf_inv.noalias()=m_V*m_B; + m_Jf_inv.noalias()=m_V*m_B; - m_chi.noalias()+=m_Jf_inv*m_tdelta; - updateJacobian(); + m_chi.noalias()+=m_Jf_inv*m_tdelta; + updateJacobian(); // m_externalPose-m_internalPose in end effector frame // this is just to compare the pose, a different formula would work too return Equal(m_internalPose.Inverse()*m_externalPose,F_identity,m_threshold); diff --git a/intern/itasc/Scene.cpp b/intern/itasc/Scene.cpp index 7ed8fc4e63c..5768a994970 100644 --- a/intern/itasc/Scene.cpp +++ b/intern/itasc/Scene.cpp @@ -146,11 +146,11 @@ bool Scene::addObject(const std::string& name, Object* object, UncontrolledObjec bool Scene::addConstraintSet(const std::string& name,ConstraintSet* task,const std::string& object1,const std::string& object2, const std::string& ee1, const std::string& ee2) { - //Check if objects exist: - ObjectMap::iterator object1_it = objects.find(object1); - ObjectMap::iterator object2_it = objects.find(object2); - if(object1_it==objects.end()||object2_it==objects.end()) - return false; + //Check if objects exist: + ObjectMap::iterator object1_it = objects.find(object1); + ObjectMap::iterator object2_it = objects.find(object2); + if(object1_it==objects.end()||object2_it==objects.end()) + return false; int ee1_index = object1_it->second->object->addEndEffector(ee1); int ee2_index = object2_it->second->object->addEndEffector(ee2); if (ee1_index < 0 || ee2_index < 0) @@ -159,11 +159,11 @@ bool Scene::addConstraintSet(const std::string& name,ConstraintSet* task,const s constraints.insert(ConstraintMap::value_type(name,new ConstraintSet_struct( task,object1_it,ee1_index,object2_it,ee2_index, Range(m_ncTotal,task->getNrOfConstraints()),Range(6*m_nsets,6)))); - if(!result.second) - return false; - m_ncTotal+=task->getNrOfConstraints(); - m_nsets+=1; - return true; + if(!result.second) + return false; + m_ncTotal+=task->getNrOfConstraints(); + m_nsets+=1; + return true; } bool Scene::addSolver(Solver* _solver){ diff --git a/intern/itasc/WSDLSSolver.cpp b/intern/itasc/WSDLSSolver.cpp index 6fdd8e6e06c..2514dbb9041 100644 --- a/intern/itasc/WSDLSSolver.cpp +++ b/intern/itasc/WSDLSSolver.cpp @@ -60,7 +60,7 @@ bool WSDLSSolver::solve(const e_matrix& A, const e_vector& Wy, const e_vector& y e_scalar N, M; // Create the Weighted jacobian - m_AWq.noalias() = A*Wq; + m_AWq.noalias() = A*Wq; for (i=0; i<m_nc; i++) m_WyAWq.row(i) = Wy(i)*m_AWq.row(i); @@ -72,11 +72,11 @@ bool WSDLSSolver::solve(const e_matrix& A, const e_vector& Wy, const e_vector& y } else { ret = KDL::svd_eigen_HH(m_WyAWq,m_U,m_S,m_V,m_temp); } - if(ret<0) - return false; + if(ret<0) + return false; m_Wy_ydot = Wy.array() * ydot.array(); - m_WqV.noalias() = Wq*m_V; + m_WqV.noalias() = Wq*m_V; qdot.setZero(); e_scalar maxDeltaS = e_scalar(0.0); e_scalar prevS = e_scalar(0.0); diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 9628dc1922be2fb6281bc66f5f7512c2a57c294 +Subproject dc166057192ea882b5cc70484d4c8bacd7cb41b diff --git a/release/datafiles/splash.png b/release/datafiles/splash.png Binary files differindex fa893b4be0d..60956db2576 100644 --- a/release/datafiles/splash.png +++ b/release/datafiles/splash.png diff --git a/release/datafiles/splash_2x.png b/release/datafiles/splash_2x.png Binary files differindex f8dd2bbeaf3..3a5000c6bbc 100644 --- a/release/datafiles/splash_2x.png +++ b/release/datafiles/splash_2x.png diff --git a/release/datafiles/splash_scale.sh b/release/datafiles/splash_scale.sh new file mode 100755 index 00000000000..8a3f2c6d1eb --- /dev/null +++ b/release/datafiles/splash_scale.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Use this script to generate splash.png from splash_2x.png. +# Supposed to give best quality image. +# +# Based on ImageMagic documentation, which is interesting +# to read anyway: +# +# http://www.imagemagick.org/Usage/filter +# http://www.imagemagick.org/Usage/filter/nicolas/ + +convert \ + splash_2x.png \ + -colorspace RGB \ + -filter Cosine \ + -resize 50% \ + -colorspace sRGB \ + splash.png diff --git a/release/datafiles/splash_template.xcf b/release/datafiles/splash_template.xcf Binary files differindex b3141471c51..12719b5c155 100644 --- a/release/datafiles/splash_template.xcf +++ b/release/datafiles/splash_template.xcf diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 407d0ea752b3af73d3f13ba072671bd09eefecb +Subproject 06dad53c80801e0e0919f086040e3d9c31bbd0a diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib -Subproject 9f29e18707917ec5be262431d2e09dbb85332f4 +Subproject 04af69be141a5757fc60b44cc1a5b72db524af3 diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py index c3b72b7075a..fa42778b53f 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py +++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py @@ -153,7 +153,7 @@ class SpellChecker: "realtime", "rekey", "remesh", - "reprojection", + "reprojection", "reproject", "resize", "restpose", "retarget", "retargets", "retargeting", "retargeted", 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 677c7b9960e..4299cf6035d 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -20,6 +20,7 @@ from bpy.types import Menu, UIList +from bpy.app.translations import pgettext_iface as iface_ def gpencil_stroke_placement_settings(context, layout): @@ -52,6 +53,34 @@ def gpencil_stroke_placement_settings(context, layout): row.prop(ts, "use_gpencil_stroke_endpoints") +def gpencil_active_brush_settings_simple(context, layout): + brush = context.active_gpencil_brush + + col = layout.column() + col.label("Active Brush: ") + + row = col.row(align=True) + 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) + + class GreasePencilDrawingToolsPanel: # subclass must set # bl_space_type = 'IMAGE_EDITOR' @@ -187,6 +216,15 @@ class GreasePencilStrokeEditPanel: col.operator_menu_enum("gpencil.stroke_arrange", text="Arrange Strokes...", property="direction") col.operator("gpencil.stroke_change_color", text="Move to Color") + if is_3d_view: + layout.separator() + col = layout.column(align=True) + col.operator("gpencil.interpolate", text="Interpolate") + col.operator("gpencil.interpolate_sequence", text="Sequence") + settings = context.tool_settings.gpencil_sculpt + col.prop(settings, "interpolate_all_layers") + col.prop(settings, "interpolate_selected_only") + layout.separator() col = layout.column(align=True) col.operator("gpencil.stroke_join", text="Join").type = 'JOIN' @@ -197,6 +235,10 @@ class GreasePencilStrokeEditPanel: if gpd: col.prop(gpd, "show_stroke_direction", text="Show Directions") + if is_3d_view: + layout.separator() + layout.operator("gpencil.reproject") + class GreasePencilBrushPanel: # subclass must set @@ -460,29 +502,7 @@ class GPENCIL_PIE_settings_palette(Menu): col.prop(palcolor, "fill_alpha", text="", slider=True) # S Brush settings - col = pie.column() - 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) + gpencil_active_brush_settings_simple(context, pie) # N - Active Layer col = pie.column() @@ -752,6 +772,7 @@ class GPENCIL_MT_palettecolor_specials(Menu): layout.separator() layout.operator("gpencil.palettecolor_select", icon='COLOR', text="Select Strokes") + layout.operator("gpencil.stroke_change_color", icon='MAN_TRANS', text="Move to Color") class GreasePencilDataPanel: @@ -834,7 +855,10 @@ class GreasePencilDataPanel: split.prop(gpl, "show_points") # Offsets + Parenting (where available) - split = layout.split(percentage=0.5) + if context.space_data.type == 'VIEW_3D': + split = layout.split(percentage=0.5) + else: + split = layout.column() # parenting is not available in 2D editors... split.active = not gpl.lock # Offsets - Color Tint @@ -868,10 +892,10 @@ class GreasePencilDataPanel: row.active = not gpl.lock if gpl.active_frame: - lock_status = "Locked" if gpl.lock_frame else "Unlocked" - lock_label = "Frame: %d (%s)" % (gpl.active_frame.frame_number, lock_status) + lock_status = iface_("Locked") if gpl.lock_frame else iface_("Unlocked") + lock_label = iface_("Frame: %d (%s)") % (gpl.active_frame.frame_number, lock_status) else: - lock_label = "Lock Frame" + lock_label = iface_("Lock Frame") row.prop(gpl, "lock_frame", text=lock_label, icon='UNLOCKED') row.operator("gpencil.active_frame_delete", text="", icon='X') @@ -956,10 +980,13 @@ class GreasePencilPaletteColorPanel: 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) + row = layout.row() + sub = row.row(align=True) + sub.label(text="Isolate:") # based on active color only 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 = row.row(align=True) + sub.label(text="Lock:") # based on other stuff... sub.operator("gpencil.stroke_lock_color", icon='BORDER_RECT', text="") sub.operator("gpencil.palette_lock_layer", icon='COLOR', text="") @@ -967,9 +994,7 @@ class GreasePencilPaletteColorPanel: if pcolor: self.draw_palettecolors(layout, pcolor) - # ---------------------------------------------- # Draw palette colors - # ---------------------------------------------- def draw_palettecolors(self, layout, pcolor): # color settings split = layout.split(percentage=0.5) @@ -1002,6 +1027,7 @@ class GreasePencilPaletteColorPanel: class GreasePencilToolsPanel: + # For use in "2D" Editors without their own toolbar # subclass must set # bl_space_type = 'IMAGE_EDITOR' # bl_options = {'DEFAULT_CLOSED'} @@ -1031,4 +1057,8 @@ class GreasePencilToolsPanel: layout.separator() layout.separator() + gpencil_active_brush_settings_simple(context, layout) + + layout.separator() + gpencil_stroke_placement_settings(context, layout) diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index 794ef5189d6..152d4e96d5d 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -345,7 +345,9 @@ class RENDER_PT_stamp(RenderButtonsPanel, Panel): layout.prop(rd, "use_stamp") col = layout.column() col.active = rd.use_stamp - col.prop(rd, "stamp_font_size", text="Font Size") + row = col.row() + row.prop(rd, "stamp_font_size", text="Font Size") + row.prop(rd, "use_stamp_labels", text="Draw labels") row = col.row() row.column().prop(rd, "stamp_foreground", slider=True) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index b9cb7f24ebd..1748d1d3738 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -411,6 +411,20 @@ class VIEW3D_MT_uv_map(Menu): layout.operator("uv.reset") +class VIEW3D_MT_edit_proportional(Menu): + bl_label = "Proportional Editing" + + def draw(self, context): + layout = self.layout + + layout.props_enum(context.tool_settings, "proportional_edit") + + layout.separator() + + layout.label("Falloff:") + layout.props_enum(context.tool_settings, "proportional_edit_falloff") + + # ********** View menus ********** @@ -2311,8 +2325,7 @@ class VIEW3D_MT_edit_mesh(Menu): layout.separator() layout.prop(toolsettings, "use_mesh_automerge") - layout.prop_menu_enum(toolsettings, "proportional_edit") - layout.prop_menu_enum(toolsettings, "proportional_edit_falloff") + layout.menu("VIEW3D_MT_edit_proportional") layout.separator() @@ -2427,6 +2440,8 @@ class VIEW3D_MT_edit_mesh_vertices(Menu): layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' + with_bullet = bpy.app.build_options.bullet + layout.operator("mesh.merge") layout.operator("mesh.rip_move") layout.operator("mesh.rip_move_fill") @@ -2447,7 +2462,8 @@ class VIEW3D_MT_edit_mesh_vertices(Menu): layout.separator() layout.operator("mesh.bevel").vertex_only = True - layout.operator("mesh.convex_hull") + if with_bullet: + layout.operator("mesh.convex_hull") layout.operator("mesh.vertices_smooth") layout.operator("mesh.remove_doubles") @@ -2681,8 +2697,7 @@ def draw_curve(self, context): layout.separator() - layout.prop_menu_enum(toolsettings, "proportional_edit") - layout.prop_menu_enum(toolsettings, "proportional_edit_falloff") + layout.menu("VIEW3D_MT_edit_proportional") layout.separator() @@ -2841,8 +2856,7 @@ class VIEW3D_MT_edit_meta(Menu): layout.separator() - layout.prop_menu_enum(toolsettings, "proportional_edit") - layout.prop_menu_enum(toolsettings, "proportional_edit_falloff") + layout.menu("VIEW3D_MT_edit_proportional") layout.separator() @@ -2879,8 +2893,7 @@ class VIEW3D_MT_edit_lattice(Menu): layout.separator() - layout.prop_menu_enum(toolsettings, "proportional_edit") - layout.prop_menu_enum(toolsettings, "proportional_edit_falloff") + layout.menu("VIEW3D_MT_edit_proportional") class VIEW3D_MT_edit_armature(Menu): @@ -3040,8 +3053,7 @@ class VIEW3D_MT_edit_gpencil(Menu): layout.separator() - layout.prop_menu_enum(toolsettings, "proportional_edit") - layout.prop_menu_enum(toolsettings, "proportional_edit_falloff") + layout.menu("VIEW3D_MT_edit_proportional") layout.separator() diff --git a/source/blender/alembic/intern/abc_hair.cc b/source/blender/alembic/intern/abc_hair.cc index 45bf9b7ab8a..14bcf6731ea 100644 --- a/source/blender/alembic/intern/abc_hair.cc +++ b/source/blender/alembic/intern/abc_hair.cc @@ -59,7 +59,7 @@ AbcHairWriter::AbcHairWriter(Scene *scene, { m_psys = psys; - OCurves curves(parent->alembicXform(), m_name, m_time_sampling); + OCurves curves(parent->alembicXform(), psys->name, m_time_sampling); m_schema = curves.getSchema(); } diff --git a/source/blender/alembic/intern/abc_points.cc b/source/blender/alembic/intern/abc_points.cc index 322bb906bf5..03014547416 100644 --- a/source/blender/alembic/intern/abc_points.cc +++ b/source/blender/alembic/intern/abc_points.cc @@ -67,7 +67,7 @@ AbcPointsWriter::AbcPointsWriter(Scene *scene, { m_psys = psys; - OPoints points(parent->alembicXform(), m_name, m_time_sampling); + OPoints points(parent->alembicXform(), psys->name, m_time_sampling); m_schema = points.getSchema(); } diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc index d057cc341f6..477448f1835 100644 --- a/source/blender/alembic/intern/alembic_capi.cc +++ b/source/blender/alembic/intern/alembic_capi.cc @@ -599,10 +599,6 @@ struct ImportJobData { ABC_INLINE bool is_mesh_and_strands(const IObject &object) { - if (object.getNumChildren() != 2) { - return false; - } - bool has_mesh = false; bool has_curve = false; @@ -624,6 +620,9 @@ ABC_INLINE bool is_mesh_and_strands(const IObject &object) else if (ICurves::matches(md)) { has_curve = true; } + else if (IPoints::matches(md)) { + has_curve = true; + } } return has_mesh && has_curve; diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index 606488f85cc..789bc8df7e5 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -783,6 +783,7 @@ void DM_vertex_attributes_from_gpu( struct GPUVertexAttribs *gattribs, DMVertexAttribs *attribs); void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert, int loop); +void DM_draw_attrib_vertex_uniforms(const DMVertexAttribs *attribs); void DM_calc_tangents_names_from_gpu( const struct GPUVertexAttribs *gattribs, diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 483fefbd89c..3ae01d8148a 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,8 +27,8 @@ /* these lines are grep'd, watch out for our not-so-awesome regex * and keep comment above the defines. * Use STRINGIFY() rather than defining with quotes */ -#define BLENDER_VERSION 277 -#define BLENDER_SUBVERSION 3 +#define BLENDER_VERSION 278 +#define BLENDER_SUBVERSION 0 /* Several breakages with 270, e.g. constraint deg vs rad */ #define BLENDER_MINVERSION 270 #define BLENDER_MINSUBVERSION 6 diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 79e500f8ceb..5f759c61bcb 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -80,6 +80,7 @@ static DerivedMesh *navmesh_dm_createNavMeshForVisualization(DerivedMesh *dm); #include "GPU_shader.h" #ifdef WITH_OPENSUBDIV +# include "BKE_depsgraph.h" # include "DNA_userdef_types.h" #endif @@ -2282,7 +2283,7 @@ static void editbmesh_calc_modifiers( { ModifierData *md, *previewmd = NULL; float (*deformedVerts)[3] = NULL; - CustomDataMask mask, previewmask = 0, append_mask = 0; + CustomDataMask mask = 0, previewmask = 0, append_mask = 0; DerivedMesh *dm = NULL, *orcodm = NULL; int i, numVerts = 0, cageIndex = modifiers_getCageIndex(scene, ob, NULL, 1); CDMaskLink *datamasks, *curr; @@ -2565,15 +2566,28 @@ static void editbmesh_calc_modifiers( * we'll be using GPU backend of OpenSubdiv. This is so * playback performance is kept as high as possible. */ -static bool calc_modifiers_skip_orco(const Object *ob) +static bool calc_modifiers_skip_orco(Scene *scene, + Object *ob, + bool use_render_params) { - const ModifierData *last_md = ob->modifiers.last; + ModifierData *last_md = ob->modifiers.last; + const int required_mode = use_render_params ? eModifierMode_Render : eModifierMode_Realtime; if (last_md != NULL && - last_md->type == eModifierType_Subsurf) + last_md->type == eModifierType_Subsurf && + modifier_isEnabled(scene, last_md, required_mode)) { + if (U.opensubdiv_compute_type == USER_OPENSUBDIV_COMPUTE_NONE) { + return false; + } + else if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) != 0) { + return false; + } + else if ((DAG_get_eval_flags_for_object(scene, ob) & DAG_EVAL_NEED_CPU) != 0) { + return false; + } SubsurfModifierData *smd = (SubsurfModifierData *)last_md; /* TODO(sergey): Deduplicate this with checks from subsurf_ccg.c. */ - return smd->use_opensubdiv && U.opensubdiv_compute_type != USER_OPENSUBDIV_COMPUTE_NONE; + return smd->use_opensubdiv != 0; } return false; } @@ -2589,7 +2603,7 @@ static void mesh_build_data( BKE_object_sculpt_modifiers_changed(ob); #ifdef WITH_OPENSUBDIV - if (calc_modifiers_skip_orco(ob)) { + if (calc_modifiers_skip_orco(scene, ob, false)) { dataMask &= ~(CD_MASK_ORCO | CD_MASK_PREVIEW_MCOL); } #endif @@ -2624,7 +2638,7 @@ static void editbmesh_build_data(Scene *scene, Object *obedit, BMEditMesh *em, C BKE_editmesh_free_derivedmesh(em); #ifdef WITH_OPENSUBDIV - if (calc_modifiers_skip_orco(obedit)) { + if (calc_modifiers_skip_orco(scene, obedit, false)) { dataMask &= ~(CD_MASK_ORCO | CD_MASK_PREVIEW_MCOL); } #endif @@ -3793,7 +3807,6 @@ void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert, glTexCoord3fv(orco); else glVertexAttrib3fv(attribs->orco.gl_index, orco); - glUniform1i(attribs->orco.gl_info_index, 0); } /* uv texture coordinates */ @@ -3812,7 +3825,6 @@ void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert, glTexCoord2fv(uv); else glVertexAttrib2fv(attribs->tface[b].gl_index, uv); - glUniform1i(attribs->tface[b].gl_info_index, 0); } /* vertex colors */ @@ -3828,17 +3840,33 @@ void DM_draw_attrib_vertex(DMVertexAttribs *attribs, int a, int index, int vert, } glVertexAttrib4fv(attribs->mcol[b].gl_index, col); - glUniform1i(attribs->mcol[b].gl_info_index, GPU_ATTR_INFO_SRGB); } /* tangent for normal mapping */ for (b = 0; b < attribs->tottang; b++) { if (attribs->tang[b].array) { /*const*/ float (*array)[4] = attribs->tang[b].array; - const float *tang = (array) ? array[a * 4 + vert] : zero; + const float *tang = (array) ? array[loop] : zero; glVertexAttrib4fv(attribs->tang[b].gl_index, tang); } - glUniform1i(attribs->tang[b].gl_info_index, 0); + } +} + +void DM_draw_attrib_vertex_uniforms(const DMVertexAttribs *attribs) +{ + int i; + if (attribs->totorco) { + glUniform1i(attribs->orco.gl_info_index, 0); + } + for (i = 0; i < attribs->tottface; i++) { + glUniform1i(attribs->tface[i].gl_info_index, 0); + } + for (i = 0; i < attribs->totmcol; i++) { + glUniform1i(attribs->mcol[i].gl_info_index, GPU_ATTR_INFO_SRGB); + } + + for (i = 0; i < attribs->tottang; i++) { + glUniform1i(attribs->tang[i].gl_info_index, 0); } } diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 2d06bc88aa7..f2dd2a3fcf6 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -941,8 +941,10 @@ static void cdDM_drawMappedFacesGLSL( matnr = new_matnr; do_draw = setMaterial(matnr + 1, &gattribs); - if (do_draw) + if (do_draw) { DM_vertex_attributes_from_gpu(dm, &gattribs, &attribs); + DM_draw_attrib_vertex_uniforms(&attribs); + } glBegin(GL_TRIANGLES); } @@ -1210,6 +1212,7 @@ static void cdDM_drawMappedFacesMat( setMaterial(userData, matnr = new_matnr, &gattribs); DM_vertex_attributes_from_gpu(dm, &gattribs, &attribs); + DM_draw_attrib_vertex_uniforms(&attribs); glBegin(GL_TRIANGLES); } diff --git a/source/blender/blenkernel/intern/editderivedmesh.c b/source/blender/blenkernel/intern/editderivedmesh.c index 1aba76baa2c..e51a3c3c4b0 100644 --- a/source/blender/blenkernel/intern/editderivedmesh.c +++ b/source/blender/blenkernel/intern/editderivedmesh.c @@ -1410,24 +1410,6 @@ static void emDM_drawMappedFacesTex( emDM_drawFacesTex_common(dm, NULL, setDrawOptions, compareDrawOptions, userData); } -static void emdm_pass_attrib_update_uniforms(const DMVertexAttribs *attribs) -{ - int i; - if (attribs->totorco) { - glUniform1i(attribs->orco.gl_info_index, 0); - } - for (i = 0; i < attribs->tottface; i++) { - glUniform1i(attribs->tface[i].gl_info_index, 0); - } - for (i = 0; i < attribs->totmcol; i++) { - glUniform1i(attribs->mcol[i].gl_info_index, GPU_ATTR_INFO_SRGB); - } - - for (i = 0; i < attribs->tottang; i++) { - glUniform1i(attribs->tang[i].gl_info_index, 0); - } -} - /** * \note * @@ -1548,7 +1530,7 @@ static void emDM_drawMappedFacesGLSL( do_draw = setMaterial(matnr = new_matnr, &gattribs); if (do_draw) { DM_vertex_attributes_from_gpu(dm, &gattribs, &attribs); - emdm_pass_attrib_update_uniforms(&attribs); + DM_draw_attrib_vertex_uniforms(&attribs); if (UNLIKELY(attribs.tottang && bm->elem_index_dirty & BM_LOOP)) { BM_mesh_elem_index_ensure(bm, BM_LOOP); } diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 626d389ac2d..8a9cb73c8c9 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -1866,7 +1866,7 @@ void BKE_image_stamp_buf( display = IMB_colormanagement_display_get_named(display_device); if (stamp_data_template == NULL) { - stampdata(scene, camera, &stamp_data, 1); + stampdata(scene, camera, &stamp_data, (scene->r.stamp & R_STAMP_HIDE_LABELS) == 0); } else { stampdata_from_template(&stamp_data, scene, stamp_data_template); diff --git a/source/blender/blenkernel/intern/lamp.c b/source/blender/blenkernel/intern/lamp.c index 1a404604eb6..cad5394155d 100644 --- a/source/blender/blenkernel/intern/lamp.c +++ b/source/blender/blenkernel/intern/lamp.c @@ -155,8 +155,6 @@ Lamp *localize_lamp(Lamp *la) if (lan->mtex[a]) { lan->mtex[a] = MEM_mallocN(sizeof(MTex), "localize_lamp"); memcpy(lan->mtex[a], la->mtex[a], sizeof(MTex)); - /* free lamp decrements */ - id_us_plus((ID *)lan->mtex[a]->tex); } } diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c index 1830ca0bd90..9b89a2fb807 100644 --- a/source/blender/blenkernel/intern/library_remap.c +++ b/source/blender/blenkernel/intern/library_remap.c @@ -875,8 +875,8 @@ void BKE_libblock_delete(Main *bmain, void *idv) BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); /* First tag all datablocks directly from target lib. - * Note that we go forward here, since we want to check dependencies before users (e.g. meshes before objects). - * Avoids to have to loop twice. */ + * Note that we go forward here, since we want to check dependencies before users (e.g. meshes before objects). + * Avoids to have to loop twice. */ for (i = 0; i < base_count; i++) { ListBase *lb = lbarray[i]; ID *id; diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 733e9030056..ba3aef81514 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -2197,8 +2197,10 @@ Mesh *BKE_mesh_new_from_object( { Mesh *tmpmesh; Curve *tmpcu = NULL, *copycu; - int render = settings == eModifierMode_Render, i; - int cage = !apply_modifiers; + int i; + const bool render = (settings == eModifierMode_Render); + const bool cage = !apply_modifiers; + bool do_mat_id_us = true; /* perform the mesh extraction based on type */ switch (ob->type) { @@ -2268,6 +2270,12 @@ Mesh *BKE_mesh_new_from_object( BKE_mesh_texspace_copy_from_object(tmpmesh, ob); BKE_libblock_free_us(bmain, tmpobj); + + /* XXX The curve to mesh conversion is convoluted... But essentially, BKE_mesh_from_nurbs_displist() + * already transfers the ownership of materials from the temp copy of the Curve ID to the new + * Mesh ID, so we do not want to increase materials' usercount later. */ + do_mat_id_us = false; + break; } @@ -2315,8 +2323,11 @@ Mesh *BKE_mesh_new_from_object( if (cage) { /* copies the data */ tmpmesh = BKE_mesh_copy(bmain, ob->data); - /* if not getting the original caged mesh, get final derived mesh */ + + /* XXX BKE_mesh_copy() already handles materials usercount. */ + do_mat_id_us = false; } + /* if not getting the original caged mesh, get final derived mesh */ else { /* Make a dummy mesh, saves copying */ DerivedMesh *dm; @@ -2360,7 +2371,7 @@ Mesh *BKE_mesh_new_from_object( tmpmesh->mat[i] = ob->matbits[i] ? ob->mat[i] : tmpcu->mat[i]; - if (tmpmesh->mat[i]) { + if (do_mat_id_us && tmpmesh->mat[i]) { id_us_plus(&tmpmesh->mat[i]->id); } } @@ -2379,7 +2390,7 @@ Mesh *BKE_mesh_new_from_object( /* are we an object material or data based? */ tmpmesh->mat[i] = ob->matbits[i] ? ob->mat[i] : tmpmb->mat[i]; - if (tmpmesh->mat[i]) { + if (do_mat_id_us && tmpmesh->mat[i]) { id_us_plus(&tmpmesh->mat[i]->id); } } @@ -2399,7 +2410,7 @@ Mesh *BKE_mesh_new_from_object( /* are we an object material or data based? */ tmpmesh->mat[i] = ob->matbits[i] ? ob->mat[i] : origmesh->mat[i]; - if (tmpmesh->mat[i]) { + if (do_mat_id_us && tmpmesh->mat[i]) { id_us_plus(&tmpmesh->mat[i]->id); } } diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 482015d3b26..6794a8e8f93 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -1247,8 +1247,6 @@ static void free_buffers(MovieClip *clip) IMB_free_anim(clip->anim); clip->anim = NULL; } - - BKE_animdata_free((ID *) clip, false); } void BKE_movieclip_clear_cache(MovieClip *clip) @@ -1487,6 +1485,7 @@ void BKE_movieclip_free(MovieClip *clip) free_buffers(clip); BKE_tracking_free(&clip->tracking); + BKE_animdata_free((ID *) clip, false); } MovieClip *BKE_movieclip_copy(Main *bmain, MovieClip *clip) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index b7a25006d08..d9301e2d3d0 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1139,7 +1139,7 @@ Object *BKE_object_copy_ex(Main *bmain, Object *ob, bool copy_caches) /* increase user numbers */ id_us_plus((ID *)obn->data); id_us_plus((ID *)obn->gpd); - id_lib_extern((ID *)obn->dup_group); + id_us_plus((ID *)obn->dup_group); for (a = 0; a < obn->totcol; a++) id_us_plus((ID *)obn->mat[a]); diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index aecaf8537bb..907721b09b6 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -318,16 +318,24 @@ void psys_check_group_weights(ParticleSettings *part) int current = 0; if (part->ren_as == PART_DRAW_GR && part->dup_group && part->dup_group->gobject.first) { - /* first remove all weights that don't have an object in the group */ + /* First try to find NULL objects from their index, + * and remove all weights that don't have an object in the group. */ dw = part->dupliweights.first; while (dw) { - if (!BKE_group_object_exists(part->dup_group, dw->ob)) { - tdw = dw->next; - BLI_freelinkN(&part->dupliweights, dw); - dw = tdw; + if (dw->ob == NULL || !BKE_group_object_exists(part->dup_group, dw->ob)) { + go = (GroupObject *)BLI_findlink(&part->dup_group->gobject, dw->index); + if (go) { + dw->ob = go->ob; + } + else { + tdw = dw->next; + BLI_freelinkN(&part->dupliweights, dw); + dw = tdw; + } } - else + else { dw = dw->next; + } } /* then add objects in the group to new list */ @@ -877,6 +885,9 @@ static void get_pointcache_keys_for_time(Object *UNUSED(ob), PointCache *cache, index2 = BKE_ptcache_mem_index_find(pm, index); index1 = BKE_ptcache_mem_index_find(pm->prev, index); + if (index2 < 0) { + return; + } BKE_ptcache_make_particle_key(key2, index2, pm->data, (float)pm->frame); if (index1 < 0) @@ -887,6 +898,9 @@ static void get_pointcache_keys_for_time(Object *UNUSED(ob), PointCache *cache, else if (cache->mem_cache.first) { pm = cache->mem_cache.first; index2 = BKE_ptcache_mem_index_find(pm, index); + if (index2 < 0) { + return; + } BKE_ptcache_make_particle_key(key2, index2, pm->data, (float)pm->frame); copy_particle_key(key1, key2, 1); } diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 60f1eb6b98c..6d57c5f09e8 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -2820,12 +2820,13 @@ static void ccgDM_drawMappedFacesGLSL(DerivedMesh *dm, int matnr = -1; int do_draw = 0; -#define PASSATTRIB(dx, dy, vert) { \ - if (attribs.totorco) \ - index = getFaceIndex(ss, f, S, x + dx, y + dy, edgeSize, gridSize); \ - else \ - index = 0; \ - DM_draw_attrib_vertex(&attribs, a, index, vert, ((a) * 4) + vert); \ +#define PASSATTRIB(dx, dy, vert) { \ + if (attribs.totorco) \ + index = getFaceIndex(ss, f, S, x + dx, y + dy, edgeSize, gridSize); \ + else \ + index = 0; \ + DM_draw_attrib_vertex(&attribs, a, index, vert, ((a) * 4) + vert); \ + DM_draw_attrib_vertex_uniforms(&attribs); \ } (void)0 totpoly = ccgSubSurf_getNumFaces(ss); @@ -3194,24 +3195,62 @@ static void ccgDM_drawMappedFacesMat(DerivedMesh *dm, #ifdef WITH_OPENSUBDIV if (ccgdm->useGpuBackend) { - int new_matnr; - bool draw_smooth; + const int level = ccgSubSurf_getSubdivisionLevels(ss); + const int face_side = 1 << level; + const int grid_side = 1 << (level - 1); + const int face_patches = face_side * face_side; + const int grid_patches = grid_side * grid_side; + const int num_base_faces = ccgSubSurf_getNumGLMeshBaseFaces(ss); + int current_patch = 0; + int mat_nr = -1; + bool draw_smooth = false; + int start_draw_patch = -1, num_draw_patches = 0; GPU_draw_update_fvar_offset(dm); if (UNLIKELY(ccgSubSurf_prepareGLMesh(ss, true, -1) == false)) { return; } - /* TODO(sergey): Single matierial currently. */ - if (faceFlags) { - draw_smooth = (faceFlags[0].flag & ME_SMOOTH); - new_matnr = (faceFlags[0].mat_nr + 1); + for (i = 0; i < num_base_faces; ++i) { + const int num_face_verts = ccgSubSurf_getNumGLMeshBaseFaceVerts(ss, i); + const int num_patches = (num_face_verts == 4) ? face_patches + : num_face_verts * grid_patches; + int new_matnr; + bool new_draw_smooth; + + if (faceFlags) { + new_draw_smooth = (faceFlags[i].flag & ME_SMOOTH); + new_matnr = (faceFlags[i].mat_nr + 1); + } + else { + new_draw_smooth = true; + new_matnr = 1; + } + if (new_draw_smooth != draw_smooth || new_matnr != mat_nr) { + if (num_draw_patches != 0) { + setMaterial(userData, mat_nr, &gattribs); + glShadeModel(draw_smooth ? GL_SMOOTH : GL_FLAT); + ccgSubSurf_drawGLMesh(ss, + true, + start_draw_patch, + num_draw_patches); + } + start_draw_patch = current_patch; + num_draw_patches = num_patches; + mat_nr = new_matnr; + draw_smooth = new_draw_smooth; + } + else { + num_draw_patches += num_patches; + } + current_patch += num_patches; } - else { - draw_smooth = true; - new_matnr = 1; + if (num_draw_patches != 0) { + setMaterial(userData, mat_nr, &gattribs); + glShadeModel(draw_smooth ? GL_SMOOTH : GL_FLAT); + ccgSubSurf_drawGLMesh(ss, + true, + start_draw_patch, + num_draw_patches); } - glShadeModel(draw_smooth ? GL_SMOOTH : GL_FLAT); - setMaterial(userData, new_matnr, &gattribs); - ccgSubSurf_drawGLMesh(ss, true, -1, -1); glShadeModel(GL_SMOOTH); return; } @@ -3222,12 +3261,13 @@ static void ccgDM_drawMappedFacesMat(DerivedMesh *dm, matnr = -1; -#define PASSATTRIB(dx, dy, vert) { \ - if (attribs.totorco) \ - index = getFaceIndex(ss, f, S, x + dx, y + dy, edgeSize, gridSize); \ - else \ - index = 0; \ - DM_draw_attrib_vertex(&attribs, a, index, vert, ((a) * 4) + vert); \ +#define PASSATTRIB(dx, dy, vert) { \ + if (attribs.totorco) \ + index = getFaceIndex(ss, f, S, x + dx, y + dy, edgeSize, gridSize); \ + else \ + index = 0; \ + DM_draw_attrib_vertex(&attribs, a, index, vert, ((a) * 4) + vert); \ + DM_draw_attrib_vertex_uniforms(&attribs); \ } (void)0 totface = ccgSubSurf_getNumFaces(ss); diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index de1e3187a70..caa9a1e357f 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -158,8 +158,6 @@ World *localize_world(World *wrld) if (wrld->mtex[a]) { wrldn->mtex[a] = MEM_mallocN(sizeof(MTex), "localize_world"); memcpy(wrldn->mtex[a], wrld->mtex[a], sizeof(MTex)); - /* free world decrements */ - id_us_plus((ID *)wrldn->mtex[a]->tex); } } diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 40454a93ec8..dd30f267f78 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -2892,8 +2892,8 @@ void plot_line_v2v2i(const int p1[2], const int p2[2], bool (*callback)(int, int int x2 = p2[0]; int y2 = p2[1]; - signed char ix; - signed char iy; + int ix; + int iy; /* if x1 == x2 or y1 == y2, then it does not matter what we set here */ int delta_x = (x2 > x1 ? ((void)(ix = 1), x2 - x1) : ((void)(ix = -1), x1 - x2)) << 1; diff --git a/source/blender/blenlib/intern/task.c b/source/blender/blenlib/intern/task.c index bd7b7f9cdbd..9d4d40e1eae 100644 --- a/source/blender/blenlib/intern/task.c +++ b/source/blender/blenlib/intern/task.c @@ -780,9 +780,10 @@ BLI_INLINE bool parallel_range_next_iter_get( ParallelRangeState * __restrict state, int * __restrict iter, int * __restrict count) { - uint32_t previter = atomic_fetch_and_add_uint32((uint32_t *)(&state->iter), state->chunk_size); + uint32_t uval = atomic_fetch_and_add_uint32((uint32_t *)(&state->iter), state->chunk_size); + int previter = *(int32_t*)&uval; - *iter = (int)previter; + *iter = previter; *count = max_ii(0, min_ii(state->chunk_size, state->stop - previter)); return (previter < state->stop); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index a6c05e9b85c..972520af201 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4106,8 +4106,14 @@ static void lib_link_particlesettings(FileData *fd, Main *main) if (index_ok) { /* if we have indexes, let's use them */ for (dw = part->dupliweights.first; dw; dw = dw->next) { - GroupObject *go = (GroupObject *)BLI_findlink(&part->dup_group->gobject, dw->index); - dw->ob = go ? go->ob : NULL; + /* Do not try to restore pointer here, we have to search for group objects in another + * separated step. + * Reason is, the used group may be linked from another library, which has not yet + * been 'lib_linked'. + * Since dw->ob is not considered as an object user (it does not make objet directly linked), + * we may have no valid way to retrieve it yet. + * See T49273. */ + dw->ob = NULL; } } else { @@ -5304,6 +5310,9 @@ static void direct_link_object(FileData *fd, Object *ob) */ ob->recalc = 0; + /* XXX This should not be needed - but seems like it can happen in some cases, so for now play safe... */ + ob->proxy_from = NULL; + /* loading saved files with editmode enabled works, but for undo we like * to stay in object mode during undo presses so keep editmode disabled. * diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 49ef8baa184..dfaa59c4e3f 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -1306,12 +1306,20 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) /* set color attributes */ copy_v4_v4(palcolor->color, gpl->color); copy_v4_v4(palcolor->fill, gpl->fill); - palcolor->flag = gpl->flag; + + if (gpl->flag & GP_LAYER_HIDE) palcolor->flag |= PC_COLOR_HIDE; + if (gpl->flag & GP_LAYER_LOCKED) palcolor->flag |= PC_COLOR_LOCKED; + if (gpl->flag & GP_LAYER_ONIONSKIN) palcolor->flag |= PC_COLOR_ONIONSKIN; + if (gpl->flag & GP_LAYER_VOLUMETRIC) palcolor->flag |= PC_COLOR_VOLUMETRIC; + if (gpl->flag & GP_LAYER_HQ_FILL) palcolor->flag |= PC_COLOR_HQ_FILL; + /* 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); - + + /* flush relevant layer-settings to strokes */ 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 */ @@ -1319,14 +1327,15 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) 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; } @@ -1339,7 +1348,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) /* ------- end of grease pencil initialization --------------- */ } - { + if (!MAIN_VERSION_ATLEAST(main, 278, 0)) { if (!DNA_struct_elem_find(fd->filesdna, "MovieTrackingTrack", "float", "weight_stab")) { MovieClip *clip; for (clip = main->movieclip.first; clip; clip = clip->id.next) { diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index f8cca5393e2..c3fd202d832 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -304,7 +304,7 @@ static void schedule_node(TaskPool *pool, Depsgraph *graph, unsigned int layers, deg_task_run_func, node, false, - TASK_PRIORITY_HIGH, + TASK_PRIORITY_LOW, thread_id); } } diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 4ef76f5ee25..48786e08f85 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -66,6 +66,7 @@ #include "ED_gpencil.h" #include "ED_screen.h" #include "ED_view3d.h" +#include "ED_space_api.h" #include "UI_interface_icons.h" #include "UI_resources.h" @@ -74,7 +75,6 @@ /* GREASE PENCIL DRAWING */ /* ----- General Defines ------ */ - /* flags for sflag */ typedef enum eDrawStrokeFlags { GP_DRAWDATA_NOSTATUS = (1 << 0), /* don't draw status info */ @@ -1338,6 +1338,39 @@ static void gp_draw_onionskins( } +/* draw interpolate strokes (used only while operator is running) */ +void ED_gp_draw_interpolation(tGPDinterpolate *tgpi, const int type) +{ + tGPDinterpolate_layer *tgpil; + float diff_mat[4][4]; + float color[4]; + + int offsx = 0; + int offsy = 0; + int winx = tgpi->ar->winx; + int winy = tgpi->ar->winy; + + UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, color); + color[3] = 0.6f; + int dflag = 0; + /* if 3d stuff, enable flags */ + if (type == REGION_DRAW_POST_VIEW) { + dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); + } + + /* turn on alpha-blending */ + glEnable(GL_BLEND); + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + /* calculate parent position */ + ED_gpencil_parent_location(tgpil->gpl, diff_mat); + if (tgpil->interFrame) { + gp_draw_strokes(tgpi->gpd, tgpil->interFrame, offsx, offsy, winx, winy, dflag, false, + tgpil->gpl->thickness, 1.0f, color, true, true, diff_mat); + } + } + glDisable(GL_BLEND); +} + /* loop over gpencil data layers, drawing them */ static void gp_draw_data_layers( bGPDbrush *brush, float alpha, bGPdata *gpd, diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 3fe53e3d0ab..3d0323dbfb1 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.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 ***** * @@ -72,7 +72,10 @@ #include "ED_gpencil.h" #include "ED_object.h" +#include "ED_screen.h" #include "ED_view3d.h" +#include "ED_screen.h" +#include "ED_space_api.h" #include "gpencil_intern.h" @@ -431,16 +434,18 @@ void GPENCIL_OT_copy(wmOperatorType *ot) static int gp_strokes_paste_poll(bContext *C) { - /* 1) Must have GP layer to paste to... + /* 1) Must have GP datablock to paste to + * - We don't need to have an active layer though, as that can easily get added + * - If the active layer is locked, we can't paste there, but that should prompt a warning instead * 2) Copy buffer must at least have something (though it may be the wrong sort...) */ - return (CTX_data_active_gpencil_layer(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf)); + return (ED_gpencil_data_get_active(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf)); } -enum { +typedef enum eGP_PasteMode { GP_COPY_ONLY = -1, GP_COPY_MERGE = 1 -}; +} eGP_PasteMode; static int gp_strokes_paste_exec(bContext *C, wmOperator *op) { @@ -448,9 +453,9 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) bGPdata *gpd = ED_gpencil_data_get_active(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"); - + + eGP_PasteMode 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"); @@ -507,39 +512,37 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) } CTX_DATA_END; - /* Ensure we have a frame to draw into - * NOTE: Since this is an op which creates strokes, - * we are obliged to add a new frame if one - * doesn't exist already - */ - - 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)) { - /* 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); + for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { + if (ED_gpencil_stroke_can_use(C, gps)) { + /* Need to verify if layer exists */ + 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); } } + + /* Ensure we have a frame to draw into + * NOTE: Since this is an op which creates strokes, + * we are obliged to add a new frame if one + * doesn't exist already + */ + 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); @@ -554,7 +557,7 @@ void GPENCIL_OT_paste(wmOperatorType *ot) {GP_COPY_MERGE, "MERGE", 0, "Merge", ""}, {0, NULL, 0, NULL, NULL} }; - + /* identifiers */ ot->name = "Paste Strokes"; ot->idname = "GPENCIL_OT_paste"; @@ -566,7 +569,8 @@ void GPENCIL_OT_paste(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - + + /* properties */ ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", ""); } @@ -1881,4 +1885,748 @@ void GPENCIL_OT_stroke_flip(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ************************************************ */ +/* ***************** Reproject Strokes ********************** */ + +static int gp_strokes_reproject_poll(bContext *C) +{ + /* 2 Requirements: + * - 1) Editable GP data + * - 2) 3D View only (2D editors don't have projection issues) + */ + return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C)); +} + +static int gp_strokes_reproject_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + GP_SpaceConversion gsc = {NULL}; + + /* init space conversion stuff */ + gp_point_conversion_init(C, &gsc); + + /* Go through each editable + selected stroke, adjusting each of its points one by one... */ + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) + { + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + int i; + float inverse_diff_mat[4][4]; + + /* Compute inverse matrix for unapplying parenting once instead of doing per-point */ + /* TODO: add this bit to the iteration macro? */ + if (gpl->parent) { + invert_m4_m4(inverse_diff_mat, diff_mat); + } + + /* Adjust each point */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + float xy[2]; + + /* 3D to Screenspace */ + /* Note: We can't use gp_point_to_xy() here because that uses ints for the screenspace + * coordinates, resulting in lost precision, which in turn causes stairstepping + * artifacts in the final points. + */ + if (gpl->parent == NULL) { + gp_point_to_xy_fl(&gsc, gps, pt, &xy[0], &xy[1]); + } + else { + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); + } + + /* Project screenspace back to 3D space (from current perspective) + * so that all points have been treated the same way + */ + gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); + + /* Unapply parent corrections */ + if (gpl->parent) { + mul_m4_v3(inverse_diff_mat, &pt->x); + } + } + } + } + GP_EDITABLE_STROKES_END; + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_reproject(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Reproject Strokes"; + ot->idname = "GPENCIL_OT_reproject"; + ot->description = "Reproject the selected strokes from the current viewpoint to get all points on the same plane again " + "(e.g. to fix problems from accidental 3D cursor movement, or viewport changes)"; + + /* callbacks */ + ot->exec = gp_strokes_reproject_exec; + ot->poll = gp_strokes_reproject_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ========= Interpolation operators ========================== */ +/* Helper: Update point with interpolation */ +static void gp_interpolate_update_points(bGPDstroke *gps_from, bGPDstroke *gps_to, bGPDstroke *new_stroke, float factor) +{ + bGPDspoint *prev, *pt, *next; + + /* update points */ + for (int i = 0; i < new_stroke->totpoints; i++) { + prev = &gps_from->points[i]; + pt = &new_stroke->points[i]; + next = &gps_to->points[i]; + + /* Interpolate all values */ + interp_v3_v3v3(&pt->x, &prev->x, &next->x, factor); + pt->pressure = interpf(prev->pressure, next->pressure, factor); + pt->strength = interpf(prev->strength, next->strength, factor); + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + } +} + +/* Helper: Update all strokes interpolated */ +static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi) +{ + tGPDinterpolate_layer *tgpil; + bGPDstroke *new_stroke, *gps_from, *gps_to; + int cStroke; + float factor; + float shift = tgpi->shift; + + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + factor = tgpil->factor + shift; + for (new_stroke = tgpil->interFrame->strokes.first; new_stroke; new_stroke = new_stroke->next) { + if (new_stroke->totpoints == 0) { + continue; + } + /* get strokes to interpolate */ + cStroke = BLI_findindex(&tgpil->interFrame->strokes, new_stroke); + gps_from = BLI_findlink(&tgpil->prevFrame->strokes, cStroke); + gps_to = BLI_findlink(&tgpil->nextFrame->strokes, cStroke); + /* update points position */ + if ((gps_from) && (gps_to)) { + gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); + } + } + } + + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); +} + +/* Helper: Verify valid strokes for interpolation */ +static bool gp_interpolate_check_todo(bContext *C, bGPdata *gpd) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + int flag = ts->gp_sculpt.flag; + + bGPDlayer *gpl; + bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); + bGPDstroke *gps_from, *gps_to; + int fFrame; + + /* get layers */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* all layers or only active */ + if (((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) { + continue; + } + /* only editable and visible layers are considered */ + if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + /* read strokes */ + for (gps_from = gpl->actframe->strokes.first; gps_from; gps_from = gps_from->next) { + /* only selected */ + if ((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { + continue; + } + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps_from) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps_from) == false) { + continue; + } + /* get final stroke to interpolate */ + fFrame = BLI_findindex(&gpl->actframe->strokes, gps_from); + gps_to = BLI_findlink(&gpl->actframe->next->strokes, fFrame); + if (gps_to == NULL) { + continue; + } + return 1; + } + } + return 0; +} + +/* Helper: Create internal strokes interpolated */ +static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) +{ + bGPDlayer *gpl; + bGPdata *gpd = tgpi->gpd; + tGPDinterpolate_layer *tgpil; + bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); + bGPDstroke *gps_from, *gps_to, *new_stroke; + int fFrame; + + /* set layers */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* all layers or only active */ + if (((tgpi->flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) { + continue; + } + /* only editable and visible layers are considered */ + if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + /* create temp data for each layer */ + tgpil = NULL; + tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer"); + + tgpil->gpl = gpl; + tgpil->prevFrame = gpl->actframe; + tgpil->nextFrame = gpl->actframe->next; + + BLI_addtail(&tgpi->ilayers, tgpil); + /* create a new temporary frame */ + tgpil->interFrame = MEM_callocN(sizeof(bGPDframe), "bGPDframe"); + tgpil->interFrame->framenum = tgpi->cframe; + + /* get interpolation factor */ + tgpil->factor = (float)(tgpi->cframe - tgpil->prevFrame->framenum) / (tgpil->nextFrame->framenum - tgpil->prevFrame->framenum + 1); + /* create new strokes data with interpolated points reading original stroke */ + for (gps_from = tgpil->prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { + bool valid = true; + /* only selected */ + if ((tgpi->flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { + valid = false; + } + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps_from) == false) { + valid = false; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(tgpil->gpl, gps_from) == false) { + valid = false; + } + /* get final stroke to interpolate */ + fFrame = BLI_findindex(&tgpil->prevFrame->strokes, gps_from); + gps_to = BLI_findlink(&tgpil->nextFrame->strokes, fFrame); + if (gps_to == NULL) { + valid = false; + } + /* create new stroke */ + new_stroke = MEM_dupallocN(gps_from); + new_stroke->points = MEM_dupallocN(gps_from->points); + new_stroke->triangles = MEM_dupallocN(gps_from->triangles); + if (valid) { + /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ + if (gps_from->totpoints > gps_to->totpoints) { + new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints); + new_stroke->totpoints = gps_to->totpoints; + new_stroke->tot_triangles = 0; + new_stroke->flag |= GP_STROKE_RECALC_CACHES; + } + /* update points position */ + gp_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor); + } + else { + /* need an empty stroke to keep index correct for lookup, but resize to smallest size */ + new_stroke->totpoints = 0; + new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points)); + new_stroke->tot_triangles = 0; + new_stroke->triangles = MEM_recallocN(new_stroke->triangles, sizeof(*new_stroke->triangles)); + } + /* add to strokes */ + BLI_addtail(&tgpil->interFrame->strokes, new_stroke); + } + } +} + +/* Helper: calculate shift based on position of mouse (we only use x-axis for now. +* since this is more convenient for users to do), and store new shift value +*/ +static void gpencil_mouse_update_shift(tGPDinterpolate *tgpi, wmOperator *op, const wmEvent *event) +{ + float mid = (float)(tgpi->ar->winx - tgpi->ar->winrct.xmin) / 2.0f; + float mpos = event->x - tgpi->ar->winrct.xmin; + if (mpos >= mid) { + tgpi->shift = (mpos - mid) / mid; + } + else { + tgpi->shift = -1.0f * (1.0f - (mpos / mid)); + } + + CLAMP(tgpi->shift, -1.0f, 1.0f); + RNA_float_set(op->ptr, "shift", tgpi->shift); +} + +/* Helper: Draw status message while the user is running the operator */ +static void gpencil_interpolate_status_indicators(tGPDinterpolate *p) +{ + Scene *scene = p->scene; + char status_str[UI_MAX_DRAW_STR]; + char msg_str[UI_MAX_DRAW_STR]; + BLI_strncpy(msg_str, IFACE_("GPencil Interpolation: ESC/RMB to cancel, Enter/LMB to confirm, WHEEL/MOVE to adjust, Factor"), UI_MAX_DRAW_STR); + + if (hasNumInput(&p->num)) { + char str_offs[NUM_STR_REP_LEN]; + + outputNumInput(&p->num, str_offs, &scene->unit); + + BLI_snprintf(status_str, sizeof(status_str), "%s: %s", msg_str, str_offs); + } + else { + BLI_snprintf(status_str, sizeof(status_str), "%s: %d", msg_str, (int)(p->shift * 100.0f)); + } + + ED_area_headerprint(p->sa, status_str); +} + +/* Helper: Update screen and stroke */ +static void gpencil_interpolate_update(bContext *C, wmOperator *op, tGPDinterpolate *tgpi) +{ + /* update shift indicator in header */ + gpencil_interpolate_status_indicators(tgpi); + /* apply... */ + tgpi->shift = RNA_float_get(op->ptr, "shift"); + /* update points position */ + gp_interpolate_update_strokes(C, tgpi); +} + +/* init new temporary interpolation data */ +static bool gp_interpolate_set_init_values(bContext *C, wmOperator *op, tGPDinterpolate *tgpi) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + + /* set current scene and window */ + tgpi->scene = CTX_data_scene(C); + tgpi->sa = CTX_wm_area(C); + tgpi->ar = CTX_wm_region(C); + tgpi->flag = ts->gp_sculpt.flag; + + /* set current frame number */ + tgpi->cframe = tgpi->scene->r.cfra; + + /* set GP datablock */ + tgpi->gpd = gpd; + + /* set interpolation weight */ + tgpi->shift = RNA_float_get(op->ptr, "shift"); + /* set layers */ + gp_interpolate_set_points(C, tgpi); + + return 1; +} + +/* Poll handler: check if context is suitable for interpolation */ +static int gpencil_interpolate_poll(bContext *C) +{ + bGPdata * gpd = CTX_data_gpencil_data(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + /* only 3D view */ + if (CTX_wm_area(C)->spacetype != SPACE_VIEW3D) { + return 0; + } + /* need data to interpolate */ + if (ELEM(NULL, gpd, gpl)) { + return 0; + } + + return 1; +} + +/* Allocate memory and initialize values */ +static tGPDinterpolate *gp_session_init_interpolation(bContext *C, wmOperator *op) +{ + tGPDinterpolate *tgpi = NULL; + + /* create new context data */ + tgpi = MEM_callocN(sizeof(tGPDinterpolate), "GPencil Interpolate Data"); + + /* define initial values */ + gp_interpolate_set_init_values(C, op, tgpi); + + /* return context data for running operator */ + return tgpi; +} + +/* Exit and free memory */ +static void gpencil_interpolate_exit(bContext *C, wmOperator *op) +{ + tGPDinterpolate *tgpi = op->customdata; + tGPDinterpolate_layer *tgpil; + + /* don't assume that operator data exists at all */ + if (tgpi) { + /* remove drawing handler */ + if (tgpi->draw_handle_screen) { + ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_screen); + } + if (tgpi->draw_handle_3d) { + ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_3d); + } + /* clear status message area */ + ED_area_headerprint(tgpi->sa, NULL); + /* finally, free memory used by temp data */ + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + BKE_gpencil_free_strokes(tgpil->interFrame); + MEM_freeN(tgpil->interFrame); + } + + BLI_freelistN(&tgpi->ilayers); + MEM_freeN(tgpi); + } + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* clear pointer */ + op->customdata = NULL; +} + +/* Cancel handler */ +static void gpencil_interpolate_cancel(bContext *C, wmOperator *op) +{ + /* this is just a wrapper around exit() */ + gpencil_interpolate_exit(C, op); +} + +/* Init interpolation: Allocate memory and set init values */ +static int gpencil_interpolate_init(bContext *C, wmOperator *op) +{ + tGPDinterpolate *tgpi; + /* check context */ + tgpi = op->customdata = gp_session_init_interpolation(C, op); + if (tgpi == NULL) { + /* something wasn't set correctly in context */ + gpencil_interpolate_exit(C, op); + return 0; + } + + /* everything is now setup ok */ + return 1; +} + +/* ********************** custom drawcall api ***************** */ +/* Helper: drawing callback for modal operator in screen mode */ +static void gpencil_interpolate_draw_screen(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) +{ + wmOperator *op = arg; + struct tGPDinterpolate *tgpi = op->customdata; + ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_PIXEL); +} + +/* Helper: drawing callback for modal operator in 3d mode */ +static void gpencil_interpolate_draw_3d(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) +{ + wmOperator *op = arg; + struct tGPDinterpolate *tgpi = op->customdata; + ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_VIEW); +} + +/* Invoke handler: Initialize the operator */ +static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + wmWindow *win = CTX_wm_window(C); + Scene *scene = CTX_data_scene(C); + bGPdata * gpd = CTX_data_gpencil_data(C); + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + tGPDinterpolate *tgpi = NULL; + + /* cannot interpolate if not between 2 frames */ + if ((gpl->actframe == NULL) || (gpl->actframe->next == NULL)) { + BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames"); + return OPERATOR_CANCELLED; + } + + /* cannot interpolate in extremes */ + if ((gpl->actframe->framenum == scene->r.cfra) || (gpl->actframe->next->framenum == scene->r.cfra)) { + BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames"); + return OPERATOR_CANCELLED; + } + + /* need editable strokes */ + if (!gp_interpolate_check_todo(C, gpd)) { + BKE_report(op->reports, RPT_ERROR, "Interpolation requires some editable stroke"); + return OPERATOR_CANCELLED; + } + + /* try to initialize context data needed */ + if (!gpencil_interpolate_init(C, op)) { + if (op->customdata) + MEM_freeN(op->customdata); + return OPERATOR_CANCELLED; + } + else + tgpi = op->customdata; + + /* enable custom drawing handlers. It needs 2 handlers because can be strokes in 3d space and screen space and each handler use different + coord system */ + tgpi->draw_handle_screen = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_interpolate_draw_screen, op, REGION_DRAW_POST_PIXEL); + tgpi->draw_handle_3d = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_interpolate_draw_3d, op, REGION_DRAW_POST_VIEW); + /* set cursor to indicate modal */ + WM_cursor_modal_set(win, BC_EW_SCROLLCURSOR); + /* update shift indicator in header */ + gpencil_interpolate_status_indicators(tgpi); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* add a modal handler for this operator */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +/* Modal handler: Events handling during interactive part */ +static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGPDinterpolate *tgpi = op->customdata; + wmWindow *win = CTX_wm_window(C); + bGPDframe *gpf_dst; + bGPDstroke *gps_src, *gps_dst; + tGPDinterpolate_layer *tgpil; + const bool has_numinput = hasNumInput(&tgpi->num); + + switch (event->type) { + case LEFTMOUSE: /* confirm */ + case RETKEY: + { + /* return to normal cursor and header status */ + ED_area_headerprint(tgpi->sa, NULL); + WM_cursor_modal_restore(win); + + /* insert keyframes as required... */ + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + gpf_dst = BKE_gpencil_layer_getframe(tgpil->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW); + gpf_dst->key_type = BEZT_KEYTYPE_BREAKDOWN; + + /* copy strokes */ + BLI_listbase_clear(&gpf_dst->strokes); + for (gps_src = tgpil->interFrame->strokes.first; gps_src; gps_src = gps_src->next) { + if (gps_src->totpoints == 0) { + continue; + } + /* make copy of source stroke, then adjust pointer to points too */ + 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); + } + } + /* clean up temp data */ + gpencil_interpolate_exit(C, op); + + /* done! */ + return OPERATOR_FINISHED; + } + + case ESCKEY: /* cancel */ + case RIGHTMOUSE: + { + /* return to normal cursor and header status */ + ED_area_headerprint(tgpi->sa, NULL); + WM_cursor_modal_restore(win); + + /* clean up temp data */ + gpencil_interpolate_exit(C, op); + + /* canceled! */ + return OPERATOR_CANCELLED; + } + case WHEELUPMOUSE: + { + tgpi->shift = tgpi->shift + 0.01f; + CLAMP(tgpi->shift, -1.0f, 1.0f); + RNA_float_set(op->ptr, "shift", tgpi->shift); + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + break; + } + case WHEELDOWNMOUSE: + { + tgpi->shift = tgpi->shift - 0.01f; + CLAMP(tgpi->shift, -1.0f, 1.0f); + RNA_float_set(op->ptr, "shift", tgpi->shift); + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + break; + } + case MOUSEMOVE: /* calculate new position */ + { + /* only handle mousemove if not doing numinput */ + if (has_numinput == false) { + /* update shift based on position of mouse */ + gpencil_mouse_update_shift(tgpi, op, event); + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + } + break; + } + default: + if ((event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) { + float value; + + /* Grab shift from numeric input, and store this new value (the user see an int) */ + value = tgpi->shift * 100.0f; + applyNumInput(&tgpi->num, &value); + + tgpi->shift = value / 100.0f; + CLAMP(tgpi->shift, -1.0f, 1.0f); + RNA_float_set(op->ptr, "shift", tgpi->shift); + + /* update screen */ + gpencil_interpolate_update(C, op, tgpi); + + break; + } + else { + /* unhandled event - allow to pass through */ + return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; + } + } + + /* still running... */ + return OPERATOR_RUNNING_MODAL; +} + +/* Define modal operator for interpolation */ +void GPENCIL_OT_interpolate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Grease Pencil Interpolation"; + ot->idname = "GPENCIL_OT_interpolate"; + ot->description = "Interpolate grease pencil strokes between frames"; + + /* api callbacks */ + ot->invoke = gpencil_interpolate_invoke; + ot->modal = gpencil_interpolate_modal; + ot->cancel = gpencil_interpolate_cancel; + ot->poll = gpencil_interpolate_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + + RNA_def_float_percentage(ot->srna, "shift", 0.0f, -1.0f, 1.0f, "Shift", "Displacement factor for the interpolate operation", -0.9f, 0.9f); +} + +/* =============== Interpolate sequence ===============*/ +/* Create Sequence Interpolation */ +static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + bGPdata * gpd = CTX_data_gpencil_data(C); + bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); + bGPDlayer *gpl; + bGPDframe *prevFrame, *nextFrame, *interFrame; + bGPDstroke *gps_from, *gps_to, *new_stroke; + float factor; + int cframe, fFrame; + int flag = ts->gp_sculpt.flag; + + /* cannot interpolate if not between 2 frames */ + if ((active_gpl->actframe == NULL) || (active_gpl->actframe->next == NULL)) { + BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames"); + return OPERATOR_CANCELLED; + } + /* cannot interpolate in extremes */ + if ((active_gpl->actframe->framenum == scene->r.cfra) || (active_gpl->actframe->next->framenum == scene->r.cfra)) { + BKE_report(op->reports, RPT_ERROR, "Interpolation requires to be between two grease pencil frames"); + return OPERATOR_CANCELLED; + } + + /* loop all layer to check if need interpolation */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* all layers or only active */ + if (((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) { + continue; + } + /* only editable and visible layers are considered */ + if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + /* store extremes */ + prevFrame = gpl->actframe; + nextFrame = gpl->actframe->next; + /* Loop over intermediary frames and create the interpolation */ + for (cframe = prevFrame->framenum + 1; cframe < nextFrame->framenum; cframe++) { + interFrame = NULL; + + /* get interpolation factor */ + factor = (float)(cframe - prevFrame->framenum) / (nextFrame->framenum - prevFrame->framenum + 1); + + /* create new strokes data with interpolated points reading original stroke */ + for (gps_from = prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { + /* only selected */ + if ((flag & GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { + continue; + } + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps_from) == false) { + continue; + } + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(gpl, gps_from) == false) { + continue; + } + /* get final stroke to interpolate */ + fFrame = BLI_findindex(&prevFrame->strokes, gps_from); + gps_to = BLI_findlink(&nextFrame->strokes, fFrame); + if (gps_to == NULL) { + continue; + } + /* create a new frame if needed */ + if (interFrame == NULL) { + interFrame = BKE_gpencil_layer_getframe(gpl, cframe, GP_GETFRAME_ADD_NEW); + interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN; + } + /* create new stroke */ + new_stroke = MEM_dupallocN(gps_from); + new_stroke->points = MEM_dupallocN(gps_from->points); + new_stroke->triangles = MEM_dupallocN(gps_from->triangles); + /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ + if (gps_from->totpoints > gps_to->totpoints) { + new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints); + new_stroke->totpoints = gps_to->totpoints; + new_stroke->tot_triangles = 0; + new_stroke->flag |= GP_STROKE_RECALC_CACHES; + } + /* update points position */ + gp_interpolate_update_points(gps_from, gps_to, new_stroke, factor); + + /* add to strokes */ + BLI_addtail(&interFrame->strokes, new_stroke); + } + } + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +/* Define sequence interpolation */ +void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Grease Pencil Sequence Interpolation"; + ot->idname = "GPENCIL_OT_interpolate_sequence"; + ot->description = "Interpolate full grease pencil strokes sequence between frames"; + + /* api callbacks */ + ot->exec = gpencil_interpolate_seq_exec; + ot->poll = gpencil_interpolate_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} +/* ========= End Interpolation operators ========================== */ diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 5f7cef9a852..c1ed603273a 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -101,6 +101,18 @@ void gp_point_to_xy(GP_SpaceConversion *settings, struct bGPDstroke *gps, struct int *r_x, int *r_y); /** + * Convert a Grease Pencil coordinate (i.e. can be 2D or 3D) to screenspace (2D) + * + * Just like gp_point_to_xy(), except the resulting coordinates are floats not ints. + * Use this version to solve "stair-step" artifacts which may arise when roundtripping the calculations. + * + * \param[out] r_x The screen-space x-coordinate of the point + * \param[out] r_y The screen-space y-coordinate of the point + */ +void gp_point_to_xy_fl(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt, + float *r_x, float *r_y); + +/** * Convert point to parent space * * \param pt Original point @@ -183,7 +195,7 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints); /** * Add randomness to stroke * \param gps Stroke data -* \param brsuh Brush data +* \param brush Brush data */ void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush); @@ -199,6 +211,7 @@ EnumPropertyItem *ED_gpencil_brushes_enum_itemf(bContext *C, PointerRNA *UNUSED( /* Enums of GP palettes */ EnumPropertyItem *ED_gpencil_palettes_enum_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free); + /* ***************************************************** */ /* Operator Defines */ @@ -249,6 +262,7 @@ void GPENCIL_OT_snap_to_cursor(struct wmOperatorType *ot); void GPENCIL_OT_snap_cursor_to_selected(struct wmOperatorType *ot); void GPENCIL_OT_snap_cursor_to_center(struct wmOperatorType *ot); +void GPENCIL_OT_reproject(struct wmOperatorType *ot); /* stroke sculpting -- */ @@ -303,10 +317,10 @@ 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); @@ -321,7 +335,12 @@ void gpencil_undo_init(struct bGPdata *gpd); void gpencil_undo_push(struct bGPdata *gpd); void gpencil_undo_finish(void); -/******************************************************* */ +/* interpolation ---------- */ + +void GPENCIL_OT_interpolate(struct wmOperatorType *ot); +void GPENCIL_OT_interpolate_sequence(struct wmOperatorType *ot); + +/* ****************************************************** */ /* FILTERED ACTION DATA - TYPES ---> XXX DEPRECEATED OLD ANIM SYSTEM CODE! */ /* XXX - TODO: replace this with the modern bAnimListElem... */ @@ -343,7 +362,7 @@ typedef struct bActListElem { short ownertype; /* type of owner */ } bActListElem; -/******************************************************* */ +/* ****************************************************** */ /* FILTER ACTION DATA - METHODS/TYPES */ /* filtering flags - under what circumstances should a channel be added */ @@ -366,6 +385,9 @@ typedef enum ACTCONT_TYPES { ACTCONT_GPENCIL } ACTCONT_TYPES; +/* ****************************************************** */ +/* Stroke Iteration Utilities */ + /** * Iterate over all editable strokes in the current context, * stopping on each usable layer + stroke pair (i.e. gpl and gps) @@ -401,4 +423,6 @@ typedef enum ACTCONT_TYPES { 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 50f4e795d70..e5d5bdbc831 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -79,7 +79,6 @@ static void ed_keymap_gpencil_general(wmKeyConfig *keyconf) RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER); RNA_boolean_set(kmi->ptr, "wait_for_input", false); - /* Tablet Mappings for Drawing ------------------ */ /* For now, only support direct drawing using the eraser, as most users using a tablet * may still want to use that as their primary pointing device! @@ -144,7 +143,10 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) 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"); - + /* Interpolation */ + WM_keymap_add_item(keymap, "GPENCIL_OT_interpolate", EKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "GPENCIL_OT_interpolate_sequence", EKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); + /* Sculpting ------------------------------------- */ /* Brush-Based Editing: @@ -375,6 +377,8 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_snap_to_cursor); WM_operatortype_append(GPENCIL_OT_snap_cursor_to_selected); + WM_operatortype_append(GPENCIL_OT_reproject); + WM_operatortype_append(GPENCIL_OT_brush_paint); /* Editing (Buttons) ------------ */ @@ -431,6 +435,10 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_brush_select); /* Editing (Time) --------------- */ + + /* Interpolation */ + WM_operatortype_append(GPENCIL_OT_interpolate); + WM_operatortype_append(GPENCIL_OT_interpolate_sequence); } void ED_operatormacros_gpencil(void) diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 70a4b2904ed..cc45cbd82af 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -2310,6 +2310,7 @@ static void gpencil_move_last_stroke_to_back(bContext *C) static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) { tGPsdata *p = op->customdata; + ToolSettings *ts = CTX_data_tool_settings(C); int estate = OPERATOR_PASS_THROUGH; /* default exit state - pass through to support MMB view nav, etc. */ /* if (event->type == NDOF_MOTION) @@ -2363,9 +2364,11 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* 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); + if (ts) { + if ((ts->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; @@ -2425,9 +2428,11 @@ 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); + if (ts) { + if ((ts->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; @@ -2511,9 +2516,11 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * 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); + if (ts) { + if ((ts->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; diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index 793ed2a07d0..7a9ad2b32c0 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -124,6 +124,17 @@ void gpencil_undo_init(bGPdata *gpd) gpencil_undo_push(gpd); } +static void gpencil_undo_free_node(bGPundonode *undo_node) +{ + /* HACK: animdata wasn't duplicated, so it shouldn't be freed here, + * or else the real copy will segfault when accessed + */ + undo_node->gpd->adt = NULL; + + BKE_gpencil_free(undo_node->gpd, false); + MEM_freeN(undo_node->gpd); +} + void gpencil_undo_push(bGPdata *gpd) { bGPundonode *undo_node; @@ -137,20 +148,36 @@ void gpencil_undo_push(bGPdata *gpd) while (undo_node) { bGPundonode *next_node = undo_node->next; - /* HACK: animdata wasn't duplicated, so it shouldn't be freed here, - * or else the real copy will segfault when accessed - */ - undo_node->gpd->adt = NULL; - - BKE_gpencil_free(undo_node->gpd, false); - MEM_freeN(undo_node->gpd); - + gpencil_undo_free_node(undo_node); BLI_freelinkN(&undo_nodes, undo_node); undo_node = next_node; } } + /* limit number of undo steps to the maximum undo steps + * - to prevent running out of memory during **really** + * long drawing sessions (triggering swapping) + */ + /* TODO: Undo-memory constraint is not respected yet, but can be added if we have any need for it */ + if (U.undosteps && !BLI_listbase_is_empty(&undo_nodes)) { + /* remove anything older than n-steps before cur_node */ + int steps = 0; + + undo_node = (cur_node) ? cur_node : undo_nodes.last; + while (undo_node) { + bGPundonode *prev_node = undo_node->prev; + + if (steps >= U.undosteps) { + gpencil_undo_free_node(undo_node); + BLI_freelinkN(&undo_nodes, undo_node); + } + + steps++; + undo_node = prev_node; + } + } + /* create new undo node */ undo_node = MEM_callocN(sizeof(bGPundonode), "gpencil undo node"); undo_node->gpd = BKE_gpencil_data_duplicate(G.main, gpd, true); @@ -165,14 +192,7 @@ void gpencil_undo_finish(void) bGPundonode *undo_node = undo_nodes.first; while (undo_node) { - /* HACK: animdata wasn't duplicated, so it shouldn't be freed here, - * or else the real copy will segfault when accessed - */ - undo_node->gpd->adt = NULL; - - BKE_gpencil_free(undo_node->gpd, false); - MEM_freeN(undo_node->gpd); - + gpencil_undo_free_node(undo_node); undo_node = undo_node->next; } diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index ed9a591dcbe..564ba639983 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -628,6 +628,63 @@ void gp_point_to_xy(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt, } } +/* Convert Grease Pencil points to screen-space values (as floats) + * WARNING: This assumes that the caller has already checked whether the stroke in question can be drawn + */ +void gp_point_to_xy_fl(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt, + float *r_x, float *r_y) +{ + ARegion *ar = gsc->ar; + View2D *v2d = gsc->v2d; + rctf *subrect = gsc->subrect; + float xyval[2]; + + /* sanity checks */ + BLI_assert(!(gps->flag & GP_STROKE_3DSPACE) || (gsc->sa->spacetype == SPACE_VIEW3D)); + BLI_assert(!(gps->flag & GP_STROKE_2DSPACE) || (gsc->sa->spacetype != SPACE_VIEW3D)); + + + if (gps->flag & GP_STROKE_3DSPACE) { + if (ED_view3d_project_float_global(ar, &pt->x, xyval, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + *r_x = xyval[0]; + *r_y = xyval[1]; + } + else { + *r_x = 0.0f; + *r_y = 0.0f; + } + } + else if (gps->flag & GP_STROKE_2DSPACE) { + float vec[3] = {pt->x, pt->y, 0.0f}; + int t_x, t_y; + + mul_m4_v3(gsc->mat, vec); + UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], &t_x, &t_y); + + if ((t_x == t_y) && (t_x == V2D_IS_CLIPPED)) { + /* XXX: Or should we just always use the values as-is? */ + *r_x = 0.0f; + *r_y = 0.0f; + } + else { + *r_x = (float)t_x; + *r_y = (float)t_y; + } + } + else { + if (subrect == NULL) { + /* normal 3D view (or view space) */ + *r_x = (pt->x / 100.0f * ar->winx); + *r_y = (pt->y / 100.0f * ar->winy); + } + else { + /* camera view, use subrect */ + *r_x = ((pt->x / 100.0f) * BLI_rctf_size_x(subrect)) + subrect->xmin; + *r_y = ((pt->y / 100.0f) * BLI_rctf_size_y(subrect)) + subrect->ymin; + } + } +} + /** * Project screenspace coordinates to 3D-space * @@ -883,10 +940,12 @@ void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush) 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]; @@ -955,8 +1014,8 @@ bool ED_gpencil_stroke_minmax( } return changed; } -/* Dynamic Enums of GP Brushes */ +/* Dynamic Enums of GP Brushes */ EnumPropertyItem *ED_gpencil_brushes_enum_itemf( bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) @@ -990,8 +1049,8 @@ EnumPropertyItem *ED_gpencil_brushes_enum_itemf( return item; } -/* Dynamic Enums of GP Palettes */ +/* Dynamic Enums of GP Palettes */ EnumPropertyItem *ED_gpencil_palettes_enum_itemf( bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index d526b0841cc..e53f29ef6b7 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -30,6 +30,8 @@ #ifndef __ED_GPENCIL_H__ #define __ED_GPENCIL_H__ +#include "ED_numinput.h" + struct ID; struct ListBase; struct bContext; @@ -51,6 +53,33 @@ struct wmKeyConfig; /* ------------- Grease-Pencil Helpers ---------------- */ +typedef struct tGPDinterpolate_layer { + struct tGPDinterpolate_layer *next, *prev; + + struct bGPDlayer *gpl; /* layer */ + struct bGPDframe *prevFrame; /* frame before current frame (interpolate-from) */ + struct bGPDframe *nextFrame; /* frame after current frame (interpolate-to) */ + struct bGPDframe *interFrame; /* interpolated frame */ + float factor; /* interpolate factor */ + +} tGPDinterpolate_layer; + +/* Temporary interpolate operation data */ +typedef struct tGPDinterpolate { + struct Scene *scene; /* current scene from context */ + struct ScrArea *sa; /* area where painting originated */ + struct ARegion *ar; /* region where painting originated */ + struct bGPdata *gpd; /* current GP datablock */ + + int cframe; /* current frame number */ + ListBase ilayers; /* (tGPDinterpolate_layer) layers to be interpolated */ + float shift; /* -1/1 value for determining the displacement influence */ + int flag; /* flag from toolsettings */ + + NumInput num; /* numeric input */ + void *draw_handle_3d; /* handle for drawing strokes while operator is running 3d stuff */ + void *draw_handle_screen; /* handle for drawing strokes while operator is running screen stuff */ +} tGPDinterpolate; /* Temporary 'Stroke Point' data * @@ -118,6 +147,7 @@ void ED_gpencil_draw_view2d(const struct bContext *C, bool onlyv2d); void ED_gpencil_draw_view3d(struct wmWindowManager *wm, struct Scene *scene, struct View3D *v3d, struct ARegion *ar, bool only3d); void ED_gpencil_draw_ex(struct Scene *scene, struct bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype); +void ED_gp_draw_interpolation(struct tGPDinterpolate *tgpi, const int type); /* ----------- Grease-Pencil AnimEdit API ------------------ */ bool ED_gplayer_frames_looper(struct bGPDlayer *gpl, struct Scene *scene, diff --git a/source/blender/editors/include/ED_numinput.h b/source/blender/editors/include/ED_numinput.h index f9a22429fc2..00558a3a787 100644 --- a/source/blender/editors/include/ED_numinput.h +++ b/source/blender/editors/include/ED_numinput.h @@ -30,6 +30,8 @@ #define NUM_STR_REP_LEN 64 #define NUM_MAX_ELEMENTS 3 +struct wmEvent; + typedef struct NumInput { short idx_max; /* idx_max < NUM_MAX_ELEMENTS */ int unit_sys; diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 1f053c806b0..26a6fdd7d1f 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -393,6 +393,8 @@ struct uiLayout *UI_popup_menu_layout(uiPopupMenu *head); void UI_popup_menu_reports(struct bContext *C, struct ReportList *reports) ATTR_NONNULL(); int UI_popup_menu_invoke(struct bContext *C, const char *idname, struct ReportList *reports) ATTR_NONNULL(1, 2); +void UI_popup_menu_retval_set(const uiBlock *block, const int retval, const bool enable); + /* Pie menus */ typedef struct uiPieMenu uiPieMenu; @@ -1019,6 +1021,12 @@ void ED_keymap_ui(struct wmKeyConfig *keyconf); void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop); int UI_drop_color_poll(struct bContext *C, struct wmDrag *drag, const struct wmEvent *event); +/* UI_OT_space_context_cycle direction */ +enum { + SPACE_CONTEXT_CYCLE_PREV, + SPACE_CONTEXT_CYCLE_NEXT, +}; + bool UI_context_copy_to_selected_list( struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, struct ListBase *r_lb, bool *r_use_path_from_id, char **r_path); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index d05a80bed62..4972e16bf2e 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -9921,6 +9921,17 @@ static int ui_handle_menus_recursive( return retval; } +/** + * Allow setting menu return value from externals. E.g. WM might need to do this for exiting files correctly. + */ +void UI_popup_menu_retval_set(const uiBlock *block, const int retval, const bool enable) +{ + uiPopupBlockHandle *menu = block->handle; + if (menu) { + menu->menuretval = enable ? (menu->menuretval | retval) : (menu->menuretval & retval); + } +} + /* *************** UI event handlers **************** */ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(userdata)) @@ -10163,7 +10174,11 @@ static void ui_popup_handler_remove(bContext *C, void *userdata) { uiPopupBlockHandle *menu = userdata; - if (menu->cancel_func) { + /* More correct would be to expect UI_RETURN_CANCEL here, but not wanting to + * cancel when removing handlers because of file exit is a rare exception. + * So instead of setting cancel flag for all menus before removing handlers, + * just explicitly flag menu with UI_RETURN_OK to avoid cancelling it. */ + if ((menu->menuretval & UI_RETURN_OK) == 0 && menu->cancel_func) { menu->cancel_func(C, menu->popup_arg); } diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 1af6d902b18..cb539bb1c5d 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1082,6 +1082,78 @@ static void UI_OT_drop_color(wmOperatorType *ot) RNA_def_boolean(ot->srna, "gamma", 0, "Gamma Corrected", "The source color is gamma corrected "); } +/* ------------------------------------------------------------------------- */ + +static EnumPropertyItem space_context_cycle_direction[] = { + {SPACE_CONTEXT_CYCLE_PREV, "PREV", 0, "Previous", ""}, + {SPACE_CONTEXT_CYCLE_NEXT, "NEXT", 0, "Next", ""}, + {0, NULL, 0, NULL, NULL} +}; + +static int space_context_cycle_poll(bContext *C) +{ + ScrArea *sa = CTX_wm_area(C); + return ELEM(sa->spacetype, SPACE_BUTS, SPACE_USERPREF); +} + +/** + * Helper to get the correct RNA pointer/property pair for changing + * the display context of active space type in \sa. + */ +static void context_cycle_prop_get( + bScreen *screen, const ScrArea *sa, + PointerRNA *r_ptr, PropertyRNA **r_prop) +{ + const char *propname; + + switch (sa->spacetype) { + case SPACE_BUTS: + RNA_pointer_create(&screen->id, &RNA_SpaceProperties, sa->spacedata.first, r_ptr); + propname = "context"; + break; + case SPACE_USERPREF: + RNA_pointer_create(NULL, &RNA_UserPreferences, &U, r_ptr); + propname = "active_section"; + break; + } + + *r_prop = RNA_struct_find_property(r_ptr, propname); +} + +static int space_context_cycle_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + const int direction = RNA_enum_get(op->ptr, "direction"); + + PointerRNA ptr; + PropertyRNA *prop; + context_cycle_prop_get(CTX_wm_screen(C), CTX_wm_area(C), &ptr, &prop); + + const int old_context = RNA_property_enum_get(&ptr, prop); + const int new_context = RNA_property_enum_step( + C, &ptr, prop, old_context, + direction == SPACE_CONTEXT_CYCLE_PREV ? -1 : 1); + RNA_property_enum_set(&ptr, prop, new_context); + RNA_property_update(C, &ptr, prop); + + return OPERATOR_FINISHED; +} + +static void UI_OT_space_context_cycle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Cycle Space Context"; + ot->description = "Cycle through the editor context by activating the next/previous one"; + ot->idname = "UI_OT_space_context_cycle"; + + /* api callbacks */ + ot->invoke = space_context_cycle_invoke; + ot->poll = space_context_cycle_poll; + + ot->flag = 0; + + RNA_def_enum(ot->srna, "direction", space_context_cycle_direction, SPACE_CONTEXT_CYCLE_NEXT, "Direction", + "Direction to cycle through"); +} /* ********************************************************* */ @@ -1102,6 +1174,7 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_edittranslation_init); #endif WM_operatortype_append(UI_OT_reloadtranslation); + WM_operatortype_append(UI_OT_space_context_cycle); /* external */ WM_operatortype_append(UI_OT_eyedropper_color); diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 79961eae79d..c131bcb8e14 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -1841,6 +1841,52 @@ void UI_panel_category_draw_all(ARegion *ar, const char *category_id_active) #undef USE_FLAT_INACTIVE } +static int ui_handle_panel_category_cycling(const wmEvent *event, ARegion *ar, const uiBut *active_but) +{ + const bool is_mousewheel = ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE); + const bool inside_tabregion = (event->mval[0] < ((PanelCategoryDyn *)ar->panels_category.first)->rect.xmax); + + /* if mouse is inside non-tab region, ctrl key is required */ + if (is_mousewheel && !event->ctrl && !inside_tabregion) + return WM_UI_HANDLER_CONTINUE; + + + if (active_but && ui_but_supports_cycling(active_but)) { + /* skip - exception to make cycling buttons + * using ctrl+mousewheel work in tabbed regions */ + } + else { + const char *category = UI_panel_category_active_get(ar, false); + if (LIKELY(category)) { + PanelCategoryDyn *pc_dyn = UI_panel_category_find(ar, category); + if (LIKELY(pc_dyn)) { + if (is_mousewheel) { + /* we can probably get rid of this and only allow ctrl+tabbing */ + pc_dyn = (event->type == WHEELDOWNMOUSE) ? pc_dyn->next : pc_dyn->prev; + } + else { + const bool backwards = event->shift; + pc_dyn = backwards ? pc_dyn->prev : pc_dyn->next; + if (!pc_dyn) { + /* proper cyclic behavior, back to first/last category (only used for ctrl+tab) */ + pc_dyn = backwards ? ar->panels_category.last : ar->panels_category.first; + } + } + + if (pc_dyn) { + /* intentionally don't reset scroll in this case, + * this allows for quick browsing between tabs */ + UI_panel_category_active_set(ar, pc_dyn->idname); + ED_region_tag_redraw(ar); + } + } + } + return WM_UI_HANDLER_BREAK; + } + + return WM_UI_HANDLER_CONTINUE; +} + /* XXX should become modal keymap */ /* AKey is opening/closing panels, independent of button state now */ @@ -1853,6 +1899,7 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar, cons retval = WM_UI_HANDLER_CONTINUE; + /* handle category tabs */ if (has_category_tabs) { if (event->val == KM_PRESS) { if (event->type == LEFTMOUSE) { @@ -1867,32 +1914,9 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar, cons retval = WM_UI_HANDLER_BREAK; } } - else if (ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) { - /* mouse wheel cycle tabs */ - - /* first check if the mouse is in the tab region */ - if (event->ctrl || (event->mval[0] < ((PanelCategoryDyn *)ar->panels_category.first)->rect.xmax)) { - if (active_but && ui_but_supports_cycling(active_but)) { - /* skip - exception to make cycling buttons - * using ctrl+mousewheel work in tabbed regions */ - } - else { - const char *category = UI_panel_category_active_get(ar, false); - if (LIKELY(category)) { - PanelCategoryDyn *pc_dyn = UI_panel_category_find(ar, category); - if (LIKELY(pc_dyn)) { - pc_dyn = (event->type == WHEELDOWNMOUSE) ? pc_dyn->next : pc_dyn->prev; - if (pc_dyn) { - /* intentionally don't reset scroll in this case, - * this allows for quick browsing between tabs */ - UI_panel_category_active_set(ar, pc_dyn->idname); - ED_region_tag_redraw(ar); - } - } - } - retval = WM_UI_HANDLER_BREAK; - } - } + else if ((event->type == TABKEY && event->ctrl) || ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) { + /* cycle tabs */ + retval = ui_handle_panel_category_cycling(event, ar, active_but); } } } diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index c507401b9a0..4ea5e2092b6 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -73,38 +73,6 @@ #define MENU_PADDING (int)(0.2f * UI_UNIT_Y) #define MENU_BORDER (int)(0.3f * U.widget_unit) -static int rna_property_enum_step(const bContext *C, PointerRNA *ptr, PropertyRNA *prop, int direction) -{ - EnumPropertyItem *item_array; - int totitem; - bool free; - int value; - int i, i_init; - int step = (direction < 0) ? -1 : 1; - int step_tot = 0; - - RNA_property_enum_items((bContext *)C, ptr, prop, &item_array, &totitem, &free); - value = RNA_property_enum_get(ptr, prop); - i = RNA_enum_from_value(item_array, value); - i_init = i; - - do { - i = mod_i(i + step, totitem); - if (item_array[i].identifier[0]) { - step_tot += step; - } - } while ((i != i_init) && (step_tot != direction)); - - if (i != i_init) { - value = item_array[i].value; - } - - if (free) { - MEM_freeN(item_array); - } - - return value; -} bool ui_but_menu_step_poll(const uiBut *but) { @@ -122,7 +90,8 @@ int ui_but_menu_step(uiBut *but, int direction) return but->menu_step_func(but->block->evil_C, direction, but->poin); } else { - return rna_property_enum_step(but->block->evil_C, &but->rnapoin, but->rnaprop, direction); + const int curval = RNA_property_enum_get(&but->rnapoin, but->rnaprop); + return RNA_property_enum_step(but->block->evil_C, &but->rnapoin, but->rnaprop, curval, direction); } } diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 302ca407add..a81add7a86e 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -145,6 +145,7 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal) opdata->em = em; opdata->is_modal = is_modal; opdata->value_mode = OFFSET_VALUE; + opdata->segments = (float) RNA_int_get(op->ptr, "segments"); pixels_per_inch = U.dpi * U.pixelsize; for (i = 0; i < NUM_VALUE_KINDS; i++) { diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 999d5b278ee..7e31deba2c7 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -3121,8 +3121,10 @@ static void mesh_separate_material_assign_mat_nr(Main *bmain, Object *ob, const BKE_material_resize_id(bmain, obdata, 1, true); ob->mat[0] = ma_ob; + id_us_plus((ID *)ma_ob); ob->matbits[0] = matbit; (*matarar)[0] = ma_obdata; + id_us_plus((ID *)ma_obdata); } else { BKE_material_clear_id(bmain, obdata, true); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 43f1b7b5e31..303846bb053 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -999,7 +999,7 @@ static int group_instance_add_exec(bContext *C, wmOperator *op) Object *ob = ED_object_add_type(C, OB_EMPTY, group->id.name + 2, loc, rot, false, layer); ob->dup_group = group; ob->transflag |= OB_DUPLIGROUP; - id_lib_extern(&group->id); + id_us_plus(&group->id); /* works without this except if you try render right after, see: 22027 */ DAG_relations_tag_update(bmain); diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 3dc7d8ebd4b..111afcdc7a7 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -910,7 +910,7 @@ static void copy_attr(Main *bmain, Scene *scene, View3D *v3d, short event) base->object->dup_group = ob->dup_group; if (ob->dup_group) - id_lib_extern(&ob->dup_group->id); + id_us_plus(&ob->dup_group->id); } else if (event == 7) { /* mass */ base->object->mass = ob->mass; diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 067a5ad2b49..82c6a14eb7f 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1609,7 +1609,7 @@ static int make_links_data_exec(bContext *C, wmOperator *op) case MAKE_LINKS_DUPLIGROUP: ob_dst->dup_group = ob_src->dup_group; if (ob_dst->dup_group) { - id_lib_extern(&ob_dst->dup_group->id); + id_us_plus(&ob_dst->dup_group->id); ob_dst->transflag |= OB_DUPLIGROUP; } break; diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index b4f3426677a..ddbf59b2cf7 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -526,7 +526,7 @@ static Scene *preview_prepare_scene(Main *bmain, Scene *scene, ID *id, int id_ty BKE_node_preview_init_tree(origwrld->nodetree, sp->sizex, sp->sizey, true); } } - + return sce; } @@ -863,8 +863,6 @@ static void shader_preview_free(void *customdata) /* get rid of copied world */ BLI_remlink(&pr_main->world, sp->worldcopy); - /* T32865 - we need to unlink the texture copies, unlike for materials */ - BKE_libblock_relink_ex(pr_main, sp->worldcopy, NULL, NULL, true); BKE_world_free(sp->worldcopy); properties = IDP_GetProperties((ID *)sp->worldcopy, false); @@ -881,7 +879,6 @@ static void shader_preview_free(void *customdata) /* get rid of copied lamp */ BLI_remlink(&pr_main->lamp, sp->lampcopy); - BKE_libblock_relink_ex(pr_main, sp->lampcopy, NULL, NULL, true); BKE_lamp_free(sp->lampcopy); properties = IDP_GetProperties((ID *)sp->lampcopy, false); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 6dbb5db53d0..e446194a1da 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -4331,7 +4331,13 @@ void ED_keymap_screen(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "SCREEN_OT_screenshot", F3KEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "SCREEN_OT_screencast", F3KEY, KM_PRESS, KM_ALT, 0); - + + /* UI */ + kmi = WM_keymap_add_item(keymap, "UI_OT_space_context_cycle", TABKEY, KM_PRESS, KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "direction", SPACE_CONTEXT_CYCLE_NEXT); + kmi = WM_keymap_add_item(keymap, "UI_OT_space_context_cycle", TABKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0); + RNA_enum_set(kmi->ptr, "direction", SPACE_CONTEXT_CYCLE_PREV); + /* tests */ WM_keymap_add_item(keymap, "SCREEN_OT_region_quadview", QKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); WM_keymap_verify_item(keymap, "SCREEN_OT_repeat_history", F3KEY, KM_PRESS, 0, 0); diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index 03f2e146b7d..52c39e5c7a1 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -217,32 +217,56 @@ static void SOUND_OT_open_mono(wmOperatorType *ot) /* ******************************************************* */ -static int sound_update_animation_flags_exec(bContext *C, wmOperator *UNUSED(op)) +static void sound_update_animation_flags(Scene *scene); + +static int sound_update_animation_flags_cb(Sequence *seq, void *user_data) { - Sequence *seq; - Scene *scene = CTX_data_scene(C); struct FCurve *fcu; + Scene *scene = (Scene *)user_data; bool driven; - SEQ_BEGIN(scene->ed, seq) - { - fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, &driven); - if (fcu || driven) - seq->flag |= SEQ_AUDIO_VOLUME_ANIMATED; - else - seq->flag &= ~SEQ_AUDIO_VOLUME_ANIMATED; + fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, &driven); + if (fcu || driven) + seq->flag |= SEQ_AUDIO_VOLUME_ANIMATED; + else + seq->flag &= ~SEQ_AUDIO_VOLUME_ANIMATED; - fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "pitch", 0, &driven); - if (fcu || driven) - seq->flag |= SEQ_AUDIO_PITCH_ANIMATED; - else - seq->flag &= ~SEQ_AUDIO_PITCH_ANIMATED; + fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "pitch", 0, &driven); + if (fcu || driven) + seq->flag |= SEQ_AUDIO_PITCH_ANIMATED; + else + seq->flag &= ~SEQ_AUDIO_PITCH_ANIMATED; - fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "pan", 0, &driven); - if (fcu || driven) - seq->flag |= SEQ_AUDIO_PAN_ANIMATED; - else - seq->flag &= ~SEQ_AUDIO_PAN_ANIMATED; + fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "pan", 0, &driven); + if (fcu || driven) + seq->flag |= SEQ_AUDIO_PAN_ANIMATED; + else + seq->flag &= ~SEQ_AUDIO_PAN_ANIMATED; + + if (seq->type == SEQ_TYPE_SCENE) { + /* TODO(sergey): For now we do manual recursion into the scene strips, + * but perhaps it should be covered by recursive_apply? + */ + sound_update_animation_flags(seq->scene); + } + + return 0; +} + +static void sound_update_animation_flags(Scene *scene) +{ + struct FCurve *fcu; + bool driven; + Sequence *seq; + + if (scene->id.tag & LIB_TAG_DOIT) { + return; + } + scene->id.tag |= LIB_TAG_DOIT; + + SEQ_BEGIN(scene->ed, seq) + { + BKE_sequencer_recursive_apply(seq, sound_update_animation_flags_cb, scene); } SEQ_END @@ -251,7 +275,12 @@ static int sound_update_animation_flags_exec(bContext *C, wmOperator *UNUSED(op) scene->audio.flag |= AUDIO_VOLUME_ANIMATED; else scene->audio.flag &= ~AUDIO_VOLUME_ANIMATED; +} +static int sound_update_animation_flags_exec(bContext *C, wmOperator *UNUSED(op)) +{ + BKE_main_id_tag_idcode(CTX_data_main(C), ID_SCE, LIB_TAG_DOIT, false); + sound_update_animation_flags(CTX_data_scene(C)); return OPERATOR_FINISHED; } @@ -348,10 +377,10 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op) BLI_path_abs(filename, bmain->name); if (split) - result = AUD_mixdown_per_channel(scene->sound_scene, SFRA * specs.rate / FPS, (EFRA - SFRA) * specs.rate / FPS, + result = AUD_mixdown_per_channel(scene->sound_scene, SFRA * specs.rate / FPS, (EFRA - SFRA + 1) * specs.rate / FPS, accuracy, filename, specs, container, codec, bitrate); else - result = AUD_mixdown(scene->sound_scene, SFRA * specs.rate / FPS, (EFRA - SFRA) * specs.rate / FPS, + result = AUD_mixdown(scene->sound_scene, SFRA * specs.rate / FPS, (EFRA - SFRA + 1) * specs.rate / FPS, accuracy, filename, specs, container, codec, bitrate); if (result) { diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index a55b18a2212..71e38f72a7a 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -128,7 +128,6 @@ void file_panels_register(struct ARegionType *art); /* file_utils.c */ void file_tile_boundbox(const ARegion *ar, FileLayout *layout, const int file, rcti *r_bounds); -bool file_is_dir(struct SpaceFile *sfile, const char *path); #endif /* __FILE_INTERN_H__ */ diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index c42ff120102..9f5e98d2431 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1894,7 +1894,7 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN file_expand_directory(C); /* special case, user may have pasted a filepath into the directory */ - if (!file_is_dir(sfile, sfile->params->dir)) { + if (!filelist_is_dir(sfile->files, sfile->params->dir)) { char tdir[FILE_MAX_LIBEXTRA]; char *group, *name; @@ -1920,7 +1920,7 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN BLI_cleanup_dir(G.main->name, sfile->params->dir); - if (file_is_dir(sfile, sfile->params->dir)) { + if (filelist_is_dir(sfile->files, sfile->params->dir)) { /* if directory exists, enter it immediately */ ED_file_change_dir(C); @@ -1993,7 +1993,7 @@ void file_filename_enter_handle(bContext *C, void *UNUSED(arg_unused), void *arg BLI_join_dirfile(filepath, sizeof(sfile->params->dir), sfile->params->dir, sfile->params->file); /* if directory, open it and empty filename field */ - if (file_is_dir(sfile, filepath)) { + if (filelist_is_dir(sfile->files, filepath)) { BLI_cleanup_dir(G.main->name, filepath); BLI_strncpy(sfile->params->dir, filepath, sizeof(sfile->params->dir)); sfile->params->file[0] = '\0'; diff --git a/source/blender/editors/space_file/file_utils.c b/source/blender/editors/space_file/file_utils.c index f19e301064d..c1caf5ae8ac 100644 --- a/source/blender/editors/space_file/file_utils.c +++ b/source/blender/editors/space_file/file_utils.c @@ -48,17 +48,3 @@ void file_tile_boundbox(const ARegion *ar, FileLayout *layout, const int file, r BLI_rcti_init(r_bounds, xmin, xmin + layout->tile_w + layout->tile_border_x, ymax - layout->tile_h - layout->tile_border_y, ymax); } - -/* Cannot directly use BLI_is_dir in libloading context... */ -bool file_is_dir(struct SpaceFile *sfile, const char *path) -{ - if (sfile->params->type == FILE_LOADLIB) { - char tdir[FILE_MAX_LIBEXTRA]; - char *name; - if (BLO_library_path_explode(sfile->params->dir, tdir, NULL, &name) && BLI_is_file(tdir)) { - /* .blend file itself and group are considered as directories, not final datablock names. */ - return name ? false : true; - } - } - return BLI_is_dir(path); -} diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index b6e4991bf52..14719322bf7 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -309,8 +309,9 @@ typedef struct FileList { struct BlendHandle *libfiledata; - /* Set given path as root directory, may change given string in place to a valid value. */ - void (*checkdirf)(struct FileList *, char *); + /* Set given path as root directory, if last bool is true may change given string in place to a valid value. + * Returns True if valid dir. */ + bool (*checkdirf)(struct FileList *, char *, const bool); /* Fill filelist (to be called by read job). */ void (*read_jobf)(struct FileList *, const char *, short *, short *, float *, ThreadMutex *); @@ -942,24 +943,37 @@ int filelist_geticon(struct FileList *filelist, const int index, const bool is_m /* ********** Main ********** */ -static void filelist_checkdir_dir(struct FileList *UNUSED(filelist), char *r_dir) +static bool filelist_checkdir_dir(struct FileList *UNUSED(filelist), char *r_dir, const bool do_change) { - BLI_make_exist(r_dir); + if (do_change) { + BLI_make_exist(r_dir); + return true; + } + else { + return BLI_is_dir(r_dir); + } } -static void filelist_checkdir_lib(struct FileList *UNUSED(filelist), char *r_dir) +static bool filelist_checkdir_lib(struct FileList *UNUSED(filelist), char *r_dir, const bool do_change) { - char dir[FILE_MAX_LIBEXTRA]; - if (!BLO_library_path_explode(r_dir, dir, NULL, NULL)) { + char tdir[FILE_MAX_LIBEXTRA]; + char *name; + + const bool is_valid = (BLI_is_dir(r_dir) || + (BLO_library_path_explode(r_dir, tdir, NULL, &name) && BLI_is_file(tdir) && !name)); + + if (do_change && !is_valid) { /* if not a valid library, we need it to be a valid directory! */ BLI_make_exist(r_dir); + return true; } + return is_valid; } -static void filelist_checkdir_main(struct FileList *filelist, char *r_dir) +static bool filelist_checkdir_main(struct FileList *filelist, char *r_dir, const bool do_change) { /* TODO */ - filelist_checkdir_lib(filelist, r_dir); + return filelist_checkdir_lib(filelist, r_dir, do_change); } static void filelist_entry_clear(FileDirEntry *entry) @@ -1378,6 +1392,11 @@ const char *filelist_dir(struct FileList *filelist) return filelist->filelist.root; } +bool filelist_is_dir(struct FileList *filelist, const char *path) +{ + return filelist->checkdirf(filelist, (char *)path, false); +} + /** * May modify in place given r_dir, which is expected to be FILE_MAX_LIBEXTRA length. */ @@ -1386,7 +1405,7 @@ void filelist_setdir(struct FileList *filelist, char *r_dir) BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA); BLI_cleanup_dir(G.main->name, r_dir); - filelist->checkdirf(filelist, r_dir); + BLI_assert(filelist->checkdirf(filelist, r_dir, true)); if (!STREQ(filelist->filelist.root, r_dir)) { BLI_strncpy(filelist->filelist.root, r_dir, sizeof(filelist->filelist.root)); diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index d70faab1d6a..f4304681780 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -86,6 +86,7 @@ void filelist_clear_ex(struct FileList *filelist, const bool do_c void filelist_free(struct FileList *filelist); const char * filelist_dir(struct FileList *filelist); +bool filelist_is_dir(struct FileList *filelist, const char *path); void filelist_setdir(struct FileList *filelist, char *r_dir); int filelist_files_ensure(struct FileList *filelist); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 1a558d40560..7abe5ff5070 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -592,7 +592,7 @@ void ED_file_change_dir(bContext *C) sfile->params->filter_search[0] = '\0'; sfile->params->active_file = -1; - if (!file_is_dir(sfile, sfile->params->dir)) { + if (!filelist_is_dir(sfile->files, sfile->params->dir)) { BLI_strncpy(sfile->params->dir, filelist_dir(sfile->files), sizeof(sfile->params->dir)); /* could return but just refresh the current dir */ } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index f9d76da9f87..a89d3494af0 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1608,18 +1608,26 @@ static int save_image_options_init(SaveImageOptions *simopts, SpaceImage *sima, /* imtype */ simopts->im_format = scene->r.im_format; is_depth_set = true; + if (!BKE_image_is_multiview(ima)) { + /* In case multiview is disabled, render settings would be invalid for render result in this area. */ + simopts->im_format.stereo3d_format = *ima->stereo3d_format; + simopts->im_format.views_format = ima->views_format; + } } else { if (ima->source == IMA_SRC_GENERATED) { simopts->im_format.imtype = R_IMF_IMTYPE_PNG; simopts->im_format.compress = ibuf->foptions.quality; + simopts->im_format.planes = ibuf->planes; } else { BKE_imbuf_to_image_format(&simopts->im_format, ibuf); } - } - simopts->im_format.planes = ibuf->planes; + /* use the multiview image settings as the default */ + simopts->im_format.stereo3d_format = *ima->stereo3d_format; + simopts->im_format.views_format = ima->views_format; + } //simopts->subimtype = scene->r.subimtype; /* XXX - this is lame, we need to make these available too! */ @@ -1660,10 +1668,6 @@ static int save_image_options_init(SaveImageOptions *simopts, SpaceImage *sima, } } - /* use the multiview image settings as the default */ - simopts->im_format.stereo3d_format = *ima->stereo3d_format; - simopts->im_format.views_format = ima->views_format; - /* color management */ BKE_color_managed_display_settings_copy(&simopts->im_format.display_settings, &scene->display_settings); BKE_color_managed_view_settings_copy(&simopts->im_format.view_settings, &scene->view_settings); diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index d9c51e427c8..ab40c55b59d 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -1327,8 +1327,10 @@ void drawnodespace(const bContext *C, ARegion *ar) path = snode->treepath.last; /* update tree path name (drawn in the bottom left) */ - if (snode->id && UNLIKELY(!STREQ(path->node_name, snode->id->name + 2))) { - BLI_strncpy(path->node_name, snode->id->name + 2, sizeof(path->node_name)); + ID *name_id = (path->nodetree && path->nodetree != snode->nodetree) ? &path->nodetree->id : snode->id; + + if (name_id && UNLIKELY(!STREQ(path->node_name, name_id->name + 2))) { + BLI_strncpy(path->node_name, name_id->name + 2, sizeof(path->node_name)); } /* current View2D center, will be set temporarily for parent node trees */ diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index d8edafb1dc9..db7e1b58e40 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -4207,7 +4207,7 @@ static bool draw_mesh_object(Scene *scene, ARegion *ar, View3D *v3d, RegionView3 /* If we are drawing shadows and any of the materials don't cast a shadow, * then don't draw the object */ if (v3d->flag2 & V3D_RENDER_SHADOW) { - for (int i = 0; i < ob->totcol; ++i) { + for (int i = 1; i <= ob->totcol; ++i) { Material *ma = give_current_material(ob, i); if (ma && !(ma->mode2 & MA_CASTSHADOW)) { return true; diff --git a/source/blender/editors/space_view3d/drawvolume.c b/source/blender/editors/space_view3d/drawvolume.c index 8dbc2788744..06677ef4476 100644 --- a/source/blender/editors/space_view3d/drawvolume.c +++ b/source/blender/editors/space_view3d/drawvolume.c @@ -288,6 +288,108 @@ static int create_view_aligned_slices(VolumeSlicer *slicer, return num_points; } +static void bind_shader(SmokeDomainSettings *sds, GPUShader *shader, GPUTexture *tex_spec, + bool use_fire, const float min[3], + const float ob_sizei[3], const float invsize[3]) +{ + int invsize_location = GPU_shader_get_uniform(shader, "invsize"); + int ob_sizei_location = GPU_shader_get_uniform(shader, "ob_sizei"); + int min_location = GPU_shader_get_uniform(shader, "min_location"); + + int soot_location; + int stepsize_location; + int densityscale_location; + int spec_location, flame_location; + int shadow_location, actcol_location; + + if (use_fire) { + spec_location = GPU_shader_get_uniform(shader, "spectrum_texture"); + flame_location = GPU_shader_get_uniform(shader, "flame_texture"); + } + else { + shadow_location = GPU_shader_get_uniform(shader, "shadow_texture"); + actcol_location = GPU_shader_get_uniform(shader, "active_color"); + soot_location = GPU_shader_get_uniform(shader, "soot_texture"); + stepsize_location = GPU_shader_get_uniform(shader, "step_size"); + densityscale_location = GPU_shader_get_uniform(shader, "density_scale"); + } + + GPU_shader_bind(shader); + + if (use_fire) { + GPU_texture_bind(sds->tex_flame, 2); + GPU_shader_uniform_texture(shader, flame_location, sds->tex_flame); + + GPU_texture_bind(tex_spec, 3); + GPU_shader_uniform_texture(shader, spec_location, tex_spec); + } + else { + float density_scale = 10.0f; + + GPU_shader_uniform_vector(shader, stepsize_location, 1, 1, &sds->dx); + GPU_shader_uniform_vector(shader, densityscale_location, 1, 1, &density_scale); + + GPU_texture_bind(sds->tex, 0); + GPU_shader_uniform_texture(shader, soot_location, sds->tex); + + GPU_texture_bind(sds->tex_shadow, 1); + GPU_shader_uniform_texture(shader, shadow_location, sds->tex_shadow); + + float active_color[3] = { 0.9, 0.9, 0.9 }; + if ((sds->active_fields & SM_ACTIVE_COLORS) == 0) + mul_v3_v3(active_color, sds->active_color); + GPU_shader_uniform_vector(shader, actcol_location, 3, 1, active_color); + } + + GPU_shader_uniform_vector(shader, min_location, 3, 1, min); + GPU_shader_uniform_vector(shader, ob_sizei_location, 3, 1, ob_sizei); + GPU_shader_uniform_vector(shader, invsize_location, 3, 1, invsize); +} + +static void unbind_shader(SmokeDomainSettings *sds, GPUTexture *tex_spec, bool use_fire) +{ + GPU_shader_unbind(); + + GPU_texture_unbind(sds->tex); + + if (use_fire) { + GPU_texture_unbind(sds->tex_flame); + GPU_texture_unbind(tex_spec); + GPU_texture_free(tex_spec); + } + else { + GPU_texture_unbind(sds->tex_shadow); + } +} + +static void draw_buffer(SmokeDomainSettings *sds, GPUShader *shader, const VolumeSlicer *slicer, + const float ob_sizei[3], const float invsize[3], const int num_points, const bool do_fire) +{ + GPUTexture *tex_spec = (do_fire) ? create_flame_spectrum_texture() : NULL; + + GLuint vertex_buffer; + glGenBuffers(1, &vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * num_points, &slicer->verts[0][0], GL_STATIC_DRAW); + + bind_shader(sds, shader, tex_spec, do_fire, slicer->min, ob_sizei, invsize); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, NULL); + + glDrawArrays(GL_TRIANGLES, 0, num_points); + + glDisableClientState(GL_VERTEX_ARRAY); + + unbind_shader(sds, tex_spec, do_fire); + + /* cleanup */ + + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDeleteBuffers(1, &vertex_buffer); +} + void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, const float min[3], const float max[3], const float viewnormal[3]) @@ -299,14 +401,23 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, const bool use_fire = (sds->active_fields & SM_ACTIVE_FIRE) && sds->tex_flame; - GPUShader *shader = GPU_shader_get_builtin_shader( - (use_fire) ? GPU_SHADER_SMOKE_FIRE : GPU_SHADER_SMOKE); + GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_SMOKE); if (!shader) { fprintf(stderr, "Unable to create GLSL smoke shader.\n"); return; } + GPUShader *fire_shader = NULL; + if (use_fire) { + fire_shader = GPU_shader_get_builtin_shader(GPU_SHADER_SMOKE_FIRE); + + if (!fire_shader) { + fprintf(stderr, "Unable to create GLSL fire shader.\n"); + return; + } + } + const float ob_sizei[3] = { 1.0f / fabsf(ob->size[0]), 1.0f / fabsf(ob->size[1]), @@ -320,50 +431,6 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, TIMEIT_START(draw); #endif - /* setup smoke shader */ - - int soot_location = GPU_shader_get_uniform(shader, "soot_texture"); - int spec_location = GPU_shader_get_uniform(shader, "spectrum_texture"); - int shadow_location = GPU_shader_get_uniform(shader, "shadow_texture"); - int flame_location = GPU_shader_get_uniform(shader, "flame_texture"); - int actcol_location = GPU_shader_get_uniform(shader, "active_color"); - int stepsize_location = GPU_shader_get_uniform(shader, "step_size"); - int densityscale_location = GPU_shader_get_uniform(shader, "density_scale"); - int invsize_location = GPU_shader_get_uniform(shader, "invsize"); - int ob_sizei_location = GPU_shader_get_uniform(shader, "ob_sizei"); - int min_location = GPU_shader_get_uniform(shader, "min_location"); - - GPU_shader_bind(shader); - - GPU_texture_bind(sds->tex, 0); - GPU_shader_uniform_texture(shader, soot_location, sds->tex); - - GPU_texture_bind(sds->tex_shadow, 1); - GPU_shader_uniform_texture(shader, shadow_location, sds->tex_shadow); - - GPUTexture *tex_spec = NULL; - - if (use_fire) { - GPU_texture_bind(sds->tex_flame, 2); - GPU_shader_uniform_texture(shader, flame_location, sds->tex_flame); - - tex_spec = create_flame_spectrum_texture(); - GPU_texture_bind(tex_spec, 3); - GPU_shader_uniform_texture(shader, spec_location, tex_spec); - } - - float active_color[3] = { 0.9, 0.9, 0.9 }; - float density_scale = 10.0f; - if ((sds->active_fields & SM_ACTIVE_COLORS) == 0) - mul_v3_v3(active_color, sds->active_color); - - GPU_shader_uniform_vector(shader, actcol_location, 3, 1, active_color); - GPU_shader_uniform_vector(shader, stepsize_location, 1, 1, &sds->dx); - GPU_shader_uniform_vector(shader, densityscale_location, 1, 1, &density_scale); - GPU_shader_uniform_vector(shader, min_location, 3, 1, min); - GPU_shader_uniform_vector(shader, ob_sizei_location, 3, 1, ob_sizei); - GPU_shader_uniform_vector(shader, invsize_location, 3, 1, invsize); - /* setup slicing information */ const int max_slices = 256; @@ -387,43 +454,23 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, glEnable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - GLuint vertex_buffer; - glGenBuffers(1, &vertex_buffer); - glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * num_points, &slicer.verts[0][0], GL_STATIC_DRAW); - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(3, GL_FLOAT, 0, NULL); - - glDrawArrays(GL_TRIANGLES, 0, num_points); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + draw_buffer(sds, shader, &slicer, ob_sizei, invsize, num_points, false); - glDisableClientState(GL_VERTEX_ARRAY); + /* Draw fire separately (T47639). */ + if (use_fire) { + glBlendFunc(GL_ONE, GL_ONE); + draw_buffer(sds, fire_shader, &slicer, ob_sizei, invsize, num_points, true); + } #ifdef DEBUG_DRAW_TIME printf("Draw Time: %f\n", (float)TIMEIT_VALUE(draw)); TIMEIT_END(draw); #endif - /* cleanup */ - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glDeleteBuffers(1, &vertex_buffer); - - GPU_texture_unbind(sds->tex); - GPU_texture_unbind(sds->tex_shadow); - - if (use_fire) { - GPU_texture_unbind(sds->tex_flame); - GPU_texture_unbind(tex_spec); - GPU_texture_free(tex_spec); - } - MEM_freeN(slicer.verts); - GPU_shader_unbind(); - glDepthMask(gl_depth_write); if (!gl_blend) { diff --git a/source/blender/editors/space_view3d/view3d_ruler.c b/source/blender/editors/space_view3d/view3d_ruler.c index 67a40ae4180..3c13ab9d595 100644 --- a/source/blender/editors/space_view3d/view3d_ruler.c +++ b/source/blender/editors/space_view3d/view3d_ruler.c @@ -357,7 +357,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) gps->flag = GP_STROKE_3DSPACE; gps->thickness = 3; /* assign color to stroke */ - strcpy(gps->colorname, palcolor->info); + BLI_strncpy(gps->colorname, palcolor->info, sizeof(gps->colorname)); gps->palcolor = palcolor; BLI_addtail(&gpf->strokes, gps); changed = true; diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index cfa083116f2..8885209ce01 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -78,6 +78,7 @@ set(SRC shaders/gpu_shader_vsm_store_frag.glsl shaders/gpu_shader_vsm_store_vert.glsl shaders/gpu_shader_fx_depth_resolve.glsl + shaders/gpu_shader_fire_frag.glsl shaders/gpu_shader_smoke_frag.glsl shaders/gpu_shader_smoke_vert.glsl @@ -99,6 +100,7 @@ set(SRC ) data_to_c_simple(shaders/gpu_shader_geometry.glsl SRC) +data_to_c_simple(shaders/gpu_shader_fire_frag.glsl SRC) data_to_c_simple(shaders/gpu_shader_smoke_frag.glsl SRC) data_to_c_simple(shaders/gpu_shader_smoke_vert.glsl SRC) data_to_c_simple(shaders/gpu_shader_material.glsl SRC) diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index c40bc8eaf27..211394e7932 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -877,7 +877,7 @@ static char *code_generate_geometry(ListBase *nodes, bool use_opensubdiv) if (input->attribtype == CD_MTFACE) { BLI_dynstr_appendf( ds, - "\tINTERP_FACE_VARYING_2(var%d, " + "\tINTERP_FACE_VARYING_ATT_2(var%d, " "int(texelFetch(FVarDataOffsetBuffer, fvar%d_offset).r), st);\n", input->attribid, input->attribid); diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader.c index df1213b01e2..5cfb323bc4b 100644 --- a/source/blender/gpu/intern/gpu_shader.c +++ b/source/blender/gpu/intern/gpu_shader.c @@ -45,6 +45,7 @@ #define MAX_EXT_DEFINE_LENGTH 1024 /* Non-generated shaders */ +extern char datatoc_gpu_shader_fire_frag_glsl[]; extern char datatoc_gpu_shader_smoke_vert_glsl[]; extern char datatoc_gpu_shader_smoke_frag_glsl[]; extern char datatoc_gpu_shader_vsm_store_vert_glsl[]; @@ -618,8 +619,8 @@ GPUShader *GPU_shader_get_builtin_shader(GPUBuiltinShader shader) case GPU_SHADER_SMOKE_FIRE: if (!GG.shaders.smoke_fire) GG.shaders.smoke_fire = GPU_shader_create( - datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_smoke_frag_glsl, - NULL, NULL, "#define USE_FIRE;\n", 0, 0, 0); + datatoc_gpu_shader_smoke_vert_glsl, datatoc_gpu_shader_fire_frag_glsl, + NULL, NULL, NULL, 0, 0, 0); retval = GG.shaders.smoke_fire; break; } diff --git a/source/blender/gpu/shaders/gpu_shader_fire_frag.glsl b/source/blender/gpu/shaders/gpu_shader_fire_frag.glsl new file mode 100644 index 00000000000..3819203bcd9 --- /dev/null +++ b/source/blender/gpu/shaders/gpu_shader_fire_frag.glsl @@ -0,0 +1,17 @@ + +varying vec3 coords; + +uniform sampler3D flame_texture; +uniform sampler1D spectrum_texture; + +void main() +{ + float flame = texture3D(flame_texture, coords).r; + vec4 emission = texture1D(spectrum_texture, flame); + + vec4 color; + color.rgb = emission.a * emission.rgb; + color.a = emission.a; + + gl_FragColor = color; +} diff --git a/source/blender/gpu/shaders/gpu_shader_geometry.glsl b/source/blender/gpu/shaders/gpu_shader_geometry.glsl index 6f063883e37..fe630dbeddb 100644 --- a/source/blender/gpu/shaders/gpu_shader_geometry.glsl +++ b/source/blender/gpu/shaders/gpu_shader_geometry.glsl @@ -31,6 +31,18 @@ uniform int osd_fvar_count; tessCoord.t); \ } +#ifdef USE_NEW_SHADING +# define INTERP_FACE_VARYING_ATT_2(result, fvarOffset, tessCoord) \ + { \ + vec2 tmp; \ + INTERP_FACE_VARYING_2(tmp, fvarOffset, tessCoord); \ + result = vec3(tmp, 0); \ + } +#else +# define INTERP_FACE_VARYING_ATT_2(result, fvarOffset, tessCoord) \ + INTERP_FACE_VARYING_2(result, fvarOffset, tessCoord) +#endif + uniform samplerBuffer FVarDataBuffer; uniform isamplerBuffer FVarDataOffsetBuffer; diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 845a78720ba..119bfb61fec 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -2185,11 +2185,11 @@ void shade_madd_clamped(vec4 col, vec4 col1, vec4 col2, out vec4 outcol) outcol = col + max(col1 * col2, vec4(0.0, 0.0, 0.0, 0.0)); } -void env_apply(vec4 col, vec4 hor, vec4 zen, vec4 f, mat4 vm, vec3 vn, out vec4 outcol) +void env_apply(vec4 col, vec3 hor, vec3 zen, vec4 f, mat4 vm, vec3 vn, out vec4 outcol) { vec3 vv = normalize(vm[2].xyz); float skyfac = 0.5 * (1.0 + dot(vn, -vv)); - outcol = col + f * mix(hor, zen, skyfac); + outcol = col + f * vec4(mix(hor, zen, skyfac), 0); } void shade_maddf(vec4 col, float f, vec4 col1, out vec4 outcol) diff --git a/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl b/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl index 4d1feb5c83e..fd790009e02 100644 --- a/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_smoke_frag.glsl @@ -8,11 +8,6 @@ uniform float density_scale; uniform sampler3D soot_texture; uniform sampler3D shadow_texture; -#ifdef USE_FIRE -uniform sampler3D flame_texture; -uniform sampler1D spectrum_texture; -#endif - void main() { /* compute color and density from volume texture */ @@ -37,12 +32,5 @@ void main() /* premultiply alpha */ vec4 color = vec4(soot_alpha * soot_color, soot_alpha); -#ifdef USE_FIRE - /* fire */ - float flame = texture3D(flame_texture, coords).r; - vec4 emission = texture1D(spectrum_texture, flame); - color.rgb += (1 - color.a) * emission.a * emission.rgb; -#endif - gl_FragColor = color; } diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 773d203bdb3..23b73424da5 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -336,7 +336,7 @@ typedef enum eGPdata_Flag { /* Convenience/cache flag to make it easier to quickly toggle onion skinning on/off */ 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) + GP_DATA_SHOW_DIRECTION = (1 << 10) } eGPdata_Flag; #endif /* __DNA_GPENCIL_TYPES_H__ */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index a4934cc1f24..5c5264afcba 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1167,7 +1167,11 @@ typedef enum eGP_BrushEdit_SettingsFlag { /* apply brush to strength */ GP_BRUSHEDIT_FLAG_APPLY_STRENGTH = (1 << 2), /* apply brush to thickness */ - GP_BRUSHEDIT_FLAG_APPLY_THICKNESS = (1 << 3) + GP_BRUSHEDIT_FLAG_APPLY_THICKNESS = (1 << 3), + /* apply interpolation to all layers */ + GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS = (1 << 4), + /* apply interpolation to only selected */ + GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED = (1 << 5) } eGP_BrushEdit_SettingsFlag; @@ -1738,9 +1742,11 @@ typedef struct Scene { #define R_STAMP_CAMERALENS 0x0800 #define R_STAMP_STRIPMETA 0x1000 #define R_STAMP_MEMORY 0x2000 +#define R_STAMP_HIDE_LABELS 0x4000 #define R_STAMP_ALL (R_STAMP_TIME|R_STAMP_FRAME|R_STAMP_DATE|R_STAMP_CAMERA|R_STAMP_SCENE| \ R_STAMP_NOTE|R_STAMP_MARKER|R_STAMP_FILENAME|R_STAMP_SEQSTRIP| \ - R_STAMP_RENDERTIME|R_STAMP_CAMERALENS|R_STAMP_MEMORY) + R_STAMP_RENDERTIME|R_STAMP_CAMERALENS|R_STAMP_MEMORY| \ + R_STAMP_HIDE_LABELS) /* alphamode */ #define R_ADDSKY 0 diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 9cbe132282f..820465ee7d1 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -916,6 +916,7 @@ int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop); void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value); int RNA_property_enum_get_default(PointerRNA *ptr, PropertyRNA *prop); void *RNA_property_enum_py_data_get(PropertyRNA *prop); +int RNA_property_enum_step(const struct bContext *C, PointerRNA *ptr, PropertyRNA *prop, int from_value, int direction); PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop); void RNA_property_pointer_set(PointerRNA *ptr, PropertyRNA *prop, PointerRNA ptr_value); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 047e5ea17ab..6879a0534e9 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -2873,6 +2873,45 @@ void *RNA_property_enum_py_data_get(PropertyRNA *prop) return eprop->py_data; } +/** + * Get the value of the item that is \a step items away from \a from_value. + * + * \param from_value: Item value to start stepping from. + * \param step: Absolute value defines step size, sign defines direction. + * E.g to get the next item, pass 1, for the previous -1. + */ +int RNA_property_enum_step(const bContext *C, PointerRNA *ptr, PropertyRNA *prop, int from_value, int step) +{ + EnumPropertyItem *item_array; + int totitem; + bool free; + int result_value = from_value; + int i, i_init; + int single_step = (step < 0) ? -1 : 1; + int step_tot = 0; + + RNA_property_enum_items((bContext *)C, ptr, prop, &item_array, &totitem, &free); + i = RNA_enum_from_value(item_array, from_value); + i_init = i; + + do { + i = mod_i(i + single_step, totitem); + if (item_array[i].identifier[0]) { + step_tot += single_step; + } + } while ((i != i_init) && (step_tot != step)); + + if (i != i_init) { + result_value = item_array[i].value; + } + + if (free) { + MEM_freeN(item_array); + } + + return result_value; +} + PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop) { PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop; diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index e4f9f856db7..2a8cc073e22 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -1889,7 +1889,7 @@ void RNA_def_material(BlenderRNA *brna) prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "index"); - RNA_def_property_ui_text(prop, "Pass Index", "Index number for the IndexMA render pass"); + RNA_def_property_ui_text(prop, "Pass Index", "Index number for the \"Material Index\" render pass"); RNA_def_property_update(prop, NC_OBJECT, "rna_Material_update"); /* flags */ diff --git a/source/blender/makesrna/intern/rna_movieclip.c b/source/blender/makesrna/intern/rna_movieclip.c index 19d78361019..15411f85ba3 100644 --- a/source/blender/makesrna/intern/rna_movieclip.c +++ b/source/blender/makesrna/intern/rna_movieclip.c @@ -347,6 +347,8 @@ static void rna_def_movieclip(BlenderRNA *brna) RNA_def_property_pointer_sdna(prop, NULL, "colorspace_settings"); RNA_def_property_struct_type(prop, "ColorManagedInputColorspaceSettings"); RNA_def_property_ui_text(prop, "Color Space Settings", "Input color space settings"); + + rna_def_animdata_common(srna); } void RNA_def_movieclip(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 2d158a3d371..addc6b6e28b 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -2560,7 +2560,7 @@ static void rna_def_object(BlenderRNA *brna) /* render */ prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "index"); - RNA_def_property_ui_text(prop, "Pass Index", "Index number for the IndexOB render pass"); + RNA_def_property_ui_text(prop, "Pass Index", "Index number for the \"Object Index\" render pass"); RNA_def_property_update(prop, NC_OBJECT, "rna_Object_internal_update"); prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 98df12719d3..f2a1e0a50ab 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -2291,7 +2291,7 @@ static void rna_def_gpencil_brushes(BlenderRNA *brna, PropertyRNA *cprop) "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"); + RNA_def_property_ui_text(prop, "Active Brush Index", "Index of active brush"); } static void rna_def_transform_orientation(BlenderRNA *brna) @@ -6289,6 +6289,11 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Stamp Output", "Render the stamp info text in the rendered image"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + prop = RNA_def_property(srna, "use_stamp_labels", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "stamp", R_STAMP_HIDE_LABELS); + RNA_def_property_ui_text(prop, "Stamp Labels", "Draw stamp labels (\"Camera\" in front of camera name, etc.)"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + prop = RNA_def_property(srna, "use_stamp_strip_meta", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "stamp", R_STAMP_STRIPMETA); RNA_def_property_ui_text(prop, "Strip Metadata", "Use metadata from the strips in the sequencer"); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index a8e80dbc4a3..7e1d0164eb4 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -1037,6 +1037,16 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna) 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, "interpolate_all_layers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS); + RNA_def_property_ui_text(prop, "Interpolate All Layers", "Interpolate all layers, not only active"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "interpolate_selected_only", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED); + RNA_def_property_ui_text(prop, "Interpolate Selected Strokes", "Interpolate only selected strokes in the original frame"); + 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); diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index a751c414d83..9d55115a14c 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -618,7 +618,7 @@ void RNA_api_ui_layout(StructRNA *srna) #endif func = RNA_def_function(srna, "label", "rna_uiItemL"); - RNA_def_function_ui_description(func, "Item. Display text and/or icon in the layout"); + RNA_def_function_ui_description(func, "Item. Displays text and/or icon in the layout"); api_ui_item_common(func); parm = RNA_def_property(func, "icon_value", PROP_INT, PROP_UNSIGNED); RNA_def_property_ui_text(parm, "Icon Value", "Override automatic icon of the item"); @@ -644,6 +644,7 @@ void RNA_api_ui_layout(StructRNA *srna) /* templates */ func = RNA_def_function(srna, "template_header", "uiTemplateHeader"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); + RNA_def_function_ui_description(func, "Inserts common Space header UI (editor type selector)"); func = RNA_def_function(srna, "template_ID", "uiTemplateID"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); @@ -853,9 +854,12 @@ void RNA_api_ui_layout(StructRNA *srna) func = RNA_def_function(srna, "template_header_3D", "uiTemplateHeader3D"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); + RNA_def_function_ui_description(func, "Inserts common 3DView header UI (selectors for context mode, shading, etc.)"); + func = RNA_def_function(srna, "template_edit_mode_selection", "uiTemplateEditModeSelection"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); + RNA_def_function_ui_description(func, "Inserts common 3DView Edit modes header UI (selector for selection mode)"); func = RNA_def_function(srna, "template_reports_banner", "uiTemplateReportsBanner"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index 7cf0cf67269..8d0b704a402 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -398,18 +398,21 @@ void RNA_api_wm(StructRNA *srna) rna_generic_op_invoke(func, 0); func = RNA_def_function(srna, "modal_handler_add", "rna_event_modal_handler_add"); + RNA_def_function_ui_description(func, "Add a modal handler to the window manager, for the given modal operator " + "(called by invoke() with self, just before returning {'RUNNING_MODAL'})"); RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_CONTEXT); parm = RNA_def_pointer(func, "operator", "Operator", "", "Operator to call"); RNA_def_property_flag(parm, PROP_REQUIRED); - RNA_def_function_return(func, RNA_def_boolean(func, "handle", 1, "", "")); + RNA_def_function_return(func, RNA_def_boolean(func, "handle", 1, "", "Whether adding the handler was successful")); func = RNA_def_function(srna, "event_timer_add", "rna_event_timer_add"); + RNA_def_function_ui_description(func, "Add a timer to the given window, to generate periodic 'TIMER' events"); parm = RNA_def_property(func, "time_step", PROP_FLOAT, PROP_NONE); RNA_def_property_flag(parm, PROP_REQUIRED); RNA_def_property_range(parm, 0.0, FLT_MAX); RNA_def_property_ui_text(parm, "Time Step", "Interval in seconds between timer events"); - RNA_def_pointer(func, "window", "Window", "", "Window to attach the timer to or None"); + RNA_def_pointer(func, "window", "Window", "", "Window to attach the timer to, or None"); parm = RNA_def_pointer(func, "result", "Timer", "", ""); RNA_def_function_return(func, parm); @@ -421,44 +424,49 @@ void RNA_api_wm(StructRNA *srna) /* Progress bar interface */ func = RNA_def_function(srna, "progress_begin", "rna_progress_begin"); RNA_def_function_ui_description(func, "Start progress report"); - parm = RNA_def_property(func, "min", PROP_FLOAT, PROP_NONE); RNA_def_property_ui_text(parm, "min", "any value in range [0,9999]"); RNA_def_property_flag(parm, PROP_REQUIRED); - parm = RNA_def_property(func, "max", PROP_FLOAT, PROP_NONE); RNA_def_property_flag(parm, PROP_REQUIRED); RNA_def_property_ui_text(parm, "max", "any value in range [min+1,9998]"); func = RNA_def_function(srna, "progress_update", "rna_progress_update"); + RNA_def_function_ui_description(func, "Update the progress feedback"); parm = RNA_def_property(func, "value", PROP_FLOAT, PROP_NONE); RNA_def_property_flag(parm, PROP_REQUIRED); - RNA_def_property_ui_text(parm, "value", "any value between min and max as set in progress_begin()"); + RNA_def_property_ui_text(parm, "value", "Any value between min and max as set in progress_begin()"); func = RNA_def_function(srna, "progress_end", "rna_progress_end"); RNA_def_function_ui_description(func, "Terminate progress report"); /* invoke functions, for use with python */ func = RNA_def_function(srna, "invoke_props_popup", "rna_Operator_props_popup"); - RNA_def_function_ui_description(func, "Operator popup invoke"); + RNA_def_function_ui_description(func, "Operator popup invoke " + "(show operator properties and execute it automatically on changes)"); rna_generic_op_invoke(func, WM_GEN_INVOKE_EVENT | WM_GEN_INVOKE_RETURN); /* invoked dialog opens popup with OK button, does not auto-exec operator. */ func = RNA_def_function(srna, "invoke_props_dialog", "WM_operator_props_dialog_popup"); - RNA_def_function_ui_description(func, "Operator dialog (non-autoexec popup) invoke"); + RNA_def_function_ui_description(func, "Operator dialog (non-autoexec popup) invoke " + "(show operator properties and only execute it on click on OK button)"); rna_generic_op_invoke(func, WM_GEN_INVOKE_SIZE | WM_GEN_INVOKE_RETURN); /* invoke enum */ func = RNA_def_function(srna, "invoke_search_popup", "rna_Operator_enum_search_invoke"); + RNA_def_function_ui_description(func, "Operator search popup invoke (search in values of " + "operator's type 'prop' EnumProperty, and execute it on confirmation)"); rna_generic_op_invoke(func, 0); /* invoke functions, for use with python */ func = RNA_def_function(srna, "invoke_popup", "WM_operator_ui_popup"); - RNA_def_function_ui_description(func, "Operator popup invoke"); + RNA_def_function_ui_description(func, "Operator popup invoke " + "(only shows operator's properties, without executing it)"); rna_generic_op_invoke(func, WM_GEN_INVOKE_SIZE | WM_GEN_INVOKE_RETURN); func = RNA_def_function(srna, "invoke_confirm", "rna_Operator_confirm"); - RNA_def_function_ui_description(func, "Operator confirmation"); + RNA_def_function_ui_description(func, "Operator confirmation popup " + "(only to let user confirm the execution, no operator properties shown)"); rna_generic_op_invoke(func, WM_GEN_INVOKE_EVENT | WM_GEN_INVOKE_RETURN); diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index 23ab61821f4..8711384e1ee 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -130,6 +130,9 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, if (U.opensubdiv_compute_type == USER_OPENSUBDIV_COMPUTE_NONE) { modifier_setError(md, "OpenSubdiv is disabled in User Preferences"); } + else if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) != 0) { + modifier_setError(md, "OpenSubdiv is not supported in paint modes"); + } else if ((DAG_get_eval_flags_for_object(md->scene, ob) & DAG_EVAL_NEED_CPU) == 0) { subsurf_flags |= SUBSURF_USE_GPU_BACKEND; do_cddm_convert = false; diff --git a/source/blender/nodes/shader/nodes/node_shader_material.c b/source/blender/nodes/shader/nodes/node_shader_material.c index 8b21b1ff33b..6850cdbf6ea 100644 --- a/source/blender/nodes/shader/nodes/node_shader_material.c +++ b/source/blender/nodes/shader/nodes/node_shader_material.c @@ -223,12 +223,27 @@ static void node_shader_init_material(bNodeTree *UNUSED(ntree), bNode *node) /* XXX this is also done as a local static function in gpu_codegen.c, * but we need this to hack around the crappy material node. */ -static GPUNodeLink *gpu_get_input_link(GPUNodeStack *in) +static GPUNodeLink *gpu_get_input_link(GPUMaterial *mat, GPUNodeStack *in) { - if (in->link) + if (in->link) { return in->link; - else - return GPU_uniform(in->vec); + } + else { + GPUNodeLink *result = NULL; + + /* note GPU_uniform() is only intended to be used as a parameter to + * GPU_link(), returning it directly results in leaks or double frees */ + if (in->type == GPU_FLOAT) + GPU_link(mat, "set_value", GPU_uniform(in->vec), &result); + else if (in->type == GPU_VEC3) + GPU_link(mat, "set_rgb", GPU_uniform(in->vec), &result); + else if (in->type == GPU_VEC4) + GPU_link(mat, "set_rgba", GPU_uniform(in->vec), &result); + else + BLI_assert(0); + + return result; + } } static int gpu_shader_material(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out) @@ -251,18 +266,18 @@ static int gpu_shader_material(GPUMaterial *mat, bNode *node, bNodeExecData *UNU /* write values */ if (hasinput[MAT_IN_COLOR]) - shi.rgb = gpu_get_input_link(&in[MAT_IN_COLOR]); + shi.rgb = gpu_get_input_link(mat, &in[MAT_IN_COLOR]); if (hasinput[MAT_IN_SPEC]) - shi.specrgb = gpu_get_input_link(&in[MAT_IN_SPEC]); + shi.specrgb = gpu_get_input_link(mat, &in[MAT_IN_SPEC]); if (hasinput[MAT_IN_REFL]) - shi.refl = gpu_get_input_link(&in[MAT_IN_REFL]); + shi.refl = gpu_get_input_link(mat, &in[MAT_IN_REFL]); /* retrieve normal */ if (hasinput[MAT_IN_NORMAL]) { GPUNodeLink *tmp; - shi.vn = gpu_get_input_link(&in[MAT_IN_NORMAL]); + shi.vn = gpu_get_input_link(mat, &in[MAT_IN_NORMAL]); if (GPU_material_use_world_space_shading(mat)) { GPU_link(mat, "vec_math_negate", shi.vn, &shi.vn); GPU_link(mat, "direction_transform_m4v3", shi.vn, GPU_builtin(GPU_VIEW_MATRIX), &shi.vn); @@ -276,15 +291,15 @@ static int gpu_shader_material(GPUMaterial *mat, bNode *node, bNodeExecData *UNU if (node->type == SH_NODE_MATERIAL_EXT) { if (hasinput[MAT_IN_MIR]) - shi.mir = gpu_get_input_link(&in[MAT_IN_MIR]); + shi.mir = gpu_get_input_link(mat, &in[MAT_IN_MIR]); if (hasinput[MAT_IN_AMB]) - shi.amb = gpu_get_input_link(&in[MAT_IN_AMB]); + shi.amb = gpu_get_input_link(mat, &in[MAT_IN_AMB]); if (hasinput[MAT_IN_EMIT]) - shi.emit = gpu_get_input_link(&in[MAT_IN_EMIT]); + shi.emit = gpu_get_input_link(mat, &in[MAT_IN_EMIT]); if (hasinput[MAT_IN_SPECTRA]) - shi.spectra = gpu_get_input_link(&in[MAT_IN_SPECTRA]); + shi.spectra = gpu_get_input_link(mat, &in[MAT_IN_SPECTRA]); if (hasinput[MAT_IN_ALPHA]) - shi.alpha = gpu_get_input_link(&in[MAT_IN_ALPHA]); + shi.alpha = gpu_get_input_link(mat, &in[MAT_IN_ALPHA]); } GPU_shaderesult_set(&shi, &shr); /* clears shr */ diff --git a/source/blender/python/intern/bpy_library_write.c b/source/blender/python/intern/bpy_library_write.c index f582cebb260..bf91253141a 100644 --- a/source/blender/python/intern/bpy_library_write.c +++ b/source/blender/python/intern/bpy_library_write.c @@ -50,7 +50,7 @@ PyDoc_STRVAR(bpy_lib_write_doc, -".. method:: write(filepath, datablocks, relative_remap=False, fake_user=False)\n" +".. method:: write(filepath, datablocks, relative_remap=False, fake_user=False, compress=False)\n" "\n" " Write data-blocks into a blend file.\n" "\n" diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index fcdab746d57..0c137221856 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -1369,12 +1369,13 @@ typedef struct wmOpPopUp { /* Only invoked by OK button in popups created with wm_block_dialog_create() */ static void dialog_exec_cb(bContext *C, void *arg1, void *arg2) { - wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win = CTX_wm_window(C); - wmOpPopUp *data = arg1; uiBlock *block = arg2; + /* Explicitly set UI_RETURN_OK flag, otherwise the menu might be cancelled + * in case WM_operator_call_ex exits/reloads the current file (T49199). */ + UI_popup_menu_retval_set(block, UI_RETURN_OK, true); + WM_operator_call_ex(C, data->op, true); /* let execute handle freeing it */ @@ -1384,8 +1385,18 @@ static void dialog_exec_cb(bContext *C, void *arg1, void *arg2) /* in this case, wm_operator_ui_popup_cancel wont run */ MEM_freeN(data); + /* get context data *after* WM_operator_call_ex which might have closed the current file and changed context */ + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + /* check window before 'block->handle' incase the - * popup execution closed the window and freed the block. see T44688. */ + * popup execution closed the window and freed the block. see T44688. + */ + /* Post 2.78 TODO: Check if this fix and others related to T44688 are still + * needed or can be improved now that requesting context data has been corrected + * (see above). We're close to release so not a good time for experiments. + * -- Julian + */ if (BLI_findindex(&wm->windows, win) != -1) { UI_popup_block_close(C, win, block); } diff --git a/source/blenderplayer/CMakeLists.txt b/source/blenderplayer/CMakeLists.txt index 2748de0e7dd..58bebc66a3e 100644 --- a/source/blenderplayer/CMakeLists.txt +++ b/source/blenderplayer/CMakeLists.txt @@ -58,7 +58,7 @@ if(WIN32 AND NOT UNIX) blenderplayer ${EXETYPE} bad_level_call_stubs/stubs.c ${CMAKE_SOURCE_DIR}/release/windows/icons/winblender.rc) - + WINDOWS_SIGN_TARGET(blenderplayer) install(TARGETS blenderplayer COMPONENT Blenderplayer DESTINATION ".") diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 7acea43d1f5..f65688e1304 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -263,6 +263,7 @@ if(WITH_PYTHON_MODULE) else() add_executable(blender ${EXETYPE} ${SRC}) + WINDOWS_SIGN_TARGET(blender) endif() if(WITH_BUILDINFO) @@ -337,7 +338,8 @@ if(WITH_PYTHON) # install(CODE "message(\"copying blender scripts...\")") # exclude addons_contrib if release - if("${BLENDER_VERSION_CYCLE}" STREQUAL "release") + if("${BLENDER_VERSION_CYCLE}" STREQUAL "release" OR + "${BLENDER_VERSION_CYCLE}" STREQUAL "rc") set(ADDON_EXCLUDE_CONDITIONAL "addons_contrib/*") else() set(ADDON_EXCLUDE_CONDITIONAL "_addons_contrib/*") # dummy, wont do anything diff --git a/source/gameengine/GamePlayer/ghost/CMakeLists.txt b/source/gameengine/GamePlayer/ghost/CMakeLists.txt index 6c09af33f9e..577e25d6198 100644 --- a/source/gameengine/GamePlayer/ghost/CMakeLists.txt +++ b/source/gameengine/GamePlayer/ghost/CMakeLists.txt @@ -81,6 +81,10 @@ if(WIN32) blender_include_dirs(../../../../intern/utfconv) endif() +if(WITH_INPUT_NDOF) + add_definitions(-DWITH_INPUT_NDOF) +endif(WITH_INPUT_NDOF) + if(WITH_CODEC_FFMPEG) add_definitions(-DWITH_FFMPEG) endif() diff --git a/source/tools b/source/tools -Subproject 373945d0978b6601c55c9d5879e0f488b18515c +Subproject 896c5f78952adb2d091d28c65086d46992dabda |