diff options
297 files changed, 6642 insertions, 3552 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index d61a7de1a33..7b98fe4ad73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -727,7 +727,7 @@ if(NOT WITH_BOOST) macro(set_and_warn _setting _val) if(${${_setting}}) - message(STATUS "'WITH_BOOST' is disabled: forceing 'set(${_setting} ${_val})'") + message(STATUS "'WITH_BOOST' is disabled: forcing 'set(${_setting} ${_val})'") endif() set(${_setting} ${_val}) endmacro() diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index ecb1cf87511..c4d6b284c48 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -344,7 +344,7 @@ OSL_FORCE_REBUILD=false OSL_SKIP=false # OpenSubdiv needs to be compiled for now -OSD_VERSION="3.1.0" +OSD_VERSION="3.1.1" OSD_VERSION_MIN=$OSD_VERSION OSD_FORCE_BUILD=false OSD_FORCE_REBUILD=false diff --git a/build_files/cmake/Modules/GTestTesting.cmake b/build_files/cmake/Modules/GTestTesting.cmake index 96c06ef8eb5..0732e10133c 100644 --- a/build_files/cmake/Modules/GTestTesting.cmake +++ b/build_files/cmake/Modules/GTestTesting.cmake @@ -45,7 +45,7 @@ macro(BLENDER_SRC_GTEST_EX NAME SRC EXTRA_LIBS DO_ADD_TEST) RUNTIME_OUTPUT_DIRECTORY_DEBUG "${TESTS_OUTPUT_DIR}" INCLUDE_DIRECTORIES "${TEST_INC}") if(${DO_ADD_TEST}) - add_test(${NAME}_test ${TESTS_OUTPUT_DIR}/${NAME}_test) + add_test(NAME ${NAME}_test COMMAND ${TESTS_OUTPUT_DIR}/${NAME}_test WORKING_DIRECTORY $<TARGET_FILE_DIR:blender>) endif() endif() endmacro() diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 2e3a1907063..09428953a46 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -416,14 +416,7 @@ function(setup_liblinks target_link_libraries(${target} ${OPENCOLORIO_LIBRARIES}) endif() if(WITH_OPENSUBDIV OR WITH_CYCLES_OPENSUBDIV) - if(WIN32 AND NOT UNIX) - file_list_suffix(OPENSUBDIV_LIBRARIES_DEBUG "${OPENSUBDIV_LIBRARIES}" "_d") - target_link_libraries_debug(${target} "${OPENSUBDIV_LIBRARIES_DEBUG}") - target_link_libraries_optimized(${target} "${OPENSUBDIV_LIBRARIES}") - unset(OPENSUBDIV_LIBRARIES_DEBUG) - else() target_link_libraries(${target} ${OPENSUBDIV_LIBRARIES}) - endif() endif() if(WITH_OPENVDB) target_link_libraries(${target} ${OPENVDB_LIBRARIES} ${TBB_LIBRARIES}) diff --git a/build_files/cmake/platform/platform_win32_msvc.cmake b/build_files/cmake/platform/platform_win32_msvc.cmake index 3b50351a131..45a44596e61 100644 --- a/build_files/cmake/platform/platform_win32_msvc.cmake +++ b/build_files/cmake/platform/platform_win32_msvc.cmake @@ -446,10 +446,20 @@ if(WITH_MOD_CLOTH_ELTOPO) endif() if(WITH_OPENSUBDIV OR WITH_CYCLES_OPENSUBDIV) - set(OPENSUBDIV_INCLUDE_DIR ${LIBDIR}/opensubdiv/include) - set(OPENSUBDIV_LIBPATH ${LIBDIR}/opensubdiv/lib) - set(OPENSUBDIV_LIBRARIES ${OPENSUBDIV_LIBPATH}/osdCPU.lib ${OPENSUBDIV_LIBPATH}/osdGPU.lib) - find_package(OpenSubdiv) + set(OPENSUBDIV_INCLUDE_DIR ${LIBDIR}/opensubdiv/include) + set(OPENSUBDIV_LIBPATH ${LIBDIR}/opensubdiv/lib) + set(OPENSUBDIV_LIBRARIES optimized ${OPENSUBDIV_LIBPATH}/osdCPU.lib + optimized ${OPENSUBDIV_LIBPATH}/osdGPU.lib + debug ${OPENSUBDIV_LIBPATH}/osdCPU_d.lib + debug ${OPENSUBDIV_LIBPATH}/osdGPU_d.lib + ) + set(OPENSUBDIV_HAS_OPENMP TRUE) + set(OPENSUBDIV_HAS_TBB FALSE) + set(OPENSUBDIV_HAS_OPENCL TRUE) + set(OPENSUBDIV_HAS_CUDA FALSE) + set(OPENSUBDIV_HAS_GLSL_TRANSFORM_FEEDBACK TRUE) + set(OPENSUBDIV_HAS_GLSL_COMPUTE TRUE) + windows_find_package(OpenSubdiv) endif() if(WITH_SDL) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index 3cb98c677c1..ec3131ca19e 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1632,13 +1632,9 @@ def write_sphinx_conf_py(basepath): file = open(filepath, "w", encoding="utf-8") fw = file.write - fw("import sys, os\n") - fw("\n") - fw("extensions = ['sphinx.ext.intersphinx']\n") - fw("\n") - fw("intersphinx_mapping = {'blender_manual': ('https://www.blender.org/manual/', None)}\n") - fw("\n") - + fw("import sys, os\n\n") + fw("extensions = ['sphinx.ext.intersphinx']\n\n") + fw("intersphinx_mapping = {'blender_manual': ('https://docs.blender.org/manual/en/dev/', None)}\n\n") fw("project = 'Blender'\n") # fw("master_doc = 'index'\n") fw("copyright = u'Blender Foundation'\n") @@ -1655,6 +1651,7 @@ def write_sphinx_conf_py(basepath): # not helpful since the source is generated, adds to upload size. fw("html_copy_source = False\n") + fw("html_show_sphinx = False\n") fw("html_split_index = True\n") fw("\n") diff --git a/doc/python_api/sphinx_doc_update.py b/doc/python_api/sphinx_doc_update.py index c7f0367a2a0..3d48c1145e1 100755 --- a/doc/python_api/sphinx_doc_update.py +++ b/doc/python_api/sphinx_doc_update.py @@ -41,9 +41,9 @@ import tempfile import zipfile -DEFAULT_RSYNC_SERVER = "www.blender.org" +DEFAULT_RSYNC_SERVER = "docs.blender.org" DEFAULT_RSYNC_ROOT = "/api/" -DEFAULT_SYMLINK_ROOT = "/data/www/vhosts/www.blender.org/api" +DEFAULT_SYMLINK_ROOT = "/data/www/vhosts/docs.blender.org/api" def argparse_create(): diff --git a/extern/gflags/README.blender b/extern/gflags/README.blender index 0c8ea9a2be3..b48d4d5d110 100644 --- a/extern/gflags/README.blender +++ b/extern/gflags/README.blender @@ -18,6 +18,8 @@ Local modifications: - Applied some modifications from fork https://github.com/Nazg-Gul/gflags.git (see https://github.com/gflags/gflags/pull/129) -- Avoid attemot of acquiring mutex lock in FlagRegistry::GlobalRegistry when +- Avoid attempt of acquiring mutex lock in FlagRegistry::GlobalRegistry when doing static flags initialization. See d81dd2d in Blender repository. +- Made `google::{anonymous}::FlagValue::ValueSize() const` inlined, so it does + not trigger strict compiler warning.
\ No newline at end of file diff --git a/extern/gflags/src/gflags.cc b/extern/gflags/src/gflags.cc index 7abe1f70da3..6dcc5170bcc 100644 --- a/extern/gflags/src/gflags.cc +++ b/extern/gflags/src/gflags.cc @@ -218,7 +218,7 @@ class FlagValue { bool Equal(const FlagValue& x) const; FlagValue* New() const; // creates a new one with default value void CopyFrom(const FlagValue& x); - int ValueSize() const; + inline int ValueSize() const; // Calls the given validate-fn on value_buffer_, and returns // whatever it returns. But first casts validate_fn_proto to a @@ -443,7 +443,7 @@ void FlagValue::CopyFrom(const FlagValue& x) { } } -int FlagValue::ValueSize() const { +inline int FlagValue::ValueSize() const { if (type_ > FV_MAX_INDEX) { assert(false); // unknown type return 0; diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt index ea7d1379390..29285c6ad68 100644 --- a/intern/cycles/CMakeLists.txt +++ b/intern/cycles/CMakeLists.txt @@ -74,7 +74,6 @@ elseif(CMAKE_COMPILER_IS_GNUCC) if(CXX_HAS_AVX2) set(CYCLES_AVX2_KERNEL_FLAGS "-ffast-math -msse -msse2 -msse3 -mssse3 -msse4.1 -mavx -mavx2 -mfma -mlzcnt -mbmi -mbmi2 -mf16c -mfpmath=sse") endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math") elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") check_cxx_compiler_flag(-msse CXX_HAS_SSE) check_cxx_compiler_flag(-mavx CXX_HAS_AVX) @@ -90,7 +89,6 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(CXX_HAS_AVX2) set(CYCLES_AVX2_KERNEL_FLAGS "-ffast-math -msse -msse2 -msse3 -mssse3 -msse4.1 -mavx -mavx2 -mfma -mlzcnt -mbmi -mbmi2 -mf16c") endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math") endif() if(CXX_HAS_SSE) diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp index 29a68bf272e..35a30ae683f 100644 --- a/intern/cycles/app/cycles_xml.cpp +++ b/intern/cycles/app/cycles_xml.cpp @@ -523,7 +523,7 @@ static void xml_read_mesh(const XMLReadState& state, pugi::xml_node node) /* we don't yet support arbitrary attributes, for now add vertex * coordinates as generated coordinates if requested */ - if (mesh->need_attribute(state.scene, ATTR_STD_GENERATED)) { + if(mesh->need_attribute(state.scene, ATTR_STD_GENERATED)) { Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED); memcpy(attr->data_float3(), mesh->verts.data(), sizeof(float3)*mesh->verts.size()); } diff --git a/intern/cycles/blender/addon/__init__.py b/intern/cycles/blender/addon/__init__.py index 80841eb7eb3..4593f590b03 100644 --- a/intern/cycles/blender/addon/__init__.py +++ b/intern/cycles/blender/addon/__init__.py @@ -23,11 +23,25 @@ bl_info = { "location": "Info header, render engine menu", "description": "Cycles Render Engine integration", "warning": "", - "wiki_url": "https://www.blender.org/manual/render/cycles/index.html", + "wiki_url": "https://docs.blender.org/manual/en/dev/render/cycles/", "tracker_url": "", "support": 'OFFICIAL', "category": "Render"} +# Support 'reload' case. +if "bpy" in locals(): + import importlib + if "engine" in locals(): + importlib.reload(engine) + if "version_update" in locals(): + importlib.reload(version_update) + if "ui" in locals(): + importlib.reload(ui) + if "properties" in locals(): + importlib.reload(properties) + if "presets" in locals(): + importlib.reload(presets) + import bpy from . import ( diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py index 8953fbc7dd1..69f728d2743 100644 --- a/intern/cycles/blender/addon/engine.py +++ b/intern/cycles/blender/addon/engine.py @@ -62,7 +62,7 @@ def _parse_command_line(): num_resumable_chunks = None current_resumable_chunk = None - # TODO(sergey): Add some nice error ptins if argument is not used properly. + # TODO(sergey): Add some nice error prints if argument is not used properly. idx = 0 while idx < len(argv) - 1: arg = argv[idx] diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index ad1ccb476ee..1e068566b43 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -528,6 +528,12 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): description="Use special type BVH optimized for hair (uses more ram but renders faster)", default=True, ) + cls.debug_bvh_time_steps = IntProperty( + name="BVH Time Steps", + description="Split BVH primitives by this number of time steps to speed up render time in cost of memory", + default=0, + min=0, max=16, + ) cls.tile_order = EnumProperty( name="Tile Order", description="Tile order for rendering", @@ -632,6 +638,20 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): items=enum_texture_limit ) + cls.ao_bounces = IntProperty( + name="AO Bounces", + default=0, + description="Approximate indirect light with background tinted ambient occlusion at the specified bounce, 0 disables this feature", + min=0, max=1024, + ) + + cls.ao_bounces_render = IntProperty( + name="AO Bounces Render", + default=0, + description="Approximate indirect light with background tinted ambient occlusion at the specified bounce, 0 disables this feature", + min=0, max=1024, + ) + # Various fine-tuning debug flags def devices_update_callback(self, context): @@ -1144,8 +1164,8 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup): description="Cycles SceneRenderLayer Settings", type=cls, ) - cls.pass_debug_bvh_traversal_steps = BoolProperty( - name="Debug BVH Traversal Steps", + cls.pass_debug_bvh_traversed_nodes = BoolProperty( + name="Debug BVH Traversed Nodes", description="Store Debug BVH Traversal Steps pass", default=False, ) @@ -1159,6 +1179,11 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup): description="Store Debug Ray Bounces pass", default=False, ) + cls.pass_debug_bvh_intersections = BoolProperty( + name="Debug BVH Intersections", + description="Store Debug BVH Intersections pass", + default=False, + ) @classmethod def unregister(cls): diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index d6463441ad8..74b61b02e33 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -217,7 +217,7 @@ class CyclesRender_PT_sampling(CyclesButtonsPanel, Panel): draw_samples_info(layout, context) -class CyclesRender_PT_geometery(CyclesButtonsPanel, Panel): +class CyclesRender_PT_geometry(CyclesButtonsPanel, Panel): bl_label = "Geometry" bl_options = {'DEFAULT_CLOSED'} @@ -226,6 +226,7 @@ class CyclesRender_PT_geometery(CyclesButtonsPanel, Panel): scene = context.scene cscene = scene.cycles + ccscene = scene.cycles_curves if cscene.feature_set == 'EXPERIMENTAL': split = layout.split() @@ -252,6 +253,25 @@ class CyclesRender_PT_geometery(CyclesButtonsPanel, Panel): row.prop(cscene, "volume_step_size") row.prop(cscene, "volume_max_steps") + layout.prop(ccscene, "use_curves", text="Use Hair") + col = layout.column() + col.active = ccscene.use_curves + + col.prop(ccscene, "primitive", text="Primitive") + col.prop(ccscene, "shape", text="Shape") + + if not (ccscene.primitive in {'CURVE_SEGMENTS', 'LINE_SEGMENTS'} and ccscene.shape == 'RIBBONS'): + col.prop(ccscene, "cull_backfacing", text="Cull back-faces") + + if ccscene.primitive == 'TRIANGLES' and ccscene.shape == 'THICK': + col.prop(ccscene, "resolution", text="Resolution") + elif ccscene.primitive == 'CURVE_SEGMENTS': + col.prop(ccscene, "subdivisions", text="Curve subdivisions") + + row = col.row() + row.prop(ccscene, "minimum_width", text="Min Pixels") + row.prop(ccscene, "maximum_width", text="Max Ext.") + class CyclesRender_PT_light_paths(CyclesButtonsPanel, Panel): bl_label = "Light Paths" @@ -412,6 +432,10 @@ class CyclesRender_PT_performance(CyclesButtonsPanel, Panel): col.prop(cscene, "debug_use_spatial_splits") col.prop(cscene, "debug_use_hair_bvh") + row = col.row() + row.active = not cscene.debug_use_spatial_splits + row.prop(cscene, "debug_bvh_time_steps") + class CyclesRender_PT_layer_options(CyclesButtonsPanel, Panel): bl_label = "Layer" @@ -511,9 +535,10 @@ class CyclesRender_PT_layer_passes(CyclesButtonsPanel, Panel): if _cycles.with_cycles_debug: col = layout.column() - col.prop(crl, "pass_debug_bvh_traversal_steps") + col.prop(crl, "pass_debug_bvh_traversed_nodes") col.prop(crl, "pass_debug_bvh_traversed_instances") col.prop(crl, "pass_debug_ray_bounces") + col.prop(crl, "pass_debug_bvh_intersections") class CyclesRender_PT_views(CyclesButtonsPanel, Panel): @@ -814,10 +839,13 @@ class CyclesObject_PT_cycles_settings(CyclesButtonsPanel, Panel): col = layout.column() col.label(text="Performance:") row = col.row() - row.active = scene.render.use_simplify and cscene.use_camera_cull - row.prop(cob, "use_camera_cull") - row.active = scene.render.use_simplify and cscene.use_distance_cull - row.prop(cob, "use_distance_cull") + sub = row.row() + sub.active = scene.render.use_simplify and cscene.use_camera_cull + sub.prop(cob, "use_camera_cull") + + sub = row.row() + sub.active = scene.render.use_simplify and cscene.use_distance_cull + sub.prop(cob, "use_distance_cull") class CYCLES_OT_use_shading_nodes(Operator): @@ -1058,10 +1086,11 @@ class CyclesWorld_PT_ambient_occlusion(CyclesButtonsPanel, Panel): layout = self.layout light = context.world.light_settings + scene = context.scene row = layout.row() sub = row.row() - sub.active = light.use_ambient_occlusion + sub.active = light.use_ambient_occlusion or scene.render.use_simplify sub.prop(light, "ao_factor", text="Factor") row.prop(light, "distance", text="Distance") @@ -1438,43 +1467,6 @@ class CyclesParticle_PT_textures(CyclesButtonsPanel, Panel): layout.template_ID(slot, "texture", new="texture.new") -class CyclesRender_PT_CurveRendering(CyclesButtonsPanel, Panel): - bl_label = "Cycles Hair Rendering" - bl_context = "particle" - - @classmethod - def poll(cls, context): - psys = context.particle_system - return CyclesButtonsPanel.poll(context) and psys and psys.settings.type == 'HAIR' - - def draw_header(self, context): - ccscene = context.scene.cycles_curves - self.layout.prop(ccscene, "use_curves", text="") - - def draw(self, context): - layout = self.layout - - scene = context.scene - ccscene = scene.cycles_curves - - layout.active = ccscene.use_curves - - layout.prop(ccscene, "primitive", text="Primitive") - layout.prop(ccscene, "shape", text="Shape") - - if not (ccscene.primitive in {'CURVE_SEGMENTS', 'LINE_SEGMENTS'} and ccscene.shape == 'RIBBONS'): - layout.prop(ccscene, "cull_backfacing", text="Cull back-faces") - - if ccscene.primitive == 'TRIANGLES' and ccscene.shape == 'THICK': - layout.prop(ccscene, "resolution", text="Resolution") - elif ccscene.primitive == 'CURVE_SEGMENTS': - layout.prop(ccscene, "subdivisions", text="Curve subdivisions") - - row = layout.row() - row.prop(ccscene, "minimum_width", text="Min Pixels") - row.prop(ccscene, "maximum_width", text="Max Ext.") - - class CyclesRender_PT_bake(CyclesButtonsPanel, Panel): bl_label = "Bake" bl_context = "render" @@ -1669,6 +1661,13 @@ class CyclesScene_PT_simplify(CyclesButtonsPanel, Panel): row.active = cscene.use_distance_cull row.prop(cscene, "distance_cull_margin", text="Distance") + split = layout.split() + col = split.column() + col.prop(cscene, "ao_bounces") + + col = split.column() + col.prop(cscene, "ao_bounces_render") + def draw_device(self, context): scene = context.scene layout = self.layout diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp index 378ae67f0c7..e42ff5d72a6 100644 --- a/intern/cycles/blender/blender_curves.cpp +++ b/intern/cycles/blender/blender_curves.cpp @@ -29,24 +29,6 @@ CCL_NAMESPACE_BEGIN -/* Utilities */ - -/* Hair curve functions */ - -void curveinterp_v3_v3v3v3v3(float3 *p, float3 *v1, float3 *v2, float3 *v3, float3 *v4, const float w[4]); -void interp_weights(float t, float data[4]); -float shaperadius(float shape, float root, float tip, float time); -void InterpolateKeySegments(int seg, int segno, int key, int curve, float3 *keyloc, float *time, ParticleCurveData *CData); -bool ObtainCacheParticleUV(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int uv_num); -bool ObtainCacheParticleVcol(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int vcol_num); -bool ObtainCacheParticleData(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background); -void ExportCurveSegments(Scene *scene, Mesh *mesh, ParticleCurveData *CData); -void ExportCurveTrianglePlanes(Mesh *mesh, ParticleCurveData *CData, - float3 RotCam, bool is_ortho); -void ExportCurveTriangleGeometry(Mesh *mesh, ParticleCurveData *CData, int resolution); -void ExportCurveTriangleUV(ParticleCurveData *CData, int vert_offset, int resol, float3 *uvdata); -void ExportCurveTriangleVcol(ParticleCurveData *CData, int vert_offset, int resol, uchar4 *cdata); - ParticleCurveData::ParticleCurveData() { } @@ -55,7 +37,7 @@ ParticleCurveData::~ParticleCurveData() { } -void interp_weights(float t, float data[4]) +static void interp_weights(float t, float data[4]) { /* Cardinal curve interpolation */ float t2 = t * t; @@ -68,17 +50,19 @@ void interp_weights(float t, float data[4]) data[3] = fc * t3 - fc * t2; } -void curveinterp_v3_v3v3v3v3(float3 *p, float3 *v1, float3 *v2, float3 *v3, float3 *v4, const float w[4]) +static void curveinterp_v3_v3v3v3v3(float3 *p, + float3 *v1, float3 *v2, float3 *v3, float3 *v4, + const float w[4]) { p->x = v1->x * w[0] + v2->x * w[1] + v3->x * w[2] + v4->x * w[3]; p->y = v1->y * w[0] + v2->y * w[1] + v3->y * w[2] + v4->y * w[3]; p->z = v1->z * w[0] + v2->z * w[1] + v3->z * w[2] + v4->z * w[3]; } -float shaperadius(float shape, float root, float tip, float time) +static float shaperadius(float shape, float root, float tip, float time) { float radius = 1.0f - time; - + if(shape != 0.0f) { if(shape < 0.0f) radius = powf(radius, 1.0f + shape); @@ -90,7 +74,13 @@ float shaperadius(float shape, float root, float tip, float time) /* curve functions */ -void InterpolateKeySegments(int seg, int segno, int key, int curve, float3 *keyloc, float *time, ParticleCurveData *CData) +static void InterpolateKeySegments(int seg, + int segno, + int key, + int curve, + float3 *keyloc, + float *time, + ParticleCurveData *CData) { float3 ckey_loc1 = CData->curvekey_co[key]; float3 ckey_loc2 = ckey_loc1; @@ -119,7 +109,11 @@ void InterpolateKeySegments(int seg, int segno, int key, int curve, float3 *keyl curveinterp_v3_v3v3v3v3(keyloc, &ckey_loc1, &ckey_loc2, &ckey_loc3, &ckey_loc4, t); } -bool ObtainCacheParticleData(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background) +static bool ObtainCacheParticleData(Mesh *mesh, + BL::Mesh *b_mesh, + BL::Object *b_ob, + ParticleCurveData *CData, + bool background) { int curvenum = 0; int keyno = 0; @@ -143,7 +137,7 @@ bool ObtainCacheParticleData(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, Par int totparts = b_psys.particles.length(); int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f); int totcurves = totchild; - + if(b_part.child_type() == 0 || totchild == 0) totcurves += totparts; @@ -161,7 +155,7 @@ bool ObtainCacheParticleData(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, Par CData->psys_shader.push_back_slow(shader); float radius = get_float(cpsys, "radius_scale") * 0.5f; - + CData->psys_rootradius.push_back_slow(radius * get_float(cpsys, "root_width")); CData->psys_tipradius.push_back_slow(radius * get_float(cpsys, "tip_width")); CData->psys_shape.push_back_slow(get_float(cpsys, "shape")); @@ -181,7 +175,7 @@ bool ObtainCacheParticleData(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, Par for(; pa_no < totparts+totchild; pa_no++) { int keynum = 0; CData->curve_firstkey.push_back_slow(keyno); - + float curve_length = 0.0f; float3 pcKey; for(int step_no = 0; step_no < ren_step; step_no++) { @@ -213,7 +207,12 @@ bool ObtainCacheParticleData(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, Par return true; } -bool ObtainCacheParticleUV(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int uv_num) +static bool ObtainCacheParticleUV(Mesh *mesh, + BL::Mesh *b_mesh, + BL::Object *b_ob, + ParticleCurveData *CData, + bool background, + int uv_num) { if(!(mesh && b_mesh && b_ob && CData)) return false; @@ -231,7 +230,7 @@ bool ObtainCacheParticleUV(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, Parti int totparts = b_psys.particles.length(); int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f); int totcurves = totchild; - + if(b_part.child_type() == 0 || totchild == 0) totcurves += totparts; @@ -267,7 +266,12 @@ bool ObtainCacheParticleUV(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, Parti return true; } -bool ObtainCacheParticleVcol(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int vcol_num) +static bool ObtainCacheParticleVcol(Mesh *mesh, + BL::Mesh *b_mesh, + BL::Object *b_ob, + ParticleCurveData *CData, + bool background, + int vcol_num) { if(!(mesh && b_mesh && b_ob && CData)) return false; @@ -285,7 +289,7 @@ bool ObtainCacheParticleVcol(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, Par int totparts = b_psys.particles.length(); int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f); int totcurves = totchild; - + if(b_part.child_type() == 0 || totchild == 0) totcurves += totparts; @@ -333,16 +337,16 @@ static void set_resolution(BL::Object *b_ob, BL::Scene *scene, bool render) } } -void ExportCurveTrianglePlanes(Mesh *mesh, ParticleCurveData *CData, - float3 RotCam, bool is_ortho) +static void ExportCurveTrianglePlanes(Mesh *mesh, ParticleCurveData *CData, + float3 RotCam, bool is_ortho) { int vertexno = mesh->verts.size(); int vertexindex = vertexno; int numverts = 0, numtris = 0; /* compute and reserve size of arrays */ - for(int sys = 0; sys < CData->psys_firstcurve.size() ; sys++) { - for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys] ; curve++) { + for(int sys = 0; sys < CData->psys_firstcurve.size(); sys++) { + for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys]; curve++) { if(CData->curve_keynum[curve] <= 1 || CData->curve_length[curve] == 0.0f) continue; @@ -354,8 +358,8 @@ void ExportCurveTrianglePlanes(Mesh *mesh, ParticleCurveData *CData, mesh->reserve_mesh(mesh->verts.size() + numverts, mesh->num_triangles() + numtris); /* actually export */ - for(int sys = 0; sys < CData->psys_firstcurve.size() ; sys++) { - for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys] ; curve++) { + for(int sys = 0; sys < CData->psys_firstcurve.size(); sys++) { + for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys]; curve++) { if(CData->curve_keynum[curve] <= 1 || CData->curve_length[curve] == 0.0f) continue; @@ -380,7 +384,7 @@ void ExportCurveTrianglePlanes(Mesh *mesh, ParticleCurveData *CData, if(curvekey == CData->curve_firstkey[curve] + CData->curve_keynum[curve] - 1) v1 = CData->curvekey_co[curvekey] - CData->curvekey_co[max(curvekey - 1, CData->curve_firstkey[curve])]; - else + else v1 = CData->curvekey_co[curvekey + 1] - CData->curvekey_co[curvekey - 1]; time = CData->curvekey_time[curvekey]/CData->curve_length[curve]; @@ -416,15 +420,17 @@ void ExportCurveTrianglePlanes(Mesh *mesh, ParticleCurveData *CData, /* texture coords still needed */ } -void ExportCurveTriangleGeometry(Mesh *mesh, ParticleCurveData *CData, int resolution) +static void ExportCurveTriangleGeometry(Mesh *mesh, + ParticleCurveData *CData, + int resolution) { int vertexno = mesh->verts.size(); int vertexindex = vertexno; int numverts = 0, numtris = 0; /* compute and reserve size of arrays */ - for(int sys = 0; sys < CData->psys_firstcurve.size() ; sys++) { - for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys] ; curve++) { + for(int sys = 0; sys < CData->psys_firstcurve.size(); sys++) { + for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys]; curve++) { if(CData->curve_keynum[curve] <= 1 || CData->curve_length[curve] == 0.0f) continue; @@ -436,8 +442,8 @@ void ExportCurveTriangleGeometry(Mesh *mesh, ParticleCurveData *CData, int resol mesh->reserve_mesh(mesh->verts.size() + numverts, mesh->num_triangles() + numtris); /* actually export */ - for(int sys = 0; sys < CData->psys_firstcurve.size() ; sys++) { - for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys] ; curve++) { + for(int sys = 0; sys < CData->psys_firstcurve.size(); sys++) { + for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys]; curve++) { if(CData->curve_keynum[curve] <= 1 || CData->curve_length[curve] == 0.0f) continue; @@ -548,7 +554,7 @@ void ExportCurveTriangleGeometry(Mesh *mesh, ParticleCurveData *CData, int resol /* texture coords still needed */ } -void ExportCurveSegments(Scene *scene, Mesh *mesh, ParticleCurveData *CData) +static void ExportCurveSegments(Scene *scene, Mesh *mesh, ParticleCurveData *CData) { int num_keys = 0; int num_curves = 0; @@ -557,13 +563,13 @@ void ExportCurveSegments(Scene *scene, Mesh *mesh, ParticleCurveData *CData) return; Attribute *attr_intercept = NULL; - + if(mesh->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) attr_intercept = mesh->curve_attributes.add(ATTR_STD_CURVE_INTERCEPT); /* compute and reserve size of arrays */ - for(int sys = 0; sys < CData->psys_firstcurve.size() ; sys++) { - for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys] ; curve++) { + for(int sys = 0; sys < CData->psys_firstcurve.size(); sys++) { + for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys]; curve++) { if(CData->curve_keynum[curve] <= 1 || CData->curve_length[curve] == 0.0f) continue; @@ -582,8 +588,8 @@ void ExportCurveSegments(Scene *scene, Mesh *mesh, ParticleCurveData *CData) num_curves = 0; /* actually export */ - for(int sys = 0; sys < CData->psys_firstcurve.size() ; sys++) { - for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys] ; curve++) { + for(int sys = 0; sys < CData->psys_firstcurve.size(); sys++) { + for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys]; curve++) { if(CData->curve_keynum[curve] <= 1 || CData->curve_length[curve] == 0.0f) continue; @@ -677,8 +683,13 @@ static void ExportCurveSegmentsMotion(Mesh *mesh, ParticleCurveData *CData, int /* in case of new attribute, we verify if there really was any motion */ if(new_attribute) { if(i != numkeys || !have_motion) { - /* no motion, remove attributes again */ - VLOG(1) << "No motion, removing attribute"; + /* No motion or hair "topology" changed, remove attributes again. */ + if(i != numkeys) { + VLOG(1) << "Hair topology changed, removing attribute."; + } + else { + VLOG(1) << "No motion, removing attribute."; + } mesh->curve_attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION); } else if(time_index > 0) { @@ -698,7 +709,10 @@ static void ExportCurveSegmentsMotion(Mesh *mesh, ParticleCurveData *CData, int } } -void ExportCurveTriangleUV(ParticleCurveData *CData, int vert_offset, int resol, float3 *uvdata) +static void ExportCurveTriangleUV(ParticleCurveData *CData, + int vert_offset, + int resol, + float3 *uvdata) { if(uvdata == NULL) return; @@ -708,8 +722,8 @@ void ExportCurveTriangleUV(ParticleCurveData *CData, int vert_offset, int resol, int vertexindex = vert_offset; - for(int sys = 0; sys < CData->psys_firstcurve.size() ; sys++) { - for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys] ; curve++) { + for(int sys = 0; sys < CData->psys_firstcurve.size(); sys++) { + for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys]; curve++) { if(CData->curve_keynum[curve] <= 1 || CData->curve_length[curve] == 0.0f) continue; @@ -743,15 +757,18 @@ void ExportCurveTriangleUV(ParticleCurveData *CData, int vert_offset, int resol, } } -void ExportCurveTriangleVcol(ParticleCurveData *CData, int vert_offset, int resol, uchar4 *cdata) +static void ExportCurveTriangleVcol(ParticleCurveData *CData, + int vert_offset, + int resol, + uchar4 *cdata) { if(cdata == NULL) return; int vertexindex = vert_offset; - for(int sys = 0; sys < CData->psys_firstcurve.size() ; sys++) { - for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys] ; curve++) { + for(int sys = 0; sys < CData->psys_firstcurve.size(); sys++) { + for(int curve = CData->psys_firstcurve[sys]; curve < CData->psys_firstcurve[sys] + CData->psys_curvenum[sys]; curve++) { if(CData->curve_keynum[curve] <= 1 || CData->curve_length[curve] == 0.0f) continue; @@ -1044,4 +1061,3 @@ void BlenderSync::sync_curves(Mesh *mesh, } CCL_NAMESPACE_END - diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index 66893d4d668..85117cfff7b 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -927,6 +927,13 @@ Mesh *BlenderSync::sync_mesh(BL::Object& b_ob, mesh->subdivision_type = object_subdivision_type(b_ob, preview, experimental); + /* Disable adaptive subdivision while baking as the baking system + * currently doesnt support the topology and will crash. + */ + if(scene->bake_manager->get_baking()) { + mesh->subdivision_type = Mesh::SUBDIVISION_NONE; + } + BL::Mesh b_mesh = object_to_mesh(b_data, b_ob, b_scene, diff --git a/intern/cycles/blender/blender_object_cull.cpp b/intern/cycles/blender/blender_object_cull.cpp index b8582df0f93..08918dd1a49 100644 --- a/intern/cycles/blender/blender_object_cull.cpp +++ b/intern/cycles/blender/blender_object_cull.cpp @@ -43,7 +43,7 @@ BlenderObjectCulling::BlenderObjectCulling(Scene *scene, BL::Scene& b_scene) camera_cull_margin_ = get_float(cscene, "camera_cull_margin"); distance_cull_margin_ = get_float(cscene, "distance_cull_margin"); - if (distance_cull_margin_ == 0.0f) { + if(distance_cull_margin_ == 0.0f) { use_scene_distance_cull_ = false; } } diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index f3df744fcc1..9f95c4d3015 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -1318,6 +1318,9 @@ void BlenderSession::update_resumable_tile_manager(int num_samples) VLOG(1) << "Samples range start is " << range_start_sample << ", " << "number of samples to render is " << range_num_samples; + scene->integrator->start_sample = range_start_sample; + scene->integrator->tag_update(scene); + session->tile_manager.range_start_sample = range_start_sample; session->tile_manager.range_num_samples = range_num_samples; } diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index f63f94ab37a..f6ec015e1b8 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -640,7 +640,8 @@ static ShaderNode *add_node(Scene *scene, image->filename.string(), image->builtin_data, get_image_interpolation(b_image_node), - get_image_extension(b_image_node)); + get_image_extension(b_image_node), + image->use_alpha); } } image->color_space = (NodeImageColorSpace)b_image_node.color_space(); @@ -686,7 +687,8 @@ static ShaderNode *add_node(Scene *scene, env->filename.string(), env->builtin_data, get_image_interpolation(b_env_node), - EXTENSION_REPEAT); + EXTENSION_REPEAT, + env->use_alpha); } } env->color_space = (NodeImageColorSpace)b_env_node.color_space(); @@ -823,7 +825,8 @@ static ShaderNode *add_node(Scene *scene, point_density->filename.string(), point_density->builtin_data, point_density->interpolation, - EXTENSION_CLIP); + EXTENSION_CLIP, + true); } node = point_density; diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index a232f7d3047..093870eebb0 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -322,6 +322,15 @@ void BlenderSync::sync_integrator() integrator->volume_samples = volume_samples; } + if(b_scene.render().use_simplify()) { + if(preview) { + integrator->ao_bounces = get_int(cscene, "ao_bounces"); + } + else { + integrator->ao_bounces = get_int(cscene, "ao_bounces_render"); + } + } + if(integrator->modified(previntegrator)) integrator->tag_update(scene); } @@ -507,8 +516,9 @@ PassType BlenderSync::get_pass_type(BL::RenderPass& b_pass) MAP_PASS("Shadow", PASS_SHADOW); #ifdef __KERNEL_DEBUG__ - MAP_PASS("Debug BVH Traversal Steps", PASS_BVH_TRAVERSAL_STEPS); + MAP_PASS("Debug BVH Traversed Nodes", PASS_BVH_TRAVERSED_NODES); MAP_PASS("Debug BVH Traversed Instances", PASS_BVH_TRAVERSED_INSTANCES); + MAP_PASS("Debug BVH Intersections", PASS_BVH_INTERSECTIONS); MAP_PASS("Debug Ray Bounces", PASS_RAY_BOUNCES); #endif @@ -560,9 +570,9 @@ array<Pass> BlenderSync::sync_render_passes(BL::RenderLayer& b_rlay, #ifdef __KERNEL_DEBUG__ PointerRNA crp = RNA_pointer_get(&b_srlay.ptr, "cycles"); - if(get_boolean(crp, "pass_debug_bvh_traversal_steps")) { - ADD_PASS(1, "Debug BVH Traversal Steps", "X"); - Pass::add(PASS_BVH_TRAVERSAL_STEPS, passes); + if(get_boolean(crp, "pass_debug_bvh_traversed_nodes")) { + ADD_PASS(1, "Debug BVH Traversed Nodes", "X"); + Pass::add(PASS_BVH_TRAVERSED_NODES, passes); } if(get_boolean(crp, "pass_debug_bvh_traversed_instances")) { ADD_PASS(1, "Debug BVH Traversed Instances", "X"); @@ -572,6 +582,10 @@ array<Pass> BlenderSync::sync_render_passes(BL::RenderLayer& b_rlay, ADD_PASS(1, "Debug Ray Bounces", "X"); Pass::add(PASS_RAY_BOUNCES, passes); } + if(get_boolean(crp, "pass_debug_bvh_intersections")) { + ADD_PASS(1, "Debug BVH Intersections", "X"); + Pass::add(PASS_BVH_INTERSECTIONS, passes); + } #endif if(b_srlay.keep_denoise_data()) { @@ -627,6 +641,7 @@ SceneParams BlenderSync::get_scene_params(BL::Scene& b_scene, params.use_bvh_spatial_split = RNA_boolean_get(&cscene, "debug_use_spatial_splits"); params.use_bvh_unaligned_nodes = RNA_boolean_get(&cscene, "debug_use_hair_bvh"); + params.num_bvh_time_steps = RNA_int_get(&cscene, "debug_bvh_time_steps"); if(background && params.shadingsystem != SHADINGSYSTEM_OSL) params.persistent_data = r.use_persistent_data(); diff --git a/intern/cycles/bvh/bvh.cpp b/intern/cycles/bvh/bvh.cpp index 4851de5b481..874a4246d1d 100644 --- a/intern/cycles/bvh/bvh.cpp +++ b/intern/cycles/bvh/bvh.cpp @@ -845,6 +845,8 @@ void QBVH::pack_aligned_inner(const BVHStackEntry& e, bounds, child, e.node->m_visibility, + e.node->m_time_from, + e.node->m_time_to, num); } @@ -852,12 +854,17 @@ void QBVH::pack_aligned_node(int idx, const BoundBox *bounds, const int *child, const uint visibility, + const float time_from, + const float time_to, const int num) { float4 data[BVH_QNODE_SIZE]; memset(data, 0, sizeof(data)); data[0].x = __uint_as_float(visibility & ~PATH_RAY_NODE_UNALIGNED); + data[0].y = time_from; + data[0].z = time_to; + for(int i = 0; i < num; i++) { float3 bb_min = bounds[i].min; float3 bb_max = bounds[i].max; @@ -908,6 +915,8 @@ void QBVH::pack_unaligned_inner(const BVHStackEntry& e, bounds, child, e.node->m_visibility, + e.node->m_time_from, + e.node->m_time_to, num); } @@ -916,12 +925,16 @@ void QBVH::pack_unaligned_node(int idx, const BoundBox *bounds, const int *child, const uint visibility, + const float time_from, + const float time_to, const int num) { float4 data[BVH_UNALIGNED_QNODE_SIZE]; memset(data, 0, sizeof(data)); data[0].x = __uint_as_float(visibility | PATH_RAY_NODE_UNALIGNED); + data[0].y = time_from; + data[0].z = time_to; for(int i = 0; i < num; i++) { Transform space = BVHUnaligned::compute_node_transform( @@ -1207,6 +1220,8 @@ void QBVH::refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility) child_bbox, &c[0], visibility, + 0.0f, + 1.0f, 4); } else { @@ -1214,6 +1229,8 @@ void QBVH::refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility) child_bbox, &c[0], visibility, + 0.0f, + 1.0f, 4); } } diff --git a/intern/cycles/bvh/bvh.h b/intern/cycles/bvh/bvh.h index f8fb3b568ca..35f4d305883 100644 --- a/intern/cycles/bvh/bvh.h +++ b/intern/cycles/bvh/bvh.h @@ -175,6 +175,8 @@ protected: const BoundBox *bounds, const int *child, const uint visibility, + const float time_from, + const float time_to, const int num); void pack_unaligned_inner(const BVHStackEntry& e, @@ -185,6 +187,8 @@ protected: const BoundBox *bounds, const int *child, const uint visibility, + const float time_from, + const float time_to, const int num); /* refit */ diff --git a/intern/cycles/bvh/bvh_build.cpp b/intern/cycles/bvh/bvh_build.cpp index 14f66aca70f..a2f8b33cb0b 100644 --- a/intern/cycles/bvh/bvh_build.cpp +++ b/intern/cycles/bvh/bvh_build.cpp @@ -26,6 +26,7 @@ #include "scene.h" #include "curves.h" +#include "util_algorithm.h" #include "util_debug.h" #include "util_foreach.h" #include "util_logging.h" @@ -112,81 +113,237 @@ BVHBuild::~BVHBuild() /* Adding References */ -void BVHBuild::add_reference_mesh(BoundBox& root, BoundBox& center, Mesh *mesh, int i) +void BVHBuild::add_reference_triangles(BoundBox& root, BoundBox& center, Mesh *mesh, int i) { - if(params.primitive_mask & PRIMITIVE_ALL_TRIANGLE) { - Attribute *attr_mP = NULL; - - if(mesh->has_motion_blur()) - attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - - size_t num_triangles = mesh->num_triangles(); - for(uint j = 0; j < num_triangles; j++) { - Mesh::Triangle t = mesh->get_triangle(j); + const Attribute *attr_mP = NULL; + if(mesh->has_motion_blur()) { + attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + } + const size_t num_triangles = mesh->num_triangles(); + for(uint j = 0; j < num_triangles; j++) { + Mesh::Triangle t = mesh->get_triangle(j); + const float3 *verts = &mesh->verts[0]; + if(attr_mP == NULL) { BoundBox bounds = BoundBox::empty; - PrimitiveType type = PRIMITIVE_TRIANGLE; - - t.bounds_grow(&mesh->verts[0], bounds); - - /* motion triangles */ - if(attr_mP) { - size_t mesh_size = mesh->verts.size(); - size_t steps = mesh->motion_steps - 1; - float3 *vert_steps = attr_mP->data_float3(); - - for(size_t i = 0; i < steps; i++) - t.bounds_grow(vert_steps + i*mesh_size, bounds); - - type = PRIMITIVE_MOTION_TRIANGLE; + t.bounds_grow(verts, bounds); + if(bounds.valid()) { + references.push_back(BVHReference(bounds, + j, + i, + PRIMITIVE_TRIANGLE)); + root.grow(bounds); + center.grow(bounds.center2()); + } + } + else if(params.num_motion_triangle_steps == 0 || params.use_spatial_split) { + /* Motion triangles, simple case: single node for the whole + * primitive. Lowest memory footprint and faster BVH build but + * least optimal ray-tracing. + */ + /* TODO(sergey): Support motion steps for spatially split BVH. */ + const size_t num_verts = mesh->verts.size(); + const size_t num_steps = mesh->motion_steps; + const float3 *vert_steps = attr_mP->data_float3(); + BoundBox bounds = BoundBox::empty; + t.bounds_grow(verts, bounds); + for(size_t step = 0; step < num_steps - 1; step++) { + t.bounds_grow(vert_steps + step*num_verts, bounds); } - if(bounds.valid()) { - references.push_back(BVHReference(bounds, j, i, type)); + references.push_back( + BVHReference(bounds, + j, + i, + PRIMITIVE_MOTION_TRIANGLE)); root.grow(bounds); center.grow(bounds.center2()); } } + else { + /* Motion triangles, trace optimized case: we split triangle + * primitives into separate nodes for each of the time steps. + * This way we minimize overlap of neighbor curve primitives. + */ + const int num_bvh_steps = params.num_motion_curve_steps * 2 + 1; + const float num_bvh_steps_inv_1 = 1.0f / (num_bvh_steps - 1); + const size_t num_verts = mesh->verts.size(); + const size_t num_steps = mesh->motion_steps; + const float3 *vert_steps = attr_mP->data_float3(); + /* Calculate bounding box of the previous time step. + * Will be reused later to avoid duplicated work on + * calculating BVH time step boundbox. + */ + float3 prev_verts[3]; + t.motion_verts(verts, + vert_steps, + num_verts, + num_steps, + 0.0f, + prev_verts); + BoundBox prev_bounds = BoundBox::empty; + prev_bounds.grow(prev_verts[0]); + prev_bounds.grow(prev_verts[1]); + prev_bounds.grow(prev_verts[2]); + /* Create all primitive time steps, */ + for(int bvh_step = 1; bvh_step < num_bvh_steps; ++bvh_step) { + const float curr_time = (float)(bvh_step) * num_bvh_steps_inv_1; + float3 curr_verts[3]; + t.motion_verts(verts, + vert_steps, + num_verts, + num_steps, + curr_time, + curr_verts); + BoundBox curr_bounds = BoundBox::empty; + curr_bounds.grow(curr_verts[0]); + curr_bounds.grow(curr_verts[1]); + curr_bounds.grow(curr_verts[2]); + BoundBox bounds = prev_bounds; + bounds.grow(curr_bounds); + if(bounds.valid()) { + const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1; + references.push_back( + BVHReference(bounds, + j, + i, + PRIMITIVE_MOTION_TRIANGLE, + prev_time, + curr_time)); + root.grow(bounds); + center.grow(bounds.center2()); + } + /* Current time boundbox becomes previous one for the + * next time step. + */ + prev_bounds = curr_bounds; + } + } } +} - if(params.primitive_mask & PRIMITIVE_ALL_CURVE) { - Attribute *curve_attr_mP = NULL; - - if(mesh->has_motion_blur()) - curve_attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - - size_t num_curves = mesh->num_curves(); - for(uint j = 0; j < num_curves; j++) { - Mesh::Curve curve = mesh->get_curve(j); - PrimitiveType type = PRIMITIVE_CURVE; - - for(int k = 0; k < curve.num_keys - 1; k++) { +void BVHBuild::add_reference_curves(BoundBox& root, BoundBox& center, Mesh *mesh, int i) +{ + const Attribute *curve_attr_mP = NULL; + if(mesh->has_motion_blur()) { + curve_attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + } + const size_t num_curves = mesh->num_curves(); + for(uint j = 0; j < num_curves; j++) { + const Mesh::Curve curve = mesh->get_curve(j); + const float *curve_radius = &mesh->curve_radius[0]; + for(int k = 0; k < curve.num_keys - 1; k++) { + if(curve_attr_mP == NULL) { + /* Really simple logic for static hair. */ BoundBox bounds = BoundBox::empty; - curve.bounds_grow(k, &mesh->curve_keys[0], &mesh->curve_radius[0], bounds); - - /* motion curve */ - if(curve_attr_mP) { - size_t mesh_size = mesh->curve_keys.size(); - size_t steps = mesh->motion_steps - 1; - float3 *key_steps = curve_attr_mP->data_float3(); - - for(size_t i = 0; i < steps; i++) - curve.bounds_grow(k, key_steps + i*mesh_size, &mesh->curve_radius[0], bounds); - - type = PRIMITIVE_MOTION_CURVE; - } - + curve.bounds_grow(k, &mesh->curve_keys[0], curve_radius, bounds); if(bounds.valid()) { - int packed_type = PRIMITIVE_PACK_SEGMENT(type, k); - + int packed_type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_CURVE, k); references.push_back(BVHReference(bounds, j, i, packed_type)); root.grow(bounds); center.grow(bounds.center2()); } } + else if(params.num_motion_curve_steps == 0 || params.use_spatial_split) { + /* Simple case of motion curves: single node for the while + * shutter time. Lowest memory usage but less optimal + * rendering. + */ + /* TODO(sergey): Support motion steps for spatially split BVH. */ + BoundBox bounds = BoundBox::empty; + curve.bounds_grow(k, &mesh->curve_keys[0], curve_radius, bounds); + const size_t num_keys = mesh->curve_keys.size(); + const size_t num_steps = mesh->motion_steps; + const float3 *key_steps = curve_attr_mP->data_float3(); + for(size_t step = 0; step < num_steps - 1; step++) { + curve.bounds_grow(k, + key_steps + step*num_keys, + curve_radius, + bounds); + } + if(bounds.valid()) { + int packed_type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_MOTION_CURVE, k); + references.push_back(BVHReference(bounds, + j, + i, + packed_type)); + root.grow(bounds); + center.grow(bounds.center2()); + } + } + else { + /* Motion curves, trace optimized case: we split curve keys + * primitives into separate nodes for each of the time steps. + * This way we minimize overlap of neighbor curve primitives. + */ + const int num_bvh_steps = params.num_motion_curve_steps * 2 + 1; + const float num_bvh_steps_inv_1 = 1.0f / (num_bvh_steps - 1); + const size_t num_steps = mesh->motion_steps; + const float3 *curve_keys = &mesh->curve_keys[0]; + const float3 *key_steps = curve_attr_mP->data_float3(); + const size_t num_keys = mesh->curve_keys.size(); + /* Calculate bounding box of the previous time step. + * Will be reused later to avoid duplicated work on + * calculating BVH time step boundbox. + */ + float4 prev_keys[4]; + curve.cardinal_motion_keys(curve_keys, + curve_radius, + key_steps, + num_keys, + num_steps, + 0.0f, + k - 1, k, k + 1, k + 2, + prev_keys); + BoundBox prev_bounds = BoundBox::empty; + curve.bounds_grow(prev_keys, prev_bounds); + /* Create all primitive time steps, */ + for(int bvh_step = 1; bvh_step < num_bvh_steps; ++bvh_step) { + const float curr_time = (float)(bvh_step) * num_bvh_steps_inv_1; + float4 curr_keys[4]; + curve.cardinal_motion_keys(curve_keys, + curve_radius, + key_steps, + num_keys, + num_steps, + curr_time, + k - 1, k, k + 1, k + 2, + curr_keys); + BoundBox curr_bounds = BoundBox::empty; + curve.bounds_grow(curr_keys, curr_bounds); + BoundBox bounds = prev_bounds; + bounds.grow(curr_bounds); + if(bounds.valid()) { + const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1; + int packed_type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_MOTION_CURVE, k); + references.push_back(BVHReference(bounds, + j, + i, + packed_type, + prev_time, + curr_time)); + root.grow(bounds); + center.grow(bounds.center2()); + } + /* Current time boundbox becomes previous one for the + * next time step. + */ + prev_bounds = curr_bounds; + } + } } } } +void BVHBuild::add_reference_mesh(BoundBox& root, BoundBox& center, Mesh *mesh, int i) +{ + if(params.primitive_mask & PRIMITIVE_ALL_TRIANGLE) { + add_reference_triangles(root, center, mesh, i); + } + if(params.primitive_mask & PRIMITIVE_ALL_CURVE) { + add_reference_curves(root, center, mesh, i); + } +} + void BVHBuild::add_reference_object(BoundBox& root, BoundBox& center, Object *ob, int i) { references.push_back(BVHReference(ob->bounds, -1, i, 0)); @@ -200,7 +357,7 @@ static size_t count_curve_segments(Mesh *mesh) for(size_t i = 0; i < num_curves; i++) num += mesh->get_curve(i).num_keys - 1; - + return num; } @@ -344,6 +501,7 @@ BVHNode* BVHBuild::run() else { /*rotate(rootnode, 4, 5);*/ rootnode->update_visibility(); + rootnode->update_time(); } if(rootnode != NULL) { VLOG(1) << "BVH build statistics:\n" @@ -371,7 +529,7 @@ void BVHBuild::progress_update() { if(time_dt() - progress_start_time < 0.25) return; - + double progress_start = (double)progress_count/(double)progress_total; double duplicates = (double)(progress_total - progress_original_total)/(double)progress_total; @@ -379,7 +537,7 @@ void BVHBuild::progress_update() progress_start * 100.0, duplicates * 100.0); progress.set_substatus(msg); - progress_start_time = time_dt(); + progress_start_time = time_dt(); } void BVHBuild::thread_build_node(InnerNode *inner, @@ -435,6 +593,7 @@ bool BVHBuild::range_within_max_leaf_size(const BVHRange& range, return false; size_t num_triangles = 0; + size_t num_motion_triangles = 0; size_t num_curves = 0; size_t num_motion_curves = 0; @@ -445,13 +604,16 @@ bool BVHBuild::range_within_max_leaf_size(const BVHRange& range, num_curves++; if(ref.prim_type() & PRIMITIVE_MOTION_CURVE) num_motion_curves++; - else if(ref.prim_type() & PRIMITIVE_ALL_TRIANGLE) + else if(ref.prim_type() & PRIMITIVE_TRIANGLE) num_triangles++; + else if(ref.prim_type() & PRIMITIVE_MOTION_TRIANGLE) + num_motion_triangles++; } - return (num_triangles < params.max_triangle_leaf_size) && - (num_curves < params.max_curve_leaf_size) && - (num_motion_curves < params.max_curve_leaf_size); + return (num_triangles <= params.max_triangle_leaf_size) && + (num_motion_triangles <= params.max_motion_triangle_leaf_size) && + (num_curves <= params.max_curve_leaf_size) && + (num_motion_curves <= params.max_motion_curve_leaf_size); } /* multithreaded binning builder */ @@ -689,18 +851,24 @@ BVHNode *BVHBuild::create_object_leaf_nodes(const BVHReference *ref, int start, prim_object[start] = ref->prim_object(); uint visibility = objects[ref->prim_object()]->visibility; - return new LeafNode(ref->bounds(), visibility, start, start+1); + BVHNode *leaf_node = new LeafNode(ref->bounds(), visibility, start, start+1); + leaf_node->m_time_from = ref->time_from(); + leaf_node->m_time_to = ref->time_to(); + return leaf_node; } else { int mid = num/2; - BVHNode *leaf0 = create_object_leaf_nodes(ref, start, mid); - BVHNode *leaf1 = create_object_leaf_nodes(ref+mid, start+mid, num-mid); + BVHNode *leaf0 = create_object_leaf_nodes(ref, start, mid); + BVHNode *leaf1 = create_object_leaf_nodes(ref+mid, start+mid, num-mid); BoundBox bounds = BoundBox::empty; bounds.grow(leaf0->m_bounds); bounds.grow(leaf1->m_bounds); - return new InnerNode(bounds, leaf0, leaf1); + BVHNode *inner_node = new InnerNode(bounds, leaf0, leaf1); + inner_node->m_time_from = min(leaf0->m_time_from, leaf1->m_time_from); + inner_node->m_time_to = max(leaf0->m_time_to, leaf1->m_time_to); + return inner_node; } } @@ -804,6 +972,16 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range, visibility[i], start_index, start_index + num); + if(true) { + float time_from = 1.0f, time_to = 0.0f; + for(int j = 0; j < num; ++j) { + const BVHReference &ref = p_ref[i][j]; + time_from = min(time_from, ref.time_from()); + time_to = max(time_to, ref.time_to()); + } + leaf_node->m_time_from = time_from; + leaf_node->m_time_to = time_to; + } if(alignment_found) { /* Need to recalculate leaf bounds with new alignment. */ leaf_node->m_bounds = BoundBox::empty; @@ -918,7 +1096,7 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range, BVHNode *inner = new InnerNode(inner_bounds, leaves[1], leaves[2]); return new InnerNode(range.bounds(), leaves[0], inner); } else { - /* Shpuld be doing more branches if more primitive types added. */ + /* Should be doing more branches if more primitive types added. */ assert(num_leaves <= 5); BoundBox inner_bounds_a = merge(leaves[0]->m_bounds, leaves[1]->m_bounds); BoundBox inner_bounds_b = merge(leaves[2]->m_bounds, leaves[3]->m_bounds); @@ -951,7 +1129,7 @@ void BVHBuild::rotate(BVHNode *node, int max_depth) /* nothing to rotate if we reached a leaf node. */ if(node->is_leaf() || max_depth < 0) return; - + InnerNode *parent = (InnerNode*)node; /* rotate all children first */ diff --git a/intern/cycles/bvh/bvh_build.h b/intern/cycles/bvh/bvh_build.h index 64180349935..ee3cde66a2f 100644 --- a/intern/cycles/bvh/bvh_build.h +++ b/intern/cycles/bvh/bvh_build.h @@ -63,6 +63,8 @@ protected: friend class BVHObjectBinning; /* Adding references. */ + void add_reference_triangles(BoundBox& root, BoundBox& center, Mesh *mesh, int i); + void add_reference_curves(BoundBox& root, BoundBox& center, Mesh *mesh, int i); void add_reference_mesh(BoundBox& root, BoundBox& center, Mesh *mesh, int i); void add_reference_object(BoundBox& root, BoundBox& center, Object *ob, int i); void add_references(BVHRange& root); diff --git a/intern/cycles/bvh/bvh_node.cpp b/intern/cycles/bvh/bvh_node.cpp index f5cd699bdf4..67580e1bc7b 100644 --- a/intern/cycles/bvh/bvh_node.cpp +++ b/intern/cycles/bvh/bvh_node.cpp @@ -176,6 +176,19 @@ uint BVHNode::update_visibility() return m_visibility; } +void BVHNode::update_time() +{ + if(!is_leaf()) { + InnerNode *inner = (InnerNode*)this; + BVHNode *child0 = inner->children[0]; + BVHNode *child1 = inner->children[1]; + child0->update_time(); + child1->update_time(); + m_time_from = min(child0->m_time_from, child1->m_time_from); + m_time_to = max(child0->m_time_to, child1->m_time_to); + } +} + /* Inner Node */ void InnerNode::print(int depth) const diff --git a/intern/cycles/bvh/bvh_node.h b/intern/cycles/bvh/bvh_node.h index 2faa40ab657..090c426de56 100644 --- a/intern/cycles/bvh/bvh_node.h +++ b/intern/cycles/bvh/bvh_node.h @@ -47,7 +47,9 @@ class BVHNode { public: BVHNode() : m_is_unaligned(false), - m_aligned_space(NULL) + m_aligned_space(NULL), + m_time_from(0.0f), + m_time_to(1.0f) { } @@ -91,12 +93,15 @@ public: void deleteSubtree(); uint update_visibility(); + void update_time(); bool m_is_unaligned; // TODO(sergey): Can be stored as 3x3 matrix, but better to have some // utilities and type defines in util_transform first. Transform *m_aligned_space; + + float m_time_from, m_time_to; }; class InnerNode : public BVHNode diff --git a/intern/cycles/bvh/bvh_params.h b/intern/cycles/bvh/bvh_params.h index 2e698a80742..65f9da1c194 100644 --- a/intern/cycles/bvh/bvh_params.h +++ b/intern/cycles/bvh/bvh_params.h @@ -43,7 +43,9 @@ public: /* number of primitives in leaf */ int min_leaf_size; int max_triangle_leaf_size; + int max_motion_triangle_leaf_size; int max_curve_leaf_size; + int max_motion_curve_leaf_size; /* object or mesh level bvh */ bool top_level; @@ -59,6 +61,17 @@ public: */ bool use_unaligned_nodes; + /* Split time range to this number of steps and create leaf node for each + * of this time steps. + * + * Speeds up rendering of motion curve primitives in the cost of higher + * memory usage. + */ + int num_motion_curve_steps; + + /* Same as above, but for triangle primitives. */ + int num_motion_triangle_steps; + /* fixed parameters */ enum { MAX_DEPTH = 64, @@ -80,13 +93,17 @@ public: min_leaf_size = 1; max_triangle_leaf_size = 8; - max_curve_leaf_size = 2; + max_motion_triangle_leaf_size = 8; + max_curve_leaf_size = 1; + max_motion_curve_leaf_size = 4; top_level = false; use_qbvh = false; use_unaligned_nodes = false; primitive_mask = PRIMITIVE_ALL; + + num_motion_curve_steps = 0; } /* SAH costs */ @@ -113,8 +130,15 @@ class BVHReference public: __forceinline BVHReference() {} - __forceinline BVHReference(const BoundBox& bounds_, int prim_index_, int prim_object_, int prim_type) - : rbounds(bounds_) + __forceinline BVHReference(const BoundBox& bounds_, + int prim_index_, + int prim_object_, + int prim_type, + float time_from = 0.0f, + float time_to = 1.0f) + : rbounds(bounds_), + time_from_(time_from), + time_to_(time_to) { rbounds.min.w = __int_as_float(prim_index_); rbounds.max.w = __int_as_float(prim_object_); @@ -125,6 +149,9 @@ public: __forceinline int prim_index() const { return __float_as_int(rbounds.min.w); } __forceinline int prim_object() const { return __float_as_int(rbounds.max.w); } __forceinline int prim_type() const { return type; } + __forceinline float time_from() const { return time_from_; } + __forceinline float time_to() const { return time_to_; } + BVHReference& operator=(const BVHReference &arg) { if(&arg != this) { @@ -133,9 +160,11 @@ public: return *this; } + protected: BoundBox rbounds; uint type; + float time_from_, time_to_; }; /* BVH Range diff --git a/intern/cycles/device/device_cuda.cpp b/intern/cycles/device/device_cuda.cpp index 11f312e5d17..b7fe34d8cbd 100644 --- a/intern/cycles/device/device_cuda.cpp +++ b/intern/cycles/device/device_cuda.cpp @@ -131,7 +131,7 @@ public: { if(first_error) { fprintf(stderr, "\nRefer to the Cycles GPU rendering documentation for possible solutions:\n"); - fprintf(stderr, "http://www.blender.org/manual/render/cycles/gpu_rendering.html\n\n"); + fprintf(stderr, "https://docs.blender.org/manual/en/dev/render/cycles/gpu_rendering.html\n\n"); first_error = false; } } diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index b3442fd0f90..27f81a0c428 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -169,6 +169,8 @@ set(SRC_GEOM_HEADERS geom/geom_curve.h geom/geom_motion_curve.h geom/geom_motion_triangle.h + geom/geom_motion_triangle_intersect.h + geom/geom_motion_triangle_shader.h geom/geom_object.h geom/geom_patch.h geom/geom_primitive.h diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/bvh_shadow_all.h index 294362ea995..df33a86bb18 100644 --- a/intern/cycles/kernel/bvh/bvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/bvh_shadow_all.h @@ -187,7 +187,7 @@ bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, /* primitive intersection */ while(prim_addr < prim_addr2) { - kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); + kernel_assert((kernel_tex_fetch(__prim_type, prim_addr) & PRIMITIVE_ALL) == p_type); bool hit; @@ -222,6 +222,7 @@ bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, #if BVH_FEATURE(BVH_HAIR) case PRIMITIVE_CURVE: case PRIMITIVE_MOTION_CURVE: { + const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr); if(kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) { hit = bvh_cardinal_curve_intersect(kg, isect_array, @@ -231,7 +232,7 @@ bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, object, prim_addr, ray->time, - type, + curve_type, NULL, 0, 0); } @@ -244,7 +245,7 @@ bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, object, prim_addr, ray->time, - type, + curve_type, NULL, 0, 0); } diff --git a/intern/cycles/kernel/bvh/bvh_subsurface.h b/intern/cycles/kernel/bvh/bvh_subsurface.h index d9623c94b2e..889bbca21e2 100644 --- a/intern/cycles/kernel/bvh/bvh_subsurface.h +++ b/intern/cycles/kernel/bvh/bvh_subsurface.h @@ -72,7 +72,7 @@ void BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, ss_isect->num_hits = 0; const int object_flag = kernel_tex_fetch(__object_flag, subsurface_object); - if(!(object_flag & SD_TRANSFORM_APPLIED)) { + if(!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { #if BVH_FEATURE(BVH_MOTION) Transform ob_itfm; bvh_instance_motion_push(kg, diff --git a/intern/cycles/kernel/bvh/bvh_traversal.h b/intern/cycles/kernel/bvh/bvh_traversal.h index a0e478e972b..80c8f31473a 100644 --- a/intern/cycles/kernel/bvh/bvh_traversal.h +++ b/intern/cycles/kernel/bvh/bvh_traversal.h @@ -213,7 +213,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, --stack_ptr; } } - BVH_DEBUG_NEXT_STEP(); + BVH_DEBUG_NEXT_NODE(); } /* if node is leaf, fetch triangle list */ @@ -235,7 +235,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, switch(type & PRIMITIVE_ALL) { case PRIMITIVE_TRIANGLE: { for(; prim_addr < prim_addr2; prim_addr++) { - BVH_DEBUG_NEXT_STEP(); + BVH_DEBUG_NEXT_INTERSECTION(); kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); if(triangle_intersect(kg, &isect_precalc, @@ -264,7 +264,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, #if BVH_FEATURE(BVH_MOTION) case PRIMITIVE_MOTION_TRIANGLE: { for(; prim_addr < prim_addr2; prim_addr++) { - BVH_DEBUG_NEXT_STEP(); + BVH_DEBUG_NEXT_INTERSECTION(); kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); if(motion_triangle_intersect(kg, isect, @@ -296,8 +296,9 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, case PRIMITIVE_CURVE: case PRIMITIVE_MOTION_CURVE: { for(; prim_addr < prim_addr2; prim_addr++) { - BVH_DEBUG_NEXT_STEP(); - kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); + BVH_DEBUG_NEXT_INTERSECTION(); + const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr); + kernel_assert((curve_type & PRIMITIVE_ALL) == (type & PRIMITIVE_ALL)); bool hit; if(kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) { hit = bvh_cardinal_curve_intersect(kg, @@ -308,7 +309,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, object, prim_addr, ray->time, - type, + curve_type, lcg_state, difl, extmax); @@ -322,7 +323,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, object, prim_addr, ray->time, - type, + curve_type, lcg_state, difl, extmax); diff --git a/intern/cycles/kernel/bvh/bvh_types.h b/intern/cycles/kernel/bvh/bvh_types.h index c3abe2e157d..ead424aaaaf 100644 --- a/intern/cycles/kernel/bvh/bvh_types.h +++ b/intern/cycles/kernel/bvh/bvh_types.h @@ -50,12 +50,17 @@ CCL_NAMESPACE_BEGIN #ifdef __KERNEL_DEBUG__ # define BVH_DEBUG_INIT() \ do { \ - isect->num_traversal_steps = 0; \ + isect->num_traversed_nodes = 0; \ isect->num_traversed_instances = 0; \ + isect->num_intersections = 0; \ } while(0) -# define BVH_DEBUG_NEXT_STEP() \ +# define BVH_DEBUG_NEXT_NODE() \ do { \ - ++isect->num_traversal_steps; \ + ++isect->num_traversed_nodes; \ + } while(0) +# define BVH_DEBUG_NEXT_INTERSECTION() \ + do { \ + ++isect->num_intersections; \ } while(0) # define BVH_DEBUG_NEXT_INSTANCE() \ do { \ @@ -63,7 +68,8 @@ CCL_NAMESPACE_BEGIN } while(0) #else /* __KERNEL_DEBUG__ */ # define BVH_DEBUG_INIT() -# define BVH_DEBUG_NEXT_STEP() +# define BVH_DEBUG_NEXT_NODE() +# define BVH_DEBUG_NEXT_INTERSECTION() # define BVH_DEBUG_NEXT_INSTANCE() #endif /* __KERNEL_DEBUG__ */ diff --git a/intern/cycles/kernel/bvh/bvh_volume.h b/intern/cycles/kernel/bvh/bvh_volume.h index 107373c17dc..57e5b8d736d 100644 --- a/intern/cycles/kernel/bvh/bvh_volume.h +++ b/intern/cycles/kernel/bvh/bvh_volume.h @@ -236,9 +236,7 @@ bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, /* instance push */ object = kernel_tex_fetch(__prim_object, -prim_addr-1); int object_flag = kernel_tex_fetch(__object_flag, object); - if(object_flag & SD_OBJECT_HAS_VOLUME) { - # if BVH_FEATURE(BVH_MOTION) bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, &isect->t, &ob_itfm); # else diff --git a/intern/cycles/kernel/bvh/bvh_volume_all.h b/intern/cycles/kernel/bvh/bvh_volume_all.h index 529848ebe7b..5a1accebaa0 100644 --- a/intern/cycles/kernel/bvh/bvh_volume_all.h +++ b/intern/cycles/kernel/bvh/bvh_volume_all.h @@ -287,7 +287,6 @@ uint BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, /* instance push */ object = kernel_tex_fetch(__prim_object, -prim_addr-1); int object_flag = kernel_tex_fetch(__object_flag, object); - if(object_flag & SD_OBJECT_HAS_VOLUME) { # if BVH_FEATURE(BVH_MOTION) diff --git a/intern/cycles/kernel/bvh/qbvh_shadow_all.h b/intern/cycles/kernel/bvh/qbvh_shadow_all.h index 5f4d06f12ea..607295f9ed5 100644 --- a/intern/cycles/kernel/bvh/qbvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/qbvh_shadow_all.h @@ -106,14 +106,20 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, while(node_addr >= 0 && node_addr != ENTRYPOINT_SENTINEL) { float4 inodes = kernel_tex_fetch(__bvh_nodes, node_addr+0); + if(false #ifdef __VISIBILITY_FLAG__ - if((__float_as_uint(inodes.x) & PATH_RAY_SHADOW) == 0) { + || ((__float_as_uint(inodes.x) & PATH_RAY_SHADOW) == 0) +#endif +#if BVH_FEATURE(BVH_MOTION) + || UNLIKELY(ray->time < inodes.y) + || UNLIKELY(ray->time > inodes.z) +#endif + ) { /* Pop. */ node_addr = traversal_stack[stack_ptr].addr; --stack_ptr; continue; } -#endif ssef dist; int child_mask = NODE_INTERSECT(kg, @@ -262,7 +268,7 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, /* Primitive intersection. */ while(prim_addr < prim_addr2) { - kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); + kernel_assert((kernel_tex_fetch(__prim_type, prim_addr) & PRIMITIVE_ALL) == p_type); bool hit; @@ -297,6 +303,7 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, #if BVH_FEATURE(BVH_HAIR) case PRIMITIVE_CURVE: case PRIMITIVE_MOTION_CURVE: { + const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr); if(kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) { hit = bvh_cardinal_curve_intersect(kg, isect_array, @@ -306,7 +313,7 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, object, prim_addr, ray->time, - type, + curve_type, NULL, 0, 0); } @@ -319,7 +326,7 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, object, prim_addr, ray->time, - type, + curve_type, NULL, 0, 0); } diff --git a/intern/cycles/kernel/bvh/qbvh_subsurface.h b/intern/cycles/kernel/bvh/qbvh_subsurface.h index ccd36df034a..84dc4003133 100644 --- a/intern/cycles/kernel/bvh/qbvh_subsurface.h +++ b/intern/cycles/kernel/bvh/qbvh_subsurface.h @@ -61,7 +61,7 @@ ccl_device void BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, ss_isect->num_hits = 0; const int object_flag = kernel_tex_fetch(__object_flag, subsurface_object); - if(!(object_flag & SD_TRANSFORM_APPLIED)) { + if(!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { #if BVH_FEATURE(BVH_MOTION) Transform ob_itfm; bvh_instance_motion_push(kg, diff --git a/intern/cycles/kernel/bvh/qbvh_traversal.h b/intern/cycles/kernel/bvh/qbvh_traversal.h index f2d8e558dcc..10ae7bee852 100644 --- a/intern/cycles/kernel/bvh/qbvh_traversal.h +++ b/intern/cycles/kernel/bvh/qbvh_traversal.h @@ -117,6 +117,10 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, float4 inodes = kernel_tex_fetch(__bvh_nodes, node_addr+0); if(UNLIKELY(node_dist > isect->t) +#if BVH_FEATURE(BVH_MOTION) + || UNLIKELY(ray->time < inodes.y) + || UNLIKELY(ray->time > inodes.z) +#endif #ifdef __VISIBILITY_FLAG__ || (__float_as_uint(inodes.x) & visibility) == 0) #endif @@ -131,7 +135,7 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, int child_mask; ssef dist; - BVH_DEBUG_NEXT_STEP(); + BVH_DEBUG_NEXT_NODE(); #if BVH_FEATURE(BVH_HAIR_MINIMUM_WIDTH) if(difl != 0.0f) { @@ -326,7 +330,7 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, switch(type & PRIMITIVE_ALL) { case PRIMITIVE_TRIANGLE: { for(; prim_addr < prim_addr2; prim_addr++) { - BVH_DEBUG_NEXT_STEP(); + BVH_DEBUG_NEXT_INTERSECTION(); kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); if(triangle_intersect(kg, &isect_precalc, @@ -347,7 +351,7 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, #if BVH_FEATURE(BVH_MOTION) case PRIMITIVE_MOTION_TRIANGLE: { for(; prim_addr < prim_addr2; prim_addr++) { - BVH_DEBUG_NEXT_STEP(); + BVH_DEBUG_NEXT_INTERSECTION(); kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); if(motion_triangle_intersect(kg, isect, @@ -371,8 +375,9 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, case PRIMITIVE_CURVE: case PRIMITIVE_MOTION_CURVE: { for(; prim_addr < prim_addr2; prim_addr++) { - BVH_DEBUG_NEXT_STEP(); - kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); + BVH_DEBUG_NEXT_INTERSECTION(); + const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr); + kernel_assert((curve_type & PRIMITIVE_ALL) == (type & PRIMITIVE_ALL)); bool hit; if(kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) { hit = bvh_cardinal_curve_intersect(kg, @@ -383,7 +388,7 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, object, prim_addr, ray->time, - type, + curve_type, lcg_state, difl, extmax); @@ -397,7 +402,7 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, object, prim_addr, ray->time, - type, + curve_type, lcg_state, difl, extmax); diff --git a/intern/cycles/kernel/bvh/qbvh_volume.h b/intern/cycles/kernel/bvh/qbvh_volume.h index 424710b69f2..dc6627e2dbb 100644 --- a/intern/cycles/kernel/bvh/qbvh_volume.h +++ b/intern/cycles/kernel/bvh/qbvh_volume.h @@ -293,9 +293,7 @@ ccl_device bool BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, /* Instance push. */ object = kernel_tex_fetch(__prim_object, -prim_addr-1); int object_flag = kernel_tex_fetch(__object_flag, object); - if(object_flag & SD_OBJECT_HAS_VOLUME) { - # if BVH_FEATURE(BVH_MOTION) bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, &isect->t, &ob_itfm); # else diff --git a/intern/cycles/kernel/bvh/qbvh_volume_all.h b/intern/cycles/kernel/bvh/qbvh_volume_all.h index eb48af6fc68..ff1fa92af6e 100644 --- a/intern/cycles/kernel/bvh/qbvh_volume_all.h +++ b/intern/cycles/kernel/bvh/qbvh_volume_all.h @@ -344,9 +344,7 @@ ccl_device uint BVH_FUNCTION_FULL_NAME(QBVH)(KernelGlobals *kg, /* Instance push. */ object = kernel_tex_fetch(__prim_object, -prim_addr-1); int object_flag = kernel_tex_fetch(__object_flag, object); - if(object_flag & SD_OBJECT_HAS_VOLUME) { - # if BVH_FEATURE(BVH_MOTION) bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, &isect_t, &ob_itfm); # else diff --git a/intern/cycles/kernel/geom/geom.h b/intern/cycles/kernel/geom/geom.h index 24ced934c8b..6838e26c242 100644 --- a/intern/cycles/kernel/geom/geom.h +++ b/intern/cycles/kernel/geom/geom.h @@ -23,6 +23,8 @@ #include "geom_subd_triangle.h" #include "geom_triangle_intersect.h" #include "geom_motion_triangle.h" +#include "geom_motion_triangle_intersect.h" +#include "geom_motion_triangle_shader.h" #include "geom_motion_curve.h" #include "geom_curve.h" #include "geom_volume.h" diff --git a/intern/cycles/kernel/geom/geom_curve.h b/intern/cycles/kernel/geom/geom_curve.h index 636dbcc71e0..9de335403ce 100644 --- a/intern/cycles/kernel/geom/geom_curve.h +++ b/intern/cycles/kernel/geom/geom_curve.h @@ -911,7 +911,7 @@ ccl_device_forceinline bool bvh_curve_intersect(KernelGlobals *kg, Intersection # undef len3_squared # undef len3 # undef dot3 -# endif +#endif } ccl_device_inline float3 curvetangent(float t, float3 p0, float3 p1, float3 p2, float3 p3) diff --git a/intern/cycles/kernel/geom/geom_motion_curve.h b/intern/cycles/kernel/geom/geom_motion_curve.h index 80b33fad68b..dc1388b6643 100644 --- a/intern/cycles/kernel/geom/geom_motion_curve.h +++ b/intern/cycles/kernel/geom/geom_motion_curve.h @@ -50,12 +50,12 @@ ccl_device_inline int find_attribute_curve_motion(KernelGlobals *kg, int object, ccl_device_inline void motion_curve_keys_for_step(KernelGlobals *kg, int offset, int numkeys, int numsteps, int step, int k0, int k1, float4 keys[2]) { if(step == numsteps) { - /* center step: regular vertex location */ + /* center step: regular key location */ keys[0] = kernel_tex_fetch(__curve_keys, k0); keys[1] = kernel_tex_fetch(__curve_keys, k1); } else { - /* center step not stored in this array */ + /* center step is not stored in this array */ if(step > numsteps) step--; @@ -97,14 +97,14 @@ ccl_device_inline void motion_curve_keys(KernelGlobals *kg, int object, int prim ccl_device_inline void motion_cardinal_curve_keys_for_step(KernelGlobals *kg, int offset, int numkeys, int numsteps, int step, int k0, int k1, int k2, int k3, float4 keys[4]) { if(step == numsteps) { - /* center step: regular vertex location */ + /* center step: regular key location */ keys[0] = kernel_tex_fetch(__curve_keys, k0); keys[1] = kernel_tex_fetch(__curve_keys, k1); keys[2] = kernel_tex_fetch(__curve_keys, k2); keys[3] = kernel_tex_fetch(__curve_keys, k3); } else { - /* center step not store in this array */ + /* center step is not stored in this array */ if(step > numsteps) step--; diff --git a/intern/cycles/kernel/geom/geom_motion_triangle.h b/intern/cycles/kernel/geom/geom_motion_triangle.h index 538c332c63a..4e84aa97776 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle.h +++ b/intern/cycles/kernel/geom/geom_motion_triangle.h @@ -76,7 +76,7 @@ ccl_device_inline void motion_triangle_normals_for_step(KernelGlobals *kg, uint4 normals[2] = float4_to_float3(kernel_tex_fetch(__tri_vnormal, tri_vindex.z)); } else { - /* center step not stored in this array */ + /* center step is not stored in this array */ if(step > numsteps) step--; @@ -117,312 +117,4 @@ ccl_device_inline void motion_triangle_vertices(KernelGlobals *kg, int object, i verts[2] = (1.0f - t)*verts[2] + t*next_verts[2]; } -/* Refine triangle intersection to more precise hit point. For rays that travel - * far the precision is often not so good, this reintersects the primitive from - * a closer distance. */ - -ccl_device_inline float3 motion_triangle_refine(KernelGlobals *kg, ShaderData *sd, const Intersection *isect, const Ray *ray, float3 verts[3]) -{ - float3 P = ray->P; - float3 D = ray->D; - float t = isect->t; - -#ifdef __INTERSECTION_REFINE__ - if(isect->object != OBJECT_NONE) { - if(UNLIKELY(t == 0.0f)) { - return P; - } -# ifdef __OBJECT_MOTION__ - Transform tfm = ccl_fetch(sd, ob_itfm); -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_INVERSE_TRANSFORM); -# endif - - P = transform_point(&tfm, P); - D = transform_direction(&tfm, D*t); - D = normalize_len(D, &t); - } - - P = P + D*t; - - /* compute refined intersection distance */ - const float3 e1 = verts[0] - verts[2]; - const float3 e2 = verts[1] - verts[2]; - const float3 s1 = cross(D, e2); - - const float invdivisor = 1.0f/dot(s1, e1); - const float3 d = P - verts[2]; - const float3 s2 = cross(d, e1); - float rt = dot(e2, s2)*invdivisor; - - /* compute refined position */ - P = P + D*rt; - - if(isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = ccl_fetch(sd, ob_tfm); -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_TRANSFORM); -# endif - - P = transform_point(&tfm, P); - } - - return P; -#else - return P + D*t; -#endif -} - -/* Same as above, except that isect->t is assumed to be in object space for instancing */ - -#ifdef __SUBSURFACE__ -# if defined(__KERNEL_CUDA__) && (defined(i386) || defined(_M_IX86)) -ccl_device_noinline -# else -ccl_device_inline -# endif -float3 motion_triangle_refine_subsurface(KernelGlobals *kg, ShaderData *sd, const Intersection *isect, const Ray *ray, float3 verts[3]) -{ - float3 P = ray->P; - float3 D = ray->D; - float t = isect->t; - -# ifdef __INTERSECTION_REFINE__ - if(isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = ccl_fetch(sd, ob_itfm); -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_INVERSE_TRANSFORM); -# endif - - P = transform_point(&tfm, P); - D = transform_direction(&tfm, D); - D = normalize(D); - } - - P = P + D*t; - - /* compute refined intersection distance */ - const float3 e1 = verts[0] - verts[2]; - const float3 e2 = verts[1] - verts[2]; - const float3 s1 = cross(D, e2); - - const float invdivisor = 1.0f/dot(s1, e1); - const float3 d = P - verts[2]; - const float3 s2 = cross(d, e1); - float rt = dot(e2, s2)*invdivisor; - - P = P + D*rt; - - if(isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = ccl_fetch(sd, ob_tfm); -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_TRANSFORM); -# endif - - P = transform_point(&tfm, P); - } - - return P; -# else - return P + D*t; -# endif -} -#endif - -/* Setup of motion triangle specific parts of ShaderData, moved into this one - * function to more easily share computation of interpolated positions and - * normals */ - -/* return 3 triangle vertex normals */ -ccl_device_noinline void motion_triangle_shader_setup(KernelGlobals *kg, ShaderData *sd, const Intersection *isect, const Ray *ray, bool subsurface) -{ - /* get shader */ - ccl_fetch(sd, shader) = kernel_tex_fetch(__tri_shader, ccl_fetch(sd, prim)); - - /* get motion info */ - int numsteps, numverts; - object_motion_info(kg, ccl_fetch(sd, object), &numsteps, &numverts, NULL); - - /* figure out which steps we need to fetch and their interpolation factor */ - int maxstep = numsteps*2; - int step = min((int)(ccl_fetch(sd, time)*maxstep), maxstep-1); - float t = ccl_fetch(sd, time)*maxstep - step; - - /* find attribute */ - AttributeElement elem; - int offset = find_attribute_motion(kg, ccl_fetch(sd, object), ATTR_STD_MOTION_VERTEX_POSITION, &elem); - kernel_assert(offset != ATTR_STD_NOT_FOUND); - - /* fetch vertex coordinates */ - float3 verts[3], next_verts[3]; - uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, ccl_fetch(sd, prim)); - - motion_triangle_verts_for_step(kg, tri_vindex, offset, numverts, numsteps, step, verts); - motion_triangle_verts_for_step(kg, tri_vindex, offset, numverts, numsteps, step+1, next_verts); - - /* interpolate between steps */ - verts[0] = (1.0f - t)*verts[0] + t*next_verts[0]; - verts[1] = (1.0f - t)*verts[1] + t*next_verts[1]; - verts[2] = (1.0f - t)*verts[2] + t*next_verts[2]; - - /* compute refined position */ -#ifdef __SUBSURFACE__ - if(!subsurface) -#endif - ccl_fetch(sd, P) = motion_triangle_refine(kg, sd, isect, ray, verts); -#ifdef __SUBSURFACE__ - else - ccl_fetch(sd, P) = motion_triangle_refine_subsurface(kg, sd, isect, ray, verts); -#endif - - /* compute face normal */ - float3 Ng; - if(ccl_fetch(sd, flag) & SD_NEGATIVE_SCALE_APPLIED) - Ng = normalize(cross(verts[2] - verts[0], verts[1] - verts[0])); - else - Ng = normalize(cross(verts[1] - verts[0], verts[2] - verts[0])); - - ccl_fetch(sd, Ng) = Ng; - ccl_fetch(sd, N) = Ng; - - /* compute derivatives of P w.r.t. uv */ -#ifdef __DPDU__ - ccl_fetch(sd, dPdu) = (verts[0] - verts[2]); - ccl_fetch(sd, dPdv) = (verts[1] - verts[2]); -#endif - - /* compute smooth normal */ - if(ccl_fetch(sd, shader) & SHADER_SMOOTH_NORMAL) { - /* find attribute */ - AttributeElement elem; - int offset = find_attribute_motion(kg, ccl_fetch(sd, object), ATTR_STD_MOTION_VERTEX_NORMAL, &elem); - kernel_assert(offset != ATTR_STD_NOT_FOUND); - - /* fetch vertex coordinates */ - float3 normals[3], next_normals[3]; - motion_triangle_normals_for_step(kg, tri_vindex, offset, numverts, numsteps, step, normals); - motion_triangle_normals_for_step(kg, tri_vindex, offset, numverts, numsteps, step+1, next_normals); - - /* interpolate between steps */ - normals[0] = (1.0f - t)*normals[0] + t*next_normals[0]; - normals[1] = (1.0f - t)*normals[1] + t*next_normals[1]; - normals[2] = (1.0f - t)*normals[2] + t*next_normals[2]; - - /* interpolate between vertices */ - float u = ccl_fetch(sd, u); - float v = ccl_fetch(sd, v); - float w = 1.0f - u - v; - ccl_fetch(sd, N) = (u*normals[0] + v*normals[1] + w*normals[2]); - } -} - -/* Ray intersection. We simply compute the vertex positions at the given ray - * time and do a ray intersection with the resulting triangle */ - -ccl_device_inline bool motion_triangle_intersect(KernelGlobals *kg, Intersection *isect, - float3 P, float3 dir, float time, uint visibility, int object, int prim_addr) -{ - /* primitive index for vertex location lookup */ - int prim = kernel_tex_fetch(__prim_index, prim_addr); - int fobject = (object == OBJECT_NONE)? kernel_tex_fetch(__prim_object, prim_addr): object; - - /* get vertex locations for intersection */ - float3 verts[3]; - motion_triangle_vertices(kg, fobject, prim, time, verts); - - /* ray-triangle intersection, unoptimized */ - float t, u, v; - - if(ray_triangle_intersect_uv(P, dir, isect->t, verts[2], verts[0], verts[1], &u, &v, &t)) { -#ifdef __VISIBILITY_FLAG__ - /* visibility flag test. we do it here under the assumption - * that most triangles are culled by node flags */ - if(kernel_tex_fetch(__prim_visibility, prim_addr) & visibility) -#endif - { - isect->t = t; - isect->u = u; - isect->v = v; - isect->prim = prim_addr; - isect->object = object; - isect->type = PRIMITIVE_MOTION_TRIANGLE; - - return true; - } - } - - return false; -} - -/* Special ray intersection routines for subsurface scattering. In that case we - * only want to intersect with primitives in the same object, and if case of - * multiple hits we pick a single random primitive as the intersection point. */ - -#ifdef __SUBSURFACE__ -ccl_device_inline void motion_triangle_intersect_subsurface( - KernelGlobals *kg, - SubsurfaceIntersection *ss_isect, - float3 P, - float3 dir, - float time, - int object, - int prim_addr, - float tmax, - uint *lcg_state, - int max_hits) -{ - /* primitive index for vertex location lookup */ - int prim = kernel_tex_fetch(__prim_index, prim_addr); - int fobject = (object == OBJECT_NONE)? kernel_tex_fetch(__prim_object, prim_addr): object; - - /* get vertex locations for intersection */ - float3 verts[3]; - motion_triangle_vertices(kg, fobject, prim, time, verts); - - /* ray-triangle intersection, unoptimized */ - float t, u, v; - - if(ray_triangle_intersect_uv(P, dir, tmax, verts[2], verts[0], verts[1], &u, &v, &t)) { - for(int i = min(max_hits, ss_isect->num_hits) - 1; i >= 0; --i) { - if(ss_isect->hits[i].t == t) { - return; - } - } - - ss_isect->num_hits++; - - int hit; - - if(ss_isect->num_hits <= max_hits) { - hit = ss_isect->num_hits - 1; - } - else { - /* reservoir sampling: if we are at the maximum number of - * hits, randomly replace element or skip it */ - hit = lcg_step_uint(lcg_state) % ss_isect->num_hits; - - if(hit >= max_hits) - return; - } - - /* record intersection */ - Intersection *isect = &ss_isect->hits[hit]; - isect->t = t; - isect->u = u; - isect->v = v; - isect->prim = prim_addr; - isect->object = object; - isect->type = PRIMITIVE_MOTION_TRIANGLE; - - /* Record geometric normal. */ - ss_isect->Ng[hit] = normalize(cross(verts[1] - verts[0], - verts[2] - verts[0])); - } -} -#endif - CCL_NAMESPACE_END - diff --git a/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h b/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h new file mode 100644 index 00000000000..d57d74ea882 --- /dev/null +++ b/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h @@ -0,0 +1,280 @@ +/* + * 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. + */ + +/* Motion Triangle Primitive + * + * These are stored as regular triangles, plus extra positions and normals at + * times other than the frame center. Computing the triangle vertex positions + * or normals at a given ray time is a matter of interpolation of the two steps + * between which the ray time lies. + * + * The extra positions and normals are stored as ATTR_STD_MOTION_VERTEX_POSITION + * and ATTR_STD_MOTION_VERTEX_NORMAL mesh attributes. + */ + +CCL_NAMESPACE_BEGIN + +/* Refine triangle intersection to more precise hit point. For rays that travel + * far the precision is often not so good, this reintersects the primitive from + * a closer distance. + */ + +ccl_device_inline float3 motion_triangle_refine(KernelGlobals *kg, + ShaderData *sd, + const Intersection *isect, + const Ray *ray, + float3 verts[3]) +{ + float3 P = ray->P; + float3 D = ray->D; + float t = isect->t; + +#ifdef __INTERSECTION_REFINE__ + if(isect->object != OBJECT_NONE) { + if(UNLIKELY(t == 0.0f)) { + return P; + } +# ifdef __OBJECT_MOTION__ + Transform tfm = ccl_fetch(sd, ob_itfm); +# else + Transform tfm = object_fetch_transform(kg, + isect->object, + OBJECT_INVERSE_TRANSFORM); +# endif + + P = transform_point(&tfm, P); + D = transform_direction(&tfm, D*t); + D = normalize_len(D, &t); + } + + P = P + D*t; + + /* Compute refined intersection distance. */ + const float3 e1 = verts[0] - verts[2]; + const float3 e2 = verts[1] - verts[2]; + const float3 s1 = cross(D, e2); + + const float invdivisor = 1.0f/dot(s1, e1); + const float3 d = P - verts[2]; + const float3 s2 = cross(d, e1); + float rt = dot(e2, s2)*invdivisor; + + /* Compute refined position. */ + P = P + D*rt; + + if(isect->object != OBJECT_NONE) { +# ifdef __OBJECT_MOTION__ + Transform tfm = ccl_fetch(sd, ob_tfm); +# else + Transform tfm = object_fetch_transform(kg, + isect->object, + OBJECT_TRANSFORM); +# endif + + P = transform_point(&tfm, P); + } + + return P; +#else + return P + D*t; +#endif +} + +/* Same as above, except that isect->t is assumed to be in object space + * for instancing. + */ + +#ifdef __SUBSURFACE__ +# if defined(__KERNEL_CUDA__) && (defined(i386) || defined(_M_IX86)) +ccl_device_noinline +# else +ccl_device_inline +# endif +float3 motion_triangle_refine_subsurface(KernelGlobals *kg, + ShaderData *sd, + const Intersection *isect, + const Ray *ray, + float3 verts[3]) +{ + float3 P = ray->P; + float3 D = ray->D; + float t = isect->t; + +# ifdef __INTERSECTION_REFINE__ + if(isect->object != OBJECT_NONE) { +# ifdef __OBJECT_MOTION__ + Transform tfm = ccl_fetch(sd, ob_itfm); +# else + Transform tfm = object_fetch_transform(kg, + isect->object, + OBJECT_INVERSE_TRANSFORM); +# endif + + P = transform_point(&tfm, P); + D = transform_direction(&tfm, D); + D = normalize(D); + } + + P = P + D*t; + + /* compute refined intersection distance */ + const float3 e1 = verts[0] - verts[2]; + const float3 e2 = verts[1] - verts[2]; + const float3 s1 = cross(D, e2); + + const float invdivisor = 1.0f/dot(s1, e1); + const float3 d = P - verts[2]; + const float3 s2 = cross(d, e1); + float rt = dot(e2, s2)*invdivisor; + + P = P + D*rt; + + if(isect->object != OBJECT_NONE) { +# ifdef __OBJECT_MOTION__ + Transform tfm = ccl_fetch(sd, ob_tfm); +# else + Transform tfm = object_fetch_transform(kg, + isect->object, + OBJECT_TRANSFORM); +# endif + + P = transform_point(&tfm, P); + } + + return P; +# else /* __INTERSECTION_REFINE__ */ + return P + D*t; +# endif /* __INTERSECTION_REFINE__ */ +} +#endif /* __SUBSURFACE__ */ + + +/* Ray intersection. We simply compute the vertex positions at the given ray + * time and do a ray intersection with the resulting triangle. + */ + +ccl_device_inline bool motion_triangle_intersect(KernelGlobals *kg, + Intersection *isect, + float3 P, + float3 dir, + float time, + uint visibility, + int object, + int prim_addr) +{ + /* Primitive index for vertex location lookup. */ + int prim = kernel_tex_fetch(__prim_index, prim_addr); + int fobject = (object == OBJECT_NONE) + ? kernel_tex_fetch(__prim_object, prim_addr) + : object; + /* Get vertex locations for intersection. */ + float3 verts[3]; + motion_triangle_vertices(kg, fobject, prim, time, verts); + /* Ray-triangle intersection, unoptimized. */ + float t, u, v; + if(ray_triangle_intersect_uv(P, + dir, + isect->t, + verts[2], verts[0], verts[1], + &u, &v, &t)) + { +#ifdef __VISIBILITY_FLAG__ + /* Visibility flag test. we do it here under the assumption + * that most triangles are culled by node flags. + */ + if(kernel_tex_fetch(__prim_visibility, prim_addr) & visibility) +#endif + { + isect->t = t; + isect->u = u; + isect->v = v; + isect->prim = prim_addr; + isect->object = object; + isect->type = PRIMITIVE_MOTION_TRIANGLE; + return true; + } + } + return false; +} + +/* Special ray intersection routines for subsurface scattering. In that case we + * only want to intersect with primitives in the same object, and if case of + * multiple hits we pick a single random primitive as the intersection point. + */ +#ifdef __SUBSURFACE__ +ccl_device_inline void motion_triangle_intersect_subsurface( + KernelGlobals *kg, + SubsurfaceIntersection *ss_isect, + float3 P, + float3 dir, + float time, + int object, + int prim_addr, + float tmax, + uint *lcg_state, + int max_hits) +{ + /* Primitive index for vertex location lookup. */ + int prim = kernel_tex_fetch(__prim_index, prim_addr); + int fobject = (object == OBJECT_NONE) + ? kernel_tex_fetch(__prim_object, prim_addr) + : object; + /* Get vertex locations for intersection. */ + float3 verts[3]; + motion_triangle_vertices(kg, fobject, prim, time, verts); + /* Ray-triangle intersection, unoptimized. */ + float t, u, v; + if(ray_triangle_intersect_uv(P, + dir, + tmax, + verts[2], verts[0], verts[1], + &u, &v, &t)) + { + for(int i = min(max_hits, ss_isect->num_hits) - 1; i >= 0; --i) { + if(ss_isect->hits[i].t == t) { + return; + } + } + ss_isect->num_hits++; + int hit; + if(ss_isect->num_hits <= max_hits) { + hit = ss_isect->num_hits - 1; + } + else { + /* Reservoir sampling: if we are at the maximum number of + * hits, randomly replace element or skip it. + */ + hit = lcg_step_uint(lcg_state) % ss_isect->num_hits; + + if(hit >= max_hits) + return; + } + /* Record intersection. */ + Intersection *isect = &ss_isect->hits[hit]; + isect->t = t; + isect->u = u; + isect->v = v; + isect->prim = prim_addr; + isect->object = object; + isect->type = PRIMITIVE_MOTION_TRIANGLE; + /* Record geometric normal. */ + ss_isect->Ng[hit] = normalize(cross(verts[1] - verts[0], + verts[2] - verts[0])); + } +} +#endif /* __SUBSURFACE__ */ + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/geom/geom_motion_triangle_shader.h b/intern/cycles/kernel/geom/geom_motion_triangle_shader.h new file mode 100644 index 00000000000..0e024a05db6 --- /dev/null +++ b/intern/cycles/kernel/geom/geom_motion_triangle_shader.h @@ -0,0 +1,123 @@ +/* + * 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. + */ + +/* Motion Triangle Primitive + * + * These are stored as regular triangles, plus extra positions and normals at + * times other than the frame center. Computing the triangle vertex positions + * or normals at a given ray time is a matter of interpolation of the two steps + * between which the ray time lies. + * + * The extra positions and normals are stored as ATTR_STD_MOTION_VERTEX_POSITION + * and ATTR_STD_MOTION_VERTEX_NORMAL mesh attributes. + */ + +CCL_NAMESPACE_BEGIN + +/* Setup of motion triangle specific parts of ShaderData, moved into this one + * function to more easily share computation of interpolated positions and + * normals */ + +/* return 3 triangle vertex normals */ +ccl_device_noinline void motion_triangle_shader_setup(KernelGlobals *kg, + ShaderData *sd, const + Intersection *isect, + const Ray *ray, + bool subsurface) +{ + /* Get shader. */ + ccl_fetch(sd, shader) = kernel_tex_fetch(__tri_shader, ccl_fetch(sd, prim)); + /* Get motion info. */ + /* TODO(sergey): This logic is really similar to motion_triangle_vertices(), + * can we de-duplicate something here? + */ + int numsteps, numverts; + object_motion_info(kg, ccl_fetch(sd, object), &numsteps, &numverts, NULL); + /* Figure out which steps we need to fetch and their interpolation factor. */ + int maxstep = numsteps*2; + int step = min((int)(ccl_fetch(sd, time)*maxstep), maxstep-1); + float t = ccl_fetch(sd, time)*maxstep - step; + /* Find attribute. */ + AttributeElement elem; + int offset = find_attribute_motion(kg, ccl_fetch(sd, object), + ATTR_STD_MOTION_VERTEX_POSITION, + &elem); + kernel_assert(offset != ATTR_STD_NOT_FOUND); + /* Fetch vertex coordinates. */ + float3 verts[3], next_verts[3]; + uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, ccl_fetch(sd, prim)); + motion_triangle_verts_for_step(kg, tri_vindex, offset, numverts, numsteps, step, verts); + motion_triangle_verts_for_step(kg, tri_vindex, offset, numverts, numsteps, step+1, next_verts); + /* Interpolate between steps. */ + verts[0] = (1.0f - t)*verts[0] + t*next_verts[0]; + verts[1] = (1.0f - t)*verts[1] + t*next_verts[1]; + verts[2] = (1.0f - t)*verts[2] + t*next_verts[2]; + /* Compute refined position. */ +#ifdef __SUBSURFACE__ + if(subsurface) { + ccl_fetch(sd, P) = motion_triangle_refine_subsurface(kg, + sd, + isect, + ray, + verts); + } + else +#endif /* __SUBSURFACE__*/ + { + ccl_fetch(sd, P) = motion_triangle_refine(kg, sd, isect, ray, verts); + } + /* Compute face normal. */ + float3 Ng; + if(ccl_fetch(sd, object_flag) & SD_OBJECT_NEGATIVE_SCALE_APPLIED) { + Ng = normalize(cross(verts[2] - verts[0], verts[1] - verts[0])); + } + else { + Ng = normalize(cross(verts[1] - verts[0], verts[2] - verts[0])); + } + ccl_fetch(sd, Ng) = Ng; + ccl_fetch(sd, N) = Ng; + /* Compute derivatives of P w.r.t. uv. */ +#ifdef __DPDU__ + ccl_fetch(sd, dPdu) = (verts[0] - verts[2]); + ccl_fetch(sd, dPdv) = (verts[1] - verts[2]); +#endif + /* Compute smooth normal. */ + if(ccl_fetch(sd, shader) & SHADER_SMOOTH_NORMAL) { + /* Find attribute. */ + AttributeElement elem; + int offset = find_attribute_motion(kg, + ccl_fetch(sd, object), + ATTR_STD_MOTION_VERTEX_NORMAL, + &elem); + kernel_assert(offset != ATTR_STD_NOT_FOUND); + /* Fetch vertex coordinates. */ + float3 normals[3], next_normals[3]; + motion_triangle_normals_for_step(kg, tri_vindex, offset, numverts, numsteps, step, normals); + motion_triangle_normals_for_step(kg, tri_vindex, offset, numverts, numsteps, step+1, next_normals); + /* Interpolate between steps. */ + normals[0] = (1.0f - t)*normals[0] + t*next_normals[0]; + normals[1] = (1.0f - t)*normals[1] + t*next_normals[1]; + normals[2] = (1.0f - t)*normals[2] + t*next_normals[2]; + /* Interpolate between vertices. */ + float u = ccl_fetch(sd, u); + float v = ccl_fetch(sd, v); + float w = 1.0f - u - v; + ccl_fetch(sd, N) = (u*normals[0] + v*normals[1] + w*normals[2]); + } +} + +CCL_NAMESPACE_END + diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/geom_object.h index 9f0fe032ba4..f51b2d18657 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/geom_object.h @@ -113,7 +113,6 @@ ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals *kg, int ccl_device_inline Transform object_fetch_transform_motion_test(KernelGlobals *kg, int object, float time, Transform *itfm) { int object_flag = kernel_tex_fetch(__object_flag, object); - if(object_flag & SD_OBJECT_MOTION) { /* if we do motion blur */ Transform tfm = object_fetch_transform_motion(kg, object, time); diff --git a/intern/cycles/kernel/geom/geom_primitive.h b/intern/cycles/kernel/geom/geom_primitive.h index 4384c2093e9..8a73bb2f78b 100644 --- a/intern/cycles/kernel/geom/geom_primitive.h +++ b/intern/cycles/kernel/geom/geom_primitive.h @@ -157,8 +157,9 @@ ccl_device_inline float4 primitive_motion_vector(KernelGlobals *kg, ShaderData * if(is_curve_primitive) { center = curve_motion_center_location(kg, sd); - if(!(ccl_fetch(sd, flag) & SD_TRANSFORM_APPLIED)) + if(!(ccl_fetch(sd, object_flag) & SD_OBJECT_TRANSFORM_APPLIED)) { object_position_transform(kg, sd, ¢er); + } } else #endif @@ -181,7 +182,7 @@ ccl_device_inline float4 primitive_motion_vector(KernelGlobals *kg, ShaderData * motion_post = primitive_attribute_float3(kg, sd, desc, NULL, NULL); #ifdef __HAIR__ - if(is_curve_primitive && (ccl_fetch(sd, flag) & SD_OBJECT_HAS_VERTEX_MOTION) == 0) { + if(is_curve_primitive && (ccl_fetch(sd, object_flag) & SD_OBJECT_HAS_VERTEX_MOTION) == 0) { object_position_transform(kg, sd, &motion_pre); object_position_transform(kg, sd, &motion_post); } diff --git a/intern/cycles/kernel/geom/geom_triangle.h b/intern/cycles/kernel/geom/geom_triangle.h index 5e286e59562..b5635d819bb 100644 --- a/intern/cycles/kernel/geom/geom_triangle.h +++ b/intern/cycles/kernel/geom/geom_triangle.h @@ -32,10 +32,12 @@ ccl_device_inline float3 triangle_normal(KernelGlobals *kg, ShaderData *sd) const float3 v2 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w+2)); /* return normal */ - if(ccl_fetch(sd, flag) & SD_NEGATIVE_SCALE_APPLIED) + if(ccl_fetch(sd, object_flag) & SD_OBJECT_NEGATIVE_SCALE_APPLIED) { return normalize(cross(v2 - v0, v1 - v0)); - else + } + else { return normalize(cross(v1 - v0, v2 - v0)); + } } /* point and normal on triangle */ @@ -46,20 +48,18 @@ ccl_device_inline void triangle_point_normal(KernelGlobals *kg, int object, int float3 v0 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w+0)); float3 v1 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w+1)); float3 v2 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w+2)); - /* compute point */ float t = 1.0f - u - v; *P = (u*v0 + v*v1 + t*v2); - /* get object flags */ int object_flag = kernel_tex_fetch(__object_flag, object); - /* compute normal */ - if(object_flag & SD_NEGATIVE_SCALE_APPLIED) + if(object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) { *Ng = normalize(cross(v2 - v0, v1 - v0)); - else + } + else { *Ng = normalize(cross(v1 - v0, v2 - v0)); - + } /* shader`*/ *shader = kernel_tex_fetch(__tri_shader, prim); } diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h index 2de7320e4bd..bcd759e53ed 100644 --- a/intern/cycles/kernel/kernel_bake.h +++ b/intern/cycles/kernel/kernel_bake.h @@ -320,7 +320,7 @@ ccl_device void kernel_bake_evaluate(KernelGlobals *kg, ccl_global uint4 *input, P, Ng, Ng, shader, object, prim, u, v, 1.0f, 0.5f, - !(kernel_tex_fetch(__object_flag, object) & SD_TRANSFORM_APPLIED), + !(kernel_tex_fetch(__object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED), LAMP_NONE); sd.I = sd.N; diff --git a/intern/cycles/kernel/kernel_debug.h b/intern/cycles/kernel/kernel_debug.h index 24d6458567e..5647bbae5b5 100644 --- a/intern/cycles/kernel/kernel_debug.h +++ b/intern/cycles/kernel/kernel_debug.h @@ -18,8 +18,9 @@ CCL_NAMESPACE_BEGIN ccl_device_inline void debug_data_init(DebugData *debug_data) { - debug_data->num_bvh_traversal_steps = 0; + debug_data->num_bvh_traversed_nodes = 0; debug_data->num_bvh_traversed_instances = 0; + debug_data->num_bvh_intersections = 0; debug_data->num_ray_bounces = 0; } @@ -30,16 +31,21 @@ ccl_device_inline void kernel_write_debug_passes(KernelGlobals *kg, int sample) { int flag = kernel_data.film.pass_flag; - if(flag & PASS_BVH_TRAVERSAL_STEPS) { - kernel_write_pass_float(buffer + kernel_data.film.pass_bvh_traversal_steps, + if(flag & PASS_BVH_TRAVERSED_NODES) { + kernel_write_pass_float(buffer + kernel_data.film.pass_bvh_traversed_nodes, sample, - debug_data->num_bvh_traversal_steps); + debug_data->num_bvh_traversed_nodes); } if(flag & PASS_BVH_TRAVERSED_INSTANCES) { kernel_write_pass_float(buffer + kernel_data.film.pass_bvh_traversed_instances, sample, debug_data->num_bvh_traversed_instances); } + if(flag & PASS_BVH_INTERSECTIONS) { + kernel_write_pass_float(buffer + kernel_data.film.pass_bvh_intersections, + sample, + debug_data->num_bvh_intersections); + } if(flag & PASS_RAY_BOUNCES) { kernel_write_pass_float(buffer + kernel_data.film.pass_ray_bounces, sample, diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/kernel_light.h index ea0b6439717..9baa9d54957 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/kernel_light.h @@ -767,7 +767,7 @@ ccl_device void object_transform_light_sample(KernelGlobals *kg, LightSample *ls { #ifdef __INSTANCING__ /* instance transform */ - if(!(kernel_tex_fetch(__object_flag, object) & SD_TRANSFORM_APPLIED)) { + if(!(kernel_tex_fetch(__object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED)) { # ifdef __OBJECT_MOTION__ Transform itfm; Transform tfm = object_fetch_transform_motion_test(kg, object, time, &itfm); diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h index f2e9fa357a6..eada6916e58 100644 --- a/intern/cycles/kernel/kernel_path.h +++ b/intern/cycles/kernel/kernel_path.h @@ -109,6 +109,10 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, /* intersect scene */ Intersection isect; uint visibility = path_state_ray_visibility(kg, state); + if(state->bounce > kernel_data.integrator.ao_bounces) { + visibility = PATH_RAY_SHADOW; + ray->t = kernel_data.background.ao_distance; + } bool hit = scene_intersect(kg, *ray, visibility, @@ -292,6 +296,9 @@ ccl_device void kernel_path_indirect(KernelGlobals *kg, break; } + else if(state->bounce > kernel_data.integrator.ao_bounces) { + break; + } /* setup shading */ shader_setup_from_ray(kg, @@ -452,7 +459,7 @@ bool kernel_path_subsurface_scatter( # ifdef __VOLUME__ ss_indirect->need_update_volume_stack = kernel_data.integrator.use_volumes && - ccl_fetch(sd, flag) & SD_OBJECT_INTERSECTS_VOLUME; + ccl_fetch(sd, object_flag) & SD_OBJECT_INTERSECTS_VOLUME; # endif /* __VOLUME__ */ /* compute lighting with the BSDF closure */ @@ -627,6 +634,11 @@ ccl_device_inline float kernel_path_integrate(KernelGlobals *kg, lcg_state = lcg_state_init(rng, &state, 0x51633e2d); } + if(state.bounce > kernel_data.integrator.ao_bounces) { + visibility = PATH_RAY_SHADOW; + ray.t = kernel_data.background.ao_distance; + } + bool hit = scene_intersect(kg, ray, visibility, &isect, &lcg_state, difl, extmax); #else bool hit = scene_intersect(kg, ray, visibility, &isect, NULL, 0.0f, 0.0f); @@ -634,8 +646,9 @@ ccl_device_inline float kernel_path_integrate(KernelGlobals *kg, #ifdef __KERNEL_DEBUG__ if(state.flag & PATH_RAY_CAMERA) { - debug_data.num_bvh_traversal_steps += isect.num_traversal_steps; + debug_data.num_bvh_traversed_nodes += isect.num_traversed_nodes; debug_data.num_bvh_traversed_instances += isect.num_traversed_instances; + debug_data.num_bvh_intersections += isect.num_intersections; } debug_data.num_ray_bounces++; #endif /* __KERNEL_DEBUG__ */ @@ -771,6 +784,9 @@ ccl_device_inline float kernel_path_integrate(KernelGlobals *kg, break; } + else if(state.bounce > kernel_data.integrator.ao_bounces) { + break; + } /* setup shading */ shader_setup_from_ray(kg, &sd, &isect, &ray); @@ -781,21 +797,25 @@ ccl_device_inline float kernel_path_integrate(KernelGlobals *kg, /* holdout */ #ifdef __HOLDOUT__ - if((sd.flag & (SD_HOLDOUT|SD_HOLDOUT_MASK)) && (state.flag & PATH_RAY_CAMERA)) { + if(((sd.flag & SD_HOLDOUT) || + (sd.object_flag & SD_OBJECT_HOLDOUT_MASK)) && + (state.flag & PATH_RAY_CAMERA)) + { if(kernel_data.background.transparent) { float3 holdout_weight; - - if(sd.flag & SD_HOLDOUT_MASK) + if(sd.object_flag & SD_OBJECT_HOLDOUT_MASK) { holdout_weight = make_float3(1.0f, 1.0f, 1.0f); - else + } + else { holdout_weight = shader_holdout_eval(kg, &sd); - + } /* any throughput is ok, should all be identical here */ L_transparent += average(holdout_weight*throughput); } - if(sd.flag & SD_HOLDOUT_MASK) + if(sd.object_flag & SD_OBJECT_HOLDOUT_MASK) { break; + } } #endif /* __HOLDOUT__ */ diff --git a/intern/cycles/kernel/kernel_path_branched.h b/intern/cycles/kernel/kernel_path_branched.h index fba83bb6c35..d8ef12546c5 100644 --- a/intern/cycles/kernel/kernel_path_branched.h +++ b/intern/cycles/kernel/kernel_path_branched.h @@ -167,8 +167,9 @@ ccl_device void kernel_branched_path_subsurface_scatter(KernelGlobals *kg, true); #ifdef __VOLUME__ Ray volume_ray = *ray; - bool need_update_volume_stack = kernel_data.integrator.use_volumes && - ccl_fetch(sd, flag) & SD_OBJECT_INTERSECTS_VOLUME; + bool need_update_volume_stack = + kernel_data.integrator.use_volumes && + ccl_fetch(sd, object_flag) & SD_OBJECT_INTERSECTS_VOLUME; #endif /* __VOLUME__ */ /* compute lighting with the BSDF closure */ @@ -287,8 +288,9 @@ ccl_device float kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, int #endif /* __HAIR__ */ #ifdef __KERNEL_DEBUG__ - debug_data.num_bvh_traversal_steps += isect.num_traversal_steps; + debug_data.num_bvh_traversed_nodes += isect.num_traversed_nodes; debug_data.num_bvh_traversed_instances += isect.num_traversed_instances; + debug_data.num_bvh_intersections += isect.num_intersections; debug_data.num_ray_bounces++; #endif /* __KERNEL_DEBUG__ */ @@ -476,21 +478,21 @@ ccl_device float kernel_branched_path_integrate(KernelGlobals *kg, RNG *rng, int /* holdout */ #ifdef __HOLDOUT__ - if(sd.flag & (SD_HOLDOUT|SD_HOLDOUT_MASK)) { + if((sd.flag & SD_HOLDOUT) || (sd.object_flag & SD_OBJECT_HOLDOUT_MASK)) { if(kernel_data.background.transparent) { float3 holdout_weight; - - if(sd.flag & SD_HOLDOUT_MASK) + if(sd.object_flag & SD_OBJECT_HOLDOUT_MASK) { holdout_weight = make_float3(1.0f, 1.0f, 1.0f); - else + } + else { holdout_weight = shader_holdout_eval(kg, &sd); - + } /* any throughput is ok, should all be identical here */ L_transparent += average(holdout_weight*throughput); } - - if(sd.flag & SD_HOLDOUT_MASK) + if(sd.object_flag & SD_OBJECT_HOLDOUT_MASK) { break; + } } #endif /* __HOLDOUT__ */ diff --git a/intern/cycles/kernel/kernel_path_common.h b/intern/cycles/kernel/kernel_path_common.h index 13597eab287..7b903556bf9 100644 --- a/intern/cycles/kernel/kernel_path_common.h +++ b/intern/cycles/kernel/kernel_path_common.h @@ -30,7 +30,7 @@ ccl_device_inline void kernel_path_trace_setup(KernelGlobals *kg, int num_samples = kernel_data.integrator.aa_samples; - if(sample == 0) { + if(sample == kernel_data.integrator.start_sample) { *rng_state = hash_int_2d(x, y); } diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h index c1388801ed9..1dcb9f280b0 100644 --- a/intern/cycles/kernel/kernel_shader.h +++ b/intern/cycles/kernel/kernel_shader.h @@ -38,7 +38,7 @@ CCL_NAMESPACE_BEGIN #ifdef __OBJECT_MOTION__ ccl_device void shader_setup_object_transforms(KernelGlobals *kg, ShaderData *sd, float time) { - if(ccl_fetch(sd, flag) & SD_OBJECT_MOTION) { + if(ccl_fetch(sd, object_flag) & SD_OBJECT_MOTION) { ccl_fetch(sd, ob_tfm) = object_fetch_transform_motion(kg, ccl_fetch(sd, object), time); ccl_fetch(sd, ob_itfm) = transform_quick_inverse(ccl_fetch(sd, ob_tfm)); } @@ -59,7 +59,9 @@ ccl_device_noinline void shader_setup_from_ray(KernelGlobals *kg, #endif ccl_fetch(sd, type) = isect->type; - ccl_fetch(sd, flag) = kernel_tex_fetch(__object_flag, ccl_fetch(sd, object)); + ccl_fetch(sd, flag) = 0; + ccl_fetch(sd, object_flag) = kernel_tex_fetch(__object_flag, + ccl_fetch(sd, object)); /* matrices and time */ #ifdef __OBJECT_MOTION__ @@ -160,10 +162,11 @@ void shader_setup_from_subsurface( const Intersection *isect, const Ray *ray) { - bool backfacing = sd->flag & SD_BACKFACING; + const bool backfacing = sd->flag & SD_BACKFACING; /* object, matrices, time, ray_length stay the same */ - sd->flag = kernel_tex_fetch(__object_flag, sd->object); + sd->flag = 0; + sd->object_flag = kernel_tex_fetch(__object_flag, sd->object); sd->prim = kernel_tex_fetch(__prim_index, isect->prim); sd->type = isect->type; @@ -271,8 +274,10 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg, ccl_fetch(sd, ray_length) = t; ccl_fetch(sd, flag) = kernel_tex_fetch(__shader_flag, (ccl_fetch(sd, shader) & SHADER_MASK)*SHADER_SIZE); + ccl_fetch(sd, object_flag) = 0; if(ccl_fetch(sd, object) != OBJECT_NONE) { - ccl_fetch(sd, flag) |= kernel_tex_fetch(__object_flag, ccl_fetch(sd, object)); + ccl_fetch(sd, object_flag) |= kernel_tex_fetch(__object_flag, + ccl_fetch(sd, object)); #ifdef __OBJECT_MOTION__ shader_setup_object_transforms(kg, sd, time); @@ -298,7 +303,7 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg, ccl_fetch(sd, N) = triangle_smooth_normal(kg, Ng, ccl_fetch(sd, prim), ccl_fetch(sd, u), ccl_fetch(sd, v)); #ifdef __INSTANCING__ - if(!(ccl_fetch(sd, flag) & SD_TRANSFORM_APPLIED)) { + if(!(ccl_fetch(sd, object_flag) & SD_OBJECT_TRANSFORM_APPLIED)) { object_normal_transform_auto(kg, sd, &ccl_fetch(sd, N)); } #endif @@ -309,7 +314,7 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg, triangle_dPdudv(kg, ccl_fetch(sd, prim), &ccl_fetch(sd, dPdu), &ccl_fetch(sd, dPdv)); # ifdef __INSTANCING__ - if(!(ccl_fetch(sd, flag) & SD_TRANSFORM_APPLIED)) { + if(!(ccl_fetch(sd, object_flag) & SD_OBJECT_TRANSFORM_APPLIED)) { object_dir_transform_auto(kg, sd, &ccl_fetch(sd, dPdu)); object_dir_transform_auto(kg, sd, &ccl_fetch(sd, dPdv)); } @@ -364,7 +369,7 @@ ccl_device void shader_setup_from_displace(KernelGlobals *kg, ShaderData *sd, P, Ng, I, shader, object, prim, u, v, 0.0f, 0.5f, - !(kernel_tex_fetch(__object_flag, object) & SD_TRANSFORM_APPLIED), + !(kernel_tex_fetch(__object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED), LAMP_NONE); } @@ -379,6 +384,7 @@ ccl_device_inline void shader_setup_from_background(KernelGlobals *kg, ShaderDat ccl_fetch(sd, I) = -ray->D; ccl_fetch(sd, shader) = kernel_data.background.surface_shader; ccl_fetch(sd, flag) = kernel_tex_fetch(__shader_flag, (ccl_fetch(sd, shader) & SHADER_MASK)*SHADER_SIZE); + ccl_fetch(sd, object_flag) = 0; #ifdef __OBJECT_MOTION__ ccl_fetch(sd, time) = ray->time; #endif @@ -420,6 +426,7 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals *kg, ShaderData *s sd->I = -ray->D; sd->shader = SHADER_NONE; sd->flag = 0; + sd->object_flag = 0; #ifdef __OBJECT_MOTION__ sd->time = ray->time; #endif @@ -1027,6 +1034,7 @@ ccl_device_inline void shader_eval_volume(KernelGlobals *kg, sd->num_closure = 0; sd->num_closure_extra = 0; sd->flag = 0; + sd->object_flag = 0; for(int i = 0; stack[i].shader != SHADER_NONE; i++) { /* setup shaderdata from stack. it's mostly setup already in @@ -1034,11 +1042,12 @@ ccl_device_inline void shader_eval_volume(KernelGlobals *kg, sd->object = stack[i].object; sd->shader = stack[i].shader; - sd->flag &= ~(SD_SHADER_FLAGS|SD_OBJECT_FLAGS); + sd->flag &= ~SD_SHADER_FLAGS; sd->flag |= kernel_tex_fetch(__shader_flag, (sd->shader & SHADER_MASK)*SHADER_SIZE); + sd->object_flag &= ~SD_OBJECT_FLAGS; if(sd->object != OBJECT_NONE) { - sd->flag |= kernel_tex_fetch(__object_flag, sd->object); + sd->object_flag |= kernel_tex_fetch(__object_flag, sd->object); #ifdef __OBJECT_MOTION__ /* todo: this is inefficient for motion blur, we should be diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index ebf8a714ee0..29a4d70074b 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -346,9 +346,10 @@ typedef enum PassType { PASS_SUBSURFACE_COLOR = (1 << 24), PASS_LIGHT = (1 << 25), /* no real pass, used to force use_light_pass */ #ifdef __KERNEL_DEBUG__ - PASS_BVH_TRAVERSAL_STEPS = (1 << 26), + PASS_BVH_TRAVERSED_NODES = (1 << 26), PASS_BVH_TRAVERSED_INSTANCES = (1 << 27), - PASS_RAY_BOUNCES = (1 << 28), + PASS_BVH_INTERSECTIONS = (1 << 28), + PASS_RAY_BOUNCES = (1 << 29), #endif } PassType; @@ -565,8 +566,9 @@ typedef ccl_addr_space struct Intersection { int type; #ifdef __KERNEL_DEBUG__ - int num_traversal_steps; + int num_traversed_nodes; int num_traversed_instances; + int num_intersections; #endif } Intersection; @@ -714,56 +716,108 @@ typedef enum ShaderContext { /* Shader Data * * Main shader state at a point on the surface or in a volume. All coordinates - * are in world space. */ + * are in world space. + */ enum ShaderDataFlag { - /* runtime flags */ - SD_BACKFACING = (1 << 0), /* backside of surface? */ - SD_EMISSION = (1 << 1), /* have emissive closure? */ - SD_BSDF = (1 << 2), /* have bsdf closure? */ - SD_BSDF_HAS_EVAL = (1 << 3), /* have non-singular bsdf closure? */ - SD_BSSRDF = (1 << 4), /* have bssrdf */ - SD_HOLDOUT = (1 << 5), /* have holdout closure? */ - SD_ABSORPTION = (1 << 6), /* have volume absorption closure? */ - SD_SCATTER = (1 << 7), /* have volume phase closure? */ - SD_AO = (1 << 8), /* have ao closure? */ - SD_TRANSPARENT = (1 << 9), /* have transparent closure? */ + /* Runtime flags. */ + + /* Set when ray hits backside of surface. */ + SD_BACKFACING = (1 << 0), + /* Shader has emissive closure. */ + SD_EMISSION = (1 << 1), + /* Shader has BSDF closure. */ + SD_BSDF = (1 << 2), + /* Shader has non-singular BSDF closure. */ + SD_BSDF_HAS_EVAL = (1 << 3), + /* Shader has BSSRDF closure. */ + SD_BSSRDF = (1 << 4), + /* Shader has holdout closure. */ + SD_HOLDOUT = (1 << 5), + /* Shader has volume absorption closure. */ + SD_ABSORPTION = (1 << 6), + /* Shader has have volume phase (scatter) closure. */ + SD_SCATTER = (1 << 7), + /* Shader has AO closure. */ + SD_AO = (1 << 8), + /* Shader has transparent closure. */ + SD_TRANSPARENT = (1 << 9), + /* BSDF requires LCG for evaluation. */ SD_BSDF_NEEDS_LCG = (1 << 10), - SD_CLOSURE_FLAGS = (SD_EMISSION|SD_BSDF|SD_BSDF_HAS_EVAL|SD_BSSRDF| - SD_HOLDOUT|SD_ABSORPTION|SD_SCATTER|SD_AO| + SD_CLOSURE_FLAGS = (SD_EMISSION | + SD_BSDF | + SD_BSDF_HAS_EVAL | + SD_BSSRDF | + SD_HOLDOUT | + SD_ABSORPTION | + SD_SCATTER | + SD_AO | SD_BSDF_NEEDS_LCG), - /* shader flags */ - SD_USE_MIS = (1 << 12), /* direct light sample */ - SD_HAS_TRANSPARENT_SHADOW = (1 << 13), /* has transparent shadow */ - SD_HAS_VOLUME = (1 << 14), /* has volume shader */ - SD_HAS_ONLY_VOLUME = (1 << 15), /* has only volume shader, no surface */ - SD_HETEROGENEOUS_VOLUME = (1 << 16), /* has heterogeneous volume */ - SD_HAS_BSSRDF_BUMP = (1 << 17), /* bssrdf normal uses bump */ - SD_VOLUME_EQUIANGULAR = (1 << 18), /* use equiangular sampling */ - 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_HAS_CONSTANT_EMISSION = (1 << 23), /* has constant emission (value stored in __shader_flag) */ - - 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_HAS_DISPLACEMENT|SD_HAS_CONSTANT_EMISSION), - - /* object flags */ - SD_HOLDOUT_MASK = (1 << 24), /* holdout for camera rays */ - SD_OBJECT_MOTION = (1 << 25), /* has object motion blur */ - SD_TRANSFORM_APPLIED = (1 << 26), /* vertices have transform applied */ - SD_NEGATIVE_SCALE_APPLIED = (1 << 27), /* vertices have negative scale applied */ - SD_OBJECT_HAS_VOLUME = (1 << 28), /* object has a volume shader */ - SD_OBJECT_INTERSECTS_VOLUME = (1 << 29), /* object intersects AABB of an object with volume shader */ - SD_OBJECT_HAS_VERTEX_MOTION = (1 << 30), /* 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| + /* Shader flags. */ + + /* direct light sample */ + SD_USE_MIS = (1 << 16), + /* Has transparent shadow. */ + SD_HAS_TRANSPARENT_SHADOW = (1 << 17), + /* Has volume shader. */ + SD_HAS_VOLUME = (1 << 18), + /* Has only volume shader, no surface. */ + SD_HAS_ONLY_VOLUME = (1 << 19), + /* Has heterogeneous volume. */ + SD_HETEROGENEOUS_VOLUME = (1 << 20), + /* BSSRDF normal uses bump. */ + SD_HAS_BSSRDF_BUMP = (1 << 21), + /* Use equiangular volume sampling */ + SD_VOLUME_EQUIANGULAR = (1 << 22), + /* Use multiple importance volume sampling. */ + SD_VOLUME_MIS = (1 << 23), + /* Use cubic interpolation for voxels. */ + SD_VOLUME_CUBIC = (1 << 24), + /* Has data connected to the displacement input. */ + SD_HAS_BUMP = (1 << 25), + /* Has true displacement. */ + SD_HAS_DISPLACEMENT = (1 << 26), + /* Has constant emission (value stored in __shader_flag) */ + SD_HAS_CONSTANT_EMISSION = (1 << 27), + + 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_HAS_DISPLACEMENT | + SD_HAS_CONSTANT_EMISSION) +}; + + /* Object flags. */ +enum ShaderDataObjectFlag { + /* Holdout for camera rays. */ + SD_OBJECT_HOLDOUT_MASK = (1 << 0), + /* Has object motion blur. */ + SD_OBJECT_MOTION = (1 << 1), + /* Vertices have transform applied. */ + SD_OBJECT_TRANSFORM_APPLIED = (1 << 2), + /* Vertices have negative scale applied. */ + SD_OBJECT_NEGATIVE_SCALE_APPLIED = (1 << 3), + /* Object has a volume shader. */ + SD_OBJECT_HAS_VOLUME = (1 << 4), + /* Object intersects AABB of an object with volume shader. */ + SD_OBJECT_INTERSECTS_VOLUME = (1 << 5), + /* Has position for motion vertices. */ + SD_OBJECT_HAS_VERTEX_MOTION = (1 << 6), + + SD_OBJECT_FLAGS = (SD_OBJECT_HOLDOUT_MASK | + SD_OBJECT_MOTION | + SD_OBJECT_TRANSFORM_APPLIED | + SD_OBJECT_NEGATIVE_SCALE_APPLIED | + SD_OBJECT_HAS_VOLUME | SD_OBJECT_INTERSECTS_VOLUME) }; @@ -802,6 +856,8 @@ typedef ccl_addr_space struct ShaderData { ccl_soa_member(int, shader); /* booleans describing shader, see ShaderDataFlag */ ccl_soa_member(int, flag); + /* booleans describing object of the shader, see ShaderDataObjectFlag */ + ccl_soa_member(int, object_flag); /* primitive id if there is one, ~0 otherwise */ ccl_soa_member(int, prim); @@ -1075,10 +1131,10 @@ typedef struct KernelFilm { int pass_pad4; #ifdef __KERNEL_DEBUG__ - int pass_bvh_traversal_steps; + int pass_bvh_traversed_nodes; int pass_bvh_traversed_instances; + int pass_bvh_intersections; int pass_ray_bounces; - int pass_pad3; #endif } KernelFilm; static_assert_align(KernelFilm, 16); @@ -1123,6 +1179,8 @@ typedef struct KernelIntegrator { int max_transmission_bounce; int max_volume_bounce; + int ao_bounces; + /* transparent */ int transparent_min_bounce; int transparent_max_bounce; @@ -1170,7 +1228,8 @@ typedef struct KernelIntegrator { float weighting_adjust; int use_gradients; - int pad; + int start_sample; + int pad1, pad2, pad3; } KernelIntegrator; static_assert_align(KernelIntegrator, 16); @@ -1228,10 +1287,9 @@ static_assert_align(KernelData, 16); * really important here. */ typedef ccl_addr_space struct DebugData { - // Total number of BVH node traversal steps and primitives intersections - // for the camera rays. - int num_bvh_traversal_steps; + int num_bvh_traversed_nodes; int num_bvh_traversed_instances; + int num_bvh_intersections; int num_ray_bounces; } DebugData; #endif diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index 26543862b80..58bbdc33920 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -102,6 +102,8 @@ ustring OSLRenderServices::u_curve_tangent_normal("geom:curve_tangent_normal"); #endif ustring OSLRenderServices::u_path_ray_length("path:ray_length"); ustring OSLRenderServices::u_path_ray_depth("path:ray_depth"); +ustring OSLRenderServices::u_path_diffuse_depth("path:diffuse_depth"); +ustring OSLRenderServices::u_path_glossy_depth("path:glossy_depth"); ustring OSLRenderServices::u_path_transparent_depth("path:transparent_depth"); ustring OSLRenderServices::u_path_transmission_depth("path:transmission_depth"); ustring OSLRenderServices::u_trace("trace"); @@ -710,7 +712,7 @@ bool OSLRenderServices::get_object_standard_attribute(KernelGlobals *kg, ShaderD else motion_triangle_vertices(kg, sd->object, sd->prim, sd->time, P); - if(!(sd->flag & SD_TRANSFORM_APPLIED)) { + if(!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { object_position_transform(kg, sd, &P[0]); object_position_transform(kg, sd, &P[1]); object_position_transform(kg, sd, &P[2]); @@ -759,6 +761,24 @@ bool OSLRenderServices::get_background_attribute(KernelGlobals *kg, ShaderData * int f = state->bounce; return set_attribute_int(f, type, derivatives, val); } + else if(name == u_path_diffuse_depth) { + /* Diffuse Ray Depth */ + PathState *state = sd->osl_path_state; + int f = state->diffuse_bounce; + return set_attribute_int(f, type, derivatives, val); + } + else if(name == u_path_glossy_depth) { + /* Glossy Ray Depth */ + PathState *state = sd->osl_path_state; + int f = state->glossy_bounce; + return set_attribute_int(f, type, derivatives, val); + } + else if(name == u_path_transmission_depth) { + /* Transmission Ray Depth */ + PathState *state = sd->osl_path_state; + int f = state->transmission_bounce; + return set_attribute_int(f, type, derivatives, val); + } else if(name == u_path_transparent_depth) { /* Transparent Ray Depth */ PathState *state = sd->osl_path_state; diff --git a/intern/cycles/kernel/osl/osl_services.h b/intern/cycles/kernel/osl/osl_services.h index 0f2e02c62b0..ec34ca77115 100644 --- a/intern/cycles/kernel/osl/osl_services.h +++ b/intern/cycles/kernel/osl/osl_services.h @@ -165,6 +165,8 @@ public: static ustring u_curve_tangent_normal; static ustring u_path_ray_length; static ustring u_path_ray_depth; + static ustring u_path_diffuse_depth; + static ustring u_path_glossy_depth; static ustring u_path_transparent_depth; static ustring u_path_transmission_depth; static ustring u_trace; diff --git a/intern/cycles/kernel/shaders/node_light_path.osl b/intern/cycles/kernel/shaders/node_light_path.osl index a021a40467d..64fe4c20132 100644 --- a/intern/cycles/kernel/shaders/node_light_path.osl +++ b/intern/cycles/kernel/shaders/node_light_path.osl @@ -27,6 +27,8 @@ shader node_light_path( output float IsVolumeScatterRay = 0.0, output float RayLength = 0.0, output float RayDepth = 0.0, + output float DiffuseDepth = 0.0, + output float GlossyDepth = 0.0, output float TransparentDepth = 0.0, output float TransmissionDepth = 0.0) { @@ -45,6 +47,14 @@ shader node_light_path( getattribute("path:ray_depth", ray_depth); RayDepth = (float)ray_depth; + int diffuse_depth; + getattribute("path:diffuse_depth", diffuse_depth); + DiffuseDepth = (float)diffuse_depth; + + int glossy_depth; + getattribute("path:glossy_depth", glossy_depth); + GlossyDepth = (float)glossy_depth; + int transparent_depth; getattribute("path:transparent_depth", transparent_depth); TransparentDepth = (float)transparent_depth; diff --git a/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h b/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h index 435d1171d5c..5d951b972ed 100644 --- a/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h +++ b/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h @@ -137,22 +137,22 @@ ccl_device void kernel_holdout_emission_blurring_pathtermination_ao( /* holdout */ #ifdef __HOLDOUT__ - if((ccl_fetch(sd, flag) & (SD_HOLDOUT|SD_HOLDOUT_MASK)) && + if(((ccl_fetch(sd, flag) & SD_HOLDOUT) || + (ccl_fetch(sd, object_flag) & SD_OBJECT_HOLDOUT_MASK)) && (state->flag & PATH_RAY_CAMERA)) { if(kernel_data.background.transparent) { float3 holdout_weight; - - if(ccl_fetch(sd, flag) & SD_HOLDOUT_MASK) + if(ccl_fetch(sd, object_flag) & SD_OBJECT_HOLDOUT_MASK) { holdout_weight = make_float3(1.0f, 1.0f, 1.0f); - else + } + else { holdout_weight = shader_holdout_eval(kg, sd); - + } /* any throughput is ok, should all be identical here */ L_transparent_coop[ray_index] += average(holdout_weight*throughput); } - - if(ccl_fetch(sd, flag) & SD_HOLDOUT_MASK) { + if(ccl_fetch(sd, object_flag) & SD_OBJECT_HOLDOUT_MASK) { ASSIGN_RAY_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER); *enqueue_flag = 1; } diff --git a/intern/cycles/kernel/split/kernel_scene_intersect.h b/intern/cycles/kernel/split/kernel_scene_intersect.h index fc4b4ee38e5..2388580051f 100644 --- a/intern/cycles/kernel/split/kernel_scene_intersect.h +++ b/intern/cycles/kernel/split/kernel_scene_intersect.h @@ -116,8 +116,9 @@ ccl_device void kernel_scene_intersect( #ifdef __KERNEL_DEBUG__ if(state.flag & PATH_RAY_CAMERA) { - debug_data->num_bvh_traversal_steps += isect->num_traversal_steps; + debug_data->num_bvh_traversed_nodes += isect->num_traversed_nodes; debug_data->num_bvh_traversed_instances += isect->num_traversed_instances; + debug_data->num_bvh_intersections += isect->num_intersections; } debug_data->num_ray_bounces++; #endif diff --git a/intern/cycles/kernel/svm/svm_light_path.h b/intern/cycles/kernel/svm/svm_light_path.h index f35ea05048b..04f6f623f18 100644 --- a/intern/cycles/kernel/svm/svm_light_path.h +++ b/intern/cycles/kernel/svm/svm_light_path.h @@ -34,6 +34,8 @@ ccl_device void svm_node_light_path(ShaderData *sd, ccl_addr_space PathState *st case NODE_LP_backfacing: info = (ccl_fetch(sd, flag) & SD_BACKFACING)? 1.0f: 0.0f; break; case NODE_LP_ray_length: info = ccl_fetch(sd, ray_length); break; case NODE_LP_ray_depth: info = (float)state->bounce; break; + case NODE_LP_ray_diffuse: info = (float)state->diffuse_bounce; break; + case NODE_LP_ray_glossy: info = (float)state->glossy_bounce; break; case NODE_LP_ray_transparent: info = (float)state->transparent_bounce; break; case NODE_LP_ray_transmission: info = (float)state->transmission_bounce; break; } diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index 77d3398b449..95920d5906c 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -188,6 +188,8 @@ typedef enum NodeLightPath { NODE_LP_backfacing, NODE_LP_ray_length, NODE_LP_ray_depth, + NODE_LP_ray_diffuse, + NODE_LP_ray_glossy, NODE_LP_ray_transparent, NODE_LP_ray_transmission, } NodeLightPath; diff --git a/intern/cycles/kernel/svm/svm_wireframe.h b/intern/cycles/kernel/svm/svm_wireframe.h index 6eed9bc1a99..87e40791333 100644 --- a/intern/cycles/kernel/svm/svm_wireframe.h +++ b/intern/cycles/kernel/svm/svm_wireframe.h @@ -57,7 +57,7 @@ ccl_device_inline float wireframe(KernelGlobals *kg, else motion_triangle_vertices(kg, ccl_fetch(sd, object), ccl_fetch(sd, prim), ccl_fetch(sd, time), Co); - if(!(ccl_fetch(sd, flag) & SD_TRANSFORM_APPLIED)) { + if(!(ccl_fetch(sd, object_flag) & SD_OBJECT_TRANSFORM_APPLIED)) { object_position_transform(kg, sd, &Co[0]); object_position_transform(kg, sd, &Co[1]); object_position_transform(kg, sd, &Co[2]); diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp index 8f00ea625f0..34e3d1e901f 100644 --- a/intern/cycles/render/buffers.cpp +++ b/intern/cycles/render/buffers.cpp @@ -339,7 +339,10 @@ bool RenderBuffers::get_pass_rect(PassType type, float exposure, int sample, int } } #ifdef WITH_CYCLES_DEBUG - else if(type == PASS_BVH_TRAVERSAL_STEPS || type == PASS_RAY_BOUNCES) { + else if(type == PASS_BVH_TRAVERSED_NODES || + type == PASS_BVH_TRAVERSED_INSTANCES || + type == PASS_BVH_INTERSECTIONS || + type == PASS_RAY_BOUNCES) { if(read_pixels) { FOREACH_PIXEL in[0] = pixels[0]*scale; diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp index afca94e8524..ce53bce1894 100644 --- a/intern/cycles/render/film.cpp +++ b/intern/cycles/render/film.cpp @@ -154,14 +154,9 @@ void Pass::add(PassType type, array<Pass>& passes) pass.components = 0; break; #ifdef WITH_CYCLES_DEBUG - case PASS_BVH_TRAVERSAL_STEPS: - pass.components = 1; - pass.exposure = false; - break; + case PASS_BVH_TRAVERSED_NODES: case PASS_BVH_TRAVERSED_INSTANCES: - pass.components = 1; - pass.exposure = false; - break; + case PASS_BVH_INTERSECTIONS: case PASS_RAY_BOUNCES: pass.components = 1; pass.exposure = false; @@ -425,12 +420,15 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) break; #ifdef WITH_CYCLES_DEBUG - case PASS_BVH_TRAVERSAL_STEPS: - kfilm->pass_bvh_traversal_steps = kfilm->pass_stride; + case PASS_BVH_TRAVERSED_NODES: + kfilm->pass_bvh_traversed_nodes = kfilm->pass_stride; break; case PASS_BVH_TRAVERSED_INSTANCES: kfilm->pass_bvh_traversed_instances = kfilm->pass_stride; break; + case PASS_BVH_INTERSECTIONS: + kfilm->pass_bvh_intersections = kfilm->pass_stride; + break; case PASS_RAY_BOUNCES: kfilm->pass_ray_bounces = kfilm->pass_stride; break; diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index ab830b19c57..fd8a1262208 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -258,12 +258,14 @@ static bool image_equals(ImageManager::Image *image, const string& filename, void *builtin_data, InterpolationType interpolation, - ExtensionType extension) + ExtensionType extension, + bool use_alpha) { return image->filename == filename && image->builtin_data == builtin_data && image->interpolation == interpolation && - image->extension == extension; + image->extension == extension && + image->use_alpha == use_alpha; } int ImageManager::add_image(const string& filename, @@ -305,7 +307,8 @@ int ImageManager::add_image(const string& filename, filename, builtin_data, interpolation, - extension)) + extension, + use_alpha)) { if(img->frame != frame) { img->frame = frame; @@ -377,7 +380,8 @@ void ImageManager::remove_image(int flat_slot) void ImageManager::remove_image(const string& filename, void *builtin_data, InterpolationType interpolation, - ExtensionType extension) + ExtensionType extension, + bool use_alpha) { size_t slot; @@ -387,7 +391,8 @@ void ImageManager::remove_image(const string& filename, filename, builtin_data, interpolation, - extension)) + extension, + use_alpha)) { remove_image(type_index_to_flattened_slot(slot, (ImageDataType)type)); return; @@ -403,7 +408,8 @@ void ImageManager::remove_image(const string& filename, void ImageManager::tag_reload_image(const string& filename, void *builtin_data, InterpolationType interpolation, - ExtensionType extension) + ExtensionType extension, + bool use_alpha) { for(size_t type = 0; type < IMAGE_DATA_NUM_TYPES; type++) { for(size_t slot = 0; slot < images[type].size(); slot++) { @@ -411,7 +417,8 @@ void ImageManager::tag_reload_image(const string& filename, filename, builtin_data, interpolation, - extension)) + extension, + use_alpha)) { images[type][slot]->need_load = true; break; diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h index 47bbd92347c..494c74f0cdd 100644 --- a/intern/cycles/render/image.h +++ b/intern/cycles/render/image.h @@ -61,11 +61,13 @@ public: void remove_image(const string& filename, void *builtin_data, InterpolationType interpolation, - ExtensionType extension); + ExtensionType extension, + bool use_alpha); void tag_reload_image(const string& filename, void *builtin_data, InterpolationType interpolation, - ExtensionType extension); + ExtensionType extension, + bool use_alpha); ImageDataType get_image_metadata(const string& filename, void *builtin_data, bool& is_linear); void device_update(Device *device, diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp index b7eb3bf6169..3289d6f2c1f 100644 --- a/intern/cycles/render/integrator.cpp +++ b/intern/cycles/render/integrator.cpp @@ -43,6 +43,8 @@ NODE_DEFINE(Integrator) SOCKET_INT(transparent_max_bounce, "Transparent Max Bounce", 7); SOCKET_BOOLEAN(transparent_shadows, "Transparent Shadows", false); + SOCKET_INT(ao_bounces, "AO Bounces", 0); + SOCKET_INT(volume_max_steps, "Volume Max Steps", 1024); SOCKET_FLOAT(volume_step_size, "Volume Step Size", 0.1f); @@ -62,6 +64,7 @@ NODE_DEFINE(Integrator) SOCKET_INT(mesh_light_samples, "Mesh Light Samples", 1); SOCKET_INT(subsurface_samples, "Subsurface Samples", 1); SOCKET_INT(volume_samples, "Volume Samples", 1); + SOCKET_INT(start_sample, "Start Sample", 0); SOCKET_BOOLEAN(sample_all_lights_direct, "Sample All Lights Direct", true); SOCKET_BOOLEAN(sample_all_lights_indirect, "Sample All Lights Indirect", true); @@ -116,6 +119,13 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene kintegrator->transparent_max_bounce = transparent_max_bounce + 1; kintegrator->transparent_min_bounce = transparent_min_bounce + 1; + if(ao_bounces == 0) { + kintegrator->ao_bounces = INT_MAX; + } + else { + kintegrator->ao_bounces = ao_bounces - 1; + } + /* Transparent Shadows * We only need to enable transparent shadows, if we actually have * transparent shaders in the scene. Otherwise we can disable it @@ -157,6 +167,7 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene kintegrator->mesh_light_samples = mesh_light_samples; kintegrator->subsurface_samples = subsurface_samples; kintegrator->volume_samples = volume_samples; + kintegrator->start_sample = start_sample; if(method == BRANCHED_PATH) { kintegrator->sample_all_lights_direct = sample_all_lights_direct; diff --git a/intern/cycles/render/integrator.h b/intern/cycles/render/integrator.h index 8c22547ed24..f96886bcd27 100644 --- a/intern/cycles/render/integrator.h +++ b/intern/cycles/render/integrator.h @@ -43,6 +43,8 @@ public: int transparent_max_bounce; bool transparent_shadows; + int ao_bounces; + int volume_max_steps; float volume_step_size; @@ -64,6 +66,7 @@ public: int mesh_light_samples; int subsurface_samples; int volume_samples; + int start_sample; bool sample_all_lights_direct; bool sample_all_lights_indirect; diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index bf7c893a3b0..c9766fb5f3e 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -49,6 +49,64 @@ void Mesh::Triangle::bounds_grow(const float3 *verts, BoundBox& bounds) const bounds.grow(verts[v[2]]); } +void Mesh::Triangle::motion_verts(const float3 *verts, + const float3 *vert_steps, + size_t num_verts, + size_t num_steps, + float time, + float3 r_verts[3]) const +{ + /* Figure out which steps we need to fetch and their interpolation factor. */ + const size_t max_step = num_steps - 1; + const size_t step = min((int)(time * max_step), max_step - 1); + const float t = time*max_step - step; + /* Fetch vertex coordinates. */ + float3 curr_verts[3]; + float3 next_verts[3]; + verts_for_step(verts, + vert_steps, + num_verts, + num_steps, + step, + curr_verts); + verts_for_step(verts, + vert_steps, + num_verts, + num_steps, + step + 1, + next_verts); + /* Interpolate between steps. */ + r_verts[0] = (1.0f - t)*curr_verts[0] + t*next_verts[0]; + r_verts[1] = (1.0f - t)*curr_verts[1] + t*next_verts[1]; + r_verts[2] = (1.0f - t)*curr_verts[2] + t*next_verts[2]; +} + +void Mesh::Triangle::verts_for_step(const float3 *verts, + const float3 *vert_steps, + size_t num_verts, + size_t num_steps, + size_t step, + float3 r_verts[3]) const +{ + const size_t center_step = ((num_steps - 1) / 2); + if(step == center_step) { + /* Center step: regular vertex location. */ + r_verts[0] = verts[v[0]]; + r_verts[1] = verts[v[1]]; + r_verts[2] = verts[v[2]]; + } + else { + /* Center step not stored in the attribute array array. */ + if(step > center_step) { + step--; + } + size_t offset = step * num_verts; + r_verts[0] = vert_steps[offset + v[0]]; + r_verts[1] = vert_steps[offset + v[1]]; + r_verts[2] = vert_steps[offset + v[2]]; + } +} + /* Curve */ void Mesh::Curve::bounds_grow(const int k, const float3 *curve_keys, const float *curve_radius, BoundBox& bounds) const @@ -104,6 +162,205 @@ void Mesh::Curve::bounds_grow(const int k, bounds.grow(upper, mr); } +void Mesh::Curve::bounds_grow(float4 keys[4], BoundBox& bounds) const +{ + float3 P[4] = { + float4_to_float3(keys[0]), + float4_to_float3(keys[1]), + float4_to_float3(keys[2]), + float4_to_float3(keys[3]), + }; + + float3 lower; + float3 upper; + + curvebounds(&lower.x, &upper.x, P, 0); + curvebounds(&lower.y, &upper.y, P, 1); + curvebounds(&lower.z, &upper.z, P, 2); + + float mr = max(keys[1].w, keys[2].w); + + bounds.grow(lower, mr); + bounds.grow(upper, mr); +} + +void Mesh::Curve::motion_keys(const float3 *curve_keys, + const float *curve_radius, + const float3 *key_steps, + size_t num_curve_keys, + size_t num_steps, + float time, + size_t k0, size_t k1, + float4 r_keys[2]) const +{ + /* Figure out which steps we need to fetch and their interpolation factor. */ + const size_t max_step = num_steps - 1; + const size_t step = min((int)(time * max_step), max_step - 1); + const float t = time*max_step - step; + /* Fetch vertex coordinates. */ + float4 curr_keys[2]; + float4 next_keys[2]; + keys_for_step(curve_keys, + curve_radius, + key_steps, + num_curve_keys, + num_steps, + step, + k0, k1, + curr_keys); + keys_for_step(curve_keys, + curve_radius, + key_steps, + num_curve_keys, + num_steps, + step + 1, + k0, k1, + next_keys); + /* Interpolate between steps. */ + r_keys[0] = (1.0f - t)*curr_keys[0] + t*next_keys[0]; + r_keys[1] = (1.0f - t)*curr_keys[1] + t*next_keys[1]; +} + +void Mesh::Curve::cardinal_motion_keys(const float3 *curve_keys, + const float *curve_radius, + const float3 *key_steps, + size_t num_curve_keys, + size_t num_steps, + float time, + size_t k0, size_t k1, + size_t k2, size_t k3, + float4 r_keys[4]) const +{ + /* Figure out which steps we need to fetch and their interpolation factor. */ + const size_t max_step = num_steps - 1; + const size_t step = min((int)(time * max_step), max_step - 1); + const float t = time*max_step - step; + /* Fetch vertex coordinates. */ + float4 curr_keys[4]; + float4 next_keys[4]; + cardinal_keys_for_step(curve_keys, + curve_radius, + key_steps, + num_curve_keys, + num_steps, + step, + k0, k1, k2, k3, + curr_keys); + cardinal_keys_for_step(curve_keys, + curve_radius, + key_steps, + num_curve_keys, + num_steps, + step + 1, + k0, k1, k2, k3, + next_keys); + /* Interpolate between steps. */ + r_keys[0] = (1.0f - t)*curr_keys[0] + t*next_keys[0]; + r_keys[1] = (1.0f - t)*curr_keys[1] + t*next_keys[1]; + r_keys[2] = (1.0f - t)*curr_keys[2] + t*next_keys[2]; + r_keys[3] = (1.0f - t)*curr_keys[3] + t*next_keys[3]; +} + +void Mesh::Curve::keys_for_step(const float3 *curve_keys, + const float *curve_radius, + const float3 *key_steps, + size_t num_curve_keys, + size_t num_steps, + size_t step, + size_t k0, size_t k1, + float4 r_keys[2]) const +{ + k0 = max(k0, 0); + k1 = min(k1, num_keys - 1); + const size_t center_step = ((num_steps - 1) / 2); + if(step == center_step) { + /* Center step: regular key location. */ + /* TODO(sergey): Consider adding make_float4(float3, float) + * function. + */ + r_keys[0] = make_float4(curve_keys[first_key + k0].x, + curve_keys[first_key + k0].y, + curve_keys[first_key + k0].z, + curve_radius[first_key + k0]); + r_keys[1] = make_float4(curve_keys[first_key + k1].x, + curve_keys[first_key + k1].y, + curve_keys[first_key + k1].z, + curve_radius[first_key + k1]); + } + else { + /* Center step is not stored in this array. */ + if(step > center_step) { + step--; + } + const size_t offset = first_key + step * num_curve_keys; + r_keys[0] = make_float4(key_steps[offset + k0].x, + key_steps[offset + k0].y, + key_steps[offset + k0].z, + curve_radius[first_key + k0]); + r_keys[1] = make_float4(key_steps[offset + k1].x, + key_steps[offset + k1].y, + key_steps[offset + k1].z, + curve_radius[first_key + k1]); + } +} + +void Mesh::Curve::cardinal_keys_for_step(const float3 *curve_keys, + const float *curve_radius, + const float3 *key_steps, + size_t num_curve_keys, + size_t num_steps, + size_t step, + size_t k0, size_t k1, + size_t k2, size_t k3, + float4 r_keys[4]) const +{ + k0 = max(k0, 0); + k3 = min(k3, num_keys - 1); + const size_t center_step = ((num_steps - 1) / 2); + if(step == center_step) { + /* Center step: regular key location. */ + r_keys[0] = make_float4(curve_keys[first_key + k0].x, + curve_keys[first_key + k0].y, + curve_keys[first_key + k0].z, + curve_radius[first_key + k0]); + r_keys[1] = make_float4(curve_keys[first_key + k1].x, + curve_keys[first_key + k1].y, + curve_keys[first_key + k1].z, + curve_radius[first_key + k1]); + r_keys[2] = make_float4(curve_keys[first_key + k2].x, + curve_keys[first_key + k2].y, + curve_keys[first_key + k2].z, + curve_radius[first_key + k2]); + r_keys[3] = make_float4(curve_keys[first_key + k3].x, + curve_keys[first_key + k3].y, + curve_keys[first_key + k3].z, + curve_radius[first_key + k3]); + } + else { + /* Center step is not stored in this array. */ + if(step > center_step) { + step--; + } + const size_t offset = first_key + step * num_curve_keys; + r_keys[0] = make_float4(key_steps[offset + k0].x, + key_steps[offset + k0].y, + key_steps[offset + k0].z, + curve_radius[first_key + k0]); + r_keys[1] = make_float4(key_steps[offset + k1].x, + key_steps[offset + k1].y, + key_steps[offset + k1].z, + curve_radius[first_key + k1]); + r_keys[2] = make_float4(key_steps[offset + k2].x, + key_steps[offset + k2].y, + key_steps[offset + k2].z, + curve_radius[first_key + k2]); + r_keys[3] = make_float4(key_steps[offset + k3].x, + key_steps[offset + k3].y, + key_steps[offset + k3].z, + curve_radius[first_key + k3]); + } +} + /* SubdFace */ float3 Mesh::SubdFace::normal(const Mesh *mesh) const @@ -394,7 +651,7 @@ void Mesh::compute_bounds() if(use_motion_blur && attr) { size_t steps_size = verts.size() * (motion_steps - 1); float3 *vert_steps = attr->data_float3(); - + for(size_t i = 0; i < steps_size; i++) bnds.grow(vert_steps[i]); } @@ -403,7 +660,7 @@ void Mesh::compute_bounds() if(use_motion_blur && curve_attr) { size_t steps_size = curve_keys.size() * (motion_steps - 1); float3 *key_steps = curve_attr->data_float3(); - + for(size_t i = 0; i < steps_size; i++) bnds.grow(key_steps[i]); } @@ -417,11 +674,11 @@ void Mesh::compute_bounds() for(size_t i = 0; i < curve_keys_size; i++) bnds.grow_safe(curve_keys[i], curve_radius[i]); - + if(use_motion_blur && attr) { size_t steps_size = verts.size() * (motion_steps - 1); float3 *vert_steps = attr->data_float3(); - + for(size_t i = 0; i < steps_size; i++) bnds.grow_safe(vert_steps[i]); } @@ -429,7 +686,7 @@ void Mesh::compute_bounds() if(use_motion_blur && curve_attr) { size_t steps_size = curve_keys.size() * (motion_steps - 1); float3 *key_steps = curve_attr->data_float3(); - + for(size_t i = 0; i < steps_size; i++) bnds.grow_safe(key_steps[i]); } @@ -464,7 +721,7 @@ void Mesh::add_face_normals() /* don't compute if already there */ if(attributes.find(ATTR_STD_FACE_NORMAL)) return; - + /* get attributes */ Attribute *attr_fN = attributes.add(ATTR_STD_FACE_NORMAL); float3 *fN = attr_fN->data_float3(); @@ -796,6 +1053,8 @@ void Mesh::compute_bvh(DeviceScene *dscene, bparams.use_qbvh = params->use_qbvh; bparams.use_unaligned_nodes = dscene->data.bvh.have_curves && params->use_bvh_unaligned_nodes; + bparams.num_motion_triangle_steps = params->num_bvh_time_steps; + bparams.num_motion_curve_steps = params->num_bvh_time_steps; delete bvh; bvh = BVH::create(bparams, objects); @@ -1002,7 +1261,7 @@ void MeshManager::update_svm_attributes(Device *device, DeviceScene *dscene, Sce if(attr_map_stride == 0) return; - + /* create attribute map */ uint4 *attr_map = dscene->attributes_map.resize(attr_map_stride*scene->objects.size()); memset(attr_map, 0, dscene->attributes_map.size()*sizeof(uint)); @@ -1564,6 +1823,8 @@ void MeshManager::device_update_bvh(Device *device, DeviceScene *dscene, Scene * bparams.use_spatial_split = scene->params.use_bvh_spatial_split; bparams.use_unaligned_nodes = dscene->data.bvh.have_curves && scene->params.use_bvh_unaligned_nodes; + bparams.num_motion_triangle_steps = scene->params.num_bvh_time_steps; + bparams.num_motion_curve_steps = scene->params.num_bvh_time_steps; delete bvh; bvh = BVH::create(bparams, scene->objects); @@ -1946,14 +2207,14 @@ bool Mesh::need_attribute(Scene *scene, AttributeStandard std) { if(std == ATTR_STD_NONE) return false; - + if(scene->need_global_attribute(std)) return true; foreach(Shader *shader, used_shaders) if(shader->attributes.find(std)) return true; - + return false; } @@ -1965,9 +2226,8 @@ bool Mesh::need_attribute(Scene * /*scene*/, ustring name) foreach(Shader *shader, used_shaders) if(shader->attributes.find(name)) return true; - + return false; } CCL_NAMESPACE_END - diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index c0310f45840..5f33e30eac2 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -31,6 +31,7 @@ CCL_NAMESPACE_BEGIN +class Attribute; class BVH; class Device; class DeviceScene; @@ -54,11 +55,27 @@ public: int v[3]; void bounds_grow(const float3 *verts, BoundBox& bounds) const; + + void motion_verts(const float3 *verts, + const float3 *vert_steps, + size_t num_verts, + size_t num_steps, + float time, + float3 r_verts[3]) const; + + void verts_for_step(const float3 *verts, + const float3 *vert_steps, + size_t num_verts, + size_t num_steps, + size_t step, + float3 r_verts[3]) const; }; Triangle get_triangle(size_t i) const { - Triangle tri = {{triangles[i*3 + 0], triangles[i*3 + 1], triangles[i*3 + 2]}}; + Triangle tri = {{triangles[i*3 + 0], + triangles[i*3 + 1], + triangles[i*3 + 2]}}; return tri; } @@ -78,11 +95,48 @@ public: const float3 *curve_keys, const float *curve_radius, BoundBox& bounds) const; + void bounds_grow(float4 keys[4], BoundBox& bounds) const; void bounds_grow(const int k, const float3 *curve_keys, const float *curve_radius, const Transform& aligned_space, BoundBox& bounds) const; + + void motion_keys(const float3 *curve_keys, + const float *curve_radius, + const float3 *key_steps, + size_t num_curve_keys, + size_t num_steps, + float time, + size_t k0, size_t k1, + float4 r_keys[2]) const; + void cardinal_motion_keys(const float3 *curve_keys, + const float *curve_radius, + const float3 *key_steps, + size_t num_curve_keys, + size_t num_steps, + float time, + size_t k0, size_t k1, + size_t k2, size_t k3, + float4 r_keys[4]) const; + + void keys_for_step(const float3 *curve_keys, + const float *curve_radius, + const float3 *key_steps, + size_t num_curve_keys, + size_t num_steps, + size_t step, + size_t k0, size_t k1, + float4 r_keys[2]) const; + void cardinal_keys_for_step(const float3 *curve_keys, + const float *curve_radius, + const float3 *key_steps, + size_t num_curve_keys, + size_t num_steps, + size_t step, + size_t k0, size_t k1, + size_t k2, size_t k3, + float4 r_keys[4]) const; }; Curve get_curve(size_t i) const diff --git a/intern/cycles/render/mesh_subdivision.cpp b/intern/cycles/render/mesh_subdivision.cpp index 913c3c74b42..57c76a9f1c8 100644 --- a/intern/cycles/render/mesh_subdivision.cpp +++ b/intern/cycles/render/mesh_subdivision.cpp @@ -92,7 +92,7 @@ namespace Far { if(vert_edges.size() == 2) { float sharpness = refiner.getLevel(0).getEdgeSharpness(vert_edges[0]); - sharpness = min(sharpness, refiner.getLevel(0).getEdgeSharpness(vert_edges[1])); + sharpness = ccl::min(sharpness, refiner.getLevel(0).getEdgeSharpness(vert_edges[1])); setBaseVertexSharpness(refiner, i, sharpness); } diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index c7f37a13fba..13b149eddfa 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -263,7 +263,8 @@ ImageTextureNode::~ImageTextureNode() image_manager->remove_image(filename.string(), builtin_data, interpolation, - extension); + extension, + use_alpha); } } @@ -462,7 +463,8 @@ EnvironmentTextureNode::~EnvironmentTextureNode() image_manager->remove_image(filename.string(), builtin_data, interpolation, - EXTENSION_REPEAT); + EXTENSION_REPEAT, + use_alpha); } } @@ -1381,7 +1383,8 @@ PointDensityTextureNode::~PointDensityTextureNode() image_manager->remove_image(filename.string(), builtin_data, interpolation, - EXTENSION_CLIP); + EXTENSION_CLIP, + true); } } @@ -3027,6 +3030,8 @@ NODE_DEFINE(LightPathNode) SOCKET_OUT_FLOAT(is_volume_scatter_ray, "Is Volume Scatter Ray"); SOCKET_OUT_FLOAT(ray_length, "Ray Length"); SOCKET_OUT_FLOAT(ray_depth, "Ray Depth"); + SOCKET_OUT_FLOAT(diffuse_depth, "Diffuse Depth"); + SOCKET_OUT_FLOAT(glossy_depth, "Glossy Depth"); SOCKET_OUT_FLOAT(transparent_depth, "Transparent Depth"); SOCKET_OUT_FLOAT(transmission_depth, "Transmission Depth"); @@ -3093,6 +3098,16 @@ void LightPathNode::compile(SVMCompiler& compiler) compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_depth, compiler.stack_assign(out)); } + out = output("Diffuse Depth"); + if(!out->links.empty()) { + compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_diffuse, compiler.stack_assign(out)); + } + + out = output("Glossy Depth"); + if(!out->links.empty()) { + compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_glossy, compiler.stack_assign(out)); + } + out = output("Transparent Depth"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_transparent, compiler.stack_assign(out)); diff --git a/intern/cycles/render/object.cpp b/intern/cycles/render/object.cpp index 8b8b988b969..8342f376836 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/render/object.cpp @@ -166,7 +166,7 @@ void Object::apply_transform(bool apply_to_motion) float3 c0 = transform_get_column(&tfm, 0); float3 c1 = transform_get_column(&tfm, 1); float3 c2 = transform_get_column(&tfm, 2); - float scalar = pow(fabsf(dot(cross(c0, c1), c2)), 1.0f/3.0f); + float scalar = powf(fabsf(dot(cross(c0, c1), c2)), 1.0f/3.0f); /* apply transform to curve keys */ for(size_t i = 0; i < mesh->curve_keys.size(); i++) { @@ -410,7 +410,7 @@ void ObjectManager::device_update_object_transform(UpdateObejctTransformState *s /* Object flag. */ if(ob->use_holdout) { - flag |= SD_HOLDOUT_MASK; + flag |= SD_OBJECT_HOLDOUT_MASK; } state->object_flag[object_index] = flag; @@ -716,9 +716,9 @@ void ObjectManager::apply_static_transforms(DeviceScene *dscene, Scene *scene, u if(progress.get_cancel()) return; } - object_flag[i] |= SD_TRANSFORM_APPLIED; + object_flag[i] |= SD_OBJECT_TRANSFORM_APPLIED; if(object->mesh->transform_negative_scaled) - object_flag[i] |= SD_NEGATIVE_SCALE_APPLIED; + object_flag[i] |= SD_OBJECT_NEGATIVE_SCALE_APPLIED; } else have_instancing = true; diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index df9363cc768..8768682043f 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -143,6 +143,7 @@ public: } bvh_type; bool use_bvh_spatial_split; bool use_bvh_unaligned_nodes; + int num_bvh_time_steps; bool use_qbvh; bool persistent_data; int texture_limit; @@ -153,6 +154,7 @@ public: bvh_type = BVH_DYNAMIC; use_bvh_spatial_split = false; use_bvh_unaligned_nodes = true; + num_bvh_time_steps = 0; use_qbvh = false; persistent_data = false; texture_limit = 0; @@ -163,6 +165,7 @@ public: && bvh_type == params.bvh_type && use_bvh_spatial_split == params.use_bvh_spatial_split && use_bvh_unaligned_nodes == params.use_bvh_unaligned_nodes + && num_bvh_time_steps == params.num_bvh_time_steps && use_qbvh == params.use_qbvh && persistent_data == params.persistent_data && texture_limit == params.texture_limit); } diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 45cc860c435..dccb0e66100 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -496,6 +496,8 @@ void Session::release_tile(RenderTile& rtile) { thread_scoped_lock tile_lock(tile_mutex); + progress.add_finished_tile(); + bool delete_tile; if(tile_manager.return_tile(rtile.tile_index, delete_tile)) { @@ -994,11 +996,11 @@ void Session::update_status_time(bool show_pause, bool show_done) substatus = string_printf("Path Tracing Tile %d/%d", tile, num_tiles); - if(device->show_samples() || (is_cpu && is_last_tile)) - { + if(device->show_samples() || (is_cpu && is_last_tile)) { /* Some devices automatically support showing the sample number: * - CUDADevice - * - OpenCLDevice when using the megakernel (the split kernel renders multiple samples at the same time, so the current sample isn't really defined) + * - OpenCLDevice when using the megakernel (the split kernel renders multiple + * samples at the same time, so the current sample isn't really defined) * - CPUDevice when using one thread * For these devices, the current sample is always shown. * diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp index 83c31a9a609..8218c9b433f 100644 --- a/intern/cycles/render/tile.cpp +++ b/intern/cycles/render/tile.cpp @@ -177,7 +177,7 @@ void TileManager::set_samples(int num_samples_) divider >>= 1; } - state.total_pixel_samples = pixel_samples + get_num_effective_samples() * params.width*params.height; + state.total_pixel_samples = pixel_samples + (uint64_t)get_num_effective_samples() * params.width*params.height; if(schedule_denoising) { state.total_pixel_samples += params.width*params.height; } diff --git a/intern/cycles/test/CMakeLists.txt b/intern/cycles/test/CMakeLists.txt index c60a99903e4..85358e3b64b 100644 --- a/intern/cycles/test/CMakeLists.txt +++ b/intern/cycles/test/CMakeLists.txt @@ -54,12 +54,23 @@ if(WITH_CYCLES_OPENSUBDIV) endif() list(APPEND ALL_CYCLES_LIBRARIES ${BOOST_LIBRARIES} + ${PNG_LIBRARIES} + ${JPEG_LIBRARIES} + ${ZLIB_LIBRARIES} + ${TIFF_LIBRARY} + ${OPENIMAGEIO_LIBRARIES} + ${OPENEXR_LIBRARIES} ) include_directories(${INC}) -link_directories(${BOOST_LIBPATH}) -link_directories(${OPENIMAGEIO_LIBPATH}) +link_directories(${OPENIMAGEIO_LIBPATH} + ${BOOST_LIBPATH} + ${PNG_LIBPATH} + ${JPEG_LIBPATH} + ${ZLIB_LIBPATH} + ${TIFF_LIBPATH} + ${OPENEXR_LIBPATH}) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINKFLAGS}") set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${PLATFORM_LINKFLAGS_DEBUG}") diff --git a/intern/cycles/util/util_boundbox.h b/intern/cycles/util/util_boundbox.h index 599222da9c5..dfe4977aef3 100644 --- a/intern/cycles/util/util_boundbox.h +++ b/intern/cycles/util/util_boundbox.h @@ -25,8 +25,6 @@ #include "util_transform.h" #include "util_types.h" -using namespace std; - CCL_NAMESPACE_BEGIN /* 3D BoundBox */ diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/util_math.h index db4946fe5b0..64cf8b24168 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/util_math.h @@ -22,6 +22,11 @@ * Basic math functions on scalar and vector types. This header is used by * both the kernel code when compiled as C++, and other C++ non-kernel code. */ +#ifndef __KERNEL_GPU__ +# include <cmath> +#endif + + #ifndef __KERNEL_OPENCL__ #include <float.h> @@ -97,6 +102,9 @@ ccl_device_inline float fminf(float a, float b) #ifndef __KERNEL_GPU__ +using std::isfinite; +using std::isnan; + ccl_device_inline int abs(int x) { return (x > 0)? x: -x; @@ -1262,6 +1270,20 @@ ccl_device_inline float __uint_as_float(uint i) return u.f; } +/* Versions of functions which are safe for fast math. */ +ccl_device_inline bool isnan_safe(float f) +{ + unsigned int x = __float_as_uint(f); + return (x << 1) > 0xff000000u; +} + +ccl_device_inline bool isfinite_safe(float f) +{ + /* By IEEE 754 rule, 2*Inf equals Inf */ + unsigned int x = __float_as_uint(f); + return (f == f) && (x == 0 || (f != 2.0f*f)); +} + /* Interpolation */ template<class A, class B> A lerp(const A& a, const A& b, const B& t) diff --git a/intern/cycles/util/util_version.h b/intern/cycles/util/util_version.h index 186a177d9d3..d609c739ac7 100644 --- a/intern/cycles/util/util_version.h +++ b/intern/cycles/util/util_version.h @@ -21,9 +21,9 @@ CCL_NAMESPACE_BEGIN -#define CYCLES_VERSION_MAJOR 1 -#define CYCLES_VERSION_MINOR 7 -#define CYCLES_VERSION_PATCH 0 +#define CYCLES_VERSION_MAJOR 1 +#define CYCLES_VERSION_MINOR 8 +#define CYCLES_VERSION_PATCH 1 #define CYCLES_MAKE_VERSION_STRING2(a,b,c) #a "." #b "." #c #define CYCLES_MAKE_VERSION_STRING(a,b,c) CYCLES_MAKE_VERSION_STRING2(a,b,c) @@ -22,7 +22,7 @@ set MUST_CLEAN= set NOBUILD= set TARGET= set WINDOWS_ARCH= - +set TESTS_CMAKE_ARGS= :argv_loop if NOT "%1" == "" ( @@ -35,6 +35,8 @@ if NOT "%1" == "" ( if "%1" == "debug" ( set BUILD_TYPE=Debug REM Build Configurations + ) else if "%1" == "with_tests" ( + set TESTS_CMAKE_ARGS=-DWITH_GTESTS=On ) else if "%1" == "full" ( set TARGET=Full set BUILD_CMAKE_ARGS=%BUILD_CMAKE_ARGS% ^ @@ -129,9 +131,6 @@ if "%BUILD_ARCH%"=="x64" ( ) -set BUILD_DIR=%BUILD_DIR%_%TARGET%_%BUILD_ARCH%_vc%BUILD_VS_VER%_%BUILD_TYPE% - - if "%target%"=="Release" ( rem for vc12 check for both cuda 7.5 and 8 if "%CUDA_PATH%"=="" ( @@ -173,7 +172,7 @@ where /Q msbuild if %ERRORLEVEL% NEQ 0 ( if "%BUILD_VS_VER%"=="12" ( rem vs12 not found, try vs14 - echo Visual Studio 2012 not found, trying Visual Studio 2015. + echo Visual Studio 2013 not found, trying Visual Studio 2015. set BUILD_VS_VER=14 set BUILD_VS_YEAR=2015 goto DetectMSVC @@ -184,6 +183,11 @@ if %ERRORLEVEL% NEQ 0 ( goto EOF ) ) + + +set BUILD_DIR=%BUILD_DIR%_%TARGET%_%BUILD_ARCH%_vc%BUILD_VS_VER%_%BUILD_TYPE% + + where /Q cmake if %ERRORLEVEL% NEQ 0 ( echo Error: "CMake" command not in the PATH. @@ -202,7 +206,7 @@ if "%TARGET%"=="" ( goto HELP ) -set BUILD_CMAKE_ARGS=%BUILD_CMAKE_ARGS% -G "Visual Studio %BUILD_VS_VER% %BUILD_VS_YEAR%%WINDOWS_ARCH%" +set BUILD_CMAKE_ARGS=%BUILD_CMAKE_ARGS% -G "Visual Studio %BUILD_VS_VER% %BUILD_VS_YEAR%%WINDOWS_ARCH%" %TESTS_CMAKE_ARGS% if NOT EXIST %BUILD_DIR%\nul ( mkdir %BUILD_DIR% ) @@ -282,6 +286,7 @@ goto EOF echo - showhash ^(Show git hashes of source tree^) echo. echo Configuration options + echo - with_tests ^(enable building unit tests^) echo - debug ^(Build an unoptimized debuggable build^) echo - packagename [newname] ^(override default cpack package name^) echo - x86 ^(override host autodetect and build 32 bit code^) diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg index b54cadd40bc..e9c114ba1bd 100644 --- a/release/datafiles/blender_icons.svg +++ b/release/datafiles/blender_icons.svg @@ -14,7 +14,7 @@ height="640" id="svg2" sodipodi:version="0.32" - inkscape:version="0.91 r13725" + inkscape:version="0.91 r" version="1.0" sodipodi:docname="blender_icons.svg" inkscape:output_extension="org.inkscape.output.svg.inkscape" @@ -28201,7 +28201,7 @@ xlink:href="#linearGradient37542-29" id="linearGradient17610" gradientUnits="userSpaceOnUse" - gradientTransform="matrix(-1,0,0,1,461.01011,-167)" + gradientTransform="matrix(-1,0,0,1,865.01833,131.0342)" x1="392.0101" y1="224.99998" x2="392.0101" @@ -28263,7 +28263,7 @@ xlink:href="#linearGradient37542-29-7" id="linearGradient17610-0" gradientUnits="userSpaceOnUse" - gradientTransform="matrix(-1,0,0,1,461.01011,-167)" + gradientTransform="translate(128.00098,130.98191)" x1="392.0101" y1="224.99998" x2="392.0101" @@ -28402,7 +28402,7 @@ x2="228.5468" y1="118.91647" x1="228.5468" - gradientTransform="matrix(1.180548,0,0,0.90042534,265.27784,265.13062)" + gradientTransform="matrix(1.180548,0,0,0.90042534,265.83288,265.61628)" gradientUnits="userSpaceOnUse" id="linearGradient17838" xlink:href="#linearGradient319-36-40-2" @@ -28433,7 +28433,7 @@ x2="228.5468" y1="118.91647" x1="228.5468" - gradientTransform="matrix(1.180548,0,0,0.90042534,223.26222,270.47438)" + gradientTransform="matrix(1.180548,0,0,0.90042534,223.81726,270.99473)" gradientUnits="userSpaceOnUse" id="linearGradient17872" xlink:href="#linearGradient319-36-40-2-4" @@ -29073,7 +29073,7 @@ xlink:href="#linearGradient27854-0-6-9" id="linearGradient17162" gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0,1,-1,0,782.48614,-14.46331)" + gradientTransform="matrix(0,1,-1,0,783.04118,-13.977664)" x1="388.86502" y1="244.02" x2="391.43173" @@ -29083,7 +29083,7 @@ xlink:href="#linearGradient37542-29-7-8" id="linearGradient17165" gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0,1,-1,0,782.48614,-14.46331)" + gradientTransform="matrix(0,1,-1,0,783.04118,-13.977664)" x1="368.97806" y1="249.99998" x2="393.85385" @@ -29113,7 +29113,7 @@ xlink:href="#linearGradient37542-29-1" id="linearGradient17185" gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0,-1,-1,0,740.48614,764.46331)" + gradientTransform="matrix(0,-1,-1,0,741.04118,764.98366)" x1="409.93588" y1="249.99998" x2="385.11514" @@ -31349,17 +31349,17 @@ objecttolerance="10000" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="1.274018" - inkscape:cx="519.70993" - inkscape:cy="325.90484" + inkscape:zoom="14.413868" + inkscape:cx="480.24726" + inkscape:cy="269.95478" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" - inkscape:window-width="1920" - inkscape:window-height="1005" - inkscape:window-x="-2" - inkscape:window-y="27" - inkscape:snap-nodes="false" + inkscape:window-width="1680" + inkscape:window-height="1020" + inkscape:window-x="0" + inkscape:window-y="30" + inkscape:snap-nodes="true" inkscape:snap-bbox="true" showguides="true" inkscape:guide-bbox="true" @@ -31369,14 +31369,16 @@ inkscape:snap-intersection-grid-guide="false" inkscape:window-maximized="1" inkscape:bbox-paths="false" - inkscape:snap-global="false" + inkscape:snap-global="true" inkscape:snap-bbox-midpoints="false" - inkscape:snap-grids="true" + inkscape:snap-grids="false" inkscape:snap-to-guides="false" inkscape:snap-page="false" units="pt" inkscape:snap-center="false" - inkscape:snap-object-midpoints="true"> + inkscape:snap-object-midpoints="true" + inkscape:snap-midpoints="false" + inkscape:snap-others="false"> <inkscape:grid type="xygrid" id="grid17394" @@ -88001,37 +88003,37 @@ <g style="display:inline;enable-background:new" id="ICON_COLLAPSEMENU" - transform="translate(279.8665,506.92392)"> + transform="translate(280,508)"> <rect y="111" x="103" height="16" width="16" id="rect24489-7-4" - style="opacity:0;fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.79999995;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> + style="display:inline;overflow:visible;visibility:visible;opacity:0;fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.79999995;marker:none;enable-background:accumulate" /> <rect style="fill:#ececec;fill-opacity:1;stroke:#141414;stroke-width:0.79452544;stroke-opacity:1" id="rect29842" - width="11.816368" - height="2.1883197" - x="105.18671" - y="-116.88043" + width="11.209318" + height="2.1883163" + x="105.39484" + y="-116.60292" transform="scale(1,-1)" /> <rect - style="fill:#ececec;fill-opacity:1;stroke:#141414;stroke-width:0.79452544;stroke-opacity:1;display:inline;enable-background:new" + style="display:inline;fill:#ececec;fill-opacity:1;stroke:#141414;stroke-width:0.79452544;stroke-opacity:1;enable-background:new" id="rect29842-4" - width="11.816368" - height="2.1883197" - x="105.31538" - y="-120.80865" + width="11.191971" + height="2.2056611" + x="105.41944" + y="-120.61786" transform="scale(1,-1)" /> <rect - style="fill:#ececec;fill-opacity:1;stroke:#141414;stroke-width:0.79452544;stroke-opacity:1;display:inline;enable-background:new" + style="display:inline;fill:#ececec;fill-opacity:1;stroke:#141414;stroke-width:0.79500002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;enable-background:new" id="rect29842-4-5" - width="11.816368" - height="2.1883197" - x="105.41832" - y="-124.71391" + width="11.22666" + height="2.2056642" + x="105.38363" + y="-124.60985" transform="scale(1,-1)" /> </g> <g @@ -89360,17 +89362,18 @@ y="69" /> <g id="g17605" - transform="translate(-1.5467961,-0.48613592)"> + transform="translate(-0.99177519,0.03419629)"> <path - sodipodi:nodetypes="ccccccccccc" + style="fill:url(#linearGradient17610);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 472.51758,370.53516 -1.04688,0.0312 0.0371,9.96875 1.00977,0 3.95117,-3.70704 -5.3e-4,3.72852 3.1119,0 0,-10.01758 -3.1119,0 5.3e-4,3.73242 z" + transform="translate(-404.00822,-298.0342)" + id="path11011-6" inkscape:connector-curvature="0" - d="m 72.839729,82.521675 2.731705,0 0,-10.016275 -2.731705,0 L 72.84,76.59374 68.51011,72.5 67.4632,72.53125 67.5,82.49999 68.51011,82.5 72.84,78.43749 z" - style="fill:url(#linearGradient17610);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" - id="path11011-6" /> + sodipodi:nodetypes="ccccccccccc" /> <path sodipodi:nodetypes="ccc" style="fill:none;stroke:url(#linearGradient17612);stroke-width:0.91056824px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" - d="m 73.7503,81.408784 0,-7.99274 0.910568,-6.3e-5" + d="m 73.368723,81.408784 0,-7.99274 1.292145,-6.3e-5" id="path10830-6" inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\BLENDER ICONSET\Kopia blender\.blender\icons\jendrzych's iconset.png" inkscape:export-xdpi="90" @@ -89397,17 +89400,18 @@ y="69" /> <g id="g17605-3" - transform="translate(-1.5467961,-0.48613592)"> + transform="translate(-1.9392553,-0.11820549)"> <path - sodipodi:nodetypes="ccccccccccc" + style="fill:url(#linearGradient17610-0);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 520.5,370.48242 -4.01758,3.80078 -6.6e-4,-3.79492 -3.04231,0 0,10.01563 3.04231,0 6.6e-4,-3.79297 4.01758,3.77148 1.01172,0 0.0371,-9.96875 z" + transform="matrix(-1,0,0,1,589.01109,-297.98191)" + id="path11011-6-2" inkscape:connector-curvature="0" - d="m 72.839729,82.521675 2.731705,0 0,-10.016275 -2.731705,0 L 72.84,76.59374 68.51011,72.5 67.4632,72.53125 67.5,82.49999 68.51011,82.5 72.84,78.43749 z" - style="fill:url(#linearGradient17610-0);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" - id="path11011-6-2" /> + sodipodi:nodetypes="ccccccccccc" /> <path sodipodi:nodetypes="ccc" style="fill:none;stroke:url(#linearGradient17612-5);stroke-width:0.91056824px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" - d="m 74.660868,81.408784 0,-7.99274 -0.910568,-6.3e-5" + d="m 74.660868,81.408784 0,-7.99274 -1.220453,-6.3e-5" id="path10830-6-2" inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\BLENDER ICONSET\Kopia blender\.blender\icons\jendrzych's iconset.png" inkscape:export-xdpi="90" @@ -89432,16 +89436,16 @@ y="-546" transform="matrix(0,-1,-1,0,0,0)" /> <path + style="fill:url(#linearGradient17165);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 533.51953,371.46094 0,3.04042 3.79492,5.9e-4 -3.77343,4.01953 0,1.01172 9.96875,0.0352 0.0312,-1.04688 -3.80274,-4.01953 3.79688,-5.9e-4 0,-3.04042 z" id="path11011-6-2-1" - style="fill:url(#linearGradient17165);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" - d="m 532.96447,373.70707 0,-2.7317 10.01627,0 0,2.7317 -4.08834,-2.7e-4 4.09374,4.32989 -0.0312,1.04691 -9.96874,-0.0368 -1e-5,-1.01011 4.06251,-4.32989 z" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccccc" /> <path inkscape:connector-curvature="0" sodipodi:nodetypes="ccc" id="path11013-5-7-1" - d="m 541.73615,378.0468 -3.75,-4 -3.75,4" + d="m 542.29119,378.53246 -3.75,-4 -3.75,4" style="fill:none;stroke:url(#linearGradient17162);stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> <path inkscape:connector-curvature="0" @@ -89449,8 +89453,8 @@ inkscape:export-xdpi="90" inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\BLENDER ICONSET\Kopia blender\.blender\icons\jendrzych's iconset.png" id="path10830-6-2-8" - d="m 542.11634,371.83103 -8.26386,0 -6e-5,0.90043" - style="fill:none;stroke:url(#linearGradient17838);stroke-width:0.92071104px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;display:inline;enable-background:new" + d="m 542.67138,372.31669 -8.26386,0 -6e-5,1.20843" + style="display:inline;fill:none;stroke:url(#linearGradient17838);stroke-width:0.92071104px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;enable-background:new" sodipodi:nodetypes="ccc" /> </g> <g @@ -89464,16 +89468,16 @@ y="-504" transform="matrix(0,1,-1,0,0,0)" /> <path + style="fill:url(#linearGradient17185);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 501.50977,371.4375 -9.96875,0.0352 0,1.01172 3.69336,3.93554 -3.71485,-0.005 0,3.13033 10.01563,0 0,-3.13033 -3.7168,0.007 3.72266,-3.9375 z" id="path11011-6-8" - style="fill:url(#linearGradient17185);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.80000001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" - d="m 490.96446,376.29293 0,2.7317 10.01628,0 0,-2.7317 -4.08834,2.7e-4 4.09374,-4.32989 -0.0312,-1.04691 -9.96874,0.0368 -10e-6,1.01011 4.06251,4.32989 z" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccccccccc" /> <path inkscape:connector-curvature="0" sodipodi:nodetypes="cc" id="path11013-5-6" - d="m 492.23615,371.9532 7.5,0" + d="m 492.79119,372.47355 7.5,0" style="fill:none;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" /> <path inkscape:connector-curvature="0" @@ -89481,8 +89485,8 @@ inkscape:export-xdpi="90" inkscape:export-filename="C:\Documents and Settings\Tata\Pulpit\BLENDER ICONSET\Kopia blender\.blender\icons\jendrzych's iconset.png" id="path10830-6-2-8-8" - d="m 500.10071,377.17478 -8.26386,0 -6e-5,0.90043" - style="fill:none;stroke:url(#linearGradient17872);stroke-width:0.92071104px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;display:inline;enable-background:new" + d="m 500.65575,377.29722 -8.26386,0 -6e-5,1.29834" + style="display:inline;fill:none;stroke:url(#linearGradient17872);stroke-width:0.92071104px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;enable-background:new" sodipodi:nodetypes="ccc" /> </g> <g diff --git a/release/datafiles/blender_icons16/icon16_collapsemenu.dat b/release/datafiles/blender_icons16/icon16_collapsemenu.dat Binary files differindex fd16fadcc8b..246afa11c6c 100644 --- a/release/datafiles/blender_icons16/icon16_collapsemenu.dat +++ b/release/datafiles/blender_icons16/icon16_collapsemenu.dat diff --git a/release/datafiles/blender_icons16/icon16_tria_down_bar.dat b/release/datafiles/blender_icons16/icon16_tria_down_bar.dat Binary files differindex 3b734da490a..191f2075b1a 100644 --- a/release/datafiles/blender_icons16/icon16_tria_down_bar.dat +++ b/release/datafiles/blender_icons16/icon16_tria_down_bar.dat diff --git a/release/datafiles/blender_icons16/icon16_tria_left_bar.dat b/release/datafiles/blender_icons16/icon16_tria_left_bar.dat Binary files differindex 15826007e7b..e1ea5764367 100644 --- a/release/datafiles/blender_icons16/icon16_tria_left_bar.dat +++ b/release/datafiles/blender_icons16/icon16_tria_left_bar.dat diff --git a/release/datafiles/blender_icons16/icon16_tria_right_bar.dat b/release/datafiles/blender_icons16/icon16_tria_right_bar.dat Binary files differindex 75b180afbd7..ce9c3f78ad7 100644 --- a/release/datafiles/blender_icons16/icon16_tria_right_bar.dat +++ b/release/datafiles/blender_icons16/icon16_tria_right_bar.dat diff --git a/release/datafiles/blender_icons16/icon16_tria_up_bar.dat b/release/datafiles/blender_icons16/icon16_tria_up_bar.dat Binary files differindex b0730cafdd4..e51757d2650 100644 --- a/release/datafiles/blender_icons16/icon16_tria_up_bar.dat +++ b/release/datafiles/blender_icons16/icon16_tria_up_bar.dat diff --git a/release/datafiles/blender_icons32/icon32_collapsemenu.dat b/release/datafiles/blender_icons32/icon32_collapsemenu.dat Binary files differindex d84dd843436..788b4009542 100644 --- a/release/datafiles/blender_icons32/icon32_collapsemenu.dat +++ b/release/datafiles/blender_icons32/icon32_collapsemenu.dat diff --git a/release/datafiles/blender_icons32/icon32_tria_down_bar.dat b/release/datafiles/blender_icons32/icon32_tria_down_bar.dat Binary files differindex 7a00e79b711..ef6e86ba154 100644 --- a/release/datafiles/blender_icons32/icon32_tria_down_bar.dat +++ b/release/datafiles/blender_icons32/icon32_tria_down_bar.dat diff --git a/release/datafiles/blender_icons32/icon32_tria_left_bar.dat b/release/datafiles/blender_icons32/icon32_tria_left_bar.dat Binary files differindex 591b2e2b1bd..a1d48baf971 100644 --- a/release/datafiles/blender_icons32/icon32_tria_left_bar.dat +++ b/release/datafiles/blender_icons32/icon32_tria_left_bar.dat diff --git a/release/datafiles/blender_icons32/icon32_tria_right_bar.dat b/release/datafiles/blender_icons32/icon32_tria_right_bar.dat Binary files differindex 1dae635d600..4f687537658 100644 --- a/release/datafiles/blender_icons32/icon32_tria_right_bar.dat +++ b/release/datafiles/blender_icons32/icon32_tria_right_bar.dat diff --git a/release/datafiles/blender_icons32/icon32_tria_up_bar.dat b/release/datafiles/blender_icons32/icon32_tria_up_bar.dat Binary files differindex 1c7b3072f31..e6c5d5561cd 100644 --- a/release/datafiles/blender_icons32/icon32_tria_up_bar.dat +++ b/release/datafiles/blender_icons32/icon32_tria_up_bar.dat diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject dc166057192ea882b5cc70484d4c8bacd7cb41b +Subproject c93ed11a47b3016cf59711ec16de2e2e94c30e9 diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 06dad53c80801e0e0919f086040e3d9c31bbd0a +Subproject 371960484a38fc64e0a2635170a41a0d8ab2f6b diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib -Subproject 04af69be141a5757fc60b44cc1a5b72db524af3 +Subproject a8515cfdfe9a98127b592f36fcbe51b7e23b969 diff --git a/release/scripts/startup/bl_operators/freestyle.py b/release/scripts/startup/bl_operators/freestyle.py index edda92284d2..fe3b00d54cd 100644 --- a/release/scripts/startup/bl_operators/freestyle.py +++ b/release/scripts/startup/bl_operators/freestyle.py @@ -62,31 +62,69 @@ class SCENE_OT_freestyle_fill_range_by_selection(bpy.types.Operator): m = linestyle.alpha_modifiers[self.name] else: m = linestyle.thickness_modifiers[self.name] - # Find the source object + # Find the reference object if m.type == 'DISTANCE_FROM_CAMERA': - source = scene.camera + ref = scene.camera + matrix_to_camera = ref.matrix_world.inverted() elif m.type == 'DISTANCE_FROM_OBJECT': if m.target is None: self.report({'ERROR'}, "Target object not specified") return {'CANCELLED'} - source = m.target + ref = m.target + target_location = ref.location else: self.report({'ERROR'}, "Unexpected modifier type: " + m.type) return {'CANCELLED'} - # Find selected mesh objects - selection = [ob for ob in scene.objects if ob.select and ob.type == 'MESH' and ob.name != source.name] - if selection: - # Compute the min/max distance between selected mesh objects and the source + # Find selected vertices in editmesh + ob = bpy.context.active_object + if ob.type == 'MESH' and ob.mode == 'EDIT' and ob.name != ref.name: + bpy.ops.object.mode_set(mode='OBJECT') + selected_verts = [v for v in bpy.context.active_object.data.vertices if v.select] + bpy.ops.object.mode_set(mode='EDIT') + # Compute the min/max distance from the reference to mesh vertices min_dist = sys.float_info.max max_dist = -min_dist - for ob in selection: - for vert in ob.data.vertices: - dist = (ob.matrix_world * vert.co - source.location).length + if m.type == 'DISTANCE_FROM_CAMERA': + ob_to_cam = matrix_to_camera * ob.matrix_world + for vert in selected_verts: + # dist in the camera space + dist = (ob_to_cam * vert.co).length + min_dist = min(dist, min_dist) + max_dist = max(dist, max_dist) + elif m.type == 'DISTANCE_FROM_OBJECT': + for vert in selected_verts: + # dist in the world space + dist = (ob.matrix_world * vert.co - target_location).length min_dist = min(dist, min_dist) max_dist = max(dist, max_dist) # Fill the Range Min/Max entries with the computed distances m.range_min = min_dist m.range_max = max_dist + return {'FINISHED'} + # Find selected mesh objects + selection = [ob for ob in scene.objects if ob.select and ob.type == 'MESH' and ob.name != ref.name] + if selection: + # Compute the min/max distance from the reference to mesh vertices + min_dist = sys.float_info.max + max_dist = -min_dist + if m.type == 'DISTANCE_FROM_CAMERA': + for ob in selection: + ob_to_cam = matrix_to_camera * ob.matrix_world + for vert in ob.data.vertices: + # dist in the camera space + dist = (ob_to_cam * vert.co).length + min_dist = min(dist, min_dist) + max_dist = max(dist, max_dist) + elif m.type == 'DISTANCE_FROM_OBJECT': + for ob in selection: + for vert in ob.data.vertices: + # dist in the world space + dist = (ob.matrix_world * vert.co - target_location).length + min_dist = min(dist, min_dist) + max_dist = max(dist, max_dist) + # Fill the Range Min/Max entries with the computed distances + m.range_min = min_dist + m.range_max = max_dist return {'FINISHED'} diff --git a/release/scripts/startup/bl_ui/properties_animviz.py b/release/scripts/startup/bl_ui/properties_animviz.py index 11081232f12..046b5eb2aa5 100644 --- a/release/scripts/startup/bl_ui/properties_animviz.py +++ b/release/scripts/startup/bl_ui/properties_animviz.py @@ -86,8 +86,12 @@ class MotionPathButtonsPanel: col = split.column() col.label(text="Show:") col.prop(mps, "show_frame_numbers", text="Frame Numbers") + if mpath is not None: + col.prop(mpath, "lines", text='Lines') + col.prop(mpath, "line_thickness", text='Thickness') col = split.column() + col.label('') col.prop(mps, "show_keyframe_highlight", text="Keyframes") sub = col.column() sub.enabled = mps.show_keyframe_highlight @@ -95,6 +99,14 @@ class MotionPathButtonsPanel: sub.prop(mps, "show_keyframe_action_all", text="+ Non-Grouped Keyframes") sub.prop(mps, "show_keyframe_numbers", text="Keyframe Numbers") + # Customize path + if mpath is not None: + row = layout.row(align=True) + row.prop(mpath, "use_custom_color", text='', toggle=True, icon='COLOR') + sub = row.row(align=True) + sub.enabled = mpath.use_custom_color + sub.prop(mpath, "color", text='') + # FIXME: this panel still needs to be ported so that it will work correctly with animviz class OnionSkinButtonsPanel: diff --git a/release/scripts/startup/bl_ui/properties_game.py b/release/scripts/startup/bl_ui/properties_game.py index 98b7a76e541..ec061370fe5 100644 --- a/release/scripts/startup/bl_ui/properties_game.py +++ b/release/scripts/startup/bl_ui/properties_game.py @@ -469,8 +469,86 @@ class SceneButtonsPanel: bl_context = "scene" +class SCENE_PT_game_physics(SceneButtonsPanel, Panel): + bl_label = "Physics" + COMPAT_ENGINES = {'BLENDER_GAME'} + + @classmethod + def poll(cls, context): + scene = context.scene + return (scene.render.engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + gs = context.scene.game_settings + + layout.prop(gs, "physics_engine", text="Engine") + if gs.physics_engine != 'NONE': + layout.prop(gs, "physics_gravity", text="Gravity") + + split = layout.split() + + col = split.column() + col.label(text="Physics Steps:") + sub = col.column(align=True) + sub.prop(gs, "physics_step_max", text="Max") + sub.prop(gs, "physics_step_sub", text="Substeps") + col.prop(gs, "fps", text="FPS") + + col = split.column() + col.label(text="Logic Steps:") + col.prop(gs, "logic_step_max", text="Max") + + col = layout.column() + col.label(text="Physics Deactivation:") + sub = col.row(align=True) + sub.prop(gs, "deactivation_linear_threshold", text="Linear Threshold") + sub.prop(gs, "deactivation_angular_threshold", text="Angular Threshold") + sub = col.row() + sub.prop(gs, "deactivation_time", text="Time") + + col = layout.column() + col.prop(gs, "use_occlusion_culling", text="Occlusion Culling") + sub = col.column() + sub.active = gs.use_occlusion_culling + sub.prop(gs, "occlusion_culling_resolution", text="Resolution") + + else: + split = layout.split() + + col = split.column() + col.label(text="Physics Steps:") + col.prop(gs, "fps", text="FPS") + + col = split.column() + col.label(text="Logic Steps:") + col.prop(gs, "logic_step_max", text="Max") + + +class SCENE_PT_game_physics_obstacles(SceneButtonsPanel, Panel): + bl_label = "Obstacle Simulation" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_GAME'} + + @classmethod + def poll(cls, context): + scene = context.scene + return (scene.render.engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + + gs = context.scene.game_settings + + layout.prop(gs, "obstacle_simulation", text="Type") + if gs.obstacle_simulation != 'NONE': + layout.prop(gs, "level_height") + layout.prop(gs, "show_obstacle_simulation") + + class SCENE_PT_game_navmesh(SceneButtonsPanel, Panel): - bl_label = "Navigation mesh" + bl_label = "Navigation Mesh" bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_GAME'} @@ -484,7 +562,7 @@ class SCENE_PT_game_navmesh(SceneButtonsPanel, Panel): rd = context.scene.game_settings.recast_data - layout.operator("mesh.navmesh_make", text="Build navigation mesh") + layout.operator("mesh.navmesh_make", text="Build Navigation Mesh") col = layout.column() col.label(text="Rasterization:") @@ -656,83 +734,6 @@ class WORLD_PT_game_mist(WorldButtonsPanel, Panel): layout.prop(world.mist_settings, "intensity", text="Minimum Intensity") -class WORLD_PT_game_physics(WorldButtonsPanel, Panel): - bl_label = "Physics" - COMPAT_ENGINES = {'BLENDER_GAME'} - - @classmethod - def poll(cls, context): - scene = context.scene - return (scene.world and scene.render.engine in cls.COMPAT_ENGINES) - - def draw(self, context): - layout = self.layout - - gs = context.scene.game_settings - - layout.prop(gs, "physics_engine", text="Engine") - if gs.physics_engine != 'NONE': - layout.prop(gs, "physics_gravity", text="Gravity") - - split = layout.split() - - col = split.column() - col.label(text="Physics Steps:") - sub = col.column(align=True) - sub.prop(gs, "physics_step_max", text="Max") - sub.prop(gs, "physics_step_sub", text="Substeps") - col.prop(gs, "fps", text="FPS") - - col = split.column() - col.label(text="Logic Steps:") - col.prop(gs, "logic_step_max", text="Max") - - col = layout.column() - col.label(text="Physics Deactivation:") - sub = col.row(align=True) - sub.prop(gs, "deactivation_linear_threshold", text="Linear Threshold") - sub.prop(gs, "deactivation_angular_threshold", text="Angular Threshold") - sub = col.row() - sub.prop(gs, "deactivation_time", text="Time") - - col = layout.column() - col.prop(gs, "use_occlusion_culling", text="Occlusion Culling") - sub = col.column() - sub.active = gs.use_occlusion_culling - sub.prop(gs, "occlusion_culling_resolution", text="Resolution") - - else: - split = layout.split() - - col = split.column() - col.label(text="Physics Steps:") - col.prop(gs, "fps", text="FPS") - - col = split.column() - col.label(text="Logic Steps:") - col.prop(gs, "logic_step_max", text="Max") - - -class WORLD_PT_game_physics_obstacles(WorldButtonsPanel, Panel): - bl_label = "Obstacle simulation" - COMPAT_ENGINES = {'BLENDER_GAME'} - - @classmethod - def poll(cls, context): - scene = context.scene - return (scene.world and scene.render.engine in cls.COMPAT_ENGINES) - - def draw(self, context): - layout = self.layout - - gs = context.scene.game_settings - - layout.prop(gs, "obstacle_simulation", text="Type") - if gs.obstacle_simulation != 'NONE': - layout.prop(gs, "level_height") - layout.prop(gs, "show_obstacle_simulation") - - class DataButtonsPanel: bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' 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 f8ee7c9a851..4529c127839 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -115,6 +115,12 @@ class GreasePencilDrawingToolsPanel: row.operator("gpencil.draw", icon='LINE_DATA', text="Line").mode = 'DRAW_STRAIGHT' row.operator("gpencil.draw", icon='MESH_DATA', text="Poly").mode = 'DRAW_POLY' + col.separator() + + sub = col.column(align=True) + sub.operator("gpencil.blank_frame_add", icon='NEW') + sub.operator("gpencil.active_frames_delete_all", icon='X', text="Delete Frame(s)") + sub = col.column(align=True) sub.prop(context.tool_settings, "use_gpencil_additive_drawing", text="Additive Drawing") sub.prop(context.tool_settings, "use_gpencil_continuous_drawing", text="Continuous Drawing") @@ -227,12 +233,7 @@ class GreasePencilStrokeEditPanel: 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) @@ -247,7 +248,57 @@ class GreasePencilStrokeEditPanel: if is_3d_view: layout.separator() - layout.operator("gpencil.reproject") + layout.operator_menu_enum("gpencil.reproject", text="Reproject Strokes...", property="type") + + +class GreasePencilInterpolatePanel: + bl_space_type = 'VIEW_3D' + bl_label = "Interpolate" + bl_category = "Grease Pencil" + bl_region_type = 'TOOLS' + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + if context.gpencil_data is None: + return False + elif context.space_data.type != 'VIEW_3D': + return False + + gpd = context.gpencil_data + return bool(context.editable_gpencil_strokes) and bool(gpd.use_stroke_edit_mode) + + @staticmethod + def draw(self, context): + layout = self.layout + settings = context.tool_settings.gpencil_interpolate + + col = layout.column(align=True) + col.operator("gpencil.interpolate", text="Interpolate") + col.operator("gpencil.interpolate_sequence", text="Sequence") + col.operator("gpencil.interpolate_reverse", text="Remove Breakdowns") + + col = layout.column(align=True) + col.label(text="Options:") + col.prop(settings, "interpolate_all_layers") + col.prop(settings, "interpolate_selected_only") + + col = layout.column(align=True) + col.label(text="Sequence Options:") + col.prop(settings, "type") + if settings.type == 'CUSTOM': + box = layout.box() + # TODO: Options for loading/saving curve presets? + box.template_curve_mapping(settings, "interpolation_curve", brush=True) + elif settings.type != 'LINEAR': + col.prop(settings, "easing") + + if settings.type == 'BACK': + layout.prop(settings, "back") + elif setting.type == 'ELASTIC': + sub = layout.column(align=True) + sub.prop(settings, "amplitude") + sub.prop(settings, "period") class GreasePencilBrushPanel: diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py index 780dc4cf982..2a085b8aada 100644 --- a/release/scripts/startup/bl_ui/space_info.py +++ b/release/scripts/startup/bl_ui/space_info.py @@ -306,7 +306,7 @@ class INFO_MT_help(Menu): layout.operator( "wm.url_open", text="Manual", icon='HELP', - ).url = "https://www.blender.org/manual" + ).url = "https://docs.blender.org/manual/en/dev/" layout.operator( "wm.url_open", text="Release Log", icon='URL', ).url = "http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/%d.%d" % bpy.app.version[:2] diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index 708db2eec8d..6f7c535fe21 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -93,7 +93,8 @@ class OUTLINER_MT_view(Menu): layout.separator() layout.operator("outliner.show_active") - layout.operator("outliner.show_one_level") + layout.operator("outliner.show_one_level", text="Show One Level") + layout.operator("outliner.show_one_level", text="Hide One Level").open = False layout.operator("outliner.show_hierarchy") layout.separator() diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 26136a8e024..6bb516cf929 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -435,15 +435,9 @@ class SEQUENCER_MT_strip(Menu): layout.operator("sequencer.crossfade_sounds") layout.separator() - layout.operator("sequencer.meta_make") layout.operator("sequencer.meta_separate") - #if (ed && (ed->metastack.first || (ed->act_seq && ed->act_seq->type == SEQ_META))) { - # uiItemS(layout); - # uiItemO(layout, NULL, 0, "sequencer.meta_toggle"); - #} - layout.separator() layout.operator("sequencer.reload", text="Reload Strips") layout.operator("sequencer.reload", text="Reload Strips and Adjust Length").adjust_length = True @@ -497,6 +491,7 @@ class SequencerButtonsPanel_Output: class SEQUENCER_PT_edit(SequencerButtonsPanel, Panel): bl_label = "Edit Strip" + bl_category = "Strip" def draw(self, context): layout = self.layout @@ -563,6 +558,7 @@ class SEQUENCER_PT_edit(SequencerButtonsPanel, Panel): class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel): bl_label = "Effect Strip" + bl_category = "Strip" @classmethod def poll(cls, context): @@ -701,6 +697,7 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel): class SEQUENCER_PT_input(SequencerButtonsPanel, Panel): bl_label = "Strip Input" + bl_category = "Strip" @classmethod def poll(cls, context): @@ -795,6 +792,7 @@ class SEQUENCER_PT_input(SequencerButtonsPanel, Panel): class SEQUENCER_PT_sound(SequencerButtonsPanel, Panel): bl_label = "Sound" + bl_category = "Strip" @classmethod def poll(cls, context): @@ -848,6 +846,7 @@ class SEQUENCER_PT_sound(SequencerButtonsPanel, Panel): class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel): bl_label = "Scene" + bl_category = "Strip" @classmethod def poll(cls, context): @@ -894,6 +893,7 @@ class SEQUENCER_PT_scene(SequencerButtonsPanel, Panel): class SEQUENCER_PT_mask(SequencerButtonsPanel, Panel): bl_label = "Mask" + bl_category = "Strip" @classmethod def poll(cls, context): @@ -923,6 +923,7 @@ class SEQUENCER_PT_mask(SequencerButtonsPanel, Panel): class SEQUENCER_PT_filter(SequencerButtonsPanel, Panel): bl_label = "Filter" + bl_category = "Strip" @classmethod def poll(cls, context): @@ -977,6 +978,7 @@ class SEQUENCER_PT_filter(SequencerButtonsPanel, Panel): class SEQUENCER_PT_proxy(SequencerButtonsPanel, Panel): bl_label = "Proxy/Timecode" + bl_category = "Strip" @classmethod def poll(cls, context): @@ -1109,6 +1111,7 @@ class SEQUENCER_PT_view_safe_areas(SequencerButtonsPanel_Output, Panel): class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel): bl_label = "Modifiers" + bl_category = "Modifiers" def draw(self, context): layout = self.layout @@ -1211,6 +1214,7 @@ class SEQUENCER_PT_custom_props(SequencerButtonsPanel, PropertyPanel, Panel): COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} _context_path = "scene.sequence_editor.active_strip" _property_type = (bpy.types.Sequence,) + bl_category = "Strip" if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 3eb76a3b0f9..60e86d7544d 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -22,6 +22,7 @@ from bpy.types import Menu, Panel, UIList from bl_ui.properties_grease_pencil_common import ( GreasePencilDrawingToolsPanel, GreasePencilStrokeEditPanel, + GreasePencilInterpolatePanel, GreasePencilStrokeSculptPanel, GreasePencilBrushPanel, GreasePencilBrushCurvesPanel @@ -1963,6 +1964,11 @@ class VIEW3D_PT_tools_grease_pencil_edit(GreasePencilStrokeEditPanel, Panel): bl_space_type = 'VIEW_3D' +# Grease Pencil stroke interpolation tools +class VIEW3D_PT_tools_grease_pencil_interpolate(GreasePencilInterpolatePanel, Panel): + bl_space_type = 'VIEW_3D' + + # Grease Pencil stroke sculpting tools class VIEW3D_PT_tools_grease_pencil_sculpt(GreasePencilStrokeSculptPanel, Panel): bl_space_type = 'VIEW_3D' diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 69358302782..e915aa5bb72 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -145,6 +145,7 @@ shader_node_categories = [ NodeItem("ShaderNodeMaterial"), NodeItem("ShaderNodeCameraData"), NodeItem("ShaderNodeFresnel"), + NodeItem("ShaderNodeLayerWeight"), NodeItem("ShaderNodeLampData"), NodeItem("ShaderNodeValue"), NodeItem("ShaderNodeRGB"), diff --git a/release/scripts/templates_py/addon_add_object.py b/release/scripts/templates_py/addon_add_object.py index d294838d3a2..56e6e79b94f 100644 --- a/release/scripts/templates_py/addon_add_object.py +++ b/release/scripts/templates_py/addon_add_object.py @@ -69,7 +69,7 @@ def add_object_button(self, context): # This allows you to right click on a button and link to the manual def add_object_manual_map(): - url_manual_prefix = "https://www.blender.org/manual/" + url_manual_prefix = "https://docs.blender.org/manual/en/dev/" url_manual_mapping = ( ("bpy.ops.mesh.add_object", "editors/3dview/object"), ) diff --git a/release/text/readme.html b/release/text/readme.html index 8e08a8ba027..45bff37b025 100644 --- a/release/text/readme.html +++ b/release/text/readme.html @@ -104,7 +104,7 @@ Full release log <a href="http://wiki.blender.org/index.php/Dev:Ref/Release_Note <span class="s4">wiki.blender.org/index.php/Dev:Ref/Release_Notes/BLENDER_VERSION</span></a><br> Tutorials <a href="http://www.blender.org/support/tutorials/"> <span class="s4">www.blender.org/support/tutorials/</span></a> <br> -Manual <a href="http://www.blender.org/manual"><span class="s4">www.blender.org/manual</span></a><br> +Manual <a href="https://docs.blender.org/manual/en/dev/"><span class="s4">https://docs.blender.org/manual/en/dev/</span></a><br> User Forum <a href="http://www.blenderartists.org/"> <span class="s4">www.blenderartists.org</span></a><br> IRC <a href="irc://irc.freenode.net/#blenderchat"> diff --git a/source/blender/alembic/CMakeLists.txt b/source/blender/alembic/CMakeLists.txt index ad85f79ef2e..a6e0be6a7f3 100644 --- a/source/blender/alembic/CMakeLists.txt +++ b/source/blender/alembic/CMakeLists.txt @@ -39,14 +39,10 @@ set(INC set(INC_SYS ${ALEMBIC_INCLUDE_DIRS} + ${BOOST_INCLUDE_DIR} ${HDF5_INCLUDE_DIRS} ${OPENEXR_INCLUDE_DIRS} ) -if(APPLE OR WIN32) - list(APPEND INC_SYS - ${BOOST_INCLUDE_DIR} - ) -endif() set(SRC intern/abc_archive.cc diff --git a/source/blender/blenfont/intern/blf_thumbs.c b/source/blender/blenfont/intern/blf_thumbs.c index 133168fccf2..b6f5ffbb092 100644 --- a/source/blender/blenfont/intern/blf_thumbs.c +++ b/source/blender/blenfont/intern/blf_thumbs.c @@ -98,6 +98,10 @@ void BLF_thumb_preview( blf_font_size(font, (unsigned int)MAX2(font_size_min, font_size_curr), dpi); + /* font->glyph_cache remains NULL if blf_font_size() failed to set font size */ + if (!font->glyph_cache) + break; + /* decrease font size each time */ font_size_curr -= (font_size_curr / font_shrink); font_shrink += 1; diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h index 8756f73df72..3ce08a14177 100644 --- a/source/blender/blenkernel/BKE_deform.h +++ b/source/blender/blenkernel/BKE_deform.h @@ -116,13 +116,4 @@ void BKE_defvert_extract_vgroup_to_polyweights( struct MDeformVert *dvert, const int defgroup, const int num_verts, struct MLoop *loops, const int num_loops, struct MPoly *polys, const int num_polys, float *r_weights, const bool invert_vgroup); -/* utility function, note that MAX_VGROUP_NAME chars is the maximum string length since its only - * used with defgroups currently */ - -void BKE_deform_split_suffix(const char string[MAX_VGROUP_NAME], char base[MAX_VGROUP_NAME], char ext[MAX_VGROUP_NAME]); -void BKE_deform_split_prefix(const char string[MAX_VGROUP_NAME], char base[MAX_VGROUP_NAME], char ext[MAX_VGROUP_NAME]); - -void BKE_deform_flip_side_name(char name[MAX_VGROUP_NAME], const char from_name[MAX_VGROUP_NAME], - const bool strip_number); - #endif /* __BKE_DEFORM_H__ */ diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h index 2d9c35f7fd0..72ae2cf4efa 100644 --- a/source/blender/blenkernel/BKE_library.h +++ b/source/blender/blenkernel/BKE_library.h @@ -86,7 +86,7 @@ bool id_make_local(struct Main *bmain, struct ID *id, const bool test, const boo bool id_single_user(struct bContext *C, struct ID *id, struct PointerRNA *ptr, struct PropertyRNA *prop); bool id_copy(struct Main *bmain, struct ID *id, struct ID **newid, bool test); void id_sort_by_name(struct ListBase *lb, struct ID *id); -void BKE_id_expand_local(struct ID *id); +void BKE_id_expand_local(struct Main *bmain, struct ID *id); void BKE_id_copy_ensure_local(struct Main *bmain, struct ID *old_id, struct ID *new_id); bool new_id(struct ListBase *lb, struct ID *id, const char *name); @@ -105,6 +105,9 @@ void BKE_main_free(struct Main *mainvar); void BKE_main_lock(struct Main *bmain); void BKE_main_unlock(struct Main *bmain); +void BKE_main_relations_create(struct Main *bmain); +void BKE_main_relations_free(struct Main *bmain); + struct BlendThumbnail *BKE_main_thumbnail_from_imbuf(struct Main *bmain, struct ImBuf *img); struct ImBuf *BKE_main_thumbnail_to_imbuf(struct Main *bmain, struct BlendThumbnail *data); void BKE_main_thumbnail_create(struct Main *bmain); diff --git a/source/blender/blenkernel/BKE_library_query.h b/source/blender/blenkernel/BKE_library_query.h index a7470107c24..1258e2fa72e 100644 --- a/source/blender/blenkernel/BKE_library_query.h +++ b/source/blender/blenkernel/BKE_library_query.h @@ -36,25 +36,28 @@ struct Main; /* Tips for the callback for cases it's gonna to modify the pointer. */ enum { - IDWALK_NOP = 0, - IDWALK_NEVER_NULL = (1 << 0), - IDWALK_NEVER_SELF = (1 << 1), + IDWALK_CB_NOP = 0, + IDWALK_CB_NEVER_NULL = (1 << 0), + IDWALK_CB_NEVER_SELF = (1 << 1), /** * Indicates whether this is direct (i.e. by local data) or indirect (i.e. by linked data) usage. * \note Object proxies are half-local, half-linked... */ - IDWALK_INDIRECT_USAGE = (1 << 2), + IDWALK_CB_INDIRECT_USAGE = (1 << 2), + + /** That ID is used as mere sub-data by its owner + * (only case currently: those f***ing nodetrees in materials etc.). + * This means callback shall not *do* anything, only use this as informative data if it needs it. */ + IDWALK_CB_PRIVATE = (1 << 3), /** * Adjusts #ID.us reference-count. * \note keep in sync with 'newlibadr_us' use in readfile.c */ - IDWALK_USER = (1 << 8), - /** - * Ensure #ID.us is at least 1 on use. - */ - IDWALK_USER_ONE = (1 << 9), + IDWALK_CB_USER = (1 << 8), + /** Ensure #ID.us is at least 1 on use. */ + IDWALK_CB_USER_ONE = (1 << 9), }; enum { @@ -68,17 +71,19 @@ enum { * * \return a set of flags to control further iteration (0 to keep going). */ -typedef int (*LibraryIDLinkCallback) (void *user_data, struct ID *id_self, struct ID **id_pointer, int cd_flag); +typedef int (*LibraryIDLinkCallback) (void *user_data, struct ID *id_self, struct ID **id_pointer, int cb_flag); /* Flags for the foreach function itself. */ enum { + IDWALK_NOP = 0, IDWALK_READONLY = (1 << 0), IDWALK_RECURSE = (1 << 1), /* Also implies IDWALK_READONLY. */ }; /* Loop over all of the ID's this datablock links to. */ -void BKE_library_foreach_ID_link(struct ID *id, LibraryIDLinkCallback callback, void *user_data, int flag); -void BKE_library_update_ID_link_user(struct ID *id_dst, struct ID *id_src, const int cd_flag); +void BKE_library_foreach_ID_link( + struct Main *bmain, struct ID *id, LibraryIDLinkCallback callback, void *user_data, int flag); +void BKE_library_update_ID_link_user(struct ID *id_dst, struct ID *id_src, const int cb_flag); int BKE_library_ID_use_ID(struct ID *id_user, struct ID *id_used); diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index a4f5c425282..387045878f3 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -51,6 +51,8 @@ extern "C" { struct EvaluationContext; struct Library; struct MainLock; +struct GHash; +struct BLI_mempool; /* Blender thumbnail, as written on file (width, height, and data as char RGBA). */ /* We pack pixel data after that struct. */ @@ -59,6 +61,22 @@ typedef struct BlendThumbnail { char rect[0]; } BlendThumbnail; +/* Structs caching relations between data-blocks in a given Main. */ +typedef struct MainIDRelationsEntry { + struct MainIDRelationsEntry *next; + /* WARNING! for user_to_used, that pointer is really an ID** one, but for used_to_user, it’s only an ID* one! */ + struct ID **id_pointer; + int usage_flag; /* Using IDWALK_ enums, in BKE_library_query.h */ +} MainIDRelationsEntry; + +typedef struct MainIDRelations { + struct GHash *id_user_to_used; + struct GHash *id_used_to_user; + + /* Private... */ + struct BLI_mempool *entry_pool; +} MainIDRelations; + typedef struct Main { struct Main *next, *prev; char name[1024]; /* 1024 = FILE_MAX */ @@ -111,6 +129,11 @@ typedef struct Main { /* Evaluation context used by viewport */ struct EvaluationContext *eval_ctx; + /* Must be generated, used and freed by same code - never assume this is valid data unless you know + * when, who and how it was created. + * Used by code doing a lot of remapping etc. at once to speed things up. */ + struct MainIDRelations *relations; + struct MainLock *lock; } Main; diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index f6c08909d23..b241b5ca5b7 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -106,8 +106,8 @@ typedef enum { } ModifierTypeFlag; /* IMPORTANT! Keep ObjectWalkFunc and IDWalkFunc signatures compatible. */ -typedef void (*ObjectWalkFunc)(void *userData, struct Object *ob, struct Object **obpoin, int cd_flag); -typedef void (*IDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cd_flag); +typedef void (*ObjectWalkFunc)(void *userData, struct Object *ob, struct Object **obpoin, int cb_flag); +typedef void (*IDWalkFunc)(void *userData, struct Object *ob, struct ID **idpoin, int cb_flag); typedef void (*TexWalkFunc)(void *userData, struct Object *ob, struct ModifierData *md, const char *propname); typedef enum ModifierApplyFlag { @@ -273,7 +273,7 @@ typedef struct ModifierTypeInfo { * * This function is optional. */ - /* TODO(sergey): Remove once we finalyl switched to the new depsgraph. */ + /* TODO(sergey): Remove once we finally switched to the new depsgraph. */ void (*updateDepsgraph)(struct ModifierData *md, struct Main *bmain, struct Scene *scene, diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index cf07a178fe8..d812ab832a1 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -136,14 +136,9 @@ void BKE_object_where_is_calc_mat4(struct Scene *scene, struct Object *ob, float /* possibly belong in own moduke? */ struct BoundBox *BKE_boundbox_alloc_unit(void); void BKE_boundbox_init_from_minmax(struct BoundBox *bb, const float min[3], const float max[3]); -bool BKE_boundbox_ray_hit_check( - const struct BoundBox *bb, - const float ray_start[3], const float ray_normal[3], - float *r_lambda); void BKE_boundbox_calc_center_aabb(const struct BoundBox *bb, float r_cent[3]); void BKE_boundbox_calc_size_aabb(const struct BoundBox *bb, float r_size[3]); void BKE_boundbox_minmax(const struct BoundBox *bb, float obmat[4][4], float r_min[3], float r_max[3]); -void BKE_boundbox_scale(struct BoundBox *bb_dst, const struct BoundBox *bb_src, float scale); struct BoundBox *BKE_boundbox_ensure_minimum_dimensions( struct BoundBox *bb, struct BoundBox *bb_temp, const float epsilon); diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h index b3e3968ca9b..e5967be0bc7 100644 --- a/source/blender/blenkernel/BKE_particle.h +++ b/source/blender/blenkernel/BKE_particle.h @@ -384,7 +384,7 @@ void psys_get_birth_coords(struct ParticleSimulationData *sim, struct ParticleDa void particle_system_update(struct Scene *scene, struct Object *ob, struct ParticleSystem *psys, const bool use_render_params); /* Callback format for performing operations on ID-pointers for particle systems */ -typedef void (*ParticleSystemIDFunc)(struct ParticleSystem *psys, struct ID **idpoin, void *userdata, int cd_flag); +typedef void (*ParticleSystemIDFunc)(struct ParticleSystem *psys, struct ID **idpoin, void *userdata, int cb_flag); void BKE_particlesystem_id_loop(struct ParticleSystem *psys, ParticleSystemIDFunc func, void *userdata); diff --git a/source/blender/blenkernel/BKE_rigidbody.h b/source/blender/blenkernel/BKE_rigidbody.h index 965a97f08ba..443c3b2b5b2 100644 --- a/source/blender/blenkernel/BKE_rigidbody.h +++ b/source/blender/blenkernel/BKE_rigidbody.h @@ -53,7 +53,7 @@ struct RigidBodyOb *BKE_rigidbody_copy_object(struct Object *ob); struct RigidBodyCon *BKE_rigidbody_copy_constraint(struct Object *ob); /* Callback format for performing operations on ID-pointers for rigidbody world. */ -typedef void (*RigidbodyWorldIDFunc)(struct RigidBodyWorld *rbw, struct ID **idpoin, void *userdata, int cd_flag); +typedef void (*RigidbodyWorldIDFunc)(struct RigidBodyWorld *rbw, struct ID **idpoin, void *userdata, int cb_flag); void BKE_rigidbody_world_id_loop(struct RigidBodyWorld *rbw, RigidbodyWorldIDFunc func, void *userdata); diff --git a/source/blender/blenkernel/BKE_sca.h b/source/blender/blenkernel/BKE_sca.h index a504f1bac3d..1579a0c7bb3 100644 --- a/source/blender/blenkernel/BKE_sca.h +++ b/source/blender/blenkernel/BKE_sca.h @@ -77,9 +77,9 @@ void sca_move_controller(struct bController *cont_to_move, struct Object *ob, in void sca_move_actuator(struct bActuator *act_to_move, struct Object *ob, int move_up); /* Callback format for performing operations on ID-pointers for sensors/controllers/actuators. */ -typedef void (*SCASensorIDFunc)(struct bSensor *sensor, struct ID **idpoin, void *userdata, int cd_flag); -typedef void (*SCAControllerIDFunc)(struct bController *controller, struct ID **idpoin, void *userdata, int cd_flag); -typedef void (*SCAActuatorIDFunc)(struct bActuator *actuator, struct ID **idpoin, void *userdata, int cd_flag); +typedef void (*SCASensorIDFunc)(struct bSensor *sensor, struct ID **idpoin, void *userdata, int cb_flag); +typedef void (*SCAControllerIDFunc)(struct bController *controller, struct ID **idpoin, void *userdata, int cb_flag); +typedef void (*SCAActuatorIDFunc)(struct bActuator *actuator, struct ID **idpoin, void *userdata, int cb_flag); void BKE_sca_sensors_id_loop(struct ListBase *senslist, SCASensorIDFunc func, void *userdata); void BKE_sca_controllers_id_loop(struct ListBase *contlist, SCAControllerIDFunc func, void *userdata); diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 1f937d837b4..160adcc3988 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -2215,6 +2215,12 @@ static void mesh_calc_modifiers( } } + /* Some modifiers, like datatransfer, may generate those data as temp layer, we do not want to keep them, + * as they are used by display code when available (i.e. even if autosmooth is disabled). */ + if (!do_loop_normals && CustomData_has_layer(&finaldm->loopData, CD_NORMAL)) { + CustomData_free_layers(&finaldm->loopData, CD_NORMAL, finaldm->numLoopData); + } + #ifdef WITH_GAMEENGINE /* NavMesh - this is a hack but saves having a NavMesh modifier */ if ((ob->gameflag & OB_NAVMESH) && (finaldm->type == DM_TYPE_CDDM)) { @@ -2550,6 +2556,15 @@ static void editbmesh_calc_modifiers( /* same as mesh_calc_modifiers (if using loop normals, poly nors have already been computed). */ if (!do_loop_normals) { dm_ensure_display_normals(*r_final); + + /* Some modifiers, like datatransfer, may generate those data, we do not want to keep them, + * as they are used by display code when available (i.e. even if autosmooth is disabled). */ + if (CustomData_has_layer(&(*r_final)->loopData, CD_NORMAL)) { + CustomData_free_layers(&(*r_final)->loopData, CD_NORMAL, (*r_final)->numLoopData); + } + if (r_cage && CustomData_has_layer(&(*r_cage)->loopData, CD_NORMAL)) { + CustomData_free_layers(&(*r_cage)->loopData, CD_NORMAL, (*r_cage)->numLoopData); + } } /* add an orco layer if needed */ diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index dcbb667adca..1e33fe4ab46 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -44,6 +44,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" @@ -494,7 +495,7 @@ bPoseChannel *BKE_pose_channel_get_mirrored(const bPose *pose, const char *name) { char name_flip[MAXBONENAME]; - BKE_deform_flip_side_name(name_flip, name, false); + BLI_string_flip_side_name(name_flip, name, false, sizeof(name_flip)); if (!STREQ(name_flip, name)) { return BKE_pose_channel_find_name(pose, name_flip); diff --git a/source/blender/blenkernel/intern/anim.c b/source/blender/blenkernel/intern/anim.c index 7d3d12ac112..2f65e71c6d2 100644 --- a/source/blender/blenkernel/intern/anim.c +++ b/source/blender/blenkernel/intern/anim.c @@ -201,7 +201,15 @@ bMotionPath *animviz_verify_motionpaths(ReportList *reports, Scene *scene, Objec mpath->flag |= MOTIONPATH_FLAG_BHEAD; else mpath->flag &= ~MOTIONPATH_FLAG_BHEAD; - + + /* set default custom values */ + mpath->color[0] = 1.0; /* Red */ + mpath->color[1] = 0.0; + mpath->color[2] = 0.0; + + mpath->line_thickness = 1; + mpath->flag |= MOTIONPATH_FLAG_LINES; /* draw lines by default */ + /* allocate a cache */ mpath->points = MEM_callocN(sizeof(bMotionPathVert) * mpath->length, "bMotionPathVerts"); diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 21279392392..0b637355ecf 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -43,6 +43,7 @@ #include "BLI_alloca.h" #include "BLI_dynstr.h" #include "BLI_listbase.h" +#include "BLI_string_utils.h" #include "BLT_translation.h" diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 2b333941c6e..0287d6ae9ca 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -87,6 +87,7 @@ bArmature *BKE_armature_add(Main *bmain, const char *name) arm->deformflag = ARM_DEF_VGROUP | ARM_DEF_ENVELOPE; arm->flag = ARM_COL_CUSTOM; /* custom bone-group colors */ arm->layer = 1; + arm->ghostsize = 1; return arm; } diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 0d509ecea06..57b707a31d3 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -239,7 +239,7 @@ void BKE_brush_make_local(Main *bmain, Brush *brush, const bool lib_local) if (lib_local || is_local) { if (!is_lib) { id_clear_lib_data(bmain, &brush->id); - BKE_id_expand_local(&brush->id); + BKE_id_expand_local(bmain, &brush->id); /* enable fake user by default */ id_fake_user_set(&brush->id); diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index deeb35bd880..3dce08eb756 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -215,7 +215,9 @@ void BKE_cachefile_clean(Scene *scene, CacheFile *cache_file) if (cache_file == mcmd->cache_file) { #ifdef WITH_ALEMBIC - CacheReader_free(mcmd->reader); + if (mcmd->reader != NULL) { + CacheReader_free(mcmd->reader); + } #endif mcmd->reader = NULL; mcmd->object_path[0] = '\0'; @@ -231,7 +233,9 @@ void BKE_cachefile_clean(Scene *scene, CacheFile *cache_file) if (cache_file == data->cache_file) { #ifdef WITH_ALEMBIC - CacheReader_free(data->reader); + if (data->reader != NULL) { + CacheReader_free(data->reader); + } #endif data->reader = NULL; data->object_path[0] = '\0'; diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index cb74dbcd8d1..58ad171ee20 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -42,7 +42,7 @@ #include "BLI_math.h" #include "BLI_kdopbvh.h" #include "BLI_utildefines.h" - +#include "BLI_string_utils.h" #include "BLT_translation.h" #include "DNA_armature_types.h" diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 612f1f477e1..98d37fb07bf 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -45,8 +45,9 @@ #include "DNA_ID.h" #include "BLI_utildefines.h" -#include "BLI_string.h" #include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" #include "BLI_math.h" #include "BLI_math_color_blend.h" #include "BLI_mempool.h" diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index 7052e0a7d25..13b1aab5e1c 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -45,8 +45,8 @@ #include "BLI_listbase.h" #include "BLI_math.h" -#include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -509,7 +509,7 @@ int *defgroup_flip_map(Object *ob, int *flip_map_len, const bool use_default) if (use_default) map[i] = i; - BKE_deform_flip_side_name(name_flip, dg->name, false); + BLI_string_flip_side_name(name_flip, dg->name, false, sizeof(name_flip)); if (!STREQ(name_flip, dg->name)) { flip_num = defgroup_name_index(ob, name_flip); @@ -545,7 +545,7 @@ int *defgroup_flip_map_single(Object *ob, int *flip_map_len, const bool use_defa dg = BLI_findlink(&ob->defbase, defgroup); - BKE_deform_flip_side_name(name_flip, dg->name, false); + BLI_string_flip_side_name(name_flip, dg->name, false, sizeof(name_flip)); if (!STREQ(name_flip, dg->name)) { flip_num = defgroup_name_index(ob, name_flip); @@ -566,7 +566,7 @@ int defgroup_flip_index(Object *ob, int index, const bool use_default) if (dg) { char name_flip[sizeof(dg->name)]; - BKE_deform_flip_side_name(name_flip, dg->name, false); + BLI_string_flip_side_name(name_flip, dg->name, false, sizeof(name_flip)); if (!STREQ(name_flip, dg->name)) { flip_index = defgroup_name_index(ob, name_flip); @@ -606,185 +606,6 @@ void defgroup_unique_name(bDeformGroup *dg, Object *ob) BLI_uniquename_cb(defgroup_unique_check, &data, DATA_("Group"), '.', dg->name, sizeof(dg->name)); } -static bool is_char_sep(const char c) -{ - return ELEM(c, '.', ' ', '-', '_'); -} - -/** - * based on `BLI_split_dirfile()` / `os.path.splitext()`, - * `"a.b.c"` -> (`"a.b"`, `".c"`). - */ -void BKE_deform_split_suffix(const char string[MAX_VGROUP_NAME], char body[MAX_VGROUP_NAME], char suf[MAX_VGROUP_NAME]) -{ - size_t len = BLI_strnlen(string, MAX_VGROUP_NAME); - size_t i; - - body[0] = suf[0] = '\0'; - - for (i = len; i > 0; i--) { - if (is_char_sep(string[i])) { - BLI_strncpy(body, string, i + 1); - BLI_strncpy(suf, string + i, (len + 1) - i); - return; - } - } - - memcpy(body, string, len + 1); -} - -/** - * `"a.b.c"` -> (`"a."`, `"b.c"`) - */ -void BKE_deform_split_prefix(const char string[MAX_VGROUP_NAME], char pre[MAX_VGROUP_NAME], char body[MAX_VGROUP_NAME]) -{ - size_t len = BLI_strnlen(string, MAX_VGROUP_NAME); - size_t i; - - body[0] = pre[0] = '\0'; - - for (i = 1; i < len; i++) { - if (is_char_sep(string[i])) { - i++; - BLI_strncpy(pre, string, i + 1); - BLI_strncpy(body, string + i, (len + 1) - i); - return; - } - } - - BLI_strncpy(body, string, len); -} - -/** - * Finds the best possible flipped name. For renaming; check for unique names afterwards. - * - * if strip_number: removes number extensions - * - * \note don't use sizeof() for 'name' or 'from_name'. - */ -void BKE_deform_flip_side_name(char name[MAX_VGROUP_NAME], const char from_name[MAX_VGROUP_NAME], - const bool strip_number) -{ - int len; - char prefix[MAX_VGROUP_NAME] = ""; /* The part before the facing */ - char suffix[MAX_VGROUP_NAME] = ""; /* The part after the facing */ - char replace[MAX_VGROUP_NAME] = ""; /* The replacement string */ - char number[MAX_VGROUP_NAME] = ""; /* The number extension string */ - char *index = NULL; - bool is_set = false; - - /* always copy the name, since this can be called with an uninitialized string */ - BLI_strncpy(name, from_name, MAX_VGROUP_NAME); - - len = BLI_strnlen(from_name, MAX_VGROUP_NAME); - if (len < 3) { - /* we don't do names like .R or .L */ - return; - } - - /* We first check the case with a .### extension, let's find the last period */ - if (isdigit(name[len - 1])) { - index = strrchr(name, '.'); // last occurrence - if (index && isdigit(index[1])) { // doesnt handle case bone.1abc2 correct..., whatever! - if (strip_number == false) { - BLI_strncpy(number, index, sizeof(number)); - } - *index = 0; - len = BLI_strnlen(name, MAX_VGROUP_NAME); - } - } - - BLI_strncpy(prefix, name, sizeof(prefix)); - - /* first case; separator . - _ with extensions r R l L */ - if ((len > 1) && is_char_sep(name[len - 2])) { - is_set = true; - switch (name[len - 1]) { - case 'l': - prefix[len - 1] = 0; - strcpy(replace, "r"); - break; - case 'r': - prefix[len - 1] = 0; - strcpy(replace, "l"); - break; - case 'L': - prefix[len - 1] = 0; - strcpy(replace, "R"); - break; - case 'R': - prefix[len - 1] = 0; - strcpy(replace, "L"); - break; - default: - is_set = false; - } - } - - /* case; beginning with r R l L, with separator after it */ - if (!is_set && is_char_sep(name[1])) { - is_set = true; - switch (name[0]) { - case 'l': - strcpy(replace, "r"); - BLI_strncpy(suffix, name + 1, sizeof(suffix)); - prefix[0] = 0; - break; - case 'r': - strcpy(replace, "l"); - BLI_strncpy(suffix, name + 1, sizeof(suffix)); - prefix[0] = 0; - break; - case 'L': - strcpy(replace, "R"); - BLI_strncpy(suffix, name + 1, sizeof(suffix)); - prefix[0] = 0; - break; - case 'R': - strcpy(replace, "L"); - BLI_strncpy(suffix, name + 1, sizeof(suffix)); - prefix[0] = 0; - break; - default: - is_set = false; - } - } - - if (!is_set && len > 5) { - /* hrms, why test for a separator? lets do the rule 'ultimate left or right' */ - if (((index = BLI_strcasestr(prefix, "right")) == prefix) || - (index == prefix + len - 5)) - { - is_set = true; - if (index[0] == 'r') { - strcpy(replace, "left"); - } - else { - strcpy(replace, (index[1] == 'I') ? "LEFT" : "Left"); - } - *index = 0; - BLI_strncpy(suffix, index + 5, sizeof(suffix)); - } - else if (((index = BLI_strcasestr(prefix, "left")) == prefix) || - (index == prefix + len - 4)) - { - is_set = true; - if (index[0] == 'l') { - strcpy(replace, "right"); - } - else { - strcpy(replace, (index[1] == 'E') ? "RIGHT" : "Right"); - } - *index = 0; - BLI_strncpy(suffix, index + 4, sizeof(suffix)); - } - } - - (void)is_set; /* quiet warning */ - - BLI_snprintf(name, MAX_VGROUP_NAME, "%s%s%s%s", prefix, replace, suffix, number); -} - float defvert_find_weight(const struct MDeformVert *dvert, const int defgroup) { MDeformWeight *dw = defvert_find_index(dvert, defgroup); diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index a8341939692..294a4ce76b7 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -2563,7 +2563,7 @@ void DAG_on_visible_update(Main *bmain, const bool do_time) } static void dag_id_flush_update__isDependentTexture( - void *userData, Object *UNUSED(ob), ID **idpoin, int UNUSED(cd_flag)) + void *userData, Object *UNUSED(ob), ID **idpoin, int UNUSED(cb_flag)) { struct { ID *id; bool is_dependent; } *data = userData; diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 1d198e36e05..dc9f3b57f1f 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -32,6 +32,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_kdtree.h" +#include "BLI_string_utils.h" #include "BLI_task.h" #include "BLI_threads.h" #include "BLI_utildefines.h" @@ -215,6 +216,7 @@ typedef struct ImgSeqFormatData { /* adjacency data flags */ #define ADJ_ON_MESH_EDGE (1 << 0) +#define ADJ_BORDER_PIXEL (1 << 1) typedef struct PaintAdjData { int *n_target; /* array of neighboring point indexes, for single sample use (n_index + neigh_num) */ @@ -222,6 +224,8 @@ typedef struct PaintAdjData { int *n_num; /* num of neighs for each point */ int *flags; /* vertex adjacency flags */ int total_targets; /* size of n_target */ + int *border; /* indices of border pixels (only for texture paint) */ + int total_border; /* size of border */ } PaintAdjData; /***************************** General Utils ******************************/ @@ -822,6 +826,8 @@ static void dynamicPaint_freeAdjData(PaintSurfaceData *data) MEM_freeN(data->adj_data->n_target); if (data->adj_data->flags) MEM_freeN(data->adj_data->flags); + if (data->adj_data->border) + MEM_freeN(data->adj_data->border); MEM_freeN(data->adj_data); data->adj_data = NULL; } @@ -1298,6 +1304,8 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, const b ad->n_target = MEM_callocN(sizeof(int) * neigh_points, "Surface Adj Targets"); ad->flags = MEM_callocN(sizeof(int) * sData->total_points, "Surface Adj Flags"); ad->total_targets = neigh_points; + ad->border = NULL; + ad->total_border = 0; /* in case of allocation error, free memory */ if (!ad->n_index || !ad->n_num || !ad->n_target || !temp_data) { @@ -2295,6 +2303,36 @@ static void dynamic_paint_create_uv_surface_neighbor_cb(void *userdata, const in #undef JITTER_SAMPLES +static float dist_squared_to_looptri_uv_edges(const MLoopTri *mlooptri, const MLoopUV *mloopuv, int tri_index, const float point[2]) +{ + float min_distance = FLT_MAX; + + for (int i = 0; i < 3; i++) { + const float dist_squared = dist_squared_to_line_segment_v2( + point, + mloopuv[mlooptri[tri_index].tri[(i + 0)]].uv, + mloopuv[mlooptri[tri_index].tri[(i + 1) % 3]].uv + ); + + if (dist_squared < min_distance) + min_distance = dist_squared; + } + + return min_distance; +} + +typedef struct DynamicPaintFindIslandBorderData { + const MeshElemMap *vert_to_looptri_map; + int w, h, px, py; + + int best_index; + float best_weight; +} DynamicPaintFindIslandBorderData; + +static void dynamic_paint_find_island_border( + const DynamicPaintCreateUVSurfaceData *data, DynamicPaintFindIslandBorderData *bdata, + int tri_index, const float pixel[2], int in_edge, int depth); + /* Tries to find the neighboring pixel in given (uv space) direction. * Result is used by effect system to move paint on the surface. * @@ -2345,167 +2383,162 @@ static int dynamic_paint_find_neighbour_pixel( * TODO: Implement something more accurate / optimized? */ { - const MLoop *mloop = data->mloop; - const MLoopTri *mlooptri = data->mlooptri; - const MLoopUV *mloopuv = data->mloopuv; - - /* Get closest edge to that subpixel on UV map */ + DynamicPaintFindIslandBorderData bdata = { + .vert_to_looptri_map = vert_to_looptri_map, + .w = w, .h = h, .px = px, .py = py, + .best_index = NOT_FOUND, .best_weight = 1.0f + }; float pixel[2]; - /* distances only used for comparison */ - float dist_squared, t_dist_squared; - - int edge1_index, edge2_index; - int e1_index, e2_index, target_tri; - float closest_point[2], lambda, dir_vec[2]; - int target_uv1 = 0, target_uv2 = 0, final_pixel[2], final_index; - - const float *s_uv1, *s_uv2, *t_uv1, *t_uv2; pixel[0] = ((float)(px + neighX[n_index]) + 0.5f) / (float)w; pixel[1] = ((float)(py + neighY[n_index]) + 0.5f) / (float)h; - /* - * Find closest edge to that pixel - */ + /* Do a small recursive search for the best island edge. */ + dynamic_paint_find_island_border(data, &bdata, cPoint->tri_index, pixel, -1, 5); - /* Dist to first edge */ - e1_index = cPoint->v1; - e2_index = cPoint->v2; - edge1_index = 0; - edge2_index = 1; - dist_squared = dist_squared_to_line_segment_v2( - pixel, - mloopuv[mlooptri[cPoint->tri_index].tri[0]].uv, - mloopuv[mlooptri[cPoint->tri_index].tri[1]].uv); - - /* Dist to second edge */ - t_dist_squared = dist_squared_to_line_segment_v2( - pixel, - mloopuv[mlooptri[cPoint->tri_index].tri[1]].uv, - mloopuv[mlooptri[cPoint->tri_index].tri[2]].uv); - if (t_dist_squared < dist_squared) { - e1_index = cPoint->v2; - e2_index = cPoint->v3; - edge1_index = 1; - edge2_index = 2; - dist_squared = t_dist_squared; - } - - /* Dist to third edge */ - t_dist_squared = dist_squared_to_line_segment_v2( - pixel, - mloopuv[mlooptri[cPoint->tri_index].tri[2]].uv, - mloopuv[mlooptri[cPoint->tri_index].tri[0]].uv); - if (t_dist_squared < dist_squared) { - e1_index = cPoint->v3; - e2_index = cPoint->v1; - edge1_index = 2; - edge2_index = 0; - dist_squared = t_dist_squared; - } + return bdata.best_index; + } +} - /* - * Now find another face that is linked to that edge - */ - target_tri = -1; +static void dynamic_paint_find_island_border( + const DynamicPaintCreateUVSurfaceData *data, DynamicPaintFindIslandBorderData *bdata, + int tri_index, const float pixel[2], int in_edge, int depth) +{ + const MLoop *mloop = data->mloop; + const MLoopTri *mlooptri = data->mlooptri; + const MLoopUV *mloopuv = data->mloopuv; - /* Use a pre-computed vert-to-looptri mapping, speeds up things a lot compared to looping over all loopti. */ - for (int i = 0; i < vert_to_looptri_map[e1_index].count; i++) { - const int lt_index = vert_to_looptri_map[e1_index].indices[i]; - const int v0 = mloop[mlooptri[lt_index].tri[0]].v; - const int v1 = mloop[mlooptri[lt_index].tri[1]].v; - const int v2 = mloop[mlooptri[lt_index].tri[2]].v; + const unsigned int *loop_idx = mlooptri[tri_index].tri; - BLI_assert(ELEM(e1_index, v0, v1, v2)); + /* Enumerate all edges of the triangle, rotating the vertex list accordingly. */ + for (int edge_idx = 0; edge_idx < 3; edge_idx++) { + /* but not the edge we have just recursed through */ + if (edge_idx == in_edge) + continue; - if (ELEM(e2_index, v0, v1, v2)) { - if (lt_index == cPoint->tri_index) - continue; + float uv0[2], uv1[2], uv2[2]; - target_tri = lt_index; + copy_v2_v2(uv0, mloopuv[loop_idx[(edge_idx + 0)]].uv); + copy_v2_v2(uv1, mloopuv[loop_idx[(edge_idx + 1) % 3]].uv); + copy_v2_v2(uv2, mloopuv[loop_idx[(edge_idx + 2) % 3]].uv); - /* Get edge UV index */ - target_uv1 = (e1_index == v0) ? 0 : ((e1_index == v1) ? 1 : 2); - target_uv2 = (e2_index == v0) ? 0 : ((e2_index == v1) ? 1 : 2); - break; - } - } + /* Verify the target point is on the opposite side of the edge from the third triangle + * vertex, to ensure that we always move closer to the goal point. */ + const float sidep = line_point_side_v2(uv0, uv1, pixel); + const float side2 = line_point_side_v2(uv0, uv1, uv2); - /* If none found pixel is on mesh edge */ - if (target_tri == -1) - return ON_MESH_EDGE; + if (side2 == 0.0f) + continue; - /* - * If target face is connected in UV space as well, just use original index - */ - s_uv1 = mloopuv[mlooptri[cPoint->tri_index].tri[edge1_index]].uv; - s_uv2 = mloopuv[mlooptri[cPoint->tri_index].tri[edge2_index]].uv; - t_uv1 = mloopuv[mlooptri[target_tri].tri[target_uv1]].uv; - t_uv2 = mloopuv[mlooptri[target_tri].tri[target_uv2]].uv; + /* Hack: allow all edges of the original triangle */ + const bool correct_side = (in_edge == -1) || (sidep < 0 && side2 > 0) || (sidep > 0 && side2 < 0); - //printf("connected UV : %f,%f & %f,%f - %f,%f & %f,%f\n", s_uv1[0], s_uv1[1], s_uv2[0], s_uv2[1], t_uv1[0], t_uv1[1], t_uv2[0], t_uv2[1]); + /* Allow exactly on edge for the non-recursive case */ + if (!correct_side && sidep != 0.0f) + continue; - if (((s_uv1[0] == t_uv1[0] && s_uv1[1] == t_uv1[1]) && - (s_uv2[0] == t_uv2[0] && s_uv2[1] == t_uv2[1])) || - ((s_uv2[0] == t_uv1[0] && s_uv2[1] == t_uv1[1]) && - (s_uv1[0] == t_uv2[0] && s_uv1[1] == t_uv2[1]))) - { - final_index = x + w * y; + /* Now find another face that is linked to that edge. */ + const int vert0 = mloop[loop_idx[(edge_idx + 0)]].v; + const int vert1 = mloop[loop_idx[(edge_idx + 1) % 3]].v; + + /* Use a pre-computed vert-to-looptri mapping, speeds up things a lot compared to looping over all loopti. */ + const MeshElemMap *map = &bdata->vert_to_looptri_map[vert0]; + + bool found_other = false; + int target_tri = -1; + int target_edge = -1; + + float ouv0[2], ouv1[2]; + + for (int i = 0; i < map->count && !found_other; i++) { + const int lt_index = map->indices[i]; + + if (lt_index == tri_index) + continue; + + const unsigned int *other_loop_idx = mlooptri[lt_index].tri; + + /* Check edges for match, looping in the same order as the outer loop. */ + for (int j = 0; j < 3; j++) + { + const int overt0 = mloop[other_loop_idx[(j + 0)]].v; + const int overt1 = mloop[other_loop_idx[(j + 1) % 3]].v; + + /* Allow for swapped vertex order */ + if (overt0 == vert0 && overt1 == vert1) { + found_other = true; + copy_v2_v2(ouv0, mloopuv[other_loop_idx[(j + 0)]].uv); + copy_v2_v2(ouv1, mloopuv[other_loop_idx[(j + 1) % 3]].uv); + } + else if (overt0 == vert1 && overt1 == vert0) { + found_other = true; + copy_v2_v2(ouv1, mloopuv[other_loop_idx[(j + 0)]].uv); + copy_v2_v2(ouv0, mloopuv[other_loop_idx[(j + 1) % 3]].uv); + } + + if (found_other) { + target_tri = lt_index; + target_edge = j; + break; + } + } + } - /* If not an active pixel, bail out */ - if (tempPoints[final_index].tri_index == -1) - return NOT_FOUND; + if (!found_other) { + if (bdata->best_index < 0) + bdata->best_index = ON_MESH_EDGE; - /* If final point is an "edge pixel", use it's "real" neighbor instead */ - if (tempPoints[final_index].neighbour_pixel != -1) { - final_index = tempPoints[final_index].neighbour_pixel; + continue; + } - /* If we ended up to our origin point */ - if (final_index == (px + w * py)) - return NOT_FOUND; + /* If this edge is connected in UV space too, recurse */ + if (equals_v2v2(uv0, ouv0) && equals_v2v2(uv1, ouv1)) { + if (depth > 0 && correct_side) { + dynamic_paint_find_island_border(data, bdata, target_tri, pixel, target_edge, depth - 1); } - return final_index; + continue; } + /* Otherwise try to map to the other side of the edge. + * First check if there already is a better solution. */ + const float dist_squared = dist_squared_to_line_segment_v2(pixel, uv0, uv1); + + if (bdata->best_index >= 0 && dist_squared >= bdata->best_weight) + continue; + /* * Find a point that is relatively at same edge position * on this other face UV */ - lambda = closest_to_line_v2( - closest_point, pixel, - mloopuv[mlooptri[cPoint->tri_index].tri[edge1_index]].uv, - mloopuv[mlooptri[cPoint->tri_index].tri[edge2_index]].uv); - CLAMP(lambda, 0.0f, 1.0f); + float closest_point[2], dir_vec[2], tgt_pixel[2]; - sub_v2_v2v2( - dir_vec, - mloopuv[mlooptri[target_tri].tri[target_uv2]].uv, - mloopuv[mlooptri[target_tri].tri[target_uv1]].uv); + float lambda = closest_to_line_v2(closest_point, pixel, uv0, uv1); + CLAMP(lambda, 0.0f, 1.0f); - mul_v2_fl(dir_vec, lambda); + sub_v2_v2v2(dir_vec, ouv1, ouv0); + madd_v2_v2v2fl(tgt_pixel, ouv0, dir_vec, lambda); - copy_v2_v2(pixel, mloopuv[mlooptri[target_tri].tri[target_uv1]].uv); - add_v2_v2(pixel, dir_vec); - pixel[0] = (pixel[0] * (float)w); - pixel[1] = (pixel[1] * (float)h); + int w = bdata->w, h = bdata->h, px = bdata->px, py = bdata->py; - final_pixel[0] = (int)floorf(pixel[0]); - final_pixel[1] = (int)floorf(pixel[1]); + int final_pixel[2] = { (int)floorf(tgt_pixel[0] * w), (int)floorf(tgt_pixel[1] * h) }; /* If current pixel uv is outside of texture */ if (final_pixel[0] < 0 || final_pixel[0] >= w || final_pixel[1] < 0 || final_pixel[1] >= h) - return OUT_OF_TEXTURE; + { + if (bdata->best_index == NOT_FOUND) + bdata->best_index = OUT_OF_TEXTURE; + + continue; + } - final_index = final_pixel[0] + w * final_pixel[1]; + const PaintUVPoint *tempPoints = data->tempPoints; + int final_index = final_pixel[0] + w * final_pixel[1]; /* If we ended up to our origin point ( mesh has smaller than pixel sized faces) */ if (final_index == (px + w * py)) - return NOT_FOUND; - /* If found pixel still lies on wrong face ( mesh has smaller than pixel sized faces) */ - if (tempPoints[final_index].tri_index != target_tri) - return NOT_FOUND; + continue; /* If final point is an "edge pixel", use it's "real" neighbor instead */ if (tempPoints[final_index].neighbour_pixel != -1) { @@ -2513,11 +2546,125 @@ static int dynamic_paint_find_neighbour_pixel( /* If we ended up to our origin point */ if (final_index == (px + w * py)) - return NOT_FOUND; + continue; + } + + /* If found pixel still lies on wrong face ( mesh has smaller than pixel sized faces) */ + if (tempPoints[final_index].tri_index != target_tri) { + /* Check if it's close enough to likely touch the intended triangle. Any triangle + * becomes thinner than a pixel at its vertices, so robustness requires some margin. */ + const float final_pt[2] = { ((final_index % w) + 0.5f) / w, ((final_index / w) + 0.5f) / h }; + const float threshold = SQUARE(0.7f) / (w * h); + + if (dist_squared_to_looptri_uv_edges(mlooptri, mloopuv, tempPoints[final_index].tri_index, final_pt) > threshold) + continue; + } + + bdata->best_index = final_index; + bdata->best_weight = dist_squared; + } +} + +static bool dynamicPaint_pointHasNeighbor(PaintAdjData *ed, int index, int neighbor) +{ + const int idx = ed->n_index[index]; + + for (int i = 0; i < ed->n_num[index]; i++) { + if (ed->n_target[idx + i] == neighbor) { + return true; + } + } + + return false; +} + +/* Makes the adjacency data symmetric, except for border pixels. I.e. if A is neighbor of B, B is neighbor of A. */ +static bool dynamicPaint_symmetrizeAdjData(PaintAdjData *ed, int active_points) +{ + int *new_n_index = MEM_callocN(sizeof(int) * active_points, "Surface Adj Index"); + int *new_n_num = MEM_callocN(sizeof(int) * active_points, "Surface Adj Counts"); + + if (new_n_num && new_n_index) { + /* Count symmetrized neigbors */ + int total_targets = 0; + + for (int index = 0; index < active_points; index++) { + total_targets += ed->n_num[index]; + new_n_num[index] = ed->n_num[index]; + } + + for (int index = 0; index < active_points; index++) { + if (ed->flags[index] & ADJ_BORDER_PIXEL) { + continue; + } + + for (int i = 0, idx = ed->n_index[index]; i < ed->n_num[index]; i++) { + const int target = ed->n_target[idx + i]; + + assert(!(ed->flags[target] & ADJ_BORDER_PIXEL)); + + if (!dynamicPaint_pointHasNeighbor(ed, target, index)) { + new_n_num[target]++; + total_targets++; + } + } } - return final_index; + /* Allocate a new target map */ + int *new_n_target = MEM_callocN(sizeof(int) * total_targets, "Surface Adj Targets"); + + if (new_n_target) { + /* Copy existing neighbors to the new map */ + int n_pos = 0; + + for (int index = 0; index < active_points; index++) { + new_n_index[index] = n_pos; + memcpy(&new_n_target[n_pos], &ed->n_target[ed->n_index[index]], sizeof(int) * ed->n_num[index]); + + /* Reset count to old, but advance position by new, leaving a gap to fill below. */ + n_pos += new_n_num[index]; + new_n_num[index] = ed->n_num[index]; + } + + assert(n_pos == total_targets); + + /* Add symmetrized - this loop behavior must exactly match the count pass above */ + for (int index = 0; index < active_points; index++) { + if (ed->flags[index] & ADJ_BORDER_PIXEL) { + continue; + } + + for (int i = 0, idx = ed->n_index[index]; i < ed->n_num[index]; i++) { + const int target = ed->n_target[idx + i]; + + if (!dynamicPaint_pointHasNeighbor(ed, target, index)) { + const int num = new_n_num[target]++; + new_n_target[new_n_index[target] + num] = index; + } + } + } + + /* Swap maps */ + MEM_freeN(ed->n_target); + ed->n_target = new_n_target; + + MEM_freeN(ed->n_index); + ed->n_index = new_n_index; + + MEM_freeN(ed->n_num); + ed->n_num = new_n_num; + + ed->total_targets = total_targets; + return true; + } } + + if (new_n_index) + MEM_freeN(new_n_index); + if (new_n_num) + MEM_freeN(new_n_num); + + return false; } int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface, float *progress, short *do_update) @@ -2668,30 +2815,28 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface, flo &vert_to_looptri_map, &vert_to_looptri_map_mem, dm->getVertArray(dm), dm->getNumVerts(dm), mlooptri, tottri, mloop, dm->getNumLoops(dm)); + int total_border = 0; + for (int ty = 0; ty < h; ty++) { for (int tx = 0; tx < w; tx++) { const int index = tx + w * ty; if (tempPoints[index].tri_index != -1) { - int start_pos = n_pos; ed->n_index[final_index[index]] = n_pos; ed->n_num[final_index[index]] = 0; + if (tempPoints[index].neighbour_pixel != -1) { + ed->flags[final_index[index]] |= ADJ_BORDER_PIXEL; + total_border++; + } + for (int i = 0; i < 8; i++) { /* Try to find a neighboring pixel in defined direction. If not found, -1 is returned */ const int n_target = dynamic_paint_find_neighbour_pixel( &data, vert_to_looptri_map, w, h, tx, ty, i); if (n_target >= 0 && n_target != index) { - bool duplicate = false; - for (int j = start_pos; j < n_pos; j++) { - if (ed->n_target[j] == final_index[n_target]) { - duplicate = true; - break; - } - } - - if (!duplicate) { + if (!dynamicPaint_pointHasNeighbor(ed, final_index[index], final_index[n_target])) { ed->n_target[n_pos] = final_index[n_target]; ed->n_num[final_index[index]]++; n_pos++; @@ -2707,6 +2852,57 @@ int dynamicPaint_createUVSurface(Scene *scene, DynamicPaintSurface *surface, flo MEM_freeN(vert_to_looptri_map); MEM_freeN(vert_to_looptri_map_mem); + + /* Make neighbors symmetric */ + if (!dynamicPaint_symmetrizeAdjData(ed, active_points)) { + error = true; + } + + /* Create a list of border pixels */ + ed->border = MEM_callocN(sizeof(int) * total_border, "Border Pixel Index"); + + if (ed->border) { + ed->total_border = total_border; + + for (int i = 0, next = 0; i < active_points; i++) { + if (ed->flags[i] & ADJ_BORDER_PIXEL) { + ed->border[next++] = i; + } + } + } + +#if 0 + /* ----------------------------------------------------------------- + * For debug, write a dump of adjacency data to a file. + * -----------------------------------------------------------------*/ + FILE *dump_file = fopen("dynpaint-adj-data.txt", "w"); + int *tmp = MEM_callocN(sizeof(int) * active_points, "tmp"); + for (int ty = 0; ty < h; ty++) { + for (int tx = 0; tx < w; tx++) { + const int index = tx + w * ty; + if (tempPoints[index].tri_index != -1) + tmp[final_index[index]] = index; + } + } + for (int ty = 0; ty < h; ty++) { + for (int tx = 0; tx < w; tx++) { + const int index = tx + w * ty; + const int fidx = final_index[index]; + + if (tempPoints[index].tri_index != -1) { + int nidx = tempPoints[index].neighbour_pixel; + fprintf(dump_file, "%d\t%d,%d\t%u\t%d,%d\t%d\t", fidx, tx, h-1-ty, tempPoints[index].tri_index, nidx<0?-1:(nidx%w), nidx<0?-1:h-1-(nidx/w), ed->flags[fidx]); + for (int i = 0; i < ed->n_num[fidx]; i++) { + int tgt = tmp[ed->n_target[ed->n_index[fidx]+i]]; + fprintf(dump_file, "%s%d,%d", i?" ":"", tgt%w, h-1-tgt/w); + } + fprintf(dump_file, "\n"); + } + } + } + MEM_freeN(tmp); + fclose(dump_file); +#endif } } @@ -4529,6 +4725,10 @@ static void dynamicPaint_doSmudge(DynamicPaintSurface *surface, DynamicPaintBrus for (step = 0; step < steps; step++) { for (index = 0; index < sData->total_points; index++) { int i; + + if (sData->adj_data->flags[index] & ADJ_BORDER_PIXEL) + continue; + PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; float smudge_str = bData->brush_velocity[index * 4 + 3]; @@ -4708,6 +4908,9 @@ static void dynamic_paint_effect_spread_cb(void *userdata, const int index) const DynamicPaintSurface *surface = data->surface; const PaintSurfaceData *sData = surface->data; + if (sData->adj_data->flags[index] & ADJ_BORDER_PIXEL) + return; + const int numOfNeighs = sData->adj_data->n_num[index]; BakeAdjPoint *bNeighs = sData->bData->bNeighs; PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; @@ -4750,6 +4953,9 @@ static void dynamic_paint_effect_shrink_cb(void *userdata, const int index) const DynamicPaintSurface *surface = data->surface; const PaintSurfaceData *sData = surface->data; + if (sData->adj_data->flags[index] & ADJ_BORDER_PIXEL) + return; + const int numOfNeighs = sData->adj_data->n_num[index]; BakeAdjPoint *bNeighs = sData->bData->bNeighs; PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; @@ -4796,6 +5002,10 @@ static void dynamic_paint_effect_drip_cb(void *userdata, const int index) const DynamicPaintSurface *surface = data->surface; const PaintSurfaceData *sData = surface->data; + + if (sData->adj_data->flags[index] & ADJ_BORDER_PIXEL) + return; + BakeAdjPoint *bNeighs = sData->bData->bNeighs; PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; const PaintPoint *prevPoint = data->prevPoint; @@ -4964,6 +5174,80 @@ static void dynamicPaint_doEffectStep( } } +static void dynamic_paint_border_cb(void *userdata, const int b_index) +{ + const DynamicPaintEffectData *data = userdata; + + const DynamicPaintSurface *surface = data->surface; + const PaintSurfaceData *sData = surface->data; + + const int index = sData->adj_data->border[b_index]; + + const int numOfNeighs = sData->adj_data->n_num[index]; + PaintPoint *pPoint = &((PaintPoint *)sData->type_data)[index]; + + const int *n_index = sData->adj_data->n_index; + const int *n_target = sData->adj_data->n_target; + + /* Average neighboring points. Intermediaries use premultiplied alpha. */ + float mix_color[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + float mix_e_color[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + float mix_wetness = 0.0f; + + for (int i = 0; i < numOfNeighs; i++) { + const int n_idx = n_index[index] + i; + const int target = n_target[n_idx]; + + PaintPoint *pPoint2 = &((PaintPoint *)sData->type_data)[target]; + + assert(!(sData->adj_data->flags[target] & ADJ_BORDER_PIXEL)); + + madd_v3_v3fl(mix_color, pPoint2->color, pPoint2->color[3]); + mix_color[3] += pPoint2->color[3]; + + madd_v3_v3fl(mix_e_color, pPoint2->e_color, pPoint2->e_color[3]); + mix_e_color[3] += pPoint2->e_color[3]; + + mix_wetness += pPoint2->wetness; + } + + const float divisor = 1.0f / numOfNeighs; + + if (mix_color[3]) { + pPoint->color[3] = mix_color[3] * divisor; + mul_v3_v3fl(pPoint->color, mix_color, divisor / pPoint->color[3]); + } + else { + pPoint->color[3] = 0.0f; + } + + if (mix_e_color[3]) { + pPoint->e_color[3] = mix_e_color[3] * divisor; + mul_v3_v3fl(pPoint->e_color, mix_e_color, divisor / pPoint->e_color[3]); + } + else { + pPoint->e_color[3] = 0.0f; + } + + pPoint->wetness = mix_wetness / numOfNeighs; +} + +static void dynamicPaint_doBorderStep(DynamicPaintSurface *surface) +{ + PaintSurfaceData *sData = surface->data; + + if (!sData->adj_data || !sData->adj_data->border) + return; + + /* Don't use prevPoint, relying on the condition that neighbors are never border pixels. */ + DynamicPaintEffectData data = { + .surface = surface + }; + + BLI_task_parallel_range( + 0, sData->adj_data->total_border, &data, dynamic_paint_border_cb, sData->adj_data->total_border > 1000); +} + static void dynamic_paint_wave_step_cb(void *userdata, const int index) { const DynamicPaintEffectData *data = userdata; @@ -5636,6 +5920,11 @@ static int dynamicPaint_doStep(Scene *scene, Object *ob, DynamicPaintSurface *su if (force) MEM_freeN(force); } + + /* paint island border pixels */ + if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { + dynamicPaint_doBorderStep(surface); + } } return ret; diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index a89d423e7a6..c67a61a5aad 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -47,6 +47,7 @@ #include "BLI_math.h" #include "BLI_easing.h" #include "BLI_threads.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BLT_translation.h" diff --git a/source/blender/blenkernel/intern/freestyle.c b/source/blender/blenkernel/intern/freestyle.c index 21fc1674dc5..0a0b023df82 100644 --- a/source/blender/blenkernel/intern/freestyle.c +++ b/source/blender/blenkernel/intern/freestyle.c @@ -40,6 +40,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_string_utils.h" // function declarations static FreestyleLineSet *alloc_lineset(void); diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index cd2eac078cf..30fc8915d46 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -39,6 +39,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_math_vector.h" +#include "BLI_string_utils.h" #include "BLT_translation.h" diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 730d5a93758..f3a85dcee2b 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -62,6 +62,7 @@ #include "BLI_blenlib.h" #include "BLI_dynstr.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BLT_translation.h" diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 6cdeaf5e59b..8a7c1dd2833 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -38,6 +38,7 @@ #include "BLI_blenlib.h" #include "BLI_math_vector.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BLT_translation.h" diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 64535f229a9..6b4b492b212 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -76,6 +76,8 @@ #include "BLI_ghash.h" #include "BLI_linklist.h" #include "BLI_memarena.h" +#include "BLI_mempool.h" +#include "BLI_string_utils.h" #include "BLI_threads.h" #include "BLT_translation.h" @@ -272,8 +274,12 @@ void BKE_id_clear_newpoin(ID *id) } static int id_expand_local_callback( - void *UNUSED(user_data), struct ID *id_self, struct ID **id_pointer, int UNUSED(cd_flag)) + void *UNUSED(user_data), struct ID *id_self, struct ID **id_pointer, int cb_flag) { + if (cb_flag & IDWALK_CB_PRIVATE) { + return IDWALK_RET_NOP; + } + /* Can hapen that we get unlinkable ID here, e.g. with shapekey referring to itself (through drivers)... * Just skip it, shape key can only be either indirectly linked, or fully local, period. * And let's curse one more time that stupid useless shapekey ID type! */ @@ -287,9 +293,9 @@ static int id_expand_local_callback( /** * Expand ID usages of given id as 'extern' (and no more indirect) linked data. Used by ID copy/make_local functions. */ -void BKE_id_expand_local(ID *id) +void BKE_id_expand_local(Main *bmain, ID *id) { - BKE_library_foreach_ID_link(id, id_expand_local_callback, NULL, 0); + BKE_library_foreach_ID_link(bmain, id, id_expand_local_callback, NULL, IDWALK_READONLY); } /** @@ -298,7 +304,7 @@ void BKE_id_expand_local(ID *id) void BKE_id_copy_ensure_local(Main *bmain, ID *old_id, ID *new_id) { if (ID_IS_LINKED_DATABLOCK(old_id)) { - BKE_id_expand_local(new_id); + BKE_id_expand_local(bmain, new_id); BKE_id_lib_local_paths(bmain, old_id->lib, new_id); } } @@ -325,7 +331,7 @@ void BKE_id_make_local_generic(Main *bmain, ID *id, const bool id_in_mainlist, c if (lib_local || is_local) { if (!is_lib) { id_clear_lib_data_ex(bmain, id, id_in_mainlist); - BKE_id_expand_local(id); + BKE_id_expand_local(bmain, id); } else { ID *id_new; @@ -1251,6 +1257,10 @@ void BKE_main_free(Main *mainvar) } } + if (mainvar->relations) { + BKE_main_relations_free(mainvar); + } + BLI_spin_end((SpinLock *)mainvar->lock); MEM_freeN(mainvar->lock); DEG_evaluation_context_free(mainvar->eval_ctx); @@ -1267,6 +1277,78 @@ void BKE_main_unlock(struct Main *bmain) BLI_spin_unlock((SpinLock *) bmain->lock); } + +static int main_relations_create_cb(void *user_data, ID *id_self, ID **id_pointer, int cb_flag) +{ + MainIDRelations *rel = user_data; + + if (*id_pointer) { + MainIDRelationsEntry *entry, **entry_p; + + entry = BLI_mempool_alloc(rel->entry_pool); + if (BLI_ghash_ensure_p(rel->id_user_to_used, id_self, (void ***)&entry_p)) { + entry->next = *entry_p; + } + else { + entry->next = NULL; + } + entry->id_pointer = id_pointer; + entry->usage_flag = cb_flag; + *entry_p = entry; + + entry = BLI_mempool_alloc(rel->entry_pool); + if (BLI_ghash_ensure_p(rel->id_used_to_user, *id_pointer, (void ***)&entry_p)) { + entry->next = *entry_p; + } + else { + entry->next = NULL; + } + entry->id_pointer = (ID **)id_self; + entry->usage_flag = cb_flag; + *entry_p = entry; + } + + return IDWALK_RET_NOP; +} + +/** Generate the mappings between used IDs and their users, and vice-versa. */ +void BKE_main_relations_create(Main *bmain) +{ + ListBase *lbarray[MAX_LIBARRAY]; + ID *id; + int a; + + if (bmain->relations != NULL) { + BKE_main_relations_free(bmain); + } + + bmain->relations = MEM_mallocN(sizeof(*bmain->relations), __func__); + bmain->relations->id_used_to_user = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); + bmain->relations->id_user_to_used = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); + bmain->relations->entry_pool = BLI_mempool_create(sizeof(MainIDRelationsEntry), 128, 128, BLI_MEMPOOL_NOP); + + for (a = set_listbasepointers(bmain, lbarray); a--; ) { + for (id = lbarray[a]->first; id; id = id->next) { + BKE_library_foreach_ID_link(NULL, id, main_relations_create_cb, bmain->relations, IDWALK_READONLY); + } + } +} + +void BKE_main_relations_free(Main *bmain) +{ + if (bmain->relations) { + if (bmain->relations->id_used_to_user) { + BLI_ghash_free(bmain->relations->id_used_to_user, NULL, NULL); + } + if (bmain->relations->id_user_to_used) { + BLI_ghash_free(bmain->relations->id_user_to_used, NULL, NULL); + } + BLI_mempool_destroy(bmain->relations->entry_pool); + MEM_freeN(bmain->relations); + bmain->relations = NULL; + } +} + /** * Generates a raw .blend file thumbnail data from given image. * @@ -1407,7 +1489,8 @@ static ID *is_dupid(ListBase *lb, ID *id, const char *name) static bool check_for_dupid(ListBase *lb, ID *id, char *name) { ID *idtest; - int nr = 0, a, left_len; + int nr = 0, a; + size_t left_len; #define MAX_IN_USE 64 bool in_use[MAX_IN_USE]; /* to speed up finding unused numbers within [1 .. MAX_IN_USE - 1] */ @@ -1441,7 +1524,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name) /* Code above may have generated invalid utf-8 string, due to raw truncation. * Ensure we get a valid one now! */ - left_len -= BLI_utf8_invalid_strip(left, left_len); + left_len -= (size_t)BLI_utf8_invalid_strip(left, left_len); for (idtest = lb->first; idtest; idtest = idtest->next) { int nrtest; @@ -1483,7 +1566,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name) * shave off the end chars until we have a unique name. * Check the null terminators match as well so we don't get Cube.000 -> Cube.00 */ if (nr == 0 && name[left_len] == '\0') { - int len; + size_t len; /* FIXME: this code will never be executed, because either nr will be * at least 1, or name will not end at left_len! */ BLI_assert(0); @@ -1621,6 +1704,53 @@ void BKE_main_id_clear_newpoins(Main *bmain) } } + +static void library_make_local_copying_check(ID *id, GSet *loop_tags, MainIDRelations *id_relations, GSet *done_ids) +{ + if (BLI_gset_haskey(done_ids, id)) { + return; /* Already checked, nothing else to do. */ + } + + MainIDRelationsEntry *entry = BLI_ghash_lookup(id_relations->id_used_to_user, id); + BLI_gset_insert(loop_tags, id); + for (; entry != NULL; entry = entry->next) { + ID *par_id = (ID *)entry->id_pointer; /* used_to_user stores ID pointer, not pointer to ID pointer... */ + + /* Shapekeys are considered 'private' to their owner ID here, and never tagged (since they cannot be linked), + * so we have to switch effective parent to their owner. */ + if (GS(par_id->name) == ID_KE) { + par_id = ((Key *)par_id)->from; + } + + if (par_id->lib == NULL) { + /* Local user, early out to avoid some gset querying... */ + continue; + } + if (!BLI_gset_haskey(done_ids, par_id)) { + if (BLI_gset_haskey(loop_tags, par_id)) { + /* We are in a 'dependency loop' of IDs, this does not say us anything, skip it. + * Note that this is the situation that can lead to archipelagoes of linked data-blocks + * (since all of them have non-local users, they would all be duplicated, leading to a loop of unused + * linked data-blocks that cannot be freed since they all use each other...). */ + continue; + } + /* Else, recursively check that user ID. */ + library_make_local_copying_check(par_id, loop_tags, id_relations, done_ids); + } + + if (par_id->tag & LIB_TAG_DOIT) { + /* This user will be fully local in future, so far so good, nothing to do here but check next user. */ + } + else { + /* This user won't be fully local in future, so current ID won't be either. And we are done checking it. */ + id->tag &= ~LIB_TAG_DOIT; + break; + } + } + BLI_gset_add(done_ids, id); + BLI_gset_remove(loop_tags, id, NULL); +} + /** Make linked datablocks local. * * \param bmain Almost certainly G.main. @@ -1631,11 +1761,10 @@ void BKE_main_id_clear_newpoins(Main *bmain) /* Note: Old (2.77) version was simply making (tagging) datablocks as local, without actually making any check whether * they were also indirectly used or not... * - * Current version uses regular id_make_local callback, which is not super-efficient since this ends up - * duplicating some IDs and then removing original ones (due to missing knowledge of which ID uses some other ID). - * - * However, we now have a first check that allows us to use 'direct localization' of a lot of IDs, so performances - * are now *reasonably* OK. + * Current version uses regular id_make_local callback, with advanced pre-processing step to detect all cases of + * IDs currently indirectly used, but which will be used by local data only once this function is finished. + * This allows to avoid any uneeded duplication of IDs, and hence all time lost afterwards to remove + * orphaned linked data-blocks... */ void BKE_library_make_local( Main *bmain, const Library *lib, GHash *old_to_new_ids, const bool untagged_only, const bool set_fake) @@ -1646,9 +1775,12 @@ void BKE_library_make_local( LinkNode *todo_ids = NULL; LinkNode *copied_ids = NULL; - LinkNode *linked_loop_candidates = NULL; MemArena *linklist_mem = BLI_memarena_new(512 * sizeof(*todo_ids), __func__); + BKE_main_relations_create(bmain); + + GSet *done_ids = BLI_gset_ptr_new(__func__); + /* Step 1: Detect datablocks to make local. */ for (a = set_listbasepointers(bmain, lbarray); a--; ) { id = lbarray[a]->first; @@ -1658,16 +1790,25 @@ void BKE_library_make_local( const bool do_skip = (id && !BKE_idcode_is_linkable(GS(id->name))); for (; id; id = id->next) { + ID *ntree = (ID *)ntreeFromID(id); + id->tag &= ~LIB_TAG_DOIT; + if (ntree != NULL) { + ntree->tag &= ~LIB_TAG_DOIT; + } if (id->lib == NULL) { id->tag &= ~(LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW); } - /* The check on the fourth line (LIB_TAG_PRE_EXISTING) is done so its - * possible to tag data you don't want to be made local, used for - * appending data, so any libdata already linked wont become local - * (very nasty to discover all your links are lost after appending). + /* The check on the fourth line (LIB_TAG_PRE_EXISTING) is done so its possible to tag data you don't want to + * be made local, used for appending data, so any libdata already linked wont become local (very nasty + * to discover all your links are lost after appending). * Also, never ever make proxified objects local, would not make any sense. */ + /* Some more notes: + * - Shapekeys are never tagged here (since they are not linkable). + * - Nodetrees used in materials etc. have to be tagged manually, since they do not exist in Main (!). + * This is ok-ish on 'make local' side of things (since those are handled by their 'owner' IDs), + * but complicates slightly the pre-processing of relations between IDs at step 2... */ else if (!do_skip && id->tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT | LIB_TAG_NEW) && ELEM(lib, NULL, id->lib) && !(GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) && @@ -1675,13 +1816,32 @@ void BKE_library_make_local( { BLI_linklist_prepend_arena(&todo_ids, id, linklist_mem); id->tag |= LIB_TAG_DOIT; + + /* Tag those nasty non-ID nodetrees, but do not add them to todo list, making them local is handled + * by 'owner' ID. This is needed for library_make_local_copying_check() to work OK at step 2. */ + if (ntree != NULL) { + ntree->tag |= LIB_TAG_DOIT; + } + } + else { + /* Linked ID that we won't be making local (needed info for step 2, see below). */ + BLI_gset_add(done_ids, id); } } } /* Step 2: Check which datablocks we can directly make local (because they are only used by already, or future, - * local data), others will need to be duplicated and further processed later. */ - BKE_library_indirectly_used_data_tag_clear(bmain); + * local data), others will need to be duplicated. */ + GSet *loop_tags = BLI_gset_ptr_new(__func__); + for (LinkNode *it = todo_ids; it; it = it->next) { + library_make_local_copying_check(it->link, loop_tags, bmain->relations, done_ids); + BLI_assert(BLI_gset_size(loop_tags) == 0); + } + BLI_gset_free(loop_tags, NULL); + BLI_gset_free(done_ids, NULL); + + /* Next step will most likely add new IDs, better to get rid of this mapping now. */ + BKE_main_relations_free(bmain); /* Step 3: Make IDs local, either directly (quick and simple), or using generic process, * which involves more complex checks and might instead create a local copy of original linked ID. */ @@ -1691,10 +1851,10 @@ void BKE_library_make_local( if (id->tag & LIB_TAG_DOIT) { /* We know all users of this object are local or will be made fully local, even if currently there are - * some indirect usages. So instead of making a copy that se'll likely get rid of later, directly make + * some indirect usages. So instead of making a copy that we'll likely get rid of later, directly make * that data block local. Saves a tremendous amount of time with complex scenes... */ id_clear_lib_data_ex(bmain, id, true); - BKE_id_expand_local(id); + BKE_id_expand_local(bmain, id); id->tag &= ~LIB_TAG_DOIT; } else { @@ -1730,6 +1890,9 @@ void BKE_library_make_local( /* Step 4: We have to remap local usages of old (linked) ID to new (local) id in a separated loop, * as lbarray ordering is not enough to ensure us we did catch all dependencies * (e.g. if making local a parent object before its child...). See T48907. */ + /* TODO This is now the biggest step by far (in term of processing time). We may be able to gain here by + * using again main->relations mapping, but... this implies BKE_libblock_remap & co to be able to update + * main->relations on the fly. Have to think about it a bit more, and see whether new code is OK first, anyway. */ for (LinkNode *it = copied_ids; it; it = it->next) { id = it->link; @@ -1748,6 +1911,53 @@ void BKE_library_make_local( } } + /* Note: Keeping both version of the code (old one being safer, since it still has checks against unused IDs) + * for now, we can remove old one once it has been tested for some time in master... */ +#if 1 + /* Step 5: proxy 'remapping' hack. */ + for (LinkNode *it = copied_ids; it; it = it->next) { + /* Attempt to re-link copied proxy objects. This allows appending of an entire scene + * from another blend file into this one, even when that blend file contains proxified + * armatures that have local references. Since the proxified object needs to be linked + * (not local), this will only work when the "Localize all" checkbox is disabled. + * TL;DR: this is a dirty hack on top of an already weak feature (proxies). */ + if (GS(id->name) == ID_OB && ((Object *)id)->proxy != NULL) { + Object *ob = (Object *)id; + Object *ob_new = (Object *)id->newid; + bool is_local = false, is_lib = false; + + /* Proxies only work when the proxified object is linked-in from a library. */ + if (ob->proxy->id.lib == NULL) { + printf("Warning, proxy object %s will loose its link to %s, because the " + "proxified object is local.\n", id->newid->name, ob->proxy->id.name); + continue; + } + + BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib); + + /* We can only switch the proxy'ing to a made-local proxy if it is no longer + * referred to from a library. Not checking for local use; if new local proxy + * was not used locally would be a nasty bug! */ + if (is_local || is_lib) { + printf("Warning, made-local proxy object %s will loose its link to %s, " + "because the linked-in proxy is referenced (is_local=%i, is_lib=%i).\n", + id->newid->name, ob->proxy->id.name, is_local, is_lib); + } + else { + /* we can switch the proxy'ing from the linked-in to the made-local proxy. + * BKE_object_make_proxy() shouldn't be used here, as it allocates memory that + * was already allocated by BKE_object_make_local_ex() (which called BKE_object_copy_ex). */ + ob_new->proxy = ob->proxy; + ob_new->proxy_group = ob->proxy_group; + ob_new->proxy_from = ob->proxy_from; + ob_new->proxy->proxy_from = ob_new; + ob->proxy = ob->proxy_from = ob->proxy_group = NULL; + } + } + } +#else + LinkNode *linked_loop_candidates = NULL; + /* Step 5: remove datablocks that have been copied to be localized and are no more used in the end... * Note that we may have to loop more than once here, to tackle dependencies between linked objects... */ bool do_loop = true; @@ -1798,6 +2008,8 @@ void BKE_library_make_local( if (!is_local) { if (!is_lib) { /* Not used at all, we can free it! */ + BLI_assert(!"Unused linked data copy remaining from MakeLibLocal process, should not happen anymore"); + printf("\t%s (from %s)\n", id->name, id->lib->id.name); BKE_libblock_free(bmain, id); it->link = NULL; do_loop = true; @@ -1811,7 +2023,7 @@ void BKE_library_make_local( /* Grrrrrrr... those half-datablocks-stuff... grrrrrrrrrrr... * Here we have to also tag them as potential candidates, otherwise they would falsy report - * ID they used as 'directly used' in fourth step. */ + * ID they used as 'directly used' in sixth step. */ ID *ntree = (ID *)ntreeFromID(id); if (ntree != NULL) { ntree->tag |= LIB_TAG_DOIT; @@ -1836,6 +2048,7 @@ void BKE_library_make_local( /* Note: in theory here we are only handling datablocks forming exclusive linked dependency-cycles-based * archipelagos, so no need to check again after we have deleted one, as done in previous step. */ if (id->tag & LIB_TAG_DOIT) { + BLI_assert(!"Unused linked data copy remaining from MakeLibLocal process (archipelago case), should not happen anymore"); /* Object's deletion rely on valid ob->data, but ob->data may have already been freed here... * Setting it to NULL may not be 100% correct, but should be safe and do the work. */ if (GS(id->name) == ID_OB) { @@ -1856,6 +2069,7 @@ void BKE_library_make_local( it->link = NULL; } } +#endif BKE_main_id_clear_newpoins(bmain); BLI_memarena_free(linklist_mem); diff --git a/source/blender/blenkernel/intern/library_query.c b/source/blender/blenkernel/intern/library_query.c index fa75c906fb1..9685f1f5af6 100644 --- a/source/blender/blenkernel/intern/library_query.c +++ b/source/blender/blenkernel/intern/library_query.c @@ -84,11 +84,12 @@ #define FOREACH_FINALIZE _finalize #define FOREACH_FINALIZE_VOID FOREACH_FINALIZE: (void)0 -#define FOREACH_CALLBACK_INVOKE_ID_PP(_data, id_pp, cb_flag) \ +#define FOREACH_CALLBACK_INVOKE_ID_PP(_data, id_pp, _cb_flag) \ + CHECK_TYPE(id_pp, ID **); \ if (!((_data)->status & IDWALK_STOP)) { \ const int _flag = (_data)->flag; \ ID *old_id = *(id_pp); \ - const int callback_return = (_data)->callback((_data)->user_data, (_data)->self_id, id_pp, cb_flag | (_data)->cd_flag); \ + const int callback_return = (_data)->callback((_data)->user_data, (_data)->self_id, id_pp, _cb_flag | (_data)->cb_flag); \ if (_flag & IDWALK_READONLY) { \ BLI_assert(*(id_pp) == old_id); \ } \ @@ -129,7 +130,7 @@ enum { typedef struct LibraryForeachIDData { ID *self_id; int flag; - int cd_flag; + int cb_flag; LibraryIDLinkCallback callback; void *user_data; int status; @@ -140,19 +141,19 @@ typedef struct LibraryForeachIDData { } LibraryForeachIDData; static void library_foreach_rigidbodyworldSceneLooper( - struct RigidBodyWorld *UNUSED(rbw), ID **id_pointer, void *user_data, int cd_flag) + struct RigidBodyWorld *UNUSED(rbw), ID **id_pointer, void *user_data, int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *) user_data; - FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cd_flag); + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); FOREACH_FINALIZE_VOID; } static void library_foreach_modifiersForeachIDLink( - void *user_data, Object *UNUSED(object), ID **id_pointer, int cd_flag) + void *user_data, Object *UNUSED(object), ID **id_pointer, int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *) user_data; - FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cd_flag); + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); FOREACH_FINALIZE_VOID; } @@ -161,44 +162,44 @@ static void library_foreach_constraintObjectLooper(bConstraint *UNUSED(con), ID bool is_reference, void *user_data) { LibraryForeachIDData *data = (LibraryForeachIDData *) user_data; - const int cd_flag = is_reference ? IDWALK_USER : IDWALK_NOP; - FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cd_flag); + const int cb_flag = is_reference ? IDWALK_CB_USER : IDWALK_CB_NOP; + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); FOREACH_FINALIZE_VOID; } static void library_foreach_particlesystemsObjectLooper( - ParticleSystem *UNUSED(psys), ID **id_pointer, void *user_data, int cd_flag) + ParticleSystem *UNUSED(psys), ID **id_pointer, void *user_data, int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *) user_data; - FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cd_flag); + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); FOREACH_FINALIZE_VOID; } static void library_foreach_sensorsObjectLooper( - bSensor *UNUSED(sensor), ID **id_pointer, void *user_data, int cd_flag) + bSensor *UNUSED(sensor), ID **id_pointer, void *user_data, int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *) user_data; - FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cd_flag); + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); FOREACH_FINALIZE_VOID; } static void library_foreach_controllersObjectLooper( - bController *UNUSED(controller), ID **id_pointer, void *user_data, int cd_flag) + bController *UNUSED(controller), ID **id_pointer, void *user_data, int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *) user_data; - FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cd_flag); + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); FOREACH_FINALIZE_VOID; } static void library_foreach_actuatorsObjectLooper( - bActuator *UNUSED(actuator), ID **id_pointer, void *user_data, int cd_flag) + bActuator *UNUSED(actuator), ID **id_pointer, void *user_data, int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *) user_data; - FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cd_flag); + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pointer, cb_flag); FOREACH_FINALIZE_VOID; } @@ -207,7 +208,7 @@ static void library_foreach_nla_strip(LibraryForeachIDData *data, NlaStrip *stri { NlaStrip *substrip; - FOREACH_CALLBACK_INVOKE(data, strip->act, IDWALK_USER); + FOREACH_CALLBACK_INVOKE(data, strip->act, IDWALK_CB_USER); for (substrip = strip->strips.first; substrip; substrip = substrip->next) { library_foreach_nla_strip(data, substrip); @@ -230,14 +231,14 @@ static void library_foreach_animationData(LibraryForeachIDData *data, AnimData * /* only used targets */ DRIVER_TARGETS_USED_LOOPER(dvar) { - FOREACH_CALLBACK_INVOKE_ID(data, dtar->id, IDWALK_NOP); + FOREACH_CALLBACK_INVOKE_ID(data, dtar->id, IDWALK_CB_NOP); } DRIVER_TARGETS_LOOPER_END } } - FOREACH_CALLBACK_INVOKE(data, adt->action, IDWALK_USER); - FOREACH_CALLBACK_INVOKE(data, adt->tmpact, IDWALK_USER); + FOREACH_CALLBACK_INVOKE(data, adt->action, IDWALK_CB_USER); + FOREACH_CALLBACK_INVOKE(data, adt->tmpact, IDWALK_CB_USER); for (nla_track = adt->nla_tracks.first; nla_track; nla_track = nla_track->next) { for (nla_strip = nla_track->strips.first; nla_strip; nla_strip = nla_strip->next) { @@ -250,23 +251,28 @@ static void library_foreach_animationData(LibraryForeachIDData *data, AnimData * static void library_foreach_mtex(LibraryForeachIDData *data, MTex *mtex) { - FOREACH_CALLBACK_INVOKE(data, mtex->object, IDWALK_NOP); - FOREACH_CALLBACK_INVOKE(data, mtex->tex, IDWALK_USER); + FOREACH_CALLBACK_INVOKE(data, mtex->object, IDWALK_CB_NOP); + FOREACH_CALLBACK_INVOKE(data, mtex->tex, IDWALK_CB_USER); FOREACH_FINALIZE_VOID; } static void library_foreach_paint(LibraryForeachIDData *data, Paint *paint) { - FOREACH_CALLBACK_INVOKE(data, paint->brush, IDWALK_USER); - FOREACH_CALLBACK_INVOKE(data, paint->palette, IDWALK_USER); + FOREACH_CALLBACK_INVOKE(data, paint->brush, IDWALK_CB_USER); + FOREACH_CALLBACK_INVOKE(data, paint->palette, IDWALK_CB_USER); FOREACH_FINALIZE_VOID; } static void library_foreach_ID_as_subdata_link( - ID *id, LibraryIDLinkCallback callback, void *user_data, int flag, LibraryForeachIDData *data) + ID **id_pp, LibraryIDLinkCallback callback, void *user_data, int flag, LibraryForeachIDData *data) { + /* Needed e.g. for callbacks handling relationships... This call shall be absolutely readonly. */ + ID *id = *id_pp; + FOREACH_CALLBACK_INVOKE_ID_PP(data, id_pp, IDWALK_CB_PRIVATE); + BLI_assert(id == *id_pp); + if (flag & IDWALK_RECURSE) { /* Defer handling into main loop, recursively calling BKE_library_foreach_ID_link in IDWALK_RECURSE case is * troublesome, see T49553. */ @@ -276,8 +282,10 @@ static void library_foreach_ID_as_subdata_link( } } else { - BKE_library_foreach_ID_link(id, callback, user_data, flag); + BKE_library_foreach_ID_link(NULL, id, callback, user_data, flag); } + + FOREACH_FINALIZE_VOID; } /** @@ -285,7 +293,7 @@ static void library_foreach_ID_as_subdata_link( * * \note: May be extended to be recursive in the future. */ -void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *user_data, int flag) +void BKE_library_foreach_ID_link(Main *bmain, ID *id, LibraryIDLinkCallback callback, void *user_data, int flag) { LibraryForeachIDData data; int i; @@ -313,9 +321,21 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u #define CALLBACK_INVOKE(check_id_super, cb_flag) \ FOREACH_CALLBACK_INVOKE(&data, check_id_super, cb_flag) - do { + for (; id != NULL; id = (flag & IDWALK_RECURSE) ? BLI_LINKSTACK_POP(data.ids_todo) : NULL) { data.self_id = id; - data.cd_flag = ID_IS_LINKED_DATABLOCK(id) ? IDWALK_INDIRECT_USAGE : 0; + data.cb_flag = ID_IS_LINKED_DATABLOCK(id) ? IDWALK_CB_INDIRECT_USAGE : 0; + + if (bmain != NULL && bmain->relations != NULL && (flag & IDWALK_READONLY)) { + /* Note that this is minor optimization, even in worst cases (like id being an object with lots of + * drivers and constraints and modifiers, or material etc. with huge node tree), + * but we might as well use it (Main->relations is always assumed valid, it's responsability of code + * creating it to free it, especially if/when it starts modifying Main database). */ + MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->id_user_to_used, id); + for (; entry != NULL; entry = entry->next) { + FOREACH_CALLBACK_INVOKE_ID_PP(&data, entry->id_pointer, entry->usage_flag); + } + continue; + } AnimData *adt = BKE_animdata_from_id(id); if (adt) { @@ -326,7 +346,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u case ID_LI: { Library *lib = (Library *) id; - CALLBACK_INVOKE(lib->parent, IDWALK_NOP); + CALLBACK_INVOKE(lib->parent, IDWALK_CB_NOP); break; } case ID_SCE: @@ -336,39 +356,39 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u SceneRenderLayer *srl; Base *base; - CALLBACK_INVOKE(scene->camera, IDWALK_NOP); - CALLBACK_INVOKE(scene->world, IDWALK_USER); - CALLBACK_INVOKE(scene->set, IDWALK_NOP); - CALLBACK_INVOKE(scene->clip, IDWALK_USER); + CALLBACK_INVOKE(scene->camera, IDWALK_CB_NOP); + CALLBACK_INVOKE(scene->world, IDWALK_CB_USER); + CALLBACK_INVOKE(scene->set, IDWALK_CB_NOP); + CALLBACK_INVOKE(scene->clip, IDWALK_CB_USER); if (scene->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link((ID *)scene->nodetree, callback, user_data, flag, &data); + library_foreach_ID_as_subdata_link((ID **)&scene->nodetree, callback, user_data, flag, &data); } /* DO NOT handle scene->basact here, it's doubling with the loop over whole scene->base later, * since basact is just a pointer to one of those items. */ - CALLBACK_INVOKE(scene->obedit, IDWALK_NOP); + CALLBACK_INVOKE(scene->obedit, IDWALK_CB_NOP); for (srl = scene->r.layers.first; srl; srl = srl->next) { FreestyleModuleConfig *fmc; FreestyleLineSet *fls; if (srl->mat_override) { - CALLBACK_INVOKE(srl->mat_override, IDWALK_USER); + CALLBACK_INVOKE(srl->mat_override, IDWALK_CB_USER); } if (srl->light_override) { - CALLBACK_INVOKE(srl->light_override, IDWALK_USER); + CALLBACK_INVOKE(srl->light_override, IDWALK_CB_USER); } for (fmc = srl->freestyleConfig.modules.first; fmc; fmc = fmc->next) { if (fmc->script) { - CALLBACK_INVOKE(fmc->script, IDWALK_NOP); + CALLBACK_INVOKE(fmc->script, IDWALK_CB_NOP); } } for (fls = srl->freestyleConfig.linesets.first; fls; fls = fls->next) { if (fls->group) { - CALLBACK_INVOKE(fls->group, IDWALK_USER); + CALLBACK_INVOKE(fls->group, IDWALK_CB_USER); } if (fls->linestyle) { - CALLBACK_INVOKE(fls->linestyle, IDWALK_USER); + CALLBACK_INVOKE(fls->linestyle, IDWALK_CB_USER); } } } @@ -377,39 +397,39 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u Sequence *seq; SEQP_BEGIN(scene->ed, seq) { - CALLBACK_INVOKE(seq->scene, IDWALK_NOP); - CALLBACK_INVOKE(seq->scene_camera, IDWALK_NOP); - CALLBACK_INVOKE(seq->clip, IDWALK_USER); - CALLBACK_INVOKE(seq->mask, IDWALK_USER); - CALLBACK_INVOKE(seq->sound, IDWALK_USER); + CALLBACK_INVOKE(seq->scene, IDWALK_CB_NOP); + CALLBACK_INVOKE(seq->scene_camera, IDWALK_CB_NOP); + CALLBACK_INVOKE(seq->clip, IDWALK_CB_USER); + CALLBACK_INVOKE(seq->mask, IDWALK_CB_USER); + CALLBACK_INVOKE(seq->sound, IDWALK_CB_USER); for (SequenceModifierData *smd = seq->modifiers.first; smd; smd = smd->next) { - CALLBACK_INVOKE(smd->mask_id, IDWALK_USER); + CALLBACK_INVOKE(smd->mask_id, IDWALK_CB_USER); } } SEQ_END } - CALLBACK_INVOKE(scene->gpd, IDWALK_USER); + CALLBACK_INVOKE(scene->gpd, IDWALK_CB_USER); for (base = scene->base.first; base; base = base->next) { - CALLBACK_INVOKE(base->object, IDWALK_USER); + CALLBACK_INVOKE(base->object, IDWALK_CB_USER); } for (TimeMarker *marker = scene->markers.first; marker; marker = marker->next) { - CALLBACK_INVOKE(marker->camera, IDWALK_NOP); + CALLBACK_INVOKE(marker->camera, IDWALK_CB_NOP); } if (toolsett) { - CALLBACK_INVOKE(toolsett->skgen_template, IDWALK_NOP); + CALLBACK_INVOKE(toolsett->skgen_template, IDWALK_CB_NOP); - CALLBACK_INVOKE(toolsett->particle.scene, IDWALK_NOP); - CALLBACK_INVOKE(toolsett->particle.object, IDWALK_NOP); - CALLBACK_INVOKE(toolsett->particle.shape_object, IDWALK_NOP); + CALLBACK_INVOKE(toolsett->particle.scene, IDWALK_CB_NOP); + CALLBACK_INVOKE(toolsett->particle.object, IDWALK_CB_NOP); + CALLBACK_INVOKE(toolsett->particle.shape_object, IDWALK_CB_NOP); library_foreach_paint(&data, &toolsett->imapaint.paint); - CALLBACK_INVOKE(toolsett->imapaint.stencil, IDWALK_USER); - CALLBACK_INVOKE(toolsett->imapaint.clone, IDWALK_USER); - CALLBACK_INVOKE(toolsett->imapaint.canvas, IDWALK_USER); + CALLBACK_INVOKE(toolsett->imapaint.stencil, IDWALK_CB_USER); + CALLBACK_INVOKE(toolsett->imapaint.clone, IDWALK_CB_USER); + CALLBACK_INVOKE(toolsett->imapaint.canvas, IDWALK_CB_USER); if (toolsett->vpaint) { library_foreach_paint(&data, &toolsett->vpaint->paint); @@ -419,7 +439,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u } if (toolsett->sculpt) { library_foreach_paint(&data, &toolsett->sculpt->paint); - CALLBACK_INVOKE(toolsett->sculpt->gravity_object, IDWALK_NOP); + CALLBACK_INVOKE(toolsett->sculpt->gravity_object, IDWALK_CB_NOP); } if (toolsett->uvsculpt) { library_foreach_paint(&data, &toolsett->uvsculpt->paint); @@ -430,7 +450,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u BKE_rigidbody_world_id_loop(scene->rigidbody_world, library_foreach_rigidbodyworldSceneLooper, &data); } - CALLBACK_INVOKE(scene->gm.dome.warptext, IDWALK_NOP); + CALLBACK_INVOKE(scene->gm.dome.warptext, IDWALK_CB_NOP); break; } @@ -441,75 +461,75 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u ParticleSystem *psys; /* Object is special, proxies make things hard... */ - const int data_cd_flag = data.cd_flag; - const int proxy_cd_flag = (object->proxy || object->proxy_group) ? IDWALK_INDIRECT_USAGE : 0; + const int data_cb_flag = data.cb_flag; + const int proxy_cb_flag = (object->proxy || object->proxy_group) ? IDWALK_CB_INDIRECT_USAGE : 0; /* object data special case */ - data.cd_flag |= proxy_cd_flag; + data.cb_flag |= proxy_cb_flag; if (object->type == OB_EMPTY) { /* empty can have NULL or Image */ - CALLBACK_INVOKE_ID(object->data, IDWALK_USER); + CALLBACK_INVOKE_ID(object->data, IDWALK_CB_USER); } else { /* when set, this can't be NULL */ if (object->data) { - CALLBACK_INVOKE_ID(object->data, IDWALK_USER | IDWALK_NEVER_NULL); + CALLBACK_INVOKE_ID(object->data, IDWALK_CB_USER | IDWALK_CB_NEVER_NULL); } } - data.cd_flag = data_cd_flag; + data.cb_flag = data_cb_flag; - CALLBACK_INVOKE(object->parent, IDWALK_NOP); - CALLBACK_INVOKE(object->track, IDWALK_NOP); + CALLBACK_INVOKE(object->parent, IDWALK_CB_NOP); + CALLBACK_INVOKE(object->track, IDWALK_CB_NOP); /* object->proxy is refcounted, but not object->proxy_group... *sigh* */ - CALLBACK_INVOKE(object->proxy, IDWALK_USER); - CALLBACK_INVOKE(object->proxy_group, IDWALK_NOP); + CALLBACK_INVOKE(object->proxy, IDWALK_CB_USER); + CALLBACK_INVOKE(object->proxy_group, IDWALK_CB_NOP); /* Special case! * Since this field is set/owned by 'user' of this ID (and not ID itself), it is only indirect usage * if proxy object is linked... Twisted. */ if (object->proxy_from) { - data.cd_flag = ID_IS_LINKED_DATABLOCK(object->proxy_from) ? IDWALK_INDIRECT_USAGE : 0; + data.cb_flag = ID_IS_LINKED_DATABLOCK(object->proxy_from) ? IDWALK_CB_INDIRECT_USAGE : 0; } - CALLBACK_INVOKE(object->proxy_from, IDWALK_NOP); - data.cd_flag = data_cd_flag; + CALLBACK_INVOKE(object->proxy_from, IDWALK_CB_NOP); + data.cb_flag = data_cb_flag; - CALLBACK_INVOKE(object->poselib, IDWALK_USER); + CALLBACK_INVOKE(object->poselib, IDWALK_CB_USER); - data.cd_flag |= proxy_cd_flag; + data.cb_flag |= proxy_cb_flag; for (i = 0; i < object->totcol; i++) { - CALLBACK_INVOKE(object->mat[i], IDWALK_USER); + CALLBACK_INVOKE(object->mat[i], IDWALK_CB_USER); } - data.cd_flag = data_cd_flag; + data.cb_flag = data_cb_flag; - CALLBACK_INVOKE(object->gpd, IDWALK_USER); - CALLBACK_INVOKE(object->dup_group, IDWALK_USER); + CALLBACK_INVOKE(object->gpd, IDWALK_CB_USER); + CALLBACK_INVOKE(object->dup_group, IDWALK_CB_USER); if (object->pd) { - CALLBACK_INVOKE(object->pd->tex, IDWALK_USER); - CALLBACK_INVOKE(object->pd->f_source, IDWALK_NOP); + CALLBACK_INVOKE(object->pd->tex, IDWALK_CB_USER); + CALLBACK_INVOKE(object->pd->f_source, IDWALK_CB_NOP); } /* Note that ob->effect is deprecated, so no need to handle it here. */ if (object->pose) { bPoseChannel *pchan; - data.cd_flag |= proxy_cd_flag; + data.cb_flag |= proxy_cb_flag; for (pchan = object->pose->chanbase.first; pchan; pchan = pchan->next) { - CALLBACK_INVOKE(pchan->custom, IDWALK_USER); + CALLBACK_INVOKE(pchan->custom, IDWALK_CB_USER); BKE_constraints_id_loop(&pchan->constraints, library_foreach_constraintObjectLooper, &data); } - data.cd_flag = data_cd_flag; + data.cb_flag = data_cb_flag; } if (object->rigidbody_constraint) { - CALLBACK_INVOKE(object->rigidbody_constraint->ob1, IDWALK_NOP); - CALLBACK_INVOKE(object->rigidbody_constraint->ob2, IDWALK_NOP); + CALLBACK_INVOKE(object->rigidbody_constraint->ob1, IDWALK_CB_NOP); + CALLBACK_INVOKE(object->rigidbody_constraint->ob2, IDWALK_CB_NOP); } if (object->lodlevels.first) { LodLevel *level; for (level = object->lodlevels.first; level; level = level->next) { - CALLBACK_INVOKE(level->source, IDWALK_NOP); + CALLBACK_INVOKE(level->source, IDWALK_CB_NOP); } } @@ -521,10 +541,10 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u } if (object->soft) { - CALLBACK_INVOKE(object->soft->collision_group, IDWALK_NOP); + CALLBACK_INVOKE(object->soft->collision_group, IDWALK_CB_NOP); if (object->soft->effector_weights) { - CALLBACK_INVOKE(object->soft->effector_weights->group, IDWALK_NOP); + CALLBACK_INVOKE(object->soft->effector_weights->group, IDWALK_CB_NOP); } } @@ -537,10 +557,10 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u case ID_ME: { Mesh *mesh = (Mesh *) id; - CALLBACK_INVOKE(mesh->texcomesh, IDWALK_USER); - CALLBACK_INVOKE(mesh->key, IDWALK_USER); + CALLBACK_INVOKE(mesh->texcomesh, IDWALK_CB_USER); + CALLBACK_INVOKE(mesh->key, IDWALK_CB_USER); for (i = 0; i < mesh->totcol; i++) { - CALLBACK_INVOKE(mesh->mat[i], IDWALK_USER); + CALLBACK_INVOKE(mesh->mat[i], IDWALK_CB_USER); } /* XXX Really not happy with this - probably texface should rather use some kind of @@ -552,7 +572,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u MTexPoly *txface = (MTexPoly *)mesh->pdata.layers[i].data; for (int j = 0; j < mesh->totpoly; j++, txface++) { - CALLBACK_INVOKE(txface->tpage, IDWALK_USER_ONE); + CALLBACK_INVOKE(txface->tpage, IDWALK_CB_USER_ONE); } } } @@ -562,7 +582,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u MTFace *tface = (MTFace *)mesh->fdata.layers[i].data; for (int j = 0; j < mesh->totface; j++, tface++) { - CALLBACK_INVOKE(tface->tpage, IDWALK_USER_ONE); + CALLBACK_INVOKE(tface->tpage, IDWALK_CB_USER_ONE); } } } @@ -573,17 +593,17 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u case ID_CU: { Curve *curve = (Curve *) id; - CALLBACK_INVOKE(curve->bevobj, IDWALK_NOP); - CALLBACK_INVOKE(curve->taperobj, IDWALK_NOP); - CALLBACK_INVOKE(curve->textoncurve, IDWALK_NOP); - CALLBACK_INVOKE(curve->key, IDWALK_USER); + CALLBACK_INVOKE(curve->bevobj, IDWALK_CB_NOP); + CALLBACK_INVOKE(curve->taperobj, IDWALK_CB_NOP); + CALLBACK_INVOKE(curve->textoncurve, IDWALK_CB_NOP); + CALLBACK_INVOKE(curve->key, IDWALK_CB_USER); for (i = 0; i < curve->totcol; i++) { - CALLBACK_INVOKE(curve->mat[i], IDWALK_USER); + CALLBACK_INVOKE(curve->mat[i], IDWALK_CB_USER); } - CALLBACK_INVOKE(curve->vfont, IDWALK_USER); - CALLBACK_INVOKE(curve->vfontb, IDWALK_USER); - CALLBACK_INVOKE(curve->vfonti, IDWALK_USER); - CALLBACK_INVOKE(curve->vfontbi, IDWALK_USER); + CALLBACK_INVOKE(curve->vfont, IDWALK_CB_USER); + CALLBACK_INVOKE(curve->vfontb, IDWALK_CB_USER); + CALLBACK_INVOKE(curve->vfonti, IDWALK_CB_USER); + CALLBACK_INVOKE(curve->vfontbi, IDWALK_CB_USER); break; } @@ -591,7 +611,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u { MetaBall *metaball = (MetaBall *) id; for (i = 0; i < metaball->totcol; i++) { - CALLBACK_INVOKE(metaball->mat[i], IDWALK_USER); + CALLBACK_INVOKE(metaball->mat[i], IDWALK_CB_USER); } break; } @@ -606,9 +626,9 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u } if (material->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link((ID *)material->nodetree, callback, user_data, flag, &data); + library_foreach_ID_as_subdata_link((ID **)&material->nodetree, callback, user_data, flag, &data); } - CALLBACK_INVOKE(material->group, IDWALK_USER); + CALLBACK_INVOKE(material->group, IDWALK_CB_USER); break; } @@ -617,26 +637,26 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u Tex *texture = (Tex *) id; if (texture->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link((ID *)texture->nodetree, callback, user_data, flag, &data); + library_foreach_ID_as_subdata_link((ID **)&texture->nodetree, callback, user_data, flag, &data); } - CALLBACK_INVOKE(texture->ima, IDWALK_USER); + CALLBACK_INVOKE(texture->ima, IDWALK_CB_USER); if (texture->env) { - CALLBACK_INVOKE(texture->env->object, IDWALK_NOP); - CALLBACK_INVOKE(texture->env->ima, IDWALK_USER); + CALLBACK_INVOKE(texture->env->object, IDWALK_CB_NOP); + CALLBACK_INVOKE(texture->env->ima, IDWALK_CB_USER); } if (texture->pd) - CALLBACK_INVOKE(texture->pd->object, IDWALK_NOP); + CALLBACK_INVOKE(texture->pd->object, IDWALK_CB_NOP); if (texture->vd) - CALLBACK_INVOKE(texture->vd->object, IDWALK_NOP); + CALLBACK_INVOKE(texture->vd->object, IDWALK_CB_NOP); if (texture->ot) - CALLBACK_INVOKE(texture->ot->object, IDWALK_NOP); + CALLBACK_INVOKE(texture->ot->object, IDWALK_CB_NOP); break; } case ID_LT: { Lattice *lattice = (Lattice *) id; - CALLBACK_INVOKE(lattice->key, IDWALK_USER); + CALLBACK_INVOKE(lattice->key, IDWALK_CB_USER); break; } @@ -650,7 +670,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u } if (lamp->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link((ID *)lamp->nodetree, callback, user_data, flag, &data); + library_foreach_ID_as_subdata_link((ID **)&lamp->nodetree, callback, user_data, flag, &data); } break; } @@ -658,7 +678,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u case ID_CA: { Camera *camera = (Camera *) id; - CALLBACK_INVOKE(camera->dof_ob, IDWALK_NOP); + CALLBACK_INVOKE(camera->dof_ob, IDWALK_CB_NOP); break; } @@ -669,14 +689,14 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u * (see also foreach_libblock_id_users_callback). */ Key *key = (Key *) id; - CALLBACK_INVOKE_ID(key->from, IDWALK_NOP); + CALLBACK_INVOKE_ID(key->from, IDWALK_CB_NOP); break; } case ID_SCR: { bScreen *screen = (bScreen *) id; - CALLBACK_INVOKE(screen->scene, IDWALK_USER_ONE); + CALLBACK_INVOKE(screen->scene, IDWALK_CB_USER_ONE); break; } @@ -690,7 +710,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u } if (world->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link((ID *)world->nodetree, callback, user_data, flag, &data); + library_foreach_ID_as_subdata_link((ID **)&world->nodetree, callback, user_data, flag, &data); } break; } @@ -698,7 +718,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u case ID_SPK: { Speaker *speaker = (Speaker *) id; - CALLBACK_INVOKE(speaker->sound, IDWALK_USER); + CALLBACK_INVOKE(speaker->sound, IDWALK_CB_USER); break; } @@ -707,7 +727,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u Group *group = (Group *) id; GroupObject *gob; for (gob = group->gobject.first; gob; gob = gob->next) { - CALLBACK_INVOKE(gob->ob, IDWALK_USER_ONE); + CALLBACK_INVOKE(gob->ob, IDWALK_CB_USER_ONE); } break; } @@ -716,9 +736,9 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u { bNodeTree *ntree = (bNodeTree *) id; bNode *node; - CALLBACK_INVOKE(ntree->gpd, IDWALK_USER); + CALLBACK_INVOKE(ntree->gpd, IDWALK_CB_USER); for (node = ntree->nodes.first; node; node = node->next) { - CALLBACK_INVOKE_ID(node->id, IDWALK_USER); + CALLBACK_INVOKE_ID(node->id, IDWALK_CB_USER); } break; } @@ -726,9 +746,9 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u case ID_BR: { Brush *brush = (Brush *) id; - CALLBACK_INVOKE(brush->toggle_brush, IDWALK_NOP); - CALLBACK_INVOKE(brush->clone.image, IDWALK_NOP); - CALLBACK_INVOKE(brush->paint_curve, IDWALK_USER); + CALLBACK_INVOKE(brush->toggle_brush, IDWALK_CB_NOP); + CALLBACK_INVOKE(brush->clone.image, IDWALK_CB_NOP); + CALLBACK_INVOKE(brush->paint_curve, IDWALK_CB_USER); library_foreach_mtex(&data, &brush->mtex); library_foreach_mtex(&data, &brush->mask_mtex); break; @@ -737,10 +757,10 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u case ID_PA: { ParticleSettings *psett = (ParticleSettings *) id; - CALLBACK_INVOKE(psett->dup_group, IDWALK_NOP); - CALLBACK_INVOKE(psett->dup_ob, IDWALK_NOP); - CALLBACK_INVOKE(psett->bb_ob, IDWALK_NOP); - CALLBACK_INVOKE(psett->collision_group, IDWALK_NOP); + CALLBACK_INVOKE(psett->dup_group, IDWALK_CB_NOP); + CALLBACK_INVOKE(psett->dup_ob, IDWALK_CB_NOP); + CALLBACK_INVOKE(psett->bb_ob, IDWALK_CB_NOP); + CALLBACK_INVOKE(psett->collision_group, IDWALK_CB_NOP); for (i = 0; i < MAX_MTEX; i++) { if (psett->mtex[i]) { @@ -749,16 +769,16 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u } if (psett->effector_weights) { - CALLBACK_INVOKE(psett->effector_weights->group, IDWALK_NOP); + CALLBACK_INVOKE(psett->effector_weights->group, IDWALK_CB_NOP); } if (psett->pd) { - CALLBACK_INVOKE(psett->pd->tex, IDWALK_USER); - CALLBACK_INVOKE(psett->pd->f_source, IDWALK_NOP); + CALLBACK_INVOKE(psett->pd->tex, IDWALK_CB_USER); + CALLBACK_INVOKE(psett->pd->f_source, IDWALK_CB_NOP); } if (psett->pd2) { - CALLBACK_INVOKE(psett->pd2->tex, IDWALK_USER); - CALLBACK_INVOKE(psett->pd2->f_source, IDWALK_NOP); + CALLBACK_INVOKE(psett->pd2->tex, IDWALK_CB_USER); + CALLBACK_INVOKE(psett->pd2->f_source, IDWALK_CB_NOP); } if (psett->boids) { @@ -769,11 +789,11 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u for (rule = state->rules.first; rule; rule = rule->next) { if (rule->type == eBoidRuleType_Avoid) { BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule; - CALLBACK_INVOKE(gabr->ob, IDWALK_NOP); + CALLBACK_INVOKE(gabr->ob, IDWALK_CB_NOP); } else if (rule->type == eBoidRuleType_FollowLeader) { BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader *)rule; - CALLBACK_INVOKE(flbr->ob, IDWALK_NOP); + CALLBACK_INVOKE(flbr->ob, IDWALK_CB_NOP); } } } @@ -789,19 +809,19 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u MovieTrackingTrack *track; MovieTrackingPlaneTrack *plane_track; - CALLBACK_INVOKE(clip->gpd, IDWALK_USER); + CALLBACK_INVOKE(clip->gpd, IDWALK_CB_USER); for (track = tracking->tracks.first; track; track = track->next) { - CALLBACK_INVOKE(track->gpd, IDWALK_USER); + CALLBACK_INVOKE(track->gpd, IDWALK_CB_USER); } for (object = tracking->objects.first; object; object = object->next) { for (track = object->tracks.first; track; track = track->next) { - CALLBACK_INVOKE(track->gpd, IDWALK_USER); + CALLBACK_INVOKE(track->gpd, IDWALK_CB_USER); } } for (plane_track = tracking->plane_tracks.first; plane_track; plane_track = plane_track->next) { - CALLBACK_INVOKE(plane_track->image, IDWALK_USER); + CALLBACK_INVOKE(plane_track->image, IDWALK_CB_USER); } break; } @@ -816,7 +836,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u for (mask_spline = mask_layer->splines.first; mask_spline; mask_spline = mask_spline->next) { for (i = 0; i < mask_spline->tot_point; i++) { MaskSplinePoint *point = &mask_spline->points[i]; - CALLBACK_INVOKE_ID(point->parent.id, IDWALK_USER); + CALLBACK_INVOKE_ID(point->parent.id, IDWALK_CB_USER); } } } @@ -834,14 +854,14 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u } if (linestyle->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - library_foreach_ID_as_subdata_link((ID *)linestyle->nodetree, callback, user_data, flag, &data); + library_foreach_ID_as_subdata_link((ID **)&linestyle->nodetree, callback, user_data, flag, &data); } for (lsm = linestyle->color_modifiers.first; lsm; lsm = lsm->next) { if (lsm->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { LineStyleColorModifier_DistanceFromObject *p = (LineStyleColorModifier_DistanceFromObject *)lsm; if (p->target) { - CALLBACK_INVOKE(p->target, IDWALK_NOP); + CALLBACK_INVOKE(p->target, IDWALK_CB_NOP); } } } @@ -849,7 +869,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u if (lsm->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { LineStyleAlphaModifier_DistanceFromObject *p = (LineStyleAlphaModifier_DistanceFromObject *)lsm; if (p->target) { - CALLBACK_INVOKE(p->target, IDWALK_NOP); + CALLBACK_INVOKE(p->target, IDWALK_CB_NOP); } } } @@ -857,7 +877,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u if (lsm->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { LineStyleThicknessModifier_DistanceFromObject *p = (LineStyleThicknessModifier_DistanceFromObject *)lsm; if (p->target) { - CALLBACK_INVOKE(p->target, IDWALK_NOP); + CALLBACK_INVOKE(p->target, IDWALK_CB_NOP); } } } @@ -868,7 +888,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u bAction *act = (bAction *) id; for (TimeMarker *marker = act->markers.first; marker; marker = marker->next) { - CALLBACK_INVOKE(marker->camera, IDWALK_NOP); + CALLBACK_INVOKE(marker->camera, IDWALK_CB_NOP); } break; } @@ -891,7 +911,7 @@ void BKE_library_foreach_ID_link(ID *id, LibraryIDLinkCallback callback, void *u break; } - } while ((id = (flag & IDWALK_RECURSE) ? BLI_LINKSTACK_POP(data.ids_todo) : NULL)); + } FOREACH_FINALIZE: if (data.ids_handled) { @@ -909,13 +929,13 @@ FOREACH_FINALIZE: /** * re-usable function, use when replacing ID's */ -void BKE_library_update_ID_link_user(ID *id_dst, ID *id_src, const int cd_flag) +void BKE_library_update_ID_link_user(ID *id_dst, ID *id_src, const int cb_flag) { - if (cd_flag & IDWALK_USER) { + if (cb_flag & IDWALK_CB_USER) { id_us_min(id_src); id_us_plus(id_dst); } - else if (cd_flag & IDWALK_USER_ONE) { + else if (cb_flag & IDWALK_CB_USER_ONE) { id_us_ensure_real(id_dst); } } @@ -1049,7 +1069,7 @@ static int foreach_libblock_id_users_callback(void *user_data, ID *self_id, ID * (iter->id->tag & LIB_TAG_EXTRAUSER) ? 1 : 0, (iter->id->tag & LIB_TAG_EXTRAUSER_SET) ? 1 : 0, (cb_flag & IDWALK_INDIRECT_USAGE) ? 1 : 0); #endif - if (cb_flag & IDWALK_INDIRECT_USAGE) { + if (cb_flag & IDWALK_CB_INDIRECT_USAGE) { iter->count_indirect++; } else { @@ -1080,7 +1100,7 @@ int BKE_library_ID_use_ID(ID *id_user, ID *id_used) iter.curr_id = id_user; iter.count_direct = iter.count_indirect = 0; - BKE_library_foreach_ID_link(iter.curr_id, foreach_libblock_id_users_callback, (void *)&iter, IDWALK_NOP); + BKE_library_foreach_ID_link(NULL, iter.curr_id, foreach_libblock_id_users_callback, (void *)&iter, IDWALK_READONLY); return iter.count_direct + iter.count_indirect; } @@ -1109,7 +1129,7 @@ static bool library_ID_is_used(Main *bmain, void *idv, const bool check_linked) } iter.curr_id = id_curr; BKE_library_foreach_ID_link( - id_curr, foreach_libblock_id_users_callback, &iter, IDWALK_NOP); + bmain, id_curr, foreach_libblock_id_users_callback, &iter, IDWALK_READONLY); is_defined = ((check_linked ? iter.count_indirect : iter.count_direct) != 0); } @@ -1160,7 +1180,7 @@ void BKE_library_ID_test_usages(Main *bmain, void *idv, bool *is_used_local, boo continue; } iter.curr_id = id_curr; - BKE_library_foreach_ID_link(id_curr, foreach_libblock_id_users_callback, &iter, IDWALK_NOP); + BKE_library_foreach_ID_link(bmain, id_curr, foreach_libblock_id_users_callback, &iter, IDWALK_READONLY); is_defined = (iter.count_direct != 0 && iter.count_indirect != 0); } @@ -1237,7 +1257,8 @@ void BKE_library_unused_linked_data_set_tag(Main *bmain, const bool do_init_tag) /* Unused ID (so far), no need to check it further. */ continue; } - BKE_library_foreach_ID_link(id, foreach_libblock_used_linked_data_tag_clear_cb, &do_loop, IDWALK_NOP); + BKE_library_foreach_ID_link( + bmain, id, foreach_libblock_used_linked_data_tag_clear_cb, &do_loop, IDWALK_READONLY); } } } @@ -1264,7 +1285,8 @@ void BKE_library_indirectly_used_data_tag_clear(Main *bmain) /* Local or non-indirectly-used ID (so far), no need to check it further. */ continue; } - BKE_library_foreach_ID_link(id, foreach_libblock_used_linked_data_tag_clear_cb, &do_loop, IDWALK_NOP); + BKE_library_foreach_ID_link( + bmain, id, foreach_libblock_used_linked_data_tag_clear_cb, &do_loop, IDWALK_READONLY); } } } diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c index d7d566a9ec0..a408b498f18 100644 --- a/source/blender/blenkernel/intern/library_remap.c +++ b/source/blender/blenkernel/intern/library_remap.c @@ -158,6 +158,10 @@ enum { static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id_p, int cb_flag) { + if (cb_flag & IDWALK_CB_PRIVATE) { + return IDWALK_RET_NOP; + } + IDRemap *id_remap_data = user_data; ID *old_id = id_remap_data->old_id; ID *new_id = id_remap_data->new_id; @@ -169,14 +173,14 @@ static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id } if (*id_p && (*id_p == old_id)) { - const bool is_indirect = (cb_flag & IDWALK_INDIRECT_USAGE) != 0; + const bool is_indirect = (cb_flag & IDWALK_CB_INDIRECT_USAGE) != 0; const bool skip_indirect = (id_remap_data->flag & ID_REMAP_SKIP_INDIRECT_USAGE) != 0; /* Note: proxy usage implies LIB_TAG_EXTERN, so on this aspect it is direct, * on the other hand since they get reset to lib data on file open/reload it is indirect too... * Edit Mode is also a 'skip direct' case. */ const bool is_obj = (GS(id->name) == ID_OB); const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id)); - const bool is_never_null = ((cb_flag & IDWALK_NEVER_NULL) && (new_id == NULL) && + const bool is_never_null = ((cb_flag & IDWALK_CB_NEVER_NULL) && (new_id == NULL) && (id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0); const bool skip_never_null = (id_remap_data->flag & ID_REMAP_SKIP_NEVER_NULL_USAGE) != 0; @@ -185,7 +189,7 @@ static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id id->name, old_id->name, old_id, new_id ? new_id->name : "<NONE>", new_id, skip_indirect); #endif - if ((id_remap_data->flag & ID_REMAP_FLAG_NEVER_NULL_USAGE) && (cb_flag & IDWALK_NEVER_NULL)) { + if ((id_remap_data->flag & ID_REMAP_FLAG_NEVER_NULL_USAGE) && (cb_flag & IDWALK_CB_NEVER_NULL)) { id->tag |= LIB_TAG_DOIT; } @@ -203,10 +207,10 @@ static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id else { BLI_assert(0); } - if (cb_flag & IDWALK_USER) { + if (cb_flag & IDWALK_CB_USER) { id_remap_data->skipped_refcounted++; } - else if (cb_flag & IDWALK_USER_ONE) { + else if (cb_flag & IDWALK_CB_USER_ONE) { /* No need to count number of times this happens, just a flag is enough. */ id_remap_data->status |= ID_REMAP_IS_USER_ONE_SKIPPED; } @@ -216,13 +220,13 @@ static int foreach_libblock_remap_callback(void *user_data, ID *id_self, ID **id *id_p = new_id; DAG_id_tag_update_ex(id_remap_data->bmain, id_self, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); } - if (cb_flag & IDWALK_USER) { + if (cb_flag & IDWALK_CB_USER) { id_us_min(old_id); /* We do not want to handle LIB_TAG_INDIRECT/LIB_TAG_EXTERN here. */ if (new_id) new_id->us++; } - else if (cb_flag & IDWALK_USER_ONE) { + else if (cb_flag & IDWALK_CB_USER_ONE) { id_us_ensure_real(new_id); /* We cannot affect old_id->us directly, LIB_TAG_EXTRAUSER(_SET) are assumed to be set as needed, * that extra user is processed in final handling... */ @@ -434,7 +438,7 @@ ATTR_NONNULL(1) static void libblock_remap_data( #endif r_id_remap_data->id = id; libblock_remap_data_preprocess(r_id_remap_data); - BKE_library_foreach_ID_link(id, foreach_libblock_remap_callback, (void *)r_id_remap_data, IDWALK_NOP); + BKE_library_foreach_ID_link(NULL, id, foreach_libblock_remap_callback, (void *)r_id_remap_data, IDWALK_NOP); } else { i = set_listbasepointers(bmain, lb_array); @@ -456,7 +460,7 @@ ATTR_NONNULL(1) static void libblock_remap_data( r_id_remap_data->id = id_curr; libblock_remap_data_preprocess(r_id_remap_data); BKE_library_foreach_ID_link( - id_curr, foreach_libblock_remap_callback, (void *)r_id_remap_data, IDWALK_NOP); + NULL, id_curr, foreach_libblock_remap_callback, (void *)r_id_remap_data, IDWALK_NOP); } } } @@ -685,13 +689,17 @@ void BKE_libblock_relink_ex( } } -static int id_relink_to_newid_looper(void *UNUSED(user_data), ID *UNUSED(self_id), ID **id_pointer, const int cd_flag) +static int id_relink_to_newid_looper(void *UNUSED(user_data), ID *UNUSED(self_id), ID **id_pointer, const int cb_flag) { + if (cb_flag & IDWALK_CB_PRIVATE) { + return IDWALK_RET_NOP; + } + ID *id = *id_pointer; if (id) { /* See: NEW_ID macro */ if (id->newid) { - BKE_library_update_ID_link_user(id->newid, id, cd_flag); + BKE_library_update_ID_link_user(id->newid, id, cb_flag); *id_pointer = id->newid; } else if (id->tag & LIB_TAG_NEW) { @@ -711,7 +719,7 @@ void BKE_libblock_relink_to_newid(ID *id) if (ID_IS_LINKED_DATABLOCK(id)) return; - BKE_library_foreach_ID_link(id, id_relink_to_newid_looper, NULL, 0); + BKE_library_foreach_ID_link(NULL, id, id_relink_to_newid_looper, NULL, 0); } void BKE_libblock_free_data(Main *UNUSED(bmain), ID *id) diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index bd21215f91e..1eb909bd9f9 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -41,6 +41,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BKE_context.h" diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 21023d9f53c..6f23b82c6df 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -36,8 +36,8 @@ #include "BLI_utildefines.h" #include "BLI_ghash.h" -#include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_string_utils.h" #include "BLI_listbase.h" #include "BLI_math.h" diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index 8d024ea9aa5..97033a9555d 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -49,6 +49,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BKE_global.h" diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c index 2068854421f..5c0b09f0ff0 100644 --- a/source/blender/blenkernel/intern/mball_tessellate.c +++ b/source/blender/blenkernel/intern/mball_tessellate.c @@ -41,8 +41,8 @@ #include "DNA_scene_types.h" #include "BLI_listbase.h" -#include "BLI_path_util.h" #include "BLI_math.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BLI_memarena.h" diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 41e4c21d814..2276d56b9c6 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -49,10 +49,11 @@ #include "DNA_object_types.h" #include "BLI_utildefines.h" -#include "BLI_path_util.h" #include "BLI_listbase.h" #include "BLI_linklist.h" +#include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_string_utils.h" #include "BLT_translation.h" diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index c321bc92a71..148fc3827e0 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -40,9 +40,9 @@ #include "MEM_guardedalloc.h" #include "BLI_utildefines.h" -#include "BLI_path_util.h" #include "BLI_listbase.h" #include "BLI_string.h" +#include "BLI_string_utils.h" #include "BLI_ghash.h" #include "BLT_translation.h" diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index f16e8f328d4..3f3b4896653 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -46,10 +46,11 @@ #include "DNA_world_types.h" #include "DNA_linestyle_types.h" -#include "BLI_string.h" -#include "BLI_math.h" #include "BLI_listbase.h" +#include "BLI_math.h" #include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BLT_translation.h" diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index e93bfcdda83..ff8be5892e9 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1204,7 +1204,7 @@ void BKE_object_make_local_ex(Main *bmain, Object *ob, const bool lib_local, con if (lib_local || is_local) { if (!is_lib) { id_clear_lib_data(bmain, &ob->id); - BKE_id_expand_local(&ob->id); + BKE_id_expand_local(bmain, &ob->id); if (clear_proxy) { if (ob->proxy_from != NULL) { ob->proxy_from->proxy = NULL; @@ -2236,18 +2236,6 @@ void BKE_boundbox_minmax(const BoundBox *bb, float obmat[4][4], float r_min[3], } } -void BKE_boundbox_scale(struct BoundBox *bb_dst, const struct BoundBox *bb_src, float scale) -{ - float cent[3]; - BKE_boundbox_calc_center_aabb(bb_src, cent); - - for (int i = 0; i < ARRAY_SIZE(bb_dst->vec); i++) { - bb_dst->vec[i][0] = ((bb_src->vec[i][0] - cent[0]) * scale) + cent[0]; - bb_dst->vec[i][1] = ((bb_src->vec[i][1] - cent[1]) * scale) + cent[1]; - bb_dst->vec[i][2] = ((bb_src->vec[i][2] - cent[2]) * scale) + cent[2]; - } -} - /** * Returns a BBox which each dimensions are at least epsilon. * \note In case a given dimension needs to be enlarged, its final value will be in [epsilon, 3 * epsilon] range. @@ -2816,45 +2804,6 @@ int BKE_object_obdata_texspace_get(Object *ob, short **r_texflag, float **r_loc, return 1; } -/* - * Test a bounding box for ray intersection - * assumes the ray is already local to the boundbox space - */ -bool BKE_boundbox_ray_hit_check( - const struct BoundBox *bb, - const float ray_start[3], const float ray_normal[3], - float *r_lambda) -{ - const int triangle_indexes[12][3] = { - {0, 1, 2}, {0, 2, 3}, - {3, 2, 6}, {3, 6, 7}, - {1, 2, 6}, {1, 6, 5}, - {5, 6, 7}, {4, 5, 7}, - {0, 3, 7}, {0, 4, 7}, - {0, 1, 5}, {0, 4, 5}}; - - bool result = false; - int i; - - for (i = 0; i < 12 && (!result || r_lambda); i++) { - float lambda; - int v1, v2, v3; - v1 = triangle_indexes[i][0]; - v2 = triangle_indexes[i][1]; - v3 = triangle_indexes[i][2]; - if (isect_ray_tri_v3(ray_start, ray_normal, bb->vec[v1], bb->vec[v2], bb->vec[v3], &lambda, NULL) && - (!r_lambda || *r_lambda > lambda)) - { - result = true; - if (r_lambda) { - *r_lambda = lambda; - } - } - } - - return result; -} - static int pc_cmp(const void *a, const void *b) { const LinkData *ad = a, *bd = b; diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c index b5e1ded35bb..ccf2aec5c7a 100644 --- a/source/blender/blenkernel/intern/object_deform.c +++ b/source/blender/blenkernel/intern/object_deform.c @@ -32,6 +32,7 @@ #include "BLI_utildefines.h" #include "BLI_ghash.h" #include "BLI_listbase.h" +#include "BLI_string_utils.h" #include "DNA_armature_types.h" #include "DNA_cloth_types.h" @@ -623,7 +624,7 @@ void BKE_object_defgroup_mirror_selection( if (dg_selection[i]) { char name_flip[MAXBONENAME]; - BKE_deform_flip_side_name(name_flip, defgroup->name, false); + BLI_string_flip_side_name(name_flip, defgroup->name, false, sizeof(name_flip)); i_mirr = STREQ(name_flip, defgroup->name) ? i : defgroup_name_index(ob, name_flip); if ((i_mirr >= 0 && i_mirr < defbase_tot) && (dg_flags_sel[i_mirr] == false)) { diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index ee435051151..943dc781246 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -4328,12 +4328,12 @@ void BKE_particlesystem_id_loop(ParticleSystem *psys, ParticleSystemIDFunc func, { ParticleTarget *pt; - func(psys, (ID **)&psys->part, userdata, IDWALK_USER | IDWALK_NEVER_NULL); - func(psys, (ID **)&psys->target_ob, userdata, IDWALK_NOP); - func(psys, (ID **)&psys->parent, userdata, IDWALK_NOP); + func(psys, (ID **)&psys->part, userdata, IDWALK_CB_USER | IDWALK_CB_NEVER_NULL); + func(psys, (ID **)&psys->target_ob, userdata, IDWALK_CB_NOP); + func(psys, (ID **)&psys->parent, userdata, IDWALK_CB_NOP); for (pt = psys->targets.first; pt; pt = pt->next) { - func(psys, (ID **)&pt->ob, userdata, IDWALK_NOP); + func(psys, (ID **)&pt->ob, userdata, IDWALK_CB_NOP); } /* Even though psys->part should never be NULL, this can happen as an exception during deletion. @@ -4343,7 +4343,7 @@ void BKE_particlesystem_id_loop(ParticleSystem *psys, ParticleSystemIDFunc func, int p; for (p = 0, pa = psys->particles; p < psys->totpart; p++, pa++) { - func(psys, (ID **)&pa->boid->ground, userdata, IDWALK_NOP); + func(psys, (ID **)&pa->boid->ground, userdata, IDWALK_CB_NOP); } } } diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index b5f34a29ebb..5353cc26295 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -974,14 +974,14 @@ void BKE_rigidbody_world_groups_relink(RigidBodyWorld *rbw) void BKE_rigidbody_world_id_loop(RigidBodyWorld *rbw, RigidbodyWorldIDFunc func, void *userdata) { - func(rbw, (ID **)&rbw->group, userdata, IDWALK_NOP); - func(rbw, (ID **)&rbw->constraints, userdata, IDWALK_NOP); - func(rbw, (ID **)&rbw->effector_weights->group, userdata, IDWALK_NOP); + func(rbw, (ID **)&rbw->group, userdata, IDWALK_CB_NOP); + func(rbw, (ID **)&rbw->constraints, userdata, IDWALK_CB_NOP); + func(rbw, (ID **)&rbw->effector_weights->group, userdata, IDWALK_CB_NOP); if (rbw->objects) { int i; for (i = 0; i < rbw->numbodies; i++) { - func(rbw, (ID **)&rbw->objects[i], userdata, IDWALK_NOP); + func(rbw, (ID **)&rbw->objects[i], userdata, IDWALK_CB_NOP); } } } diff --git a/source/blender/blenkernel/intern/sca.c b/source/blender/blenkernel/intern/sca.c index fa221348932..7920d8b5696 100644 --- a/source/blender/blenkernel/intern/sca.c +++ b/source/blender/blenkernel/intern/sca.c @@ -992,19 +992,19 @@ void BKE_sca_sensors_id_loop(ListBase *senslist, SCASensorIDFunc func, void *use bSensor *sensor; for (sensor = senslist->first; sensor; sensor = sensor->next) { - func(sensor, (ID **)&sensor->ob, userdata, IDWALK_NOP); + func(sensor, (ID **)&sensor->ob, userdata, IDWALK_CB_NOP); switch (sensor->type) { case SENS_TOUCH: /* DEPRECATED */ { bTouchSensor *ts = sensor->data; - func(sensor, (ID **)&ts->ma, userdata, IDWALK_NOP); + func(sensor, (ID **)&ts->ma, userdata, IDWALK_CB_NOP); break; } case SENS_MESSAGE: { bMessageSensor *ms = sensor->data; - func(sensor, (ID **)&ms->fromObject, userdata, IDWALK_NOP); + func(sensor, (ID **)&ms->fromObject, userdata, IDWALK_CB_NOP); break; } case SENS_ALWAYS: @@ -1035,7 +1035,7 @@ void BKE_sca_controllers_id_loop(ListBase *contlist, SCAControllerIDFunc func, v case CONT_PYTHON: { bPythonCont *pc = controller->data; - func(controller, (ID **)&pc->text, userdata, IDWALK_NOP); + func(controller, (ID **)&pc->text, userdata, IDWALK_CB_NOP); break; } case CONT_LOGIC_AND: @@ -1056,89 +1056,89 @@ void BKE_sca_actuators_id_loop(ListBase *actlist, SCAActuatorIDFunc func, void * bActuator *actuator; for (actuator = actlist->first; actuator; actuator = actuator->next) { - func(actuator, (ID **)&actuator->ob, userdata, IDWALK_NOP); + func(actuator, (ID **)&actuator->ob, userdata, IDWALK_CB_NOP); switch (actuator->type) { case ACT_ADD_OBJECT: /* DEPRECATED */ { bAddObjectActuator *aoa = actuator->data; - func(actuator, (ID **)&aoa->ob, userdata, IDWALK_NOP); + func(actuator, (ID **)&aoa->ob, userdata, IDWALK_CB_NOP); break; } case ACT_ACTION: { bActionActuator *aa = actuator->data; - func(actuator, (ID **)&aa->act, userdata, IDWALK_NOP); + func(actuator, (ID **)&aa->act, userdata, IDWALK_CB_NOP); break; } case ACT_SOUND: { bSoundActuator *sa = actuator->data; - func(actuator, (ID **)&sa->sound, userdata, IDWALK_NOP); + func(actuator, (ID **)&sa->sound, userdata, IDWALK_CB_NOP); break; } case ACT_EDIT_OBJECT: { bEditObjectActuator *eoa = actuator->data; - func(actuator, (ID **)&eoa->ob, userdata, IDWALK_NOP); - func(actuator, (ID **)&eoa->me, userdata, IDWALK_NOP); + func(actuator, (ID **)&eoa->ob, userdata, IDWALK_CB_NOP); + func(actuator, (ID **)&eoa->me, userdata, IDWALK_CB_NOP); break; } case ACT_SCENE: { bSceneActuator *sa = actuator->data; - func(actuator, (ID **)&sa->scene, userdata, IDWALK_NOP); - func(actuator, (ID **)&sa->camera, userdata, IDWALK_NOP); + func(actuator, (ID **)&sa->scene, userdata, IDWALK_CB_NOP); + func(actuator, (ID **)&sa->camera, userdata, IDWALK_CB_NOP); break; } case ACT_PROPERTY: { bPropertyActuator *pa = actuator->data; - func(actuator, (ID **)&pa->ob, userdata, IDWALK_NOP); + func(actuator, (ID **)&pa->ob, userdata, IDWALK_CB_NOP); break; } case ACT_OBJECT: { bObjectActuator *oa = actuator->data; - func(actuator, (ID **)&oa->reference, userdata, IDWALK_NOP); + func(actuator, (ID **)&oa->reference, userdata, IDWALK_CB_NOP); break; } case ACT_CAMERA: { bCameraActuator *ca = actuator->data; - func(actuator, (ID **)&ca->ob, userdata, IDWALK_NOP); + func(actuator, (ID **)&ca->ob, userdata, IDWALK_CB_NOP); break; } case ACT_MESSAGE: { bMessageActuator *ma = actuator->data; - func(actuator, (ID **)&ma->toObject, userdata, IDWALK_NOP); + func(actuator, (ID **)&ma->toObject, userdata, IDWALK_CB_NOP); break; } case ACT_2DFILTER: { bTwoDFilterActuator *tdfa = actuator->data; - func(actuator, (ID **)&tdfa->text, userdata, IDWALK_NOP); + func(actuator, (ID **)&tdfa->text, userdata, IDWALK_CB_NOP); break; } case ACT_PARENT: { bParentActuator *pa = actuator->data; - func(actuator, (ID **)&pa->ob, userdata, IDWALK_NOP); + func(actuator, (ID **)&pa->ob, userdata, IDWALK_CB_NOP); break; } case ACT_ARMATURE: { bArmatureActuator *aa = actuator->data; - func(actuator, (ID **)&aa->target, userdata, IDWALK_NOP); - func(actuator, (ID **)&aa->subtarget, userdata, IDWALK_NOP); + func(actuator, (ID **)&aa->target, userdata, IDWALK_CB_NOP); + func(actuator, (ID **)&aa->subtarget, userdata, IDWALK_CB_NOP); break; } case ACT_STEERING: { bSteeringActuator *sa = actuator->data; - func(actuator, (ID **)&sa->target, userdata, IDWALK_NOP); - func(actuator, (ID **)&sa->navmesh, userdata, IDWALK_NOP); + func(actuator, (ID **)&sa->target, userdata, IDWALK_CB_NOP); + func(actuator, (ID **)&sa->navmesh, userdata, IDWALK_CB_NOP); break; } /* Note: some types seems to be non-implemented? ACT_LAMP, ACT_MATERIAL... */ diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 8938e6c391f..d9439328c83 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -56,6 +56,7 @@ #include "BLI_utildefines.h" #include "BLI_callbacks.h" #include "BLI_string.h" +#include "BLI_string_utils.h" #include "BLI_threads.h" #include "BLI_task.h" @@ -286,13 +287,16 @@ Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type) ts->imapaint.paintcursor = NULL; id_us_plus((ID *)ts->imapaint.stencil); ts->particle.paintcursor = NULL; + /* duplicate Grease Pencil Drawing Brushes */ BLI_listbase_clear(&ts->gp_brushes); for (bGPDbrush *brush = sce->toolsettings->gp_brushes.first; brush; brush = brush->next) { bGPDbrush *newbrush = BKE_gpencil_brush_duplicate(brush); BLI_addtail(&ts->gp_brushes, newbrush); } - + + /* duplicate Grease Pencil interpolation curve */ + ts->gp_interpolate.custom_ipo = curvemapping_copy(ts->gp_interpolate.custom_ipo); } /* make a private copy of the avicodecdata */ @@ -439,12 +443,17 @@ void BKE_scene_free(Scene *sce) BKE_paint_free(&sce->toolsettings->uvsculpt->paint); MEM_freeN(sce->toolsettings->uvsculpt); } + BKE_paint_free(&sce->toolsettings->imapaint.paint); + /* free Grease Pencil Drawing Brushes */ BKE_gpencil_free_brushes(&sce->toolsettings->gp_brushes); BLI_freelistN(&sce->toolsettings->gp_brushes); - - BKE_paint_free(&sce->toolsettings->imapaint.paint); - + + /* free Grease Pencil interpolation curve */ + if (sce->toolsettings->gp_interpolate.custom_ipo) { + curvemapping_free(sce->toolsettings->gp_interpolate.custom_ipo); + } + MEM_freeN(sce->toolsettings); sce->toolsettings = NULL; } @@ -2047,6 +2056,8 @@ bool BKE_scene_remove_render_layer(Main *bmain, Scene *scene, SceneRenderLayer * return false; } + BKE_freestyle_config_free(&srl->freestyleConfig); + if (srl->prop) { IDP_FreeProperty(srl->prop); MEM_freeN(srl->prop); diff --git a/source/blender/blenkernel/intern/seqmodifier.c b/source/blender/blenkernel/intern/seqmodifier.c index 95c6b7736e1..89c2f76c661 100644 --- a/source/blender/blenkernel/intern/seqmodifier.c +++ b/source/blender/blenkernel/intern/seqmodifier.c @@ -34,8 +34,8 @@ #include "MEM_guardedalloc.h" #include "BLI_listbase.h" -#include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BLI_math.h" diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 672857e88fe..88575c7d3be 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -235,8 +235,9 @@ Text *BKE_text_add(Main *bmain, const char *name) /* to a valid utf-8 sequences */ int txt_extended_ascii_as_utf8(char **str) { - int bad_char, added = 0, i = 0; - int length = strlen(*str); + ptrdiff_t bad_char, i = 0; + const ptrdiff_t length = (ptrdiff_t)strlen(*str); + int added = 0; while ((*str)[i]) { if ((bad_char = BLI_utf8_invalid_byte(*str + i, length - i)) == -1) @@ -248,7 +249,7 @@ int txt_extended_ascii_as_utf8(char **str) if (added != 0) { char *newstr = MEM_mallocN(length + added + 1, "text_line"); - int mi = 0; + ptrdiff_t mi = 0; i = 0; while ((*str)[i]) { diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index 96ab8693122..990d250b854 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -49,8 +49,8 @@ #include "BLI_math.h" #include "BLI_math_base.h" #include "BLI_listbase.h" -#include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_string_utils.h" #include "BLI_threads.h" #include "BLT_translation.h" diff --git a/source/blender/blenkernel/intern/tracking_util.c b/source/blender/blenkernel/intern/tracking_util.c index a90b1dee927..1c056cda68d 100644 --- a/source/blender/blenkernel/intern/tracking_util.c +++ b/source/blender/blenkernel/intern/tracking_util.c @@ -42,8 +42,8 @@ #include "BLI_math.h" #include "BLI_listbase.h" #include "BLI_ghash.h" -#include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_string_utils.h" #include "BLT_translation.h" @@ -625,17 +625,17 @@ static ImBuf *make_grayscale_ibuf_copy(ImBuf *ibuf) */ size = (size_t)grayscale->x * (size_t)grayscale->y * sizeof(float); grayscale->channels = 1; - if ((grayscale->rect_float = MEM_mapallocN(size, "tracking grayscale image"))) { + if ((grayscale->rect_float = MEM_mapallocN(size, "tracking grayscale image")) != NULL) { grayscale->mall |= IB_rectfloat; grayscale->flags |= IB_rectfloat; - } - for (i = 0; i < grayscale->x * grayscale->y; ++i) { - const float *pixel = ibuf->rect_float + ibuf->channels * i; + for (i = 0; i < grayscale->x * grayscale->y; ++i) { + const float *pixel = ibuf->rect_float + ibuf->channels * i; - grayscale->rect_float[i] = 0.2126f * pixel[0] + - 0.7152f * pixel[1] + - 0.0722f * pixel[2]; + grayscale->rect_float[i] = 0.2126f * pixel[0] + + 0.7152f * pixel[1] + + 0.0722f * pixel[2]; + } } return grayscale; @@ -653,14 +653,14 @@ static void ibuf_to_float_image(const ImBuf *ibuf, libmv_FloatImage *float_image static ImBuf *float_image_to_ibuf(libmv_FloatImage *float_image) { ImBuf *ibuf = IMB_allocImBuf(float_image->width, float_image->height, 32, 0); - size_t size = (size_t)ibuf->x * (size_t)ibuf->y * - float_image->channels * sizeof(float); + size_t size = (size_t)ibuf->x * (size_t)ibuf->y * float_image->channels * sizeof(float); ibuf->channels = float_image->channels; - if ((ibuf->rect_float = MEM_mapallocN(size, "tracking grayscale image"))) { + if ((ibuf->rect_float = MEM_mapallocN(size, "tracking grayscale image")) != NULL) { ibuf->mall |= IB_rectfloat; ibuf->flags |= IB_rectfloat; + + memcpy(ibuf->rect_float, float_image->buffer, size); } - memcpy(ibuf->rect_float, float_image->buffer, size); return ibuf; } diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index d33c2cb3279..4a85e859c16 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -293,6 +293,10 @@ void isect_ray_aabb_v3_precalc( bool isect_ray_aabb_v3( const struct IsectRayAABB_Precalc *data, const float bb_min[3], const float bb_max[3], float *tmin); +bool isect_ray_aabb_v3_simple( + const float orig[3], const float dir[3], + const float bb_min[3], const float bb_max[3], + float *tmin, float *tmax); struct NearestRayToAABB_Precalc { float ray_origin[3]; @@ -389,6 +393,8 @@ void box_minmax_bounds_m4(float min[3], float max[3], void map_to_tube(float *r_u, float *r_v, const float x, const float y, const float z); void map_to_sphere(float *r_u, float *r_v, const float x, const float y, const float z); +void map_to_plane_v2_v3v3(float r_co[2], const float co[3], const float no[3]); +void map_to_plane_axis_angle_v2_v3v3fl(float r_co[2], const float co[3], const float axis[3], const float angle); /********************************** Normals **********************************/ diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index 1a626ff44bd..baa1f792018 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -91,13 +91,8 @@ bool BLI_testextensie_glob(const char *str, const char *ext_fnmatch) ATTR_NONNUL bool BLI_replace_extension(char *path, size_t maxlen, const char *ext) ATTR_NONNULL(); bool BLI_ensure_extension(char *path, size_t maxlen, const char *ext) ATTR_NONNULL(); bool BLI_ensure_filename(char *filepath, size_t maxlen, const char *filename) ATTR_NONNULL(); -bool BLI_uniquename(struct ListBase *list, void *vlink, const char *defname, char delim, int name_offs, int len); -bool BLI_uniquename_cb(bool (*unique_check)(void *arg, const char *name), - void *arg, const char *defname, char delim, char *name, int name_len); -void BLI_newname(char *name, int add); int BLI_stringdec(const char *string, char *head, char *start, unsigned short *numlen); void BLI_stringenc(char *string, const char *head, const char *tail, unsigned short numlen, int pic); -int BLI_split_name_num(char *left, int *nr, const char *name, const char delim); /* removes trailing slash */ void BLI_cleanup_file(const char *relabase, char *path) ATTR_NONNULL(2); diff --git a/source/blender/blenlib/BLI_string_utf8.h b/source/blender/blenlib/BLI_string_utf8.h index 0740b574c1a..32504a88b48 100644 --- a/source/blender/blenlib/BLI_string_utf8.h +++ b/source/blender/blenlib/BLI_string_utf8.h @@ -36,8 +36,8 @@ extern "C" { char *BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL(); size_t BLI_strncpy_utf8_rlen(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL(); char *BLI_strncat_utf8(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL(); -int BLI_utf8_invalid_byte(const char *str, int length) ATTR_NONNULL(); -int BLI_utf8_invalid_strip(char *str, int length) ATTR_NONNULL(); +ptrdiff_t BLI_utf8_invalid_byte(const char *str, size_t length) ATTR_NONNULL(); +int BLI_utf8_invalid_strip(char *str, size_t length) ATTR_NONNULL(); int BLI_str_utf8_size(const char *p) ATTR_NONNULL(); /* warning, can return -1 on bad chars */ int BLI_str_utf8_size_safe(const char *p) ATTR_NONNULL(); diff --git a/source/blender/blenlib/BLI_string_utils.h b/source/blender/blenlib/BLI_string_utils.h new file mode 100644 index 00000000000..bb19ed574bb --- /dev/null +++ b/source/blender/blenlib/BLI_string_utils.h @@ -0,0 +1,63 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017 by the Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BLI_STRING_UTILS_H__ +#define __BLI_STRING_UTILS_H__ + +/** \file BLI_string_utils.h + * \ingroup bli + */ + +#include <stdarg.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#include "BLI_compiler_attrs.h" + +struct ListBase; + +typedef bool (*UniquenameCheckCallback)(void *arg, const char *name); + +size_t BLI_split_name_num(char *left, int *nr, const char *name, const char delim); + +void BLI_string_split_suffix(const char *string, char *r_body, char *r_suf, const size_t str_len); +void BLI_string_split_prefix(const char *string, char *r_pre, char *r_body, const size_t str_len); + +void BLI_string_flip_side_name(char *r_name, const char *from_name, const bool strip_number, const size_t name_len); + +bool BLI_uniquename_cb( + UniquenameCheckCallback unique_check, void *arg, const char *defname, char delim, char *name, size_t name_len); +bool BLI_uniquename( + struct ListBase *list, void *vlink, const char *defname, char delim, int name_offs, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* __BLI_STRING_UTILS_H__ */ diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 6e717a3ae7e..3277519c66e 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -109,6 +109,7 @@ set(SRC intern/string.c intern/string_cursor_utf8.c intern/string_utf8.c + intern/string_utils.c intern/system.c intern/task.c intern/threads.c @@ -195,6 +196,7 @@ set(SRC BLI_string.h BLI_string_cursor_utf8.h BLI_string_utf8.h + BLI_string_utils.h BLI_sys_types.h BLI_system.h BLI_task.h diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 74ede1e7559..aeb6a550cd9 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -2309,6 +2309,34 @@ bool isect_ray_aabb_v3( return true; } +/* + * Test a bounding box (AABB) for ray intersection + * assumes the ray is already local to the boundbox space + */ +bool isect_ray_aabb_v3_simple( + const float orig[3], const float dir[3], + const float bb_min[3], const float bb_max[3], + float *tmin, float *tmax) +{ + double t[7]; + float hit_dist[2]; + t[1] = (double)(bb_min[0] - orig[0]) / dir[0]; + t[2] = (double)(bb_max[0] - orig[0]) / dir[0]; + t[3] = (double)(bb_min[1] - orig[1]) / dir[1]; + t[4] = (double)(bb_max[1] - orig[1]) / dir[1]; + t[5] = (double)(bb_min[2] - orig[2]) / dir[2]; + t[6] = (double)(bb_max[2] - orig[2]) / dir[2]; + hit_dist[0] = (float)fmax(fmax(fmin(t[1], t[2]), fmin(t[3], t[4])), fmin(t[5], t[6])); + hit_dist[1] = (float)fmin(fmin(fmax(t[1], t[2]), fmax(t[3], t[4])), fmax(t[5], t[6])); + if ((hit_dist[1] < 0 || hit_dist[0] > hit_dist[1])) + return false; + else { + if (tmin) *tmin = hit_dist[0]; + if (tmax) *tmax = hit_dist[1]; + return true; + } +} + void dist_squared_ray_to_aabb_v3_precalc( struct NearestRayToAABB_Precalc *data, const float ray_origin[3], const float ray_direction[3]) @@ -4048,6 +4076,26 @@ void map_to_sphere(float *r_u, float *r_v, const float x, const float y, const f } } +void map_to_plane_v2_v3v3(float r_co[2], const float co[3], const float no[3]) +{ + float target[3] = {0.0f, 0.0f, 1.0f}; + float axis[3]; + + cross_v3_v3v3(axis, no, target); + normalize_v3(axis); + + map_to_plane_axis_angle_v2_v3v3fl(r_co, co, axis, angle_normalized_v3v3(no, target)); +} + +void map_to_plane_axis_angle_v2_v3v3fl(float r_co[2], const float co[3], const float axis[3], const float angle) +{ + float tmp[3]; + + rotate_normalized_v3_v3v3fl(tmp, co, axis, angle); + + copy_v2_v2(r_co, tmp); +} + /********************************* Normals **********************************/ void accumulate_vertex_normals_tri( diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index f0d0bd00dea..6644e6605a1 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -63,9 +63,6 @@ #include "MEM_guardedalloc.h" -/* local */ -#define UNIQUE_NAME_MAX 128 - /* Declarations */ #ifdef WIN32 @@ -147,162 +144,6 @@ void BLI_stringenc(char *string, const char *head, const char *tail, unsigned sh sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail); } -/** - * Looks for a numeric suffix preceded by delim character on the end of - * name, puts preceding part into *left and value of suffix into *nr. - * Returns the length of *left. - * - * Foo.001 -> "Foo", 1 - * Returning the length of "Foo" - * - * \param left Where to return copy of part preceding delim - * \param nr Where to return value of numeric suffix - * \param name String to split - * \param delim Delimiter character - * \return Length of \a left - */ -int BLI_split_name_num(char *left, int *nr, const char *name, const char delim) -{ - const int name_len = strlen(name); - - *nr = 0; - memcpy(left, name, (name_len + 1) * sizeof(char)); - - /* name doesn't end with a delimiter "foo." */ - if ((name_len > 1 && name[name_len - 1] == delim) == 0) { - int a = name_len; - while (a--) { - if (name[a] == delim) { - left[a] = '\0'; /* truncate left part here */ - *nr = atol(name + a + 1); - /* casting down to an int, can overflow for large numbers */ - if (*nr < 0) - *nr = 0; - return a; - } - else if (isdigit(name[a]) == 0) { - /* non-numeric suffix - give up */ - break; - } - } - } - - return name_len; -} - -/** - * Ensures name is unique (according to criteria specified by caller in unique_check callback), - * incrementing its numeric suffix as necessary. Returns true if name had to be adjusted. - * - * \param unique_check Return true if name is not unique - * \param arg Additional arg to unique_check--meaning is up to caller - * \param defname To initialize name if latter is empty - * \param delim Delimits numeric suffix in name - * \param name Name to be ensured unique - * \param name_len Maximum length of name area - * \return true if there if the name was changed - */ -bool BLI_uniquename_cb(bool (*unique_check)(void *arg, const char *name), - void *arg, const char *defname, char delim, char *name, int name_len) -{ - if (name[0] == '\0') { - BLI_strncpy(name, defname, name_len); - } - - if (unique_check(arg, name)) { - char numstr[16]; - char tempname[UNIQUE_NAME_MAX]; - char left[UNIQUE_NAME_MAX]; - int number; - int len = BLI_split_name_num(left, &number, name, delim); - do { - /* add 1 to account for \0 */ - const int numlen = BLI_snprintf(numstr, sizeof(numstr), "%c%03d", delim, ++number) + 1; - - /* highly unlikely the string only has enough room for the number - * but support anyway */ - if ((len == 0) || (numlen >= name_len)) { - /* number is know not to be utf-8 */ - BLI_strncpy(tempname, numstr, name_len); - } - else { - char *tempname_buf; - tempname_buf = tempname + BLI_strncpy_utf8_rlen(tempname, left, name_len - numlen); - memcpy(tempname_buf, numstr, numlen); - } - } while (unique_check(arg, tempname)); - - BLI_strncpy(name, tempname, name_len); - - return true; - } - - return false; -} - -/* little helper macro for BLI_uniquename */ -#ifndef GIVE_STRADDR -# define GIVE_STRADDR(data, offset) ( ((char *)data) + offset) -#endif - -/* Generic function to set a unique name. It is only designed to be used in situations - * where the name is part of the struct, and also that the name is at most UNIQUE_NAME_MAX chars long. - * - * For places where this is used, see constraint.c for example... - * - * name_offs: should be calculated using offsetof(structname, membername) macro from stddef.h - * len: maximum length of string (to prevent overflows, etc.) - * defname: the name that should be used by default if none is specified already - * delim: the character which acts as a delimiter between parts of the name - */ -static bool uniquename_find_dupe(ListBase *list, void *vlink, const char *name, int name_offs) -{ - Link *link; - - for (link = list->first; link; link = link->next) { - if (link != vlink) { - if (STREQ(GIVE_STRADDR(link, name_offs), name)) { - return true; - } - } - } - - return false; -} - -static bool uniquename_unique_check(void *arg, const char *name) -{ - struct {ListBase *lb; void *vlink; int name_offs; } *data = arg; - return uniquename_find_dupe(data->lb, data->vlink, name, data->name_offs); -} - -/** - * Ensures that the specified block has a unique name within the containing list, - * incrementing its numeric suffix as necessary. Returns true if name had to be adjusted. - * - * \param list List containing the block - * \param vlink The block to check the name for - * \param defname To initialize block name if latter is empty - * \param delim Delimits numeric suffix in name - * \param name_offs Offset of name within block structure - * \param name_len Maximum length of name area - */ -bool BLI_uniquename(ListBase *list, void *vlink, const char *defname, char delim, int name_offs, int name_len) -{ - struct {ListBase *lb; void *vlink; int name_offs; } data; - data.lb = list; - data.vlink = vlink; - data.name_offs = name_offs; - - assert((name_len > 1) && (name_len <= UNIQUE_NAME_MAX)); - - /* See if we are given an empty string */ - if (ELEM(NULL, vlink, defname)) - return false; - - return BLI_uniquename_cb(uniquename_unique_check, &data, defname, delim, GIVE_STRADDR(vlink, name_offs), name_len); -} - static int BLI_path_unc_prefix_len(const char *path); /* defined below in same file */ /* ******************** string encoding ***************** */ diff --git a/source/blender/blenlib/intern/string_utf8.c b/source/blender/blenlib/intern/string_utf8.c index 0ab11810b48..83d4a75952f 100644 --- a/source/blender/blenlib/intern/string_utf8.c +++ b/source/blender/blenlib/intern/string_utf8.c @@ -74,7 +74,7 @@ static const size_t utf8_skip_data[256] = { * * \return the offset of the first invalid byte. */ -int BLI_utf8_invalid_byte(const char *str, int length) +ptrdiff_t BLI_utf8_invalid_byte(const char *str, size_t length) { const unsigned char *p, *perr, *pend = (const unsigned char *)str + length; unsigned char c; @@ -161,18 +161,24 @@ int BLI_utf8_invalid_byte(const char *str, int length) utf8_error: - return (int)((const char *)perr - (const char *)str); + return ((const char *)perr - (const char *)str); } -int BLI_utf8_invalid_strip(char *str, int length) +/** + * Remove any invalid utf-8 byte (taking into account multi-bytes sequence of course). + * + * @return number of stripped bytes. + */ +int BLI_utf8_invalid_strip(char *str, size_t length) { - int bad_char, tot = 0; + ptrdiff_t bad_char; + int tot = 0; BLI_assert(str[length] == '\0'); while ((bad_char = BLI_utf8_invalid_byte(str, length)) != -1) { str += bad_char; - length -= (bad_char + 1); + length -= (size_t)(bad_char + 1); if (length == 0) { /* last character bad, strip it */ @@ -182,7 +188,7 @@ int BLI_utf8_invalid_strip(char *str, int length) } else { /* strip, keep looking */ - memmove(str, str + 1, (size_t)length + 1); /* +1 for NULL char! */ + memmove(str, str + 1, length + 1); /* +1 for NULL char! */ tot++; } } diff --git a/source/blender/blenlib/intern/string_utils.c b/source/blender/blenlib/intern/string_utils.c new file mode 100644 index 00000000000..8d91a55a5ad --- /dev/null +++ b/source/blender/blenlib/intern/string_utils.c @@ -0,0 +1,385 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017 by the Blender FOundation. + * All rights reserved. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + * + */ + +/** \file blender/blenlib/intern/string_utils.c + * \ingroup bli + */ + +#include <ctype.h> +#include <string.h> +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_string_utf8.h" +#include "BLI_string_utils.h" +#include "BLI_utildefines.h" + +#include "DNA_listBase.h" + + +#ifdef __GNUC__ +# pragma GCC diagnostic error "-Wsign-conversion" +#endif + +/** + * Looks for a numeric suffix preceded by delim character on the end of + * name, puts preceding part into *left and value of suffix into *nr. + * Returns the length of *left. + * + * Foo.001 -> "Foo", 1 + * Returning the length of "Foo" + * + * \param left Where to return copy of part preceding delim + * \param nr Where to return value of numeric suffix + * \param name String to split + * \param delim Delimiter character + * \return Length of \a left + */ +size_t BLI_split_name_num(char *left, int *nr, const char *name, const char delim) +{ + const size_t name_len = strlen(name); + + *nr = 0; + memcpy(left, name, (name_len + 1) * sizeof(char)); + + /* name doesn't end with a delimiter "foo." */ + if ((name_len > 1 && name[name_len - 1] == delim) == 0) { + size_t a = name_len; + while (a--) { + if (name[a] == delim) { + left[a] = '\0'; /* truncate left part here */ + *nr = atol(name + a + 1); + /* casting down to an int, can overflow for large numbers */ + if (*nr < 0) + *nr = 0; + return a; + } + else if (isdigit(name[a]) == 0) { + /* non-numeric suffix - give up */ + break; + } + } + } + + return name_len; +} + +static bool is_char_sep(const char c) +{ + return ELEM(c, '.', ' ', '-', '_'); +} + +/** + * based on `BLI_split_dirfile()` / `os.path.splitext()`, + * `"a.b.c"` -> (`"a.b"`, `".c"`). + */ +void BLI_string_split_suffix(const char *string, char *r_body, char *r_suf, const size_t str_len) +{ + size_t len = BLI_strnlen(string, str_len); + size_t i; + + r_body[0] = r_suf[0] = '\0'; + + for (i = len; i > 0; i--) { + if (is_char_sep(string[i])) { + BLI_strncpy(r_body, string, i + 1); + BLI_strncpy(r_suf, string + i, (len + 1) - i); + return; + } + } + + memcpy(r_body, string, len + 1); +} + +/** + * `"a.b.c"` -> (`"a."`, `"b.c"`) + */ +void BLI_string_split_prefix(const char *string, char *r_pre, char *r_body, const size_t str_len) +{ + size_t len = BLI_strnlen(string, str_len); + size_t i; + + r_body[0] = r_pre[0] = '\0'; + + for (i = 1; i < len; i++) { + if (is_char_sep(string[i])) { + i++; + BLI_strncpy(r_pre, string, i + 1); + BLI_strncpy(r_body, string + i, (len + 1) - i); + return; + } + } + + BLI_strncpy(r_body, string, len); +} + +/** + * Finds the best possible flipped (left/right) name. For renaming; check for unique names afterwards. + * + * \param r_name flipped name, assumed to be a pointer to a string of at least \a name_len size. + * \param from_name original name, assumed to be a pointer to a string of at least \a name_len size. + * \param strip_number If set, remove number extensions. + */ +void BLI_string_flip_side_name(char *r_name, const char *from_name, const bool strip_number, const size_t name_len) +{ + size_t len; + char *prefix = alloca(name_len); /* The part before the facing */ + char *suffix = alloca(name_len); /* The part after the facing */ + char *replace = alloca(name_len); /* The replacement string */ + char *number = alloca(name_len); /* The number extension string */ + char *index = NULL; + bool is_set = false; + + *prefix = *suffix = *replace = *number = '\0'; + + /* always copy the name, since this can be called with an uninitialized string */ + BLI_strncpy(r_name, from_name, name_len); + + len = BLI_strnlen(from_name, name_len); + if (len < 3) { + /* we don't do names like .R or .L */ + return; + } + + /* We first check the case with a .### extension, let's find the last period */ + if (isdigit(r_name[len - 1])) { + index = strrchr(r_name, '.'); // last occurrence + if (index && isdigit(index[1])) { // doesnt handle case bone.1abc2 correct..., whatever! + if (strip_number == false) { + BLI_strncpy(number, index, name_len); + } + *index = 0; + len = BLI_strnlen(r_name, name_len); + } + } + + BLI_strncpy(prefix, r_name, name_len); + + /* first case; separator . - _ with extensions r R l L */ + if ((len > 1) && is_char_sep(r_name[len - 2])) { + is_set = true; + switch (r_name[len - 1]) { + case 'l': + prefix[len - 1] = 0; + strcpy(replace, "r"); + break; + case 'r': + prefix[len - 1] = 0; + strcpy(replace, "l"); + break; + case 'L': + prefix[len - 1] = 0; + strcpy(replace, "R"); + break; + case 'R': + prefix[len - 1] = 0; + strcpy(replace, "L"); + break; + default: + is_set = false; + } + } + + /* case; beginning with r R l L, with separator after it */ + if (!is_set && is_char_sep(r_name[1])) { + is_set = true; + switch (r_name[0]) { + case 'l': + strcpy(replace, "r"); + BLI_strncpy(suffix, r_name + 1, name_len); + prefix[0] = 0; + break; + case 'r': + strcpy(replace, "l"); + BLI_strncpy(suffix, r_name + 1, name_len); + prefix[0] = 0; + break; + case 'L': + strcpy(replace, "R"); + BLI_strncpy(suffix, r_name + 1, name_len); + prefix[0] = 0; + break; + case 'R': + strcpy(replace, "L"); + BLI_strncpy(suffix, r_name + 1, name_len); + prefix[0] = 0; + break; + default: + is_set = false; + } + } + + if (!is_set && len > 5) { + /* hrms, why test for a separator? lets do the rule 'ultimate left or right' */ + if (((index = BLI_strcasestr(prefix, "right")) == prefix) || + (index == prefix + len - 5)) + { + is_set = true; + if (index[0] == 'r') { + strcpy(replace, "left"); + } + else { + strcpy(replace, (index[1] == 'I') ? "LEFT" : "Left"); + } + *index = 0; + BLI_strncpy(suffix, index + 5, name_len); + } + else if (((index = BLI_strcasestr(prefix, "left")) == prefix) || + (index == prefix + len - 4)) + { + is_set = true; + if (index[0] == 'l') { + strcpy(replace, "right"); + } + else { + strcpy(replace, (index[1] == 'E') ? "RIGHT" : "Right"); + } + *index = 0; + BLI_strncpy(suffix, index + 4, name_len); + } + } + + BLI_snprintf(r_name, name_len, "%s%s%s%s", prefix, replace, suffix, number); +} + + +/* Unique name utils. */ + +/** + * Ensures name is unique (according to criteria specified by caller in unique_check callback), + * incrementing its numeric suffix as necessary. Returns true if name had to be adjusted. + * + * \param unique_check Return true if name is not unique + * \param arg Additional arg to unique_check--meaning is up to caller + * \param defname To initialize name if latter is empty + * \param delim Delimits numeric suffix in name + * \param name Name to be ensured unique + * \param name_len Maximum length of name area + * \return true if there if the name was changed + */ +bool BLI_uniquename_cb( + UniquenameCheckCallback unique_check, void *arg, const char *defname, char delim, char *name, size_t name_len) +{ + if (name[0] == '\0') { + BLI_strncpy(name, defname, name_len); + } + + if (unique_check(arg, name)) { + char numstr[16]; + char *tempname = alloca(name_len); + char *left = alloca(name_len); + int number; + size_t len = BLI_split_name_num(left, &number, name, delim); + do { + /* add 1 to account for \0 */ + const size_t numlen = BLI_snprintf(numstr, sizeof(numstr), "%c%03d", delim, ++number) + 1; + + /* highly unlikely the string only has enough room for the number + * but support anyway */ + if ((len == 0) || (numlen >= name_len)) { + /* number is know not to be utf-8 */ + BLI_strncpy(tempname, numstr, name_len); + } + else { + char *tempname_buf; + tempname_buf = tempname + BLI_strncpy_utf8_rlen(tempname, left, name_len - numlen); + memcpy(tempname_buf, numstr, numlen); + } + } while (unique_check(arg, tempname)); + + BLI_strncpy(name, tempname, name_len); + + return true; + } + + return false; +} + +/* little helper macro for BLI_uniquename */ +#ifndef GIVE_STRADDR +# define GIVE_STRADDR(data, offset) ( ((char *)data) + offset) +#endif + +/* Generic function to set a unique name. It is only designed to be used in situations + * where the name is part of the struct. + * + * For places where this is used, see constraint.c for example... + * + * name_offs: should be calculated using offsetof(structname, membername) macro from stddef.h + * len: maximum length of string (to prevent overflows, etc.) + * defname: the name that should be used by default if none is specified already + * delim: the character which acts as a delimiter between parts of the name + */ +static bool uniquename_find_dupe(ListBase *list, void *vlink, const char *name, int name_offs) +{ + Link *link; + + for (link = list->first; link; link = link->next) { + if (link != vlink) { + if (STREQ(GIVE_STRADDR(link, name_offs), name)) { + return true; + } + } + } + + return false; +} + +static bool uniquename_unique_check(void *arg, const char *name) +{ + struct {ListBase *lb; void *vlink; int name_offs; } *data = arg; + return uniquename_find_dupe(data->lb, data->vlink, name, data->name_offs); +} + +/** + * Ensures that the specified block has a unique name within the containing list, + * incrementing its numeric suffix as necessary. Returns true if name had to be adjusted. + * + * \param list List containing the block + * \param vlink The block to check the name for + * \param defname To initialize block name if latter is empty + * \param delim Delimits numeric suffix in name + * \param name_offs Offset of name within block structure + * \param name_len Maximum length of name area + */ +bool BLI_uniquename(ListBase *list, void *vlink, const char *defname, char delim, int name_offs, size_t name_len) +{ + struct {ListBase *lb; void *vlink; int name_offs; } data; + data.lb = list; + data.vlink = vlink; + data.name_offs = name_offs; + + BLI_assert(name_len > 1); + + /* See if we are given an empty string */ + if (ELEM(NULL, vlink, defname)) + return false; + + return BLI_uniquename_cb(uniquename_unique_check, &data, defname, delim, GIVE_STRADDR(vlink, name_offs), name_len); +} diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 24361a68bb2..8a3e493088b 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -3244,6 +3244,11 @@ static void direct_link_constraints(FileData *fd, ListBase *lb) con->flag |= CONSTRAINT_SPACEONCE; break; } + case CONSTRAINT_TYPE_TRANSFORM_CACHE: + { + bTransformCacheConstraint *data = con->data; + data->reader = NULL; + } } } } @@ -4703,12 +4708,12 @@ static void direct_link_latt(FileData *fd, Lattice *lt) /* ************ READ OBJECT ***************** */ static void lib_link_modifiers__linkModifiers( - void *userData, Object *ob, ID **idpoin, int cd_flag) + void *userData, Object *ob, ID **idpoin, int cb_flag) { FileData *fd = userData; *idpoin = newlibadr(fd, ob->id.lib, *idpoin); - if (*idpoin != NULL && (cd_flag & IDWALK_USER) != 0) { + if (*idpoin != NULL && (cb_flag & IDWALK_CB_USER) != 0) { id_us_plus_no_lib(*idpoin); } } @@ -5932,6 +5937,7 @@ static void direct_link_scene(FileData *fd, Scene *sce) sce->toolsettings->wpaint->wpaint_prev = NULL; sce->toolsettings->wpaint->tot = 0; } + /* relink grease pencil drawing brushes */ link_list(fd, &sce->toolsettings->gp_brushes); for (bGPDbrush *brush = sce->toolsettings->gp_brushes.first; brush; brush = brush->next) { @@ -5948,6 +5954,12 @@ static void direct_link_scene(FileData *fd, Scene *sce) direct_link_curvemapping(fd, brush->cur_jitter); } } + + /* relink grease pencil interpolation curves */ + sce->toolsettings->gp_interpolate.custom_ipo = newdataadr(fd, sce->toolsettings->gp_interpolate.custom_ipo); + if (sce->toolsettings->gp_interpolate.custom_ipo) { + direct_link_curvemapping(fd, sce->toolsettings->gp_interpolate.custom_ipo); + } } if (sce->ed) { @@ -8386,9 +8398,10 @@ static void do_versions(FileData *fd, Library *lib, Main *main) static void do_versions_after_linking(Main *main) { - UNUSED_VARS(main); // printf("%s for %s (%s), %d.%d\n", __func__, main->curlib ? main->curlib->name : main->name, // main->curlib ? "LIB" : "MAIN", main->versionfile, main->subversionfile); + + do_versions_after_linking_270(main); } static void lib_link_all(FileData *fd, Main *main) @@ -9295,7 +9308,7 @@ static void expand_armature(FileData *fd, Main *mainvar, bArmature *arm) } static void expand_object_expandModifiers( - void *userData, Object *UNUSED(ob), ID **idpoin, int UNUSED(cd_flag)) + void *userData, Object *UNUSED(ob), ID **idpoin, int UNUSED(cb_flag)) { struct { FileData *fd; Main *mainvar; } *data= userData; diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index 7719aaa2b0d..d97bef13a78 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -170,5 +170,7 @@ void blo_do_versions_250(struct FileData *fd, struct Library *lib, struct Main * void blo_do_versions_260(struct FileData *fd, struct Library *lib, struct Main *main); void blo_do_versions_270(struct FileData *fd, struct Library *lib, struct Main *main); +void do_versions_after_linking_270(struct Main *main); + #endif diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 907baab0aee..ab3d14af882 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -56,6 +56,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_string_utils.h" #include "BLT_translation.h" diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 410e186ca2c..7df5a77595a 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -34,6 +34,7 @@ /* allow readfile to use deprecated functionality */ #define DNA_DEPRECATED_ALLOW +#include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_brush_types.h" #include "DNA_camera_types.h" @@ -58,6 +59,7 @@ #include "DNA_genfile.h" +#include "BKE_animsys.h" #include "BKE_colortools.h" #include "BKE_library.h" #include "BKE_main.h" @@ -78,6 +80,9 @@ #include "NOD_composite.h" +#include "NOD_common.h" +#include "NOD_socket.h" + #include "readfile.h" #include "MEM_guardedalloc.h" @@ -197,6 +202,54 @@ static void do_version_bones_super_bbone(ListBase *lb) } } +/* TODO(sergey): Consider making it somewhat more generic function in BLI_anim.h. */ +static void anim_change_prop_name(FCurve *fcu, + const char *prefix, + const char *old_prop_name, + const char *new_prop_name) +{ + const char *old_path = BLI_sprintfN("%s.%s", prefix, old_prop_name); + if (STREQ(fcu->rna_path, old_path)) { + MEM_freeN(fcu->rna_path); + fcu->rna_path = BLI_sprintfN("%s.%s", prefix, new_prop_name); + } + MEM_freeN((char *)old_path); +} + +static void do_version_hue_sat_node(bNodeTree *ntree, bNode *node) +{ + if (node->storage == NULL) { + return; + } + + /* Make sure new sockets are properly created. */ + node_verify_socket_templates(ntree, node); + /* Convert value from old storage to new sockets. */ + NodeHueSat *nhs = node->storage; + bNodeSocket *hue = nodeFindSocket(node, SOCK_IN, "Hue"), + *saturation = nodeFindSocket(node, SOCK_IN, "Saturation"), + *value = nodeFindSocket(node, SOCK_IN, "Value"); + ((bNodeSocketValueFloat *)hue->default_value)->value = nhs->hue; + ((bNodeSocketValueFloat *)saturation->default_value)->value = nhs->sat; + ((bNodeSocketValueFloat *)value->default_value)->value = nhs->val; + /* Take care of possible animation. */ + AnimData *adt = BKE_animdata_from_id(&ntree->id); + if (adt != NULL && adt->action != NULL) { + const char *prefix = BLI_sprintfN("nodes[\"%s\"]", node->name); + for (FCurve *fcu = adt->action->curves.first; fcu != NULL; fcu = fcu->next) { + if (STRPREFIX(fcu->rna_path, prefix)) { + anim_change_prop_name(fcu, prefix, "color_hue", "inputs[1].default_value"); + anim_change_prop_name(fcu, prefix, "color_saturation", "inputs[2].default_value"); + anim_change_prop_name(fcu, prefix, "color_value", "inputs[3].default_value"); + } + } + MEM_freeN((char *)prefix); + } + /* Free storage, it is no longer used. */ + MEM_freeN(node->storage); + node->storage = NULL; +} + void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) { if (!MAIN_VERSION_ATLEAST(main, 270, 0)) { @@ -1493,6 +1546,36 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) for (Brush *br = main->brush.first; br; br = br->id.next) { br->fill_threshold /= sqrt_3; } + + /* Custom motion paths */ + if (!DNA_struct_elem_find(fd->filesdna, "bMotionPath", "int", "line_thickness")) { + Object *ob; + for (ob = main->object.first; ob; ob = ob->id.next) { + bMotionPath *mpath; + bPoseChannel *pchan; + mpath = ob->mpath; + if (mpath) { + mpath->color[0] = 1.0f; + mpath->color[1] = 0.0f; + mpath->color[2] = 0.0f; + mpath->line_thickness = 1; + mpath->flag |= MOTIONPATH_FLAG_LINES; + } + /* bones motion path */ + if (ob->pose) { + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + mpath = pchan->mpath; + if (mpath) { + mpath->color[0] = 1.0f; + mpath->color[1] = 0.0f; + mpath->color[2] = 0.0f; + mpath->line_thickness = 1; + mpath->flag |= MOTIONPATH_FLAG_LINES; + } + } + } + } + } } /* To be added to next subversion bump! */ @@ -1536,3 +1619,20 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) } FOREACH_NODETREE_END } } + +void do_versions_after_linking_270(Main *main) +{ + /* To be added to next subversion bump! */ + { + FOREACH_NODETREE(main, ntree, id) { + if (ntree->type == NTREE_COMPOSIT) { + ntreeSetTypes(NULL, ntree); + for (bNode *node = ntree->nodes.first; node; node = node->next) { + if (node->type == CMP_NODE_HUE_SAT) { + do_version_hue_sat_node(ntree, node); + } + } + } + } FOREACH_NODETREE_END + } +} diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 343aed390e6..5794c99c1bb 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2691,6 +2691,10 @@ static void write_scenes(WriteData *wd, ListBase *scebase) write_curvemapping(wd, brush->cur_jitter); } } + /* write grease-pencil custom ipo curve to file */ + if (tos->gp_interpolate.custom_ipo) { + write_curvemapping(wd, tos->gp_interpolate.custom_ipo); + } write_paint(wd, &tos->imapaint.paint); diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index 132a7ccd4fa..e46a31cb2e9 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -387,15 +387,11 @@ BMFace *BM_face_create_ngon_verts( * * \note Since this is a vcloud there is no direction. */ -BMFace *BM_face_create_ngon_vcloud( - BMesh *bm, BMVert **vert_arr, int len, - const BMFace *f_example, const eBMCreateFlag create_flag) +void BM_verts_sort_radial_plane(BMVert **vert_arr, int len) { struct SortIntByFloat *vang = BLI_array_alloca(vang, len); BMVert **vert_arr_map = BLI_array_alloca(vert_arr_map, len); - BMFace *f; - float totv_inv = 1.0f / (float)len; int i = 0; @@ -470,26 +466,9 @@ BMFace *BM_face_create_ngon_vcloud( /* now calculate every points angle around the normal (signed) */ for (i = 0; i < len; i++) { - float co[3]; - float proj_vec[3]; - float angle; - - /* center relative vec */ - sub_v3_v3v3(co, vert_arr[i]->co, cent); - - /* align to plane */ - project_v3_v3v3(proj_vec, co, nor); - sub_v3_v3(co, proj_vec); - - /* now 'co' is valid - we can compare its angle against the far vec */ - angle = angle_v3v3(far_vec, co); - - if (dot_v3v3(co, sign_vec) < 0.0f) { - angle = -angle; - } - - vang[i].sort_value = angle; + vang[i].sort_value = angle_signed_on_axis_v3v3v3_v3(far, cent, vert_arr[i]->co, nor); vang[i].data = i; + vert_arr_map[i] = vert_arr[i]; } /* sort by angle and magic! - we have our ngon */ @@ -497,14 +476,9 @@ BMFace *BM_face_create_ngon_vcloud( /* --- */ - /* create edges and find the winding (if faces are attached to any existing edges) */ for (i = 0; i < len; i++) { - vert_arr_map[i] = vert_arr[vang[i].data]; + vert_arr[i] = vert_arr_map[vang[i].data]; } - - f = BM_face_create_ngon_verts(bm, vert_arr_map, len, f_example, create_flag, true, true); - - return f; } /*************************************************************/ diff --git a/source/blender/bmesh/intern/bmesh_construct.h b/source/blender/bmesh/intern/bmesh_construct.h index 9c6483de42b..a52a17cd2f3 100644 --- a/source/blender/bmesh/intern/bmesh_construct.h +++ b/source/blender/bmesh/intern/bmesh_construct.h @@ -34,6 +34,9 @@ bool BM_verts_from_edges(BMVert **vert_arr, BMEdge **edge_arr, const int len); bool BM_edges_from_verts(BMEdge **edge_arr, BMVert **vert_arr, const int len); void BM_edges_from_verts_ensure(BMesh *bm, BMEdge **edge_arr, BMVert **vert_arr, const int len); +/* sort before creation */ +void BM_verts_sort_radial_plane(BMVert **vert_arr, int len); + BMFace *BM_face_create_quad_tri( BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, const BMFace *f_example, const eBMCreateFlag create_flag); @@ -50,10 +53,6 @@ BMFace *BM_face_create_ngon_verts( const BMFace *f_example, const eBMCreateFlag create_flag, const bool calc_winding, const bool create_edges); -BMFace *BM_face_create_ngon_vcloud( - BMesh *bm, BMVert **vert_arr, int len, - const BMFace *f_example, const eBMCreateFlag create_flag); - void BM_elem_attrs_copy_ex( BMesh *bm_src, BMesh *bm_dst, const void *ele_src_v, void *ele_dst_v, const char hflag_mask); diff --git a/source/blender/bmesh/intern/bmesh_marking.c b/source/blender/bmesh/intern/bmesh_marking.c index 7178a8132d2..7f2032d5f53 100644 --- a/source/blender/bmesh/intern/bmesh_marking.c +++ b/source/blender/bmesh/intern/bmesh_marking.c @@ -70,7 +70,7 @@ static void recount_totsels(BMesh *bm) } } -/** \name BMesh helper functions for selection flushing. +/** \name BMesh helper functions for selection & hide flushing. * \{ */ static bool bm_vert_is_edge_select_any_other(const BMVert *v, const BMEdge *e_first) @@ -102,6 +102,20 @@ static bool bm_vert_is_edge_select_any(const BMVert *v) } #endif +static bool bm_vert_is_edge_visible_any(const BMVert *v) +{ + if (v->e) { + const BMEdge *e_iter, *e_first; + e_iter = e_first = v->e; + do { + if (!BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN)) { + return true; + } + } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first); + } + return false; +} + static bool bm_edge_is_face_select_any_other(BMLoop *l_first) { const BMLoop *l_iter = l_first; @@ -131,6 +145,20 @@ static bool bm_edge_is_face_select_any(const BMEdge *e) } #endif +static bool bm_edge_is_face_visible_any(const BMEdge *e) +{ + if (e->l) { + const BMLoop *l_iter, *l_first; + l_iter = l_first = e->l; + do { + if (!BM_elem_flag_test(l_iter->f, BM_ELEM_HIDDEN)) { + return true; + } + } while ((l_iter = l_iter->radial_next) != l_first); + } + return false; +} + /** \} */ /** @@ -1198,87 +1226,111 @@ void BM_mesh_elem_hflag_enable_all( /***************** Mesh Hiding stuff *********** */ +/** + * Hide unless any connected elements are visible. + * Run this after hiding a connected edge or face. + */ static void vert_flush_hide_set(BMVert *v) { - BMIter iter; - BMEdge *e; - bool hide = true; - - BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { - hide = hide && BM_elem_flag_test(e, BM_ELEM_HIDDEN); - } - - BM_elem_flag_set(v, BM_ELEM_HIDDEN, hide); + BM_elem_flag_set(v, BM_ELEM_HIDDEN, !bm_vert_is_edge_visible_any(v)); } -static void edge_flush_hide(BMEdge *e) +/** + * Hide unless any connected elements are visible. + * Run this after hiding a connected face. + */ +static void edge_flush_hide_set(BMEdge *e) { - BMIter iter; - BMFace *f; - bool hide = true; - - BM_ITER_ELEM (f, &iter, e, BM_FACES_OF_EDGE) { - hide = hide && BM_elem_flag_test(f, BM_ELEM_HIDDEN); - } - - BM_elem_flag_set(e, BM_ELEM_HIDDEN, hide); + BM_elem_flag_set(e, BM_ELEM_HIDDEN, !bm_edge_is_face_visible_any(e)); } void BM_vert_hide_set(BMVert *v, const bool hide) { /* vert hiding: vert + surrounding edges and faces */ - BMIter iter, fiter; - BMEdge *e; - BMFace *f; - BLI_assert(v->head.htype == BM_VERT); + if (hide) { + BLI_assert(!BM_elem_flag_test(v, BM_ELEM_SELECT)); + } BM_elem_flag_set(v, BM_ELEM_HIDDEN, hide); - BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { - BM_elem_flag_set(e, BM_ELEM_HIDDEN, hide); - - BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) { - BM_elem_flag_set(f, BM_ELEM_HIDDEN, hide); - } + if (v->e) { + BMEdge *e_iter, *e_first; + e_iter = e_first = v->e; + do { + BM_elem_flag_set(e_iter, BM_ELEM_HIDDEN, hide); + if (e_iter->l) { + const BMLoop *l_radial_iter, *l_radial_first; + l_radial_iter = l_radial_first = e_iter->l; + do { + BM_elem_flag_set(l_radial_iter->f, BM_ELEM_HIDDEN, hide); + } while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_first); + } + } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first); } } void BM_edge_hide_set(BMEdge *e, const bool hide) { - BMIter iter; - BMFace *f; - /* BMVert *v; */ - BLI_assert(e->head.htype == BM_EDGE); + if (hide) { + BLI_assert(!BM_elem_flag_test(e, BM_ELEM_SELECT)); + } /* edge hiding: faces around the edge */ - BM_ITER_ELEM (f, &iter, e, BM_FACES_OF_EDGE) { - BM_elem_flag_set(f, BM_ELEM_HIDDEN, hide); + if (e->l) { + const BMLoop *l_iter, *l_first; + l_iter = l_first = e->l; + do { + BM_elem_flag_set(l_iter->f, BM_ELEM_HIDDEN, hide); + } while ((l_iter = l_iter->radial_next) != l_first); } BM_elem_flag_set(e, BM_ELEM_HIDDEN, hide); /* hide vertices if necessary */ - vert_flush_hide_set(e->v1); - vert_flush_hide_set(e->v2); + if (hide) { + vert_flush_hide_set(e->v1); + vert_flush_hide_set(e->v2); + } + else { + BM_elem_flag_disable(e->v1, BM_ELEM_HIDDEN); + BM_elem_flag_disable(e->v2, BM_ELEM_HIDDEN); + } } void BM_face_hide_set(BMFace *f, const bool hide) { - BMIter iter; - BMLoop *l; - BLI_assert(f->head.htype == BM_FACE); + if (hide) { + BLI_assert(!BM_elem_flag_test(f, BM_ELEM_SELECT)); + } BM_elem_flag_set(f, BM_ELEM_HIDDEN, hide); - BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) { - edge_flush_hide(l->e); + if (hide) { + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter; + + l_iter = l_first; + do { + edge_flush_hide_set(l_iter->e); + } while ((l_iter = l_iter->next) != l_first); + + l_iter = l_first; + do { + vert_flush_hide_set(l_iter->v); + } while ((l_iter = l_iter->next) != l_first); } + else { + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter; - BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) { - vert_flush_hide_set(l->v); + l_iter = l_first; + do { + BM_elem_flag_disable(l_iter->e, BM_ELEM_HIDDEN); + BM_elem_flag_disable(l_iter->v, BM_ELEM_HIDDEN); + } while ((l_iter = l_iter->next) != l_first); } } diff --git a/source/blender/bmesh/operators/bmo_create.c b/source/blender/bmesh/operators/bmo_create.c index 7b8cb36ab59..a980baf8626 100644 --- a/source/blender/bmesh/operators/bmo_create.c +++ b/source/blender/bmesh/operators/bmo_create.c @@ -290,7 +290,11 @@ void bmo_contextual_create_exec(BMesh *bm, BMOperator *op) BMFace *f; BMO_iter_as_array(op->slots_in, "geom", BM_VERT, (void **)vert_arr, totv); - f = BM_face_create_ngon_vcloud(bm, vert_arr, totv, NULL, BM_CREATE_NO_DOUBLE); + + BM_verts_sort_radial_plane(vert_arr, totv); + + /* create edges and find the winding (if faces are attached to any existing edges) */ + f = BM_face_create_ngon_verts(bm, vert_arr, totv, NULL, BM_CREATE_NO_DOUBLE, true, true); if (f) { BMO_face_flag_enable(bm, f, ELE_OUT); diff --git a/source/blender/bmesh/operators/bmo_inset.c b/source/blender/bmesh/operators/bmo_inset.c index c52c608e671..e2ff09669d7 100644 --- a/source/blender/bmesh/operators/bmo_inset.c +++ b/source/blender/bmesh/operators/bmo_inset.c @@ -647,6 +647,10 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op) } (void)0 #define VERT_ORIG_GET(_v) \ (const float *)BLI_ghash_lookup_default(vert_coords, (_v), (_v)->co) + /* memory for the coords isn't given back to the arena, + * acceptable in this case since it runs a fixed number of times. */ +#define VERT_ORIG_REMOVE(_v) \ + BLI_ghash_remove(vert_coords, (_v), NULL, NULL) for (i = 0, es = edge_info; i < edge_info_len; i++, es++) { @@ -972,7 +976,11 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op) v_glue = v_split; } else { - BM_vert_splice(bm, v_glue, v_split); + if (BM_vert_splice(bm, v_glue, v_split)) { + if (use_vert_coords_orig) { + VERT_ORIG_REMOVE(v_split); + } + } } } } diff --git a/source/blender/collada/AnimationImporter.cpp b/source/blender/collada/AnimationImporter.cpp index 5cd01eff263..3801c9300df 100644 --- a/source/blender/collada/AnimationImporter.cpp +++ b/source/blender/collada/AnimationImporter.cpp @@ -35,8 +35,8 @@ #include "BLI_listbase.h" #include "BLI_math.h" -#include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_string_utils.h" #include "BLT_translation.h" diff --git a/source/blender/collada/ArmatureExporter.cpp b/source/blender/collada/ArmatureExporter.cpp index 4f5cf83f5ca..9c26ba83b44 100644 --- a/source/blender/collada/ArmatureExporter.cpp +++ b/source/blender/collada/ArmatureExporter.cpp @@ -69,17 +69,21 @@ void ArmatureExporter::add_armature_bones(Object *ob_arm, Scene *sce, // write bone nodes bArmature * armature = (bArmature *)ob_arm->data; - ED_armature_to_edit(armature); + bool is_edited = armature->edbo != NULL; - bArmature *arm = (bArmature *)ob_arm->data; - for (Bone *bone = (Bone *)arm->bonebase.first; bone; bone = bone->next) { + if (!is_edited) + ED_armature_to_edit(armature); + + for (Bone *bone = (Bone *)armature->bonebase.first; bone; bone = bone->next) { // start from root bones if (!bone->parent) add_bone_node(bone, ob_arm, sce, se, child_objects); } - ED_armature_from_edit(armature); - ED_armature_edit_free(armature); + if (!is_edited) { + ED_armature_from_edit(armature); + ED_armature_edit_free(armature); + } } void ArmatureExporter::write_bone_URLs(COLLADASW::InstanceController &ins, Object *ob_arm, Bone *bone) diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cpp b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cpp index 29c296a896d..36bc176b1a6 100644 --- a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cpp +++ b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cpp @@ -37,11 +37,12 @@ HueSaturationValueNode::HueSaturationValueNode(bNode *editorNode) : Node(editorN void HueSaturationValueNode::convertToOperations(NodeConverter &converter, const CompositorContext &/*context*/) const { - NodeInput *valueSocket = this->getInputSocket(0); - NodeInput *colorSocket = this->getInputSocket(1); + NodeInput *colorSocket = this->getInputSocket(0); + NodeInput *hueSocket = this->getInputSocket(1); + NodeInput *saturationSocket = this->getInputSocket(2); + NodeInput *valueSocket = this->getInputSocket(3); + NodeInput *facSocket = this->getInputSocket(4); NodeOutput *outputSocket = this->getOutputSocket(0); - bNode *editorsnode = getbNode(); - NodeHueSat *storage = (NodeHueSat *)editorsnode->storage; ConvertRGBToHSVOperation *rgbToHSV = new ConvertRGBToHSVOperation(); converter.addOperation(rgbToHSV); @@ -50,9 +51,9 @@ void HueSaturationValueNode::convertToOperations(NodeConverter &converter, const converter.addOperation(hsvToRGB); ChangeHSVOperation *changeHSV = new ChangeHSVOperation(); - changeHSV->setHue(storage->hue); - changeHSV->setSaturation(storage->sat); - changeHSV->setValue(storage->val); + converter.mapInputSocket(hueSocket, changeHSV->getInputSocket(1)); + converter.mapInputSocket(saturationSocket, changeHSV->getInputSocket(2)); + converter.mapInputSocket(valueSocket, changeHSV->getInputSocket(3)); converter.addOperation(changeHSV); MixBlendOperation *blend = new MixBlendOperation(); @@ -64,6 +65,6 @@ void HueSaturationValueNode::convertToOperations(NodeConverter &converter, const converter.addLink(changeHSV->getOutputSocket(), hsvToRGB->getInputSocket(0)); converter.addLink(hsvToRGB->getOutputSocket(), blend->getInputSocket(2)); converter.mapInputSocket(colorSocket, blend->getInputSocket(1)); - converter.mapInputSocket(valueSocket, blend->getInputSocket(0)); + converter.mapInputSocket(facSocket, blend->getInputSocket(0)); converter.mapOutputSocket(outputSocket, blend->getOutputSocket()); } diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.cpp b/source/blender/compositor/operations/COM_ChangeHSVOperation.cpp index 964f1d64667..7ea974a41dc 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.cpp +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.cpp @@ -25,6 +25,9 @@ ChangeHSVOperation::ChangeHSVOperation() : NodeOperation() { this->addInputSocket(COM_DT_COLOR); + this->addInputSocket(COM_DT_VALUE); + this->addInputSocket(COM_DT_VALUE); + this->addInputSocket(COM_DT_VALUE); this->addOutputSocket(COM_DT_COLOR); this->m_inputOperation = NULL; } @@ -32,24 +35,34 @@ ChangeHSVOperation::ChangeHSVOperation() : NodeOperation() void ChangeHSVOperation::initExecution() { this->m_inputOperation = getInputSocketReader(0); + this->m_hueOperation = getInputSocketReader(1); + this->m_saturationOperation = getInputSocketReader(2); + this->m_valueOperation = getInputSocketReader(3); } void ChangeHSVOperation::deinitExecution() { this->m_inputOperation = NULL; + this->m_hueOperation = NULL; + this->m_saturationOperation = NULL; + this->m_valueOperation = NULL; } void ChangeHSVOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) { float inputColor1[4]; + float hue[4], saturation[4], value[4]; this->m_inputOperation->readSampled(inputColor1, x, y, sampler); + this->m_hueOperation->readSampled(hue, x, y, sampler); + this->m_saturationOperation->readSampled(saturation, x, y, sampler); + this->m_valueOperation->readSampled(value, x, y, sampler); - output[0] = inputColor1[0] + (this->m_hue - 0.5f); + output[0] = inputColor1[0] + (hue[0] - 0.5f); if (output[0] > 1.0f) output[0] -= 1.0f; else if (output[0] < 0.0f) output[0] += 1.0f; - output[1] = inputColor1[1] * this->m_saturation; - output[2] = inputColor1[2] * this->m_value; + output[1] = inputColor1[1] * saturation[0]; + output[2] = inputColor1[2] * value[0]; output[3] = inputColor1[3]; } diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.h b/source/blender/compositor/operations/COM_ChangeHSVOperation.h index 76025e86b7a..800c09c05ff 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.h +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.h @@ -32,10 +32,9 @@ class ChangeHSVOperation : public NodeOperation { private: SocketReader *m_inputOperation; - - float m_hue; - float m_saturation; - float m_value; + SocketReader *m_hueOperation; + SocketReader *m_saturationOperation; + SocketReader *m_valueOperation; public: /** @@ -51,9 +50,5 @@ public: */ void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); - void setHue(float hue) { this->m_hue = hue; } - void setSaturation(float saturation) { this->m_saturation = saturation; } - void setValue(float value) { this->m_value = value; } - }; #endif diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index e312c4e0dcb..6b7fb5246ca 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -121,7 +121,7 @@ struct BuilderWalkUserData { static void modifier_walk(void *user_data, struct Object * /*ob*/, struct Object **obpoin, - int /*cd_flag*/) + int /*cb_flag*/) { BuilderWalkUserData *data = (BuilderWalkUserData *)user_data; if (*obpoin) { @@ -320,6 +320,23 @@ OperationDepsNode *DepsgraphNodeBuilder::find_operation_node( /* **** Build functions for entity nodes **** */ +void DepsgraphNodeBuilder::begin_build(Main *bmain) { + /* LIB_TAG_DOIT is used to indicate whether node for given ID was already + * created or not. This flag is being set in add_id_node(), so functions + * shouldn't bother with setting it, they only might query this flag when + * needed. + */ + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + /* XXX nested node trees are not included in tag-clearing above, + * so we need to do this manually. + */ + FOREACH_NODETREE(bmain, nodetree, id) { + if (id != (ID *)nodetree) { + nodetree->id.tag &= ~LIB_TAG_DOIT; + } + } FOREACH_NODETREE_END +} + void DepsgraphNodeBuilder::build_group(Scene *scene, Base *base, Group *group) @@ -626,18 +643,18 @@ void DepsgraphNodeBuilder::build_world(World *world) } /* world itself */ - IDDepsNode *world_node = add_id_node(world_id); /* world shading/params? */ + add_id_node(world_id); /* world shading/params? */ build_animdata(world_id); /* TODO: other settings? */ /* textures */ - build_texture_stack(world_node, world->mtex); + build_texture_stack(world->mtex); /* world's nodetree */ if (world->nodetree) { - build_nodetree(world_node, world->nodetree); + build_nodetree(world->nodetree); } } @@ -807,13 +824,18 @@ void DepsgraphNodeBuilder::build_obdata_geom(Scene *scene, Object *ob) } /* materials */ - for (int a = 1; a <= ob->totcol; a++) { - Material *ma = give_current_material(ob, a); - if (ma != NULL) { - // XXX?! - ComponentDepsNode *geom_node = add_component_node(&ob->id, DEPSNODE_TYPE_GEOMETRY); - build_material(geom_node, ma); + if (ob->totcol != 0) { + for (int a = 1; a <= ob->totcol; a++) { + Material *ma = give_current_material(ob, a); + if (ma != NULL) { + build_material(ma); + } } + add_operation_node(&ob->id, + DEPSNODE_TYPE_SHADING, + DEPSOP_TYPE_EXEC, + NULL, + DEG_OPCODE_PLACEHOLDER, "Material Update"); } /* geometry collision */ @@ -976,7 +998,7 @@ void DepsgraphNodeBuilder::build_lamp(Object *ob) build_animdata(&la->id); /* node for obdata */ - ComponentDepsNode *param_node = add_component_node(lamp_id, DEPSNODE_TYPE_PARAMETERS); + add_component_node(lamp_id, DEPSNODE_TYPE_PARAMETERS); /* TODO(sergey): Is it really how we're supposed to work with drivers? */ add_operation_node(lamp_id, DEPSNODE_TYPE_PARAMETERS, DEPSOP_TYPE_EXEC, NULL, @@ -984,14 +1006,14 @@ void DepsgraphNodeBuilder::build_lamp(Object *ob) /* lamp's nodetree */ if (la->nodetree) { - build_nodetree(param_node, la->nodetree); + build_nodetree(la->nodetree); } /* textures */ - build_texture_stack(param_node, la->mtex); + build_texture_stack(la->mtex); } -void DepsgraphNodeBuilder::build_nodetree(DepsNode *owner_node, bNodeTree *ntree) +void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) { if (!ntree) return; @@ -1011,10 +1033,10 @@ void DepsgraphNodeBuilder::build_nodetree(DepsNode *owner_node, bNodeTree *ntree if (id != NULL) { short id_type = GS(id->name); if (id_type == ID_MA) { - build_material(owner_node, (Material *)id); + build_material((Material *)id); } else if (id_type == ID_TE) { - build_texture(owner_node, (Tex *)id); + build_texture((Tex *)id); } else if (id_type == ID_IM) { build_image((Image *)id); @@ -1022,7 +1044,7 @@ void DepsgraphNodeBuilder::build_nodetree(DepsNode *owner_node, bNodeTree *ntree else if (bnode->type == NODE_GROUP) { bNodeTree *group_ntree = (bNodeTree *)id; if ((group_ntree->id.tag & LIB_TAG_DOIT) == 0) { - build_nodetree(owner_node, group_ntree); + build_nodetree(group_ntree); } } } @@ -1032,7 +1054,7 @@ void DepsgraphNodeBuilder::build_nodetree(DepsNode *owner_node, bNodeTree *ntree } /* Recursively build graph for material */ -void DepsgraphNodeBuilder::build_material(DepsNode *owner_node, Material *ma) +void DepsgraphNodeBuilder::build_material(Material *ma) { ID *ma_id = &ma->id; if (ma_id->tag & LIB_TAG_DOIT) { @@ -1050,14 +1072,14 @@ void DepsgraphNodeBuilder::build_material(DepsNode *owner_node, Material *ma) build_animdata(ma_id); /* textures */ - build_texture_stack(owner_node, ma->mtex); + build_texture_stack(ma->mtex); /* material's nodetree */ - build_nodetree(owner_node, ma->nodetree); + build_nodetree(ma->nodetree); } /* Texture-stack attached to some shading datablock */ -void DepsgraphNodeBuilder::build_texture_stack(DepsNode *owner_node, MTex **texture_stack) +void DepsgraphNodeBuilder::build_texture_stack(MTex **texture_stack) { int i; @@ -1065,12 +1087,12 @@ void DepsgraphNodeBuilder::build_texture_stack(DepsNode *owner_node, MTex **text for (i = 0; i < MAX_MTEX; i++) { MTex *mtex = texture_stack[i]; if (mtex && mtex->tex) - build_texture(owner_node, mtex->tex); + build_texture(mtex->tex); } } /* Recursively build graph for texture */ -void DepsgraphNodeBuilder::build_texture(DepsNode *owner_node, Tex *tex) +void DepsgraphNodeBuilder::build_texture(Tex *tex) { ID *tex_id = &tex->id; if (tex_id->tag & LIB_TAG_DOIT) { @@ -1080,7 +1102,7 @@ void DepsgraphNodeBuilder::build_texture(DepsNode *owner_node, Tex *tex) /* Texture itself. */ build_animdata(tex_id); /* Texture's nodetree. */ - build_nodetree(owner_node, tex->nodetree); + build_nodetree(tex->nodetree); /* Special cases for different IDs which texture uses. */ if (tex->type == TEX_IMAGE) { if (tex->ima != NULL) { @@ -1114,8 +1136,8 @@ void DepsgraphNodeBuilder::build_compositor(Scene *scene) //graph->get_node(&scene->id, NULL, DEPSNODE_TYPE_COMPOSITING, NULL); /* for now, nodetrees are just parameters; compositing occurs in internals of renderer... */ - ComponentDepsNode *owner_node = add_component_node(&scene->id, DEPSNODE_TYPE_PARAMETERS); - build_nodetree(owner_node, scene->nodetree); + add_component_node(&scene->id, DEPSNODE_TYPE_PARAMETERS); + build_nodetree(scene->nodetree); } void DepsgraphNodeBuilder::build_gpencil(bGPdata *gpd) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 9cb8bc5d45c..c5035f35f6e 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -72,6 +72,8 @@ struct DepsgraphNodeBuilder { DepsgraphNodeBuilder(Main *bmain, Depsgraph *graph); ~DepsgraphNodeBuilder(); + void begin_build(Main *bmain); + RootDepsNode *add_root_node(); IDDepsNode *add_id_node(ID *id); TimeSourceDepsNode *add_time_source(ID *id); @@ -147,10 +149,10 @@ struct DepsgraphNodeBuilder { void build_obdata_geom(Scene *scene, Object *ob); void build_camera(Object *ob); void build_lamp(Object *ob); - void build_nodetree(DepsNode *owner_node, bNodeTree *ntree); - void build_material(DepsNode *owner_node, Material *ma); - void build_texture(DepsNode *owner_node, Tex *tex); - void build_texture_stack(DepsNode *owner_node, MTex **texture_stack); + void build_nodetree(bNodeTree *ntree); + void build_material(Material *ma); + void build_texture(Tex *tex); + void build_texture_stack(MTex **texture_stack); void build_image(Image *image); void build_world(World *world); void build_compositor(Scene *scene); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc index 4a5f3dc8664..f2437ce1fac 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_rig.cc @@ -119,7 +119,17 @@ void DepsgraphNodeBuilder::build_rig(Scene *scene, Object *ob) * Eventually, we need some type of proxy/isolation mechanism in-between here * to ensure that we can use same rig multiple times in same scene... */ - build_animdata(&arm->id); + if ((arm->id.tag & LIB_TAG_DOIT) == 0) { + build_animdata(&arm->id); + + /* Make sure pose is up-to-date with armature updates. */ + add_operation_node(&arm->id, + DEPSNODE_TYPE_PARAMETERS, + DEPSOP_TYPE_EXEC, + NULL, + DEG_OPCODE_PLACEHOLDER, + "Armature Eval"); + } /* Rebuild pose if not up to date. */ if (ob->pose == NULL || (ob->pose->flag & POSE_RECALC)) { @@ -141,14 +151,6 @@ void DepsgraphNodeBuilder::build_rig(Scene *scene, Object *ob) } } - /* Make sure pose is up-to-date with armature updates. */ - add_operation_node(&arm->id, - DEPSNODE_TYPE_PARAMETERS, - DEPSOP_TYPE_EXEC, - NULL, - DEG_OPCODE_PLACEHOLDER, - "Armature Eval"); - /** * Pose Rig Graph * ============== diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc index bcd4bc51448..99e61692231 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_scene.cc @@ -65,20 +65,6 @@ namespace DEG { void DepsgraphNodeBuilder::build_scene(Main *bmain, Scene *scene) { - /* LIB_TAG_DOIT is used to indicate whether node for given ID was already - * created or not. This flag is being set in add_id_node(), so functions - * shouldn't bother with setting it, they only might query this flag when - * needed. - */ - BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); - /* XXX nested node trees are not included in tag-clearing above, - * so we need to do this manually. - */ - FOREACH_NODETREE(bmain, nodetree, id) { - if (id != (ID *)nodetree) - nodetree->id.tag &= ~LIB_TAG_DOIT; - } FOREACH_NODETREE_END - /* scene ID block */ add_id_node(&scene->id); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index b5272d3acf2..94cff521edc 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -129,8 +129,8 @@ static bool python_driver_depends_on_time(ChannelDriver *driver) /* Function calls are considered dependent on a time. */ return true; } - if (strstr(driver->expression, "time") != NULL) { - /* Variable `time` depends on time. */ + if (strstr(driver->expression, "frame") != NULL) { + /* Variable `frame` depends on time. */ /* TODO(sergey): This is a bit weak, but not sure about better way of * handling this. */ @@ -339,6 +339,22 @@ void DepsgraphRelationBuilder::add_forcefield_relations(const OperationKey &key, /* **** Functions to build relations between entities **** */ +void DepsgraphRelationBuilder::begin_build(Main *bmain) +{ + /* LIB_TAG_DOIT is used to indicate whether node for given ID was already + * created or not. + */ + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + /* XXX nested node trees are notr included in tag-clearing above, + * so we need to do this manually. + */ + FOREACH_NODETREE(bmain, nodetree, id) { + if (id != (ID *)nodetree) { + nodetree->id.tag &= ~LIB_TAG_DOIT; + } + } FOREACH_NODETREE_END +} + void DepsgraphRelationBuilder::build_group(Main *bmain, Scene *scene, Object *object, @@ -489,7 +505,7 @@ void DepsgraphRelationBuilder::build_object(Main *bmain, Scene *scene, Object *o /* grease pencil */ if (ob->gpd) { - build_gpencil(&ob->id, ob->gpd); + build_gpencil(ob->gpd); } } @@ -1062,10 +1078,10 @@ void DepsgraphRelationBuilder::build_world(World *world) /* TODO: other settings? */ /* textures */ - build_texture_stack(world_id, world->mtex); + build_texture_stack(world->mtex); /* world's nodetree */ - build_nodetree(world_id, world->nodetree); + build_nodetree(world->nodetree); } void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) @@ -1402,14 +1418,19 @@ void DepsgraphRelationBuilder::build_obdata_geom(Main *bmain, Scene *scene, Obje } /* materials */ - if (ob->totcol) { - int a; - - for (a = 1; a <= ob->totcol; a++) { + if (ob->totcol != 0) { + ComponentKey object_shading_key(&ob->id, DEPSNODE_TYPE_SHADING); + for (int a = 1; a <= ob->totcol; a++) { Material *ma = give_current_material(ob, a); - - if (ma) - build_material(&ob->id, ma); + if (ma != NULL) { + build_material(ma); + ComponentKey material_shading_key(&ma->id, + DEPSNODE_TYPE_SHADING); + add_relation(material_shading_key, + object_shading_key, + DEPSREL_TYPE_UPDATE, + "Object Shading"); + } } } @@ -1568,17 +1589,17 @@ void DepsgraphRelationBuilder::build_lamp(Object *ob) /* lamp's nodetree */ if (la->nodetree) { - build_nodetree(lamp_id, la->nodetree); + build_nodetree(la->nodetree); ComponentKey nodetree_key(&la->nodetree->id, DEPSNODE_TYPE_PARAMETERS); add_relation(nodetree_key, parameters_key, DEPSREL_TYPE_COMPONENT_ORDER, "NTree->Lamp Parameters"); } /* textures */ - build_texture_stack(lamp_id, la->mtex); + build_texture_stack(la->mtex); } -void DepsgraphRelationBuilder::build_nodetree(ID *owner, bNodeTree *ntree) +void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) { if (!ntree) return; @@ -1596,15 +1617,15 @@ void DepsgraphRelationBuilder::build_nodetree(ID *owner, bNodeTree *ntree) LINKLIST_FOREACH (bNode *, bnode, &ntree->nodes) { if (bnode->id) { if (GS(bnode->id->name) == ID_MA) { - build_material(owner, (Material *)bnode->id); + build_material((Material *)bnode->id); } else if (bnode->type == ID_TE) { - build_texture(owner, (Tex *)bnode->id); + build_texture((Tex *)bnode->id); } else if (bnode->type == NODE_GROUP) { bNodeTree *group_ntree = (bNodeTree *)bnode->id; if ((group_ntree->id.tag & LIB_TAG_DOIT) == 0) { - build_nodetree(owner, group_ntree); + build_nodetree(group_ntree); group_ntree->id.tag |= LIB_TAG_DOIT; } OperationKey group_parameters_key(&group_ntree->id, @@ -1622,12 +1643,10 @@ void DepsgraphRelationBuilder::build_nodetree(ID *owner, bNodeTree *ntree) add_relation(animation_key, parameters_key, DEPSREL_TYPE_COMPONENT_ORDER, "NTree Parameters"); } - - // TODO: link from nodetree to owner_component? } /* Recursively build graph for material */ -void DepsgraphRelationBuilder::build_material(ID *owner, Material *ma) +void DepsgraphRelationBuilder::build_material(Material *ma) { ID *ma_id = &ma->id; if (ma_id->tag & LIB_TAG_DOIT) { @@ -1639,14 +1658,26 @@ void DepsgraphRelationBuilder::build_material(ID *owner, Material *ma) build_animdata(ma_id); /* textures */ - build_texture_stack(owner, ma->mtex); + build_texture_stack(ma->mtex); /* material's nodetree */ - build_nodetree(owner, ma->nodetree); + if (ma->nodetree != NULL) { + build_nodetree(ma->nodetree); + OperationKey ntree_key(&ma->nodetree->id, + DEPSNODE_TYPE_PARAMETERS, + DEG_OPCODE_PLACEHOLDER, + "Parameters Eval"); + OperationKey material_key(&ma->id, + DEPSNODE_TYPE_SHADING, + DEG_OPCODE_PLACEHOLDER, + "Material Update"); + add_relation(ntree_key, material_key, + DEPSREL_TYPE_UPDATE, "Material's NTree"); + } } /* Recursively build graph for texture */ -void DepsgraphRelationBuilder::build_texture(ID *owner, Tex *tex) +void DepsgraphRelationBuilder::build_texture(Tex *tex) { ID *tex_id = &tex->id; if (tex_id->tag & LIB_TAG_DOIT) { @@ -1658,11 +1689,11 @@ void DepsgraphRelationBuilder::build_texture(ID *owner, Tex *tex) build_animdata(tex_id); /* texture's nodetree */ - build_nodetree(owner, tex->nodetree); + build_nodetree(tex->nodetree); } /* Texture-stack attached to some shading datablock */ -void DepsgraphRelationBuilder::build_texture_stack(ID *owner, MTex **texture_stack) +void DepsgraphRelationBuilder::build_texture_stack(MTex **texture_stack) { int i; @@ -1670,17 +1701,17 @@ void DepsgraphRelationBuilder::build_texture_stack(ID *owner, MTex **texture_sta for (i = 0; i < MAX_MTEX; i++) { MTex *mtex = texture_stack[i]; if (mtex && mtex->tex) - build_texture(owner, mtex->tex); + build_texture(mtex->tex); } } void DepsgraphRelationBuilder::build_compositor(Scene *scene) { /* For now, just a plain wrapper? */ - build_nodetree(&scene->id, scene->nodetree); + build_nodetree(scene->nodetree); } -void DepsgraphRelationBuilder::build_gpencil(ID *UNUSED(owner), bGPdata *gpd) +void DepsgraphRelationBuilder::build_gpencil(bGPdata *gpd) { /* animation */ build_animdata(&gpd->id); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 6e8485bee30..054e4103290 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -171,6 +171,8 @@ struct DepsgraphRelationBuilder { DepsgraphRelationBuilder(Depsgraph *graph); + void begin_build(Main *bmain); + template <typename KeyFrom, typename KeyTo> void add_relation(const KeyFrom& key_from, const KeyTo& key_to, @@ -217,12 +219,12 @@ struct DepsgraphRelationBuilder void build_obdata_geom(Main *bmain, Scene *scene, Object *ob); void build_camera(Object *ob); void build_lamp(Object *ob); - void build_nodetree(ID *owner, bNodeTree *ntree); - void build_material(ID *owner, Material *ma); - void build_texture(ID *owner, Tex *tex); - void build_texture_stack(ID *owner, MTex **texture_stack); + void build_nodetree(bNodeTree *ntree); + void build_material(Material *ma); + void build_texture(Tex *tex); + void build_texture_stack(MTex **texture_stack); void build_compositor(Scene *scene); - void build_gpencil(ID *owner, bGPdata *gpd); + void build_gpencil(bGPdata *gpd); void build_cachefile(CacheFile *cache_file); void build_mask(Mask *mask); void build_movieclip(MovieClip *clip); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc index 6b51a957da0..8a3476cff45 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_scene.cc @@ -69,20 +69,8 @@ namespace DEG { void DepsgraphRelationBuilder::build_scene(Main *bmain, Scene *scene) { - /* LIB_TAG_DOIT is used to indicate whether node for given ID was already - * created or not. - */ - BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); - /* XXX nested node trees are not included in tag-clearing above, - * so we need to do this manually. - */ - FOREACH_NODETREE(bmain, nodetree, id) { - if (id != (ID *)nodetree) - nodetree->id.tag &= ~LIB_TAG_DOIT; - } FOREACH_NODETREE_END - if (scene->set) { - // TODO: link set to scene, especially our timesource... + build_scene(bmain, scene->set); } /* scene objects */ @@ -132,7 +120,7 @@ void DepsgraphRelationBuilder::build_scene(Main *bmain, Scene *scene) /* grease pencil */ if (scene->gpd) { - build_gpencil(&scene->id, scene->gpd); + build_gpencil(scene->gpd); } /* Masks. */ diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index 9952f714145..3a69469053c 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -209,6 +209,7 @@ void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene) * - this way it should be the first in the graph, * reflecting its role as the entrypoint */ + node_builder.begin_build(bmain); node_builder.add_root_node(); node_builder.build_scene(bmain, scene); @@ -221,6 +222,7 @@ void DEG_graph_build_from_scene(Depsgraph *graph, Main *bmain, Scene *scene) * it doesnt add any operations anyway and is not clear what part of the * scene is to be connected. */ + relation_builder.begin_build(bmain); #if 0 relation_builder.add_relation(RootKey(), IDKey(scene), diff --git a/source/blender/depsgraph/intern/depsgraph_type_defines.cc b/source/blender/depsgraph/intern/depsgraph_type_defines.cc index 4ce91516c84..39c189629f2 100644 --- a/source/blender/depsgraph/intern/depsgraph_type_defines.cc +++ b/source/blender/depsgraph/intern/depsgraph_type_defines.cc @@ -145,7 +145,7 @@ DepsOperationStringifier::DepsOperationStringifier() const char *DepsOperationStringifier::operator[](eDepsOperation_Code opcode) { - BLI_assert((opcode > 0) && (opcode < DEG_NUM_OPCODES)); + BLI_assert((opcode >= 0) && (opcode < DEG_NUM_OPCODES)); if (opcode >= 0 && opcode < DEG_NUM_OPCODES) { return names_[opcode]; } diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index c12a050e9ba..2f73eb6b71c 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -2223,7 +2223,7 @@ typedef struct tAnimFilterModifiersContext { /* dependency walker callback for modifier dependencies */ -static void animfilter_modifier_idpoin_cb(void *afm_ptr, Object *ob, ID **idpoin, int UNUSED(cd_flag)) +static void animfilter_modifier_idpoin_cb(void *afm_ptr, Object *ob, ID **idpoin, int UNUSED(cb_flag)) { tAnimFilterModifiersContext *afm = (tAnimFilterModifiersContext *)afm_ptr; ID *owner_id = &ob->id; diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 1703210f0b6..c1e82583521 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -37,6 +37,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_string_utils.h" #include "DNA_anim_types.h" #include "DNA_object_types.h" @@ -668,7 +669,7 @@ static void flip_names(tAnimCopybufItem *aci, char **name) /* more ninja stuff, temporary substitute with NULL terminator */ str_start[length] = 0; - BKE_deform_flip_side_name(bname_new, str_start, false); + BLI_string_flip_side_name(bname_new, str_start, false, sizeof(bname_new)); str_start[length] = '\"'; str_iter = *name = MEM_mallocN(sizeof(char) * (prefix_l + postfix_l + length + 1), "flipped_path"); diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index 6228874343b..bbc81f522fa 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -39,6 +39,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_ghash.h" +#include "BLI_string_utils.h" #include "BKE_action.h" #include "BKE_constraint.h" @@ -619,9 +620,9 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) if (EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED)) { - char name_flip[MAX_VGROUP_NAME]; + char name_flip[MAXBONENAME]; - BKE_deform_flip_side_name(name_flip, ebone_iter->name, false); + BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip)); if (STREQ(name_flip, ebone_iter->name)) { /* if the name matches, we don't have the potential to be mirrored, just skip */ @@ -679,9 +680,9 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) /* will be set if the mirror bone already exists (no need to make a new one) */ (ebone_iter->temp.ebone == NULL)) { - char name_flip[MAX_VGROUP_NAME]; + char name_flip[MAXBONENAME]; - BKE_deform_flip_side_name(name_flip, ebone_iter->name, false); + BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip)); /* bones must have a side-suffix */ if (!STREQ(name_flip, ebone_iter->name)) { diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index 56dbdb3a639..fa192ed6f36 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -37,6 +37,8 @@ #include "BLI_blenlib.h" #include "BLI_ghash.h" +#include "BLI_string_utils.h" +#include "BLI_utildefines.h" #include "BLT_translation.h" @@ -297,6 +299,55 @@ void ED_armature_bone_rename(bArmature *arm, const char *oldnamep, const char *n } } +typedef struct BoneFlipNameData { + struct BoneFlipNameData *next, *prev; + char *name; + char name_flip[MAXBONENAME]; +} BoneFlipNameData; + +/** + * Renames (by flipping) all selected bones at once. + * + * This way if we are flipping related bones (e.g., Bone.L, Bone.R) at the same time + * all the bones are safely renamed, without conflicting with each other. + * + * \param arm Armature the bones belong to + * \param bones ListBase of BoneConflict elems, populated via ED_armature_bones_flip_names_add + */ +void ED_armature_bones_flip_names(bArmature *arm, ListBase *bones_names) +{ + ListBase bones_names_conflicts = {NULL}; + BoneFlipNameData *bfn; + + /* First pass: generate flip names, and blindly rename. + * If rename did not yield expected result, store both bone's name and expected flipped one into temp list + * for second pass. */ + for (LinkData *link = bones_names->first; link; link = link->next) { + char name_flip[MAXBONENAME]; + char *name = link->data; + + /* Do not strip numbers, otherwise we'll end up with completely mismatched names in cases like + * Bone.R, Bone.R.001, Bone.R.002, etc. */ + BLI_string_flip_side_name(name_flip, name, false, sizeof(name_flip)); + + ED_armature_bone_rename(arm, name, name_flip); + + if (!STREQ(name, name_flip)) { + bfn = alloca(sizeof(BoneFlipNameData)); + bfn->name = name; + BLI_strncpy(bfn->name_flip, name_flip, sizeof(bfn->name_flip)); + BLI_addtail(&bones_names_conflicts, bfn); + } + } + + /* Second pass to handle the bones that have naming conflicts with other bones. + * Note that if the other bone was not selected, its name was not flipped, so conflict remains and that second + * rename simply generates a new numbered alternative name. */ + for (bfn = bones_names_conflicts.first; bfn; bfn = bfn->next) { + ED_armature_bone_rename(arm, bfn->name, bfn->name_flip); + } +} + /* ************************************************** */ /* Bone Renaming - EditMode */ @@ -304,20 +355,24 @@ static int armature_flip_names_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = CTX_data_edit_object(C); bArmature *arm; - + /* paranoia checks */ - if (ELEM(NULL, ob, ob->pose)) + if (ELEM(NULL, ob, ob->pose)) return OPERATOR_CANCELLED; + arm = ob->data; - - /* loop through selected bones, auto-naming them */ + + ListBase bones_names= {NULL}; + CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) { - char name_flip[MAXBONENAME]; - BKE_deform_flip_side_name(name_flip, ebone->name, true); - ED_armature_bone_rename(arm, ebone->name, name_flip); + BLI_addtail(&bones_names, BLI_genericNodeN(ebone->name)); } CTX_DATA_END; + + ED_armature_bones_flip_names(arm, &bones_names); + + BLI_freelistN(&bones_names); /* since we renamed stuff... */ DAG_id_tag_update(&ob->id, OB_RECALC_DATA); diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 5a70a45fad4..e9946abba0b 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -35,9 +35,10 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_string_utils.h" #include "BKE_context.h" -#include "BKE_deform.h" +//#include "BKE_deform.h" #include "BKE_report.h" #include "BIF_gl.h" @@ -817,10 +818,10 @@ static void select_similar_prefix(bArmature *arm, EditBone *ebone_act) { EditBone *ebone; - char body_tmp[MAX_VGROUP_NAME]; - char prefix_act[MAX_VGROUP_NAME]; + char body_tmp[MAXBONENAME]; + char prefix_act[MAXBONENAME]; - BKE_deform_split_prefix(ebone_act->name, prefix_act, body_tmp); + BLI_string_split_prefix(ebone_act->name, prefix_act, body_tmp, sizeof(ebone_act->name)); if (prefix_act[0] == '\0') return; @@ -828,8 +829,8 @@ static void select_similar_prefix(bArmature *arm, EditBone *ebone_act) /* Find matches */ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { if (EBONE_SELECTABLE(arm, ebone)) { - char prefix_other[MAX_VGROUP_NAME]; - BKE_deform_split_prefix(ebone->name, prefix_other, body_tmp); + char prefix_other[MAXBONENAME]; + BLI_string_split_prefix(ebone->name, prefix_other, body_tmp, sizeof(ebone->name)); if (STREQ(prefix_act, prefix_other)) { ED_armature_ebone_select_set(ebone, true); } @@ -841,10 +842,10 @@ static void select_similar_suffix(bArmature *arm, EditBone *ebone_act) { EditBone *ebone; - char body_tmp[MAX_VGROUP_NAME]; - char suffix_act[MAX_VGROUP_NAME]; + char body_tmp[MAXBONENAME]; + char suffix_act[MAXBONENAME]; - BKE_deform_split_suffix(ebone_act->name, body_tmp, suffix_act); + BLI_string_split_suffix(ebone_act->name, body_tmp, suffix_act, sizeof(ebone_act->name)); if (suffix_act[0] == '\0') return; @@ -852,8 +853,8 @@ static void select_similar_suffix(bArmature *arm, EditBone *ebone_act) /* Find matches */ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { if (EBONE_SELECTABLE(arm, ebone)) { - char suffix_other[MAX_VGROUP_NAME]; - BKE_deform_split_suffix(ebone->name, body_tmp, suffix_other); + char suffix_other[MAXBONENAME]; + BLI_string_split_suffix(ebone->name, body_tmp, suffix_other, sizeof(ebone->name)); if (STREQ(suffix_act, suffix_other)) { ED_armature_ebone_select_set(ebone, true); } diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c index 28fddbab796..e8d41f722d7 100644 --- a/source/blender/editors/armature/armature_skinning.c +++ b/source/blender/editors/armature/armature_skinning.c @@ -39,6 +39,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_string_utils.h" #include "BKE_action.h" #include "BKE_armature.h" @@ -360,7 +361,7 @@ static void add_verts_to_dgroups(ReportList *reports, Scene *scene, Object *ob, if (dgroup && mirror) { char name_flip[MAXBONENAME]; - BKE_deform_flip_side_name(name_flip, dgroup->name, false); + BLI_string_flip_side_name(name_flip, dgroup->name, false, sizeof(name_flip)); dgroupflip[j] = defgroup_find_name(ob, name_flip); } } diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index 6979a324b69..a3b439536b7 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -34,6 +34,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_string_utils.h" #include "BKE_armature.h" #include "BKE_context.h" @@ -262,7 +263,7 @@ EditBone *ED_armature_bone_get_mirrored(const ListBase *edbo, EditBone *ebo) if (ebo == NULL) return NULL; - BKE_deform_flip_side_name(name_flip, ebo->name, false); + BLI_string_flip_side_name(name_flip, ebo->name, false, sizeof(name_flip)); if (!STREQ(name_flip, ebo->name)) { return ED_armature_bone_find_name(edbo, name_flip); diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index 322476dcca0..86b7271bfff 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -593,20 +593,24 @@ static int pose_flip_names_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); bArmature *arm; - + /* paranoia checks */ if (ELEM(NULL, ob, ob->pose)) return OPERATOR_CANCELLED; + arm = ob->data; - - /* loop through selected bones, auto-naming them */ + + ListBase bones_names = {NULL}; + CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) { - char name_flip[MAXBONENAME]; - BKE_deform_flip_side_name(name_flip, pchan->name, true); - ED_armature_bone_rename(arm, pchan->name, name_flip); + BLI_addtail(&bones_names, BLI_genericNodeN(pchan->name)); } CTX_DATA_END; + + ED_armature_bones_flip_names(arm, &bones_names); + + BLI_freelistN(&bones_names); /* since we renamed stuff... */ DAG_id_tag_update(&ob->id, OB_RECALC_DATA); diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index 9309592bb46..e3c64b523b1 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -34,6 +34,7 @@ #include "BLI_blenlib.h" #include "BLI_dlrbTree.h" +#include "BLI_string_utils.h" #include "BLT_translation.h" diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index b645f1fb2f3..063ba37f20d 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -36,6 +36,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_string_utils.h" #include "BKE_animsys.h" #include "BKE_action.h" @@ -286,7 +287,7 @@ static bPoseChannel *pose_bone_do_paste(Object *ob, bPoseChannel *chan, const bo /* get the name - if flipping, we must flip this first */ if (flip) - BKE_deform_flip_side_name(name, chan->name, false); + BLI_string_flip_side_name(name, chan->name, false, sizeof(name)); else BLI_strncpy(name, chan->name, sizeof(name)); diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index 6604d595573..3d5317b2ebd 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -44,6 +44,7 @@ set(SRC gpencil_convert.c gpencil_data.c gpencil_edit.c + gpencil_interpolate.c gpencil_ops.c gpencil_paint.c gpencil_select.c diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 2fd574d6523..a542bf8fa11 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -1563,7 +1563,7 @@ static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, i { bGPdata *gpd_source = NULL; ToolSettings *ts; - bGPDbrush *brush; + bGPDbrush *brush = NULL; if (scene) { ts = scene->toolsettings; brush = BKE_gpencil_brush_getactive(ts); @@ -1572,8 +1572,7 @@ static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, i BKE_gpencil_brush_init_presets(ts); brush = BKE_gpencil_brush_getactive(ts); } - } - if (scene) { + if (spacetype == SPACE_VIEW3D) { gpd_source = (scene->gpd ? scene->gpd : NULL); } @@ -1581,13 +1580,12 @@ static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, i /* currently drawing only gpencil data from either clip or track, but not both - XXX fix logic behind */ gpd_source = (scene->clip->gpd ? scene->clip->gpd : NULL); } - + if (gpd_source) { if (brush != NULL) { gp_draw_data(brush, ts->gp_sculpt.alpha, gpd_source, offsx, offsy, winx, winy, cfra, dflag); } - } } diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index ae83e899649..6980ad46241 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -42,6 +42,7 @@ #include "BLI_utildefines.h" #include "BLI_ghash.h" #include "BLI_math.h" +#include "BLI_string_utils.h" #include "BLT_translation.h" diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 15f65b394a9..5b011b679a6 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -682,6 +682,87 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot) RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf); } +/* ********************* Add Blank Frame *************************** */ + +/* Basically the same as the drawing op */ +static int UNUSED_FUNCTION(gp_blank_frame_add_poll)(bContext *C) +{ + if (ED_operator_regionactive(C)) { + /* check if current context can support GPencil data */ + if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { + return 1; + } + else { + CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); + } + } + else { + CTX_wm_operator_poll_msg_set(C, "Active region not set"); + } + + return 0; +} + +static int gp_blank_frame_add_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *active_gpl = BKE_gpencil_layer_getactive(gpd); + + const bool all_layers = RNA_boolean_get(op->ptr, "all_layers"); + + /* Initialise datablock and an active layer if nothing exists yet */ + if (ELEM(NULL, gpd, active_gpl)) { + /* let's just be lazy, and call the "Add New Layer" operator, which sets everything up as required */ + WM_operator_name_call(C, "GPENCIL_OT_layer_add", WM_OP_EXEC_DEFAULT, NULL); + } + + /* Go through each layer, adding a frame after the active one + * and/or shunting all the others out of the way + */ + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + if ((all_layers == false) && (gpl != active_gpl)) { + continue; + } + + /* 1) Check for an existing frame on the current frame */ + bGPDframe *gpf = BKE_gpencil_layer_find_frame(gpl, CFRA); + if (gpf) { + /* Shunt all frames after (and including) the existing one later by 1-frame */ + for (; gpf; gpf = gpf->next) { + gpf->framenum += 1; + } + } + + /* 2) Now add a new frame, with nothing in it */ + gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_NEW); + } + CTX_DATA_END; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_blank_frame_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Blank Frame"; + ot->idname = "GPENCIL_OT_blank_frame_add"; + ot->description = "Add a new frame with nothing in it on the current frame. " + "If there is already a frame, all existing frames are shifted one frame later"; + + /* callbacks */ + ot->exec = gp_blank_frame_add_exec; + ot->poll = gp_add_poll; + + /* properties */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + RNA_def_boolean(ot->srna, "all_layers", false, "All Layers", "Create blank frame in all layers, not only active"); +} + /* ******************* Delete Active Frame ************************ */ static int gp_actframe_delete_poll(bContext *C) @@ -1897,6 +1978,13 @@ void GPENCIL_OT_stroke_flip(wmOperatorType *ot) /* ***************** Reproject Strokes ********************** */ +typedef enum eGP_ReprojectModes { + /* On same plane, parallel to viewplane */ + GP_REPROJECT_PLANAR = 0, + /* Reprojected on to the scene geometry */ + GP_REPROJECT_SURFACE, +} eGP_ReprojectModes; + static int gp_strokes_reproject_poll(bContext *C) { /* 2 Requirements: @@ -1906,14 +1994,23 @@ static int gp_strokes_reproject_poll(bContext *C) return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C)); } -static int gp_strokes_reproject_exec(bContext *C, wmOperator *UNUSED(op)) +static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); GP_SpaceConversion gsc = {NULL}; + eGP_ReprojectModes mode = RNA_boolean_get(op->ptr, "type"); /* init space conversion stuff */ gp_point_conversion_init(C, &gsc); + /* init autodist for geometry projection */ + if (mode == GP_REPROJECT_SURFACE) { + view3d_region_operator_needs_opengl(CTX_wm_window(C), gsc.ar); + ED_view3d_autodist_init(scene, gsc.ar, CTX_wm_view3d(C), 0); + } + + // TODO: For deforming geometry workflow, create new frames? + /* Go through each editable + selected stroke, adjusting each of its points one by one... */ GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { @@ -1949,7 +2046,27 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *UNUSED(op)) /* 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); + if (mode == GP_REPROJECT_PLANAR) { + /* Planar - All on same plane parallel to the viewplane */ + gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); + } + else { + /* Geometry - Snap to surfaces of visible geometry */ + /* XXX: There will be precision loss (possible stairstep artifacts) from this conversion to satisfy the API's */ + const int screen_co[2] = {(int)xy[0], (int)xy[1]}; + + int depth_margin = 0; // XXX: 4 for strokes, 0 for normal + float depth; + + /* XXX: The proper procedure computes the depths into an array, to have smooth transitions when all else fails... */ + if (ED_view3d_autodist_depth(gsc.ar, screen_co, depth_margin, &depth)) { + ED_view3d_autodist_simple(gsc.ar, screen_co, &pt->x, 0, &depth); + } + else { + /* Default to planar */ + gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); + } + } /* Unapply parent corrections */ if (gpl->parent) { @@ -1966,21 +2083,36 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *UNUSED(op)) void GPENCIL_OT_reproject(wmOperatorType *ot) { + static EnumPropertyItem reproject_type[] = { + {GP_REPROJECT_PLANAR, "PLANAR", 0, "Planar", + "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint " + "using 'Cursor' Stroke Placement"}, + {GP_REPROJECT_SURFACE, "SURFACE", 0, "Surface", + "Reproject the strokes on to the scene geometry, as if drawn using 'Surface' placement"}, + {0, NULL, 0, NULL, NULL} + }; + /* 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)"; + ot->description = "Reproject the selected strokes from the current viewpoint as if they had been newly drawn " + "(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, " + "or for matching deforming geometry)"; /* callbacks */ + ot->invoke = WM_menu_invoke; ot->exec = gp_strokes_reproject_exec; ot->poll = gp_strokes_reproject_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", reproject_type, GP_REPROJECT_PLANAR, "Projection Type", ""); } /* ******************* Stroke subdivide ************************** */ + /* helper: Count how many points need to be inserted */ static int gp_count_subdivision_cuts(bGPDstroke *gps) { @@ -2098,673 +2230,3 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } - -/* ========= 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; - - /* save initial factor for active layer to define shift limits */ - tgpi->init_factor = (float)(tgpi->cframe - active_gpl->actframe->framenum) / (active_gpl->actframe->next->framenum - active_gpl->actframe->framenum + 1); - /* limits are 100% below 0 and 100% over the 100% */ - tgpi->low_limit = -1.0f - tgpi->init_factor; - tgpi->high_limit = 2.0f - tgpi->init_factor; - - /* 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 by layer (usually must be equal for all layers, but not sure) */ - 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) * tgpi->high_limit) / mid; - } - else { - tgpi->shift = tgpi->low_limit - ((mpos * tgpi->low_limit) / mid); - } - - CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); - 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->init_factor + 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 in active layer"); - 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 in active layer"); - 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, tgpi->low_limit, tgpi->high_limit); - 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, tgpi->low_limit, tgpi->high_limit); - 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; - float factor = tgpi->init_factor; - - /* Grab shift from numeric input, and store this new value (the user see an int) */ - value = (factor + tgpi->shift) * 100.0f; - applyNumInput(&tgpi->num, &value); - tgpi->shift = value / 100.0f; - /* recalculate the shift to get the right value in the frame scale */ - tgpi->shift = tgpi->shift - factor; - - CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); - 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 e2e5fc28710..5c7c9b84adb 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -287,6 +287,8 @@ void GPENCIL_OT_unlock_all(struct wmOperatorType *ot); void GPENCIL_OT_layer_isolate(struct wmOperatorType *ot); void GPENCIL_OT_layer_merge(struct wmOperatorType *ot); +void GPENCIL_OT_blank_frame_add(struct wmOperatorType *ot); + void GPENCIL_OT_active_frame_delete(struct wmOperatorType *ot); void GPENCIL_OT_active_frames_delete_all(struct wmOperatorType *ot); @@ -340,6 +342,7 @@ void gpencil_undo_finish(void); void GPENCIL_OT_interpolate(struct wmOperatorType *ot); void GPENCIL_OT_interpolate_sequence(struct wmOperatorType *ot); +void GPENCIL_OT_interpolate_reverse(struct wmOperatorType *ot); /* ****************************************************** */ /* FILTERED ACTION DATA - TYPES ---> XXX DEPRECEATED OLD ANIM SYSTEM CODE! */ diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c new file mode 100644 index 00000000000..297058168a0 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -0,0 +1,1145 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez, Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + * + * Operators for interpolating new Grease Pencil frames from existing strokes + */ + +/** \file blender/editors/gpencil/gpencil_interpolate.c + * \ingroup edgpencil + */ + + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_easing.h" +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "DNA_color_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_gpencil.h" +#include "BKE_library.h" +#include "BKE_report.h" +#include "BKE_screen.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "UI_view2d.h" + +#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" + +/* ************************************************ */ +/* Core/Shared Utilities */ + +/* Poll callback for interpolation operators */ +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; +} + +/* Perform 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); + } +} + +/* ****************** Interpolate Interactive *********************** */ + +/* Helper: Update all strokes interpolated */ +static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi) +{ + tGPDinterpolate_layer *tgpil; + const float shift = tgpi->shift; + + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + bGPDstroke *new_stroke; + const float factor = tgpil->factor + shift; + + for (new_stroke = tgpil->interFrame->strokes.first; new_stroke; new_stroke = new_stroke->next) { + bGPDstroke *gps_from, *gps_to; + int stroke_idx; + + if (new_stroke->totpoints == 0) { + continue; + } + + /* get strokes to interpolate */ + stroke_idx = BLI_findindex(&tgpil->interFrame->strokes, new_stroke); + + gps_from = BLI_findlink(&tgpil->prevFrame->strokes, stroke_idx); + gps_to = BLI_findlink(&tgpil->nextFrame->strokes, stroke_idx); + + /* 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); + eGP_Interpolate_SettingsFlag flag = ts->gp_interpolate.flag; + + /* get layers */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* all layers or only active */ + if (!(flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && !(gpl->flag & GP_LAYER_ACTIVE)) { + continue; + } + /* only editable and visible layers are considered */ + if (!gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) { + continue; + } + + /* read strokes */ + for (bGPDstroke *gps_from = gpl->actframe->strokes.first; gps_from; gps_from = gps_from->next) { + bGPDstroke *gps_to; + int fFrame; + + /* only selected */ + if ((flag & GP_TOOLFLAG_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 true; + } + } + return false; +} + +/* Helper: Create internal strokes interpolated */ +static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) +{ + bGPdata *gpd = tgpi->gpd; + bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); + bGPDframe *actframe = active_gpl->actframe; + + /* save initial factor for active layer to define shift limits */ + tgpi->init_factor = (float)(tgpi->cframe - actframe->framenum) / (actframe->next->framenum - actframe->framenum + 1); + + /* limits are 100% below 0 and 100% over the 100% */ + tgpi->low_limit = -1.0f - tgpi->init_factor; + tgpi->high_limit = 2.0f - tgpi->init_factor; + + /* set layers */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + tGPDinterpolate_layer *tgpil; + + /* all layers or only active */ + if (!(tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && (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 = 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 by layer (usually must be equal for all layers, but not sure) */ + 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 (bGPDstroke *gps_from = tgpil->prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { + bGPDstroke *gps_to; + int fFrame; + + bGPDstroke *new_stroke; + bool valid = true; + + + /* only selected */ + if ((tgpi->flag & GP_TOOLFLAG_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); + } + } +} + +/* ----------------------- */ +/* Drawing Callbacks */ + +/* Drawing callback for modal operator in screen mode */ +static void gpencil_interpolate_draw_screen(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) +{ + tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; + ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_PIXEL); +} + +/* Drawing callback for modal operator in 3d mode */ +static void gpencil_interpolate_draw_3d(const bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) +{ + tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; + ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_VIEW); +} + +/* ----------------------- */ + +/* 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) * tgpi->high_limit) / mid; + } + else { + tgpi->shift = tgpi->low_limit - ((mpos * tgpi->low_limit) / mid); + } + + CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); + 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->init_factor + p->shift) * 100.0f)); + } + + ED_area_headerprint(p->sa, status_str); +} + +/* 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); +} + +/* ----------------------- */ + +/* 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; +} + +/* 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_interpolate.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; +} + +/* Allocate memory and initialize values */ +static tGPDinterpolate *gp_session_init_interpolation(bContext *C, wmOperator *op) +{ + tGPDinterpolate *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; +} + +/* 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; +} + +/* ----------------------- */ + +/* 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); + bGPDframe *actframe = gpl->actframe; + tGPDinterpolate *tgpi = NULL; + + /* cannot interpolate if not between 2 frames */ + if (ELEM(NULL, actframe, actframe->next)) { + BKE_report(op->reports, RPT_ERROR, "Cannot find a pair of grease pencil frames to interpolate between in active layer"); + return OPERATOR_CANCELLED; + } + + /* cannot interpolate in extremes */ + if (ELEM(CFRA, actframe->framenum, actframe->next->framenum)) { + BKE_report(op->reports, RPT_ERROR, "Cannot interpolate as current frame already has existing 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 strokes"); + 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 strokes can 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, tgpi, REGION_DRAW_POST_PIXEL); + tgpi->draw_handle_3d = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_interpolate_draw_3d, tgpi, 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, tgpi->low_limit, tgpi->high_limit); + 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, tgpi->low_limit, tgpi->high_limit); + 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)) { + const float factor = tgpi->init_factor; + float value; + + /* Grab shift from numeric input, and store this new value (the user see an int) */ + value = (factor + tgpi->shift) * 100.0f; + applyNumInput(&tgpi->num, &value); + tgpi->shift = value / 100.0f; + + /* recalculate the shift to get the right value in the frame scale */ + tgpi->shift = tgpi->shift - factor; + + CLAMP(tgpi->shift, tgpi->low_limit, tgpi->high_limit); + 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; +} + +/* Cancel handler */ +static void gpencil_interpolate_cancel(bContext *C, wmOperator *op) +{ + /* this is just a wrapper around exit() */ + gpencil_interpolate_exit(C, op); +} + +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"; + + /* 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; + + /* properties */ + RNA_def_float_percentage(ot->srna, "shift", 0.0f, -1.0f, 1.0f, "Shift", "Bias factor for which frame has more influence on the interpolated strokes", -0.9f, 0.9f); +} + +/* ****************** Interpolate Sequence *********************** */ + +/* Helper: Perform easing equation calculations for GP interpolation operator */ +static float gp_interpolate_seq_easing_calc(GP_Interpolate_Settings *ipo_settings, float time) +{ + const float begin = 0.0f; + const float change = 1.0f; + const float duration = 1.0f; + + const float back = ipo_settings->back; + const float amplitude = ipo_settings->amplitude; + const float period = ipo_settings->period; + + eBezTriple_Easing easing = ipo_settings->easing; + float result = time; + + switch (ipo_settings->type) { + case GP_IPO_BACK: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_back_ease_in(time, begin, change, duration, back); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_back_ease_out(time, begin, change, duration, back); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_back_ease_in_out(time, begin, change, duration, back); + break; + + default: /* default/auto: same as ease out */ + result = BLI_easing_back_ease_out(time, begin, change, duration, back); + break; + } + break; + + case GP_IPO_BOUNCE: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_bounce_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_bounce_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_bounce_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease out */ + result = BLI_easing_bounce_ease_out(time, begin, change, duration); + break; + } + break; + + case GP_IPO_CIRC: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_circ_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_circ_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_circ_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_circ_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_CUBIC: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_cubic_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_cubic_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_cubic_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_cubic_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_ELASTIC: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_elastic_ease_in(time, begin, change, duration, amplitude, period); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_elastic_ease_in_out(time, begin, change, duration, amplitude, period); + break; + + default: /* default/auto: same as ease out */ + result = BLI_easing_elastic_ease_out(time, begin, change, duration, amplitude, period); + break; + } + break; + + case GP_IPO_EXPO: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_expo_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_expo_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_expo_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_expo_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_QUAD: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_quad_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_quad_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_quad_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_quad_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_QUART: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_quart_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_quart_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_quart_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_quart_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_QUINT: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_quint_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_quint_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_quint_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_quint_ease_in(time, begin, change, duration); + break; + } + break; + + case GP_IPO_SINE: + switch (easing) { + case BEZT_IPO_EASE_IN: + result = BLI_easing_sine_ease_in(time, begin, change, duration); + break; + case BEZT_IPO_EASE_OUT: + result = BLI_easing_sine_ease_out(time, begin, change, duration); + break; + case BEZT_IPO_EASE_IN_OUT: + result = BLI_easing_sine_ease_in_out(time, begin, change, duration); + break; + + default: /* default/auto: same as ease in */ + result = BLI_easing_sine_ease_in(time, begin, change, duration); + break; + } + break; + + default: + printf("%s: Unknown interpolation type - %d\n", __func__, ipo_settings->type); + break; + } + + return result; +} + +static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); + bGPDframe *actframe = active_gpl->actframe; + + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + GP_Interpolate_Settings *ipo_settings = &ts->gp_interpolate; + eGP_Interpolate_SettingsFlag flag = ipo_settings->flag; + + /* cannot interpolate if not between 2 frames */ + if (ELEM(NULL, actframe, actframe->next)) { + BKE_report(op->reports, RPT_ERROR, "Cannot find a pair of grease pencil frames to interpolate between in active layer"); + return OPERATOR_CANCELLED; + } + /* cannot interpolate in extremes */ + if (ELEM(CFRA, actframe->framenum, actframe->next->framenum)) { + BKE_report(op->reports, RPT_ERROR, "Cannot interpolate as current frame already has existing grease pencil frames"); + return OPERATOR_CANCELLED; + } + + /* loop all layer to check if need interpolation */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + bGPDframe *prevFrame, *nextFrame; + bGPDstroke *gps_from, *gps_to; + int cframe, fFrame; + + /* all layers or only active */ + if (((flag & GP_TOOLFLAG_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++) { + bGPDframe *interFrame = NULL; + float factor; + + /* get interpolation factor */ + factor = (float)(cframe - prevFrame->framenum) / (nextFrame->framenum - prevFrame->framenum + 1); + + if (ipo_settings->type == GP_IPO_CURVEMAP) { + /* custom curvemap */ + if (ipo_settings->custom_ipo) { + factor = curvemapping_evaluateF(ipo_settings->custom_ipo, 0, factor); + } + else { + BKE_report(op->reports, RPT_ERROR, "Custom interpolation curve does not exist"); + } + } + else if (ipo_settings->type >= GP_IPO_BACK) { + /* easing equation... */ + factor = gp_interpolate_seq_easing_calc(ipo_settings, factor); + } + + /* create new strokes data with interpolated points reading original stroke */ + for (gps_from = prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { + bGPDstroke *new_stroke; + + /* only selected */ + if ((flag & GP_TOOLFLAG_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; +} + +void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Interpolate Sequence"; + ot->idname = "GPENCIL_OT_interpolate_sequence"; + ot->description = "Generate 'in-betweens' to smoothly interpolate between Grease Pencil frames"; + + /* api callbacks */ + ot->exec = gpencil_interpolate_seq_exec; + ot->poll = gpencil_interpolate_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ******************** Remove Breakdowns ************************ */ + +/* Same as gpencil_interpolate_poll(), + * except we ALSO need to have an active frame that is a breakdown + */ +static int gpencil_interpolate_reverse_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; + } + + /* need to be on a breakdown frame */ + if ((gpl->actframe == NULL) || (gpl->actframe->key_type != BEZT_KEYTYPE_BREAKDOWN)) { + CTX_wm_operator_poll_msg_set(C, "Expected current frame to be a breakdown"); + return 0; + } + + return 1; +} + +static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op)) +{ + /* Go through each layer, deleting the breakdowns around the current frame, + * but only if there is a keyframe nearby to stop at + */ + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + bGPDframe *start_key = NULL; + bGPDframe *end_key = NULL; + bGPDframe *gpf, *gpfn; + + /* Only continue if we're currently on a breakdown keyframe */ + if ((gpl->actframe == NULL) || (gpl->actframe->key_type != BEZT_KEYTYPE_BREAKDOWN)) + continue; + + /* Search left for "start_key" (i.e. the first breakdown to remove) */ + gpf = gpl->actframe; + while (gpf) { + if (gpf->key_type == BEZT_KEYTYPE_BREAKDOWN) { + /* A breakdown... keep going left */ + start_key = gpf; + gpf = gpf->prev; + } + else { + /* Not a breakdown (may be a key, or an extreme, or something else that wasn't generated)... stop */ + break; + } + } + + /* Search right for "end_key" (i.e. the last breakdown to remove) */ + gpf = gpl->actframe; + while (gpf) { + if (gpf->key_type == BEZT_KEYTYPE_BREAKDOWN) { + /* A breakdown... keep going right */ + end_key = gpf; + gpf = gpf->next; + } + else { + /* Not a breakdown... stop */ + break; + } + } + + /* Did we find anything? */ + /* NOTE: We should only proceed if there's something before/after these extents... + * Otherwise, there's just an extent of breakdowns with no keys to interpolate between + */ + if ((start_key && end_key) && + ELEM(NULL, start_key->prev, end_key->next) == false) + { + /* Set actframe to the key before start_key, since the keys have been removed now */ + gpl->actframe = start_key->prev; + + /* Free each frame we're removing (except the last one) */ + for (gpf = start_key; gpf && gpf != end_key; gpf = gpfn) { + gpfn = gpf->next; + + /* free strokes and their associated memory */ + BKE_gpencil_free_strokes(gpf); + BLI_freelinkN(&gpl->frames, gpf); + } + + /* Now free the last one... */ + BKE_gpencil_free_strokes(end_key); + BLI_freelinkN(&gpl->frames, end_key); + } + } + CTX_DATA_END; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_interpolate_reverse(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Breakdowns"; + ot->idname = "GPENCIL_OT_interpolate_reverse"; + ot->description = "Remove breakdown frames generated by interpolating between two Grease Pencil frames"; + + /* callbacks */ + ot->exec = gpencil_interpolate_reverse_exec; + ot->poll = gpencil_interpolate_reverse_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* *************************************************************** */ diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 057d53ea458..78e1a0dda36 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -102,6 +102,10 @@ static void ed_keymap_gpencil_general(wmKeyConfig *keyconf) WM_keymap_add_menu_pie(keymap, "GPENCIL_PIE_tool_palette", QKEY, KM_PRESS, 0, DKEY); WM_keymap_add_menu_pie(keymap, "GPENCIL_PIE_settings_palette", WKEY, KM_PRESS, 0, DKEY); + /* Add Blank Frame */ + /* XXX: BKEY or NKEY? BKEY is easier to reach from DKEY, so we'll use that for now */ + WM_keymap_add_item(keymap, "GPENCIL_OT_blank_frame_add", BKEY, KM_PRESS, 0, DKEY); + /* Delete Active Frame - For easier video tutorials/review sessions */ /* NOTE: This works even when not in EditMode */ WM_keymap_add_item(keymap, "GPENCIL_OT_active_frames_delete_all", XKEY, KM_PRESS, 0, DKEY); @@ -401,6 +405,8 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_layer_isolate); WM_operatortype_append(GPENCIL_OT_layer_merge); + WM_operatortype_append(GPENCIL_OT_blank_frame_add); + WM_operatortype_append(GPENCIL_OT_active_frame_delete); WM_operatortype_append(GPENCIL_OT_active_frames_delete_all); @@ -443,6 +449,7 @@ void ED_operatortypes_gpencil(void) /* Interpolation */ WM_operatortype_append(GPENCIL_OT_interpolate); WM_operatortype_append(GPENCIL_OT_interpolate_sequence); + WM_operatortype_append(GPENCIL_OT_interpolate_reverse); } void ED_operatormacros_gpencil(void) diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index c23bfb1ff60..5879306b06c 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1623,16 +1623,24 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode) * 2) Ensure that p->gpf refers to the frame used for the active layer * (to avoid problems with other tools which expect it to exist) */ - bGPDlayer *gpl; - for (gpl = p->gpd->layers.first; gpl; gpl = gpl->next) { + bool has_layer_to_erase = false; + + for (bGPDlayer *gpl = p->gpd->layers.first; gpl; gpl = gpl->next) { /* Skip if layer not editable */ if (gpencil_layer_is_editable(gpl) == false) continue; /* Add a new frame if needed (and based off the active frame, * as we need some existing strokes to erase) + * + * Note: We don't add a new frame if there's nothing there now, so + * -> If there are no frames at all, don't add one + * -> If there are no strokes in that frame, don't add a new empty frame */ - gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY); + if (gpl->actframe && gpl->actframe->strokes.first) { + gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY); + has_layer_to_erase = true; + } /* XXX: we omit GP_FRAME_PAINT here for now, * as it is only really useful for doing @@ -1652,10 +1660,10 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode) } } - if (p->gpf == NULL) { + if (has_layer_to_erase == false) { p->status = GP_STATUS_ERROR; //if (G.debug & G_DEBUG) - printf("Error: No frame created (gpencil_paint_init)\n"); + printf("Error: Eraser will not be affecting anything (gpencil_paint_init)\n"); return; } } @@ -2432,6 +2440,14 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* enable continuous if release D key in mid drawing */ p->scene->toolsettings->gpencil_flags |= GP_TOOL_FLAG_PAINTSESSIONS_ON; } + else if ((event->type == BKEY) && (event->val == KM_RELEASE)) { + /* Add Blank Frame + * - Since this operator is non-modal, we can just call it here, and keep going... + * - This operator is especially useful when animating + */ + WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL); + estate = OPERATOR_RUNNING_MODAL; + } else { estate = OPERATOR_RUNNING_MODAL; } @@ -2594,7 +2610,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } else if (p->status != GP_STATUS_ERROR) { /* User clicked outside bounds of window while idling, so exit paintmode - * NOTE: Don't eter this case if an error occurred while finding the + * NOTE: Don't enter this case if an error occurred while finding the * region (as above) */ /* if drawing polygon and enable on back, must move stroke */ diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 904132b8876..7ad61671b1b 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -171,6 +171,7 @@ void create_vgroups_from_armature(struct ReportList *reports, struct Scene *scen /* if bone is already in list, pass it as param to ignore it */ void unique_editbone_name(struct ListBase *ebones, char *name, EditBone *bone); void ED_armature_bone_rename(struct bArmature *arm, const char *oldnamep, const char *newnamep); +void ED_armature_bones_flip_names(struct bArmature *arm, struct ListBase *bones_names); void undo_push_armature(struct bContext *C, const char *name); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index a913421d12c..682db20af55 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1993,7 +1993,7 @@ uiBut *ui_but_drag_multi_edit_get(uiBut *but) static bool ui_but_icon_extra_is_visible_text_clear(const uiBut *but) { BLI_assert(but->type == UI_BTYPE_TEXT); - return ((but->flag & UI_BUT_VALUE_CLEAR) && but->drawstr && but->drawstr[0]); + return ((but->flag & UI_BUT_VALUE_CLEAR) && but->drawstr[0]); } static bool ui_but_icon_extra_is_visible_search_unlink(const uiBut *but) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index a761bcfdf5e..734cd02a056 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2963,7 +2963,7 @@ static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const in if (pbuf) { if (ui_but_is_utf8(but)) { - buf_len -= BLI_utf8_invalid_strip(pbuf, buf_len); + buf_len -= BLI_utf8_invalid_strip(pbuf, (size_t)buf_len); } ui_textedit_insert_buf(but, data, pbuf, buf_len); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index b02a909d009..7262b453e02 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -581,6 +581,9 @@ static void ui_item_enum_expand( UI_block_layout_set_current(block, layout_radial); } else { + if (layout->item.type == ITEM_LAYOUT_RADIAL) { + layout_radial = layout; + } UI_block_layout_set_current(block, layout); } } @@ -593,8 +596,9 @@ static void ui_item_enum_expand( for (item = item_array; item->identifier; item++) { if (!item->identifier[0]) { - if (radial) + if (radial && layout_radial) { uiItemS(layout_radial); + } continue; } diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index c57b0215d46..8f004bcf72b 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -684,27 +684,38 @@ static void edbm_add_edge_face_exec__tricky_finalize_sel(BMesh *bm, BMElem *ele_ /* now we need to find the edge that isnt connected to this element */ BM_select_history_clear(bm); + /* Notes on hidden geometry: + * - un-hide the face since its possible hidden was copied when copying surrounding face attributes. + * - un-hide before adding to select history + * since we may extend into an existing, hidden vert/edge. + */ + + BM_elem_flag_disable(f, BM_ELEM_HIDDEN); + BM_face_select_set(bm, f, false); + if (ele_desel->head.htype == BM_VERT) { BMLoop *l = BM_face_vert_share_loop(f, (BMVert *)ele_desel); BLI_assert(f->len == 3); - BM_face_select_set(bm, f, false); BM_vert_select_set(bm, (BMVert *)ele_desel, false); - BM_edge_select_set(bm, l->next->e, true); BM_select_history_store(bm, l->next->e); } else { BMLoop *l = BM_face_edge_share_loop(f, (BMEdge *)ele_desel); BLI_assert(f->len == 4 || f->len == 3); - BM_face_select_set(bm, f, false); + BM_edge_select_set(bm, (BMEdge *)ele_desel, false); if (f->len == 4) { - BM_edge_select_set(bm, l->next->next->e, true); - BM_select_history_store(bm, l->next->next->e); + BMEdge *e_active = l->next->next->e; + BM_elem_flag_disable(e_active, BM_ELEM_HIDDEN); + BM_edge_select_set(bm, e_active, true); + BM_select_history_store(bm, e_active); } else { - BM_vert_select_set(bm, l->next->next->v, true); - BM_select_history_store(bm, l->next->next->v); + BMVert *v_active = l->next->next->v; + BM_elem_flag_disable(v_active, BM_ELEM_HIDDEN); + BM_vert_select_set(bm, v_active, true); + BM_select_history_store(bm, v_active); } } } @@ -758,6 +769,14 @@ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op) else #endif { + /* Newly created faces may include existing hidden edges, + * copying face data from surrounding, may have copied hidden face flag too. + * + * Important that faces use flushing since 'edges.out' wont include hidden edges that already existed. + */ + BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_HIDDEN, true); + BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_HIDDEN, false); + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true); } diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 111afcdc7a7..edd7b5dd1be 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -41,6 +41,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" +#include "BLI_string_utils.h" #include "BLT_translation.h" diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index d30022c01f8..b5fbe4ba586 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2133,7 +2133,7 @@ enum { }; static int tag_localizable_looper( - void *UNUSED(user_data), ID *UNUSED(self_id), ID **id_pointer, const int UNUSED(cd_flag)) + void *UNUSED(user_data), ID *UNUSED(self_id), ID **id_pointer, const int UNUSED(cb_flag)) { if (*id_pointer) { (*id_pointer)->tag &= ~LIB_TAG_DOIT; @@ -2170,12 +2170,12 @@ static void tag_localizable_objects(bContext *C, const int mode) */ for (Object *object = bmain->object.first; object; object = object->id.next) { if ((object->id.tag & LIB_TAG_DOIT) == 0) { - BKE_library_foreach_ID_link(&object->id, tag_localizable_looper, NULL, IDWALK_READONLY); + BKE_library_foreach_ID_link(NULL, &object->id, tag_localizable_looper, NULL, IDWALK_READONLY); } if (object->data) { ID *data_id = (ID *) object->data; if ((data_id->tag & LIB_TAG_DOIT) == 0) { - BKE_library_foreach_ID_link(data_id, tag_localizable_looper, NULL, IDWALK_READONLY); + BKE_library_foreach_ID_link(NULL, data_id, tag_localizable_looper, NULL, IDWALK_READONLY); } } } diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c index f1b7186f8a1..b5131df3eaa 100644 --- a/source/blender/editors/object/object_select.c +++ b/source/blender/editors/object/object_select.c @@ -45,6 +45,7 @@ #include "BLI_math.h" #include "BLI_listbase.h" #include "BLI_rand.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -1131,7 +1132,7 @@ static int object_select_mirror_exec(bContext *C, wmOperator *op) { char name_flip[MAXBONENAME]; - BKE_deform_flip_side_name(name_flip, primbase->object->id.name + 2, true); + BLI_string_flip_side_name(name_flip, primbase->object->id.name + 2, true, sizeof(name_flip)); if (!STREQ(name_flip, primbase->object->id.name + 2)) { Object *ob = (Object *)BKE_libblock_find_name(ID_OB, name_flip); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 991025a4d5d..729dd9dc57b 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -36,6 +36,7 @@ #include "BLI_array_utils.h" #include "BLI_bitmap.h" #include "BLI_stack.h" +#include "BLI_string_utils.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -275,7 +276,7 @@ static int wpaint_mirror_vgroup_ensure(Object *ob, const int vgroup_active) int mirrdef; char name_flip[MAXBONENAME]; - BKE_deform_flip_side_name(name_flip, defgroup->name, false); + BLI_string_flip_side_name(name_flip, defgroup->name, false, sizeof(name_flip)); mirrdef = defgroup_name_index(ob, name_flip); if (mirrdef == -1) { if (BKE_defgroup_new(ob, name_flip)) { diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index 9fc96e06299..0764f586de9 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -170,6 +170,8 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) unsigned char col1a[3], col2a[3]; unsigned char col1b[3], col2b[3]; + const bool show_group_colors = !(saction->flag & SACTION_NODRAWGCOLORS); + /* get theme colors */ UI_GetThemeColor3ubv(TH_BACK, col2); @@ -247,8 +249,36 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) } case ANIMTYPE_GROUP: { - if (sel) glColor4ub(col1a[0], col1a[1], col1a[2], 0x22); - else glColor4ub(col2a[0], col2a[1], col2a[2], 0x22); + bActionGroup *agrp = ale->data; + if (show_group_colors && agrp->customCol) { + if (sel) { + unsigned char *cp = (unsigned char *)agrp->cs.select; + glColor4ub(cp[0], cp[1], cp[2], 0x45); + } + else { + unsigned char *cp = (unsigned char *)agrp->cs.solid; + glColor4ub(cp[0], cp[1], cp[2], 0x1D); + } + } + else { + if (sel) glColor4ub(col1a[0], col1a[1], col1a[2], 0x22); + else glColor4ub(col2a[0], col2a[1], col2a[2], 0x22); + } + break; + } + case ANIMTYPE_FCURVE: + { + FCurve *fcu = ale->data; + if (show_group_colors && fcu->grp && fcu->grp->customCol) { + unsigned char *cp = (unsigned char *)fcu->grp->cs.active; + + if (sel) glColor4ub(cp[0], cp[1], cp[2], 0x65); + else glColor4ub(cp[0], cp[1], cp[2], 0x0B); + } + else { + if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22); + else glColor4ub(col2[0], col2[1], col2[2], 0x22); + } break; } default: diff --git a/source/blender/editors/space_logic/logic_ops.c b/source/blender/editors/space_logic/logic_ops.c index 074368a82c5..1559515221e 100644 --- a/source/blender/editors/space_logic/logic_ops.c +++ b/source/blender/editors/space_logic/logic_ops.c @@ -37,6 +37,7 @@ #include "DNA_scene_types.h" #include "BLI_blenlib.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BLT_translation.h" diff --git a/source/blender/editors/space_logic/logic_window.c b/source/blender/editors/space_logic/logic_window.c index 874e54ba5e7..3de44174d6a 100644 --- a/source/blender/editors/space_logic/logic_window.c +++ b/source/blender/editors/space_logic/logic_window.c @@ -47,8 +47,8 @@ #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" -#include "BLI_path_util.h" #include "BKE_action.h" #include "BKE_context.h" diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index 3243579f7d0..5355b8012db 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -502,51 +502,57 @@ static void nla_panel_modifiers(const bContext *C, Panel *pa) void nla_buttons_register(ARegionType *art) { PanelType *pt; - + pt = MEM_callocN(sizeof(PanelType), "spacetype nla panel animdata"); strcpy(pt->idname, "NLA_PT_animdata"); strcpy(pt->label, N_("Animation Data")); + strcpy(pt->category, "Animations"); strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); pt->draw = nla_panel_animdata; pt->poll = nla_animdata_panel_poll; pt->flag = PNL_DEFAULT_CLOSED; BLI_addtail(&art->paneltypes, pt); - + pt = MEM_callocN(sizeof(PanelType), "spacetype nla panel track"); strcpy(pt->idname, "NLA_PT_track"); strcpy(pt->label, N_("Active Track")); + strcpy(pt->category, "Animations"); strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); pt->draw = nla_panel_track; pt->poll = nla_track_panel_poll; BLI_addtail(&art->paneltypes, pt); - + pt = MEM_callocN(sizeof(PanelType), "spacetype nla panel properties"); strcpy(pt->idname, "NLA_PT_properties"); strcpy(pt->label, N_("Active Strip")); + strcpy(pt->category, "Animations"); strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); pt->draw = nla_panel_properties; pt->poll = nla_strip_panel_poll; BLI_addtail(&art->paneltypes, pt); - + pt = MEM_callocN(sizeof(PanelType), "spacetype nla panel properties"); strcpy(pt->idname, "NLA_PT_actionclip"); strcpy(pt->label, N_("Action Clip")); + strcpy(pt->category, "Animations"); strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); pt->draw = nla_panel_actclip; pt->poll = nla_strip_actclip_panel_poll; BLI_addtail(&art->paneltypes, pt); - + pt = MEM_callocN(sizeof(PanelType), "spacetype nla panel evaluation"); strcpy(pt->idname, "NLA_PT_evaluation"); strcpy(pt->label, N_("Evaluation")); + strcpy(pt->category, "Animations"); strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); pt->draw = nla_panel_evaluation; pt->poll = nla_strip_eval_panel_poll; BLI_addtail(&art->paneltypes, pt); - + pt = MEM_callocN(sizeof(PanelType), "spacetype nla panel modifiers"); strcpy(pt->idname, "NLA_PT_modifiers"); strcpy(pt->label, N_("Modifiers")); + strcpy(pt->category, "Modifiers"); strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); pt->draw = nla_panel_modifiers; pt->poll = nla_strip_eval_panel_poll; diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 7bad109be60..41ae40fc961 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -1629,17 +1629,6 @@ static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), P uiItemR(col, ptr, "use_antialias_z", 0, NULL, ICON_NONE); } - -static void node_composit_buts_hue_sat(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayout *col; - - col = uiLayoutColumn(layout, false); - uiItemR(col, ptr, "color_hue", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "color_saturation", UI_ITEM_R_SLIDER, NULL, ICON_NONE); - uiItemR(col, ptr, "color_value", UI_ITEM_R_SLIDER, NULL, ICON_NONE); -} - static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE); @@ -2580,9 +2569,6 @@ static void node_composit_set_butfunc(bNodeType *ntype) case CMP_NODE_ALPHAOVER: ntype->draw_buttons = node_composit_buts_alphaover; break; - case CMP_NODE_HUE_SAT: - ntype->draw_buttons = node_composit_buts_hue_sat; - break; case CMP_NODE_TEXTURE: ntype->draw_buttons = node_buts_texture; break; diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 33a5a7ca7b7..99242fd12f9 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -40,6 +40,7 @@ #include "BLI_math.h" #include "BLI_blenlib.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BLI_mempool.h" diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index df3620843ad..f603fa1b0f1 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -248,6 +248,7 @@ static int text_open_exec(bContext *C, wmOperator *op) pprop = op->customdata; if (pprop->prop) { + id_us_ensure_real(&text->id); RNA_id_pointer_create(&text->id, &idptr); RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr); RNA_property_update(C, &pprop->ptr, pprop->prop); diff --git a/source/blender/editors/space_view3d/drawanimviz.c b/source/blender/editors/space_view3d/drawanimviz.c index cf738de0202..f0e65f84205 100644 --- a/source/blender/editors/space_view3d/drawanimviz.c +++ b/source/blender/editors/space_view3d/drawanimviz.c @@ -75,6 +75,80 @@ void draw_motion_paths_init(View3D *v3d, ARegion *ar) glLoadMatrixf(rv3d->viewmat); } +/* set color +* - more intense for active/selected bones, less intense for unselected bones +* - black for before current frame, green for current frame, blue for after current frame +* - intensity decreases as distance from current frame increases +* +* If the user select custom color, the color is replaced for the color selected in UI panel +* - 75% Darker color is used for previous frames +* - 50% Darker color for current frame +* - User selected color for next frames +*/ +static void set_motion_path_color(Scene *scene, bMotionPath *mpath, int i, short sel, int sfra, int efra, + float prev_color[3], float frame_color[3], float next_color[3]) +{ + int frame = sfra + i; + int blend_base = (abs(frame - CFRA) == 1) ? TH_CFRAME : TH_BACK; /* "bleed" cframe color to ease color blending */ + +#define SET_INTENSITY(A, B, C, min, max) (((1.0f - ((C - B) / (C - A))) * (max - min)) + min) + float intensity; /* how faint */ + + if (frame < CFRA) { + if (mpath->flag & MOTIONPATH_FLAG_CUSTOM) { + /* Custom color: previous frames color is darker than current frame */ + glColor3fv(prev_color); + } + else { + /* black - before cfra */ + if (sel) { + /* intensity = 0.5f; */ + intensity = SET_INTENSITY(sfra, i, CFRA, 0.25f, 0.75f); + } + else { + /* intensity = 0.8f; */ + intensity = SET_INTENSITY(sfra, i, CFRA, 0.68f, 0.92f); + } + UI_ThemeColorBlend(TH_WIRE, blend_base, intensity); + } + } + else if (frame > CFRA) { + if (mpath->flag & MOTIONPATH_FLAG_CUSTOM) { + /* Custom color: next frames color is equal to user selected color */ + glColor3fv(next_color); + } + else { + /* blue - after cfra */ + if (sel) { + /* intensity = 0.5f; */ + intensity = SET_INTENSITY(CFRA, i, efra, 0.25f, 0.75f); + } + else { + /* intensity = 0.8f; */ + intensity = SET_INTENSITY(CFRA, i, efra, 0.68f, 0.92f); + } + UI_ThemeColorBlend(TH_BONE_POSE, blend_base, intensity); + } + } + else { + if (mpath->flag & MOTIONPATH_FLAG_CUSTOM) { + /* Custom color: current frame color is slightly darker than user selected color */ + glColor3fv(frame_color); + } + else { + /* green - on cfra */ + if (sel) { + intensity = 0.5f; + } + else { + intensity = 0.99f; + } + UI_ThemeColorBlendShade(TH_CFRAME, TH_BACK, intensity, 10); + } + } +#undef SET_INTENSITY +} + /* Draw the given motion path for an Object or a Bone * - assumes that the viewport has already been initialized properly * i.e. draw_motion_paths_init() has been called @@ -86,6 +160,28 @@ void draw_motion_path_instance(Scene *scene, bMotionPathVert *mpv, *mpv_start; int i, stepsize = avs->path_step; int sfra, efra, sind, len; + float prev_color[3]; + float frame_color[3]; + float next_color[3]; + + /* Custom color - Previous frames: color is darker than current frame */ + prev_color[0] = mpath->color[0] * 0.25f; + prev_color[1] = mpath->color[1] * 0.25f; + prev_color[2] = mpath->color[2] * 0.25f; + + /* Custom color - Current frame: color is slightly darker than user selected color */ + frame_color[0] = mpath->color[0] * 0.50f; + frame_color[1] = mpath->color[1] * 0.50f; + frame_color[2] = mpath->color[2] * 0.50f; + + /* Custom color - Next frames: color is equal to user selection */ + next_color[0] = mpath->color[0]; + next_color[1] = mpath->color[1]; + next_color[2] = mpath->color[2]; + + /* Save old line width */ + GLfloat old_width; + glGetFloatv(GL_LINE_WIDTH, &old_width); /* get frame ranges */ if (avs->path_type == MOTIONPATH_TYPE_ACFRA) { @@ -130,64 +226,27 @@ void draw_motion_path_instance(Scene *scene, mpv_start = (mpath->points + sind); /* draw curve-line of path */ - - glBegin(GL_LINE_STRIP); - for (i = 0, mpv = mpv_start; i < len; i++, mpv++) { - short sel = (pchan) ? (pchan->bone->flag & BONE_SELECTED) : (ob->flag & SELECT); - float intensity; /* how faint */ - - int frame = sfra + i; - int blend_base = (abs(frame - CFRA) == 1) ? TH_CFRAME : TH_BACK; /* "bleed" cframe color to ease color blending */ - - /* set color - * - more intense for active/selected bones, less intense for unselected bones - * - black for before current frame, green for current frame, blue for after current frame - * - intensity decreases as distance from current frame increases - */ -#define SET_INTENSITY(A, B, C, min, max) (((1.0f - ((C - B) / (C - A))) * (max - min)) + min) - if (frame < CFRA) { - /* black - before cfra */ - if (sel) { - /* intensity = 0.5f; */ - intensity = SET_INTENSITY(sfra, i, CFRA, 0.25f, 0.75f); - } - else { - /* intensity = 0.8f; */ - intensity = SET_INTENSITY(sfra, i, CFRA, 0.68f, 0.92f); - } - UI_ThemeColorBlend(TH_WIRE, blend_base, intensity); - } - else if (frame > CFRA) { - /* blue - after cfra */ - if (sel) { - /* intensity = 0.5f; */ - intensity = SET_INTENSITY(CFRA, i, efra, 0.25f, 0.75f); - } - else { - /* intensity = 0.8f; */ - intensity = SET_INTENSITY(CFRA, i, efra, 0.68f, 0.92f); - } - UI_ThemeColorBlend(TH_BONE_POSE, blend_base, intensity); - } - else { - /* green - on cfra */ - if (sel) { - intensity = 0.5f; - } - else { - intensity = 0.99f; - } - UI_ThemeColorBlendShade(TH_CFRAME, TH_BACK, intensity, 10); + /* Draw lines only if line drawing option is enabled */ + if (mpath->flag & MOTIONPATH_FLAG_LINES) { + /* set line thickness */ + glLineWidth(mpath->line_thickness); + + glBegin(GL_LINE_STRIP); + for (i = 0, mpv = mpv_start; i < len; i++, mpv++) { + short sel = (pchan) ? (pchan->bone->flag & BONE_SELECTED) : (ob->flag & SELECT); + /* Set color */ + set_motion_path_color(scene, mpath, i, sel, sfra, efra, prev_color, frame_color, next_color); + /* draw a vertex with this color */ + glVertex3fv(mpv->co); } -#undef SET_INTENSITY - /* draw a vertex with this color */ - glVertex3fv(mpv->co); + glEnd(); + /* back to old line thickness */ + glLineWidth(old_width); } - - glEnd(); - - glPointSize(1.0); + + /* Point must be bigger than line thickness */ + glPointSize(mpath->line_thickness + 1.0); /* draw little black point at each frame * NOTE: this is not really visible/noticeable @@ -197,8 +256,13 @@ void draw_motion_path_instance(Scene *scene, glVertex3fv(mpv->co); glEnd(); - /* Draw little white dots at each framestep value */ - UI_ThemeColor(TH_TEXT_HI); + /* Draw little white dots at each framestep value or replace with custom color */ + if (mpath->flag & MOTIONPATH_FLAG_CUSTOM) { + glColor4fv(mpath->color); + } + else { + UI_ThemeColor(TH_TEXT_HI); + } glBegin(GL_POINTS); for (i = 0, mpv = mpv_start; i < len; i += stepsize, mpv += stepsize) glVertex3fv(mpv->co); @@ -208,11 +272,11 @@ void draw_motion_path_instance(Scene *scene, * NOTE: this is only done when keyframes are shown, since this adds similar types of clutter */ if ((avs->path_viewflag & MOTIONPATH_VIEW_KFRAS) && - (sfra < CFRA) && (CFRA <= efra)) + (sfra < CFRA) && (CFRA <= efra)) { UI_ThemeColor(TH_CFRAME); - glPointSize(6.0f); + glPointSize(mpath->line_thickness + 5.0); glBegin(GL_POINTS); mpv = mpv_start + (CFRA - sfra); glVertex3fv(mpv->co); @@ -289,7 +353,13 @@ void draw_motion_path_instance(Scene *scene, UI_GetThemeColor3ubv(TH_VERTEX_SELECT, col); col[3] = 255; - glPointSize(4.0f); + /* if custom, point must be bigger than line */ + if (mpath->flag & MOTIONPATH_FLAG_CUSTOM) { + glPointSize(mpath->line_thickness + 3.0); + } + else { + glPointSize(4.0f); + } glColor3ubv(col); glBegin(GL_POINTS); diff --git a/source/blender/editors/space_view3d/drawarmature.c b/source/blender/editors/space_view3d/drawarmature.c index 95a2df68e4a..149144d969a 100644 --- a/source/blender/editors/space_view3d/drawarmature.c +++ b/source/blender/editors/space_view3d/drawarmature.c @@ -2463,6 +2463,10 @@ static void draw_ghost_poses_range(Scene *scene, View3D *v3d, ARegion *ar, Base if (end <= start) return; + /* prevent infinite loops if this is set to 0 - T49527 */ + if (arm->ghostsize < 1) + arm->ghostsize = 1; + stepsize = (float)(arm->ghostsize); range = (float)(end - start); @@ -2608,7 +2612,11 @@ static void draw_ghost_poses(Scene *scene, View3D *v3d, ARegion *ar, Base *base) calc_action_range(adt->action, &start, &end, 0); if (start == end) return; - + + /* prevent infinite loops if this is set to 0 - T49527 */ + if (arm->ghostsize < 1) + arm->ghostsize = 1; + stepsize = (float)(arm->ghostsize); range = (float)(arm->ghostep) * stepsize + 0.5f; /* plus half to make the for loop end correct */ diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 20c62e91d01..31ffa019e4e 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -2176,7 +2176,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve calculateCenter(t); if (event) { - initMouseInput(t, &t->mouse, t->center2d, event->mval); + initMouseInput(t, &t->mouse, t->center2d, event->mval, event->shift); } switch (mode) { @@ -8495,7 +8495,7 @@ static void initTimeScale(TransInfo *t) center[1] = t->mouse.imval[1]; /* force a reinit with the center2d used here */ - initMouseInput(t, &t->mouse, center, t->mouse.imval); + initMouseInput(t, &t->mouse, center, t->mouse.imval, false); initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP); diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index a59f9dc43dd..7ea4448a44e 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -728,7 +728,7 @@ typedef enum { INPUT_CUSTOM_RATIO_FLIP, } MouseInputMode; -void initMouseInput(TransInfo *t, MouseInput *mi, const float center[2], const int mval[2]); +void initMouseInput(TransInfo *t, MouseInput *mi, const float center[2], const int mval[2], const bool precision); void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode); eRedrawFlag handleMouseInput(struct TransInfo *t, struct MouseInput *mi, const struct wmEvent *event); void applyMouseInput(struct TransInfo *t, struct MouseInput *mi, const int mval[2], float output[3]); diff --git a/source/blender/editors/transform/transform_input.c b/source/blender/editors/transform/transform_input.c index 9b7d19eacd5..42cc918ec8c 100644 --- a/source/blender/editors/transform/transform_input.c +++ b/source/blender/editors/transform/transform_input.c @@ -234,10 +234,10 @@ static void InputAngleSpring(TransInfo *t, MouseInput *mi, const double mval[2], output[1] = toutput[0]; } -void initMouseInput(TransInfo *UNUSED(t), MouseInput *mi, const float center[2], const int mval[2]) +void initMouseInput(TransInfo *UNUSED(t), MouseInput *mi, const float center[2], const int mval[2], const bool precision) { mi->factor = 0; - mi->precision = 0; + mi->precision = precision; mi->center[0] = center[0]; mi->center[1] = center[1]; diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 90a4aa3614d..058af768885 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -42,7 +42,7 @@ #include "BLI_math.h" #include "BLI_listbase.h" #include "BLI_string.h" -#include "BLI_path_util.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BKE_action.h" diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index f8bb124e943..87ac54b2bd9 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -569,7 +569,9 @@ static void initSnappingMode(TransInfo *t) else if (t->tsnap.applySnap != NULL && // A snapping function actually exist (obedit == NULL) ) // Object Mode { - t->tsnap.modeSelect = SNAP_NOT_SELECTED; + /* In "Edit Strokes" mode, Snap tool can perform snap to selected or active objects (see T49632) + * TODO: perform self snap in gpencil_strokes */ + t->tsnap.modeSelect = ((t->options & CTX_GPENCIL_STROKES) != 0) ? SNAP_ALL : SNAP_NOT_SELECTED; } else { /* Grid if snap is not possible */ @@ -1214,7 +1216,7 @@ bool snapObjectsTransform( t->tsnap.object_context, t->scene->toolsettings->snap_mode, &(const struct SnapObjectParams){ - .snap_select = ((t->options & CTX_GPENCIL_STROKES) != 0) ? SNAP_NOT_ACTIVE : t->tsnap.modeSelect, + .snap_select = t->tsnap.modeSelect, .use_object_edit_cage = (t->flag & T_EDIT) != 0, }, mval, dist_px, NULL, @@ -1304,7 +1306,7 @@ bool peelObjectsTransform( t->tsnap.object_context, mval, &(const struct SnapObjectParams){ - .snap_select = ((t->options & CTX_GPENCIL_STROKES) != 0) ? SNAP_NOT_ACTIVE : t->tsnap.modeSelect, + .snap_select = t->tsnap.modeSelect, .use_object_edit_cage = (t->flag & T_EDIT) != 0, }, use_peel_object, diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 02900d7022c..85209b07677 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -59,6 +59,24 @@ #include "transform.h" +enum eViewProj { + VIEW_PROJ_NONE = -1, + VIEW_PROJ_ORTHO = 0, + VIEW_PROJ_PERSP = -1, +}; + +typedef struct SnapData { + short snap_to; + float mval[2]; + float ray_origin[3]; + float ray_start[3]; + float ray_dir[3]; + float pmat[4][4]; /* perspective matrix */ + float win_half[2];/* win x and y */ + enum eViewProj view_proj; + float depth_range[2]; +} SnapData; + typedef struct SnapObjectData { enum { SNAP_MESH = 1, @@ -110,12 +128,6 @@ struct SnapObjectContext { }; -enum eViewProj { - VIEW_PROJ_NONE = -1, - VIEW_PROJ_ORTHO = 0, - VIEW_PROJ_PERSP = -1, -}; - static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt); @@ -223,6 +235,12 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH /** \Common utilities * \{ */ +MINLINE float depth_get(const float co[3], const float ray_start[3], const float ray_dir[3]) +{ + float dvec[3]; + sub_v3_v3v3(dvec, co, ray_start); + return dot_v3v3(dvec, ray_dir); +} /** * Struct that kepts basic information about a BVHTree build from a editmesh. @@ -232,60 +250,40 @@ typedef struct BVHTreeFromMeshType { char type; } BVHTreeFromMeshType; -typedef struct PreDefProject { - float pmat[4][4]; /* perspective matrix multiplied by object matrix */ - float win_half[2]; - float dist_px_sq; -} PreDefProject; - -static void precalc_project( - PreDefProject *projectdefs, const ARegion *ar, - const float dist_px, float obmat[4][4]) -{ - float (*pmat)[4] = ((RegionView3D *)ar->regiondata)->persmat; - if (obmat) { - mul_m4_m4m4(projectdefs->pmat, pmat, obmat); - } - else { - copy_m4_m4(projectdefs->pmat, pmat); - } - projectdefs->win_half[0] = ar->winx / 2; - projectdefs->win_half[1] = ar->winy / 2; - projectdefs->dist_px_sq = SQUARE(dist_px); -} - /** - * From a threshold (maximum distance to snap in pixels) returns: + * Generates a struct with the immutable parameters that will be used on all objects. * - * - The *real* distance (3D) if you are in orthographic-view. - * - The *tangent* (view cone radius at distance 1.0) if you are in perspective-view. + * \param snap_to: Element to snap, Vertice, Edge or Face. + * \param view_proj: ORTHO or PERSP. + * Currently only works one at a time, but can eventually operate as flag. + * + * \param mval: Mouse coords. + * (When NULL, ray-casting is handled without any projection matrix correction.) + * \param ray_origin: ray_start before being moved toward the ray_normal at the distance from vew3d clip_min. + * \param ray_start: ray_origin moved for the start clipping plane (clip_min). + * \param ray_direction: Unit length direction of the ray. + * \param depth_range: distances of clipe plane min and clip plane max; */ -static float dist_px_to_dist3d_or_tangent(const ARegion *ar, const float dist_px) -{ - const RegionView3D *rv3d = ar->regiondata; - if (ar->winx >= ar->winy) - return 2 * (dist_px / ar->winx) / rv3d->winmat[0][0]; - else - return 2 * (dist_px / ar->winy) / rv3d->winmat[1][1]; -} - -static const float *get_vert_co(const BVHTreeFromMeshType *meshdata, const int index) +static void snap_data_set( + SnapData *snapdata, + const ARegion *ar, const unsigned short snap_to, const enum eViewProj view_proj, + const float mval[2], const float ray_origin[3], const float ray_start[3], + const float ray_direction[3], const float depth_range[2]) { - switch (meshdata->type) { - case SNAP_MESH: - { - BVHTreeFromMesh *data = meshdata->userdata; - const MVert *vert = data->vert; - return vert[index].co; - } - case SNAP_EDIT_MESH: - { - BVHTreeFromEditMesh *data = meshdata->userdata; - BMVert *eve = BM_vert_at_index(data->em->bm, index); - return eve->co; - } - } - return NULL; + if (ar) { + copy_m4_m4(snapdata->pmat, ((RegionView3D *)ar->regiondata)->persmat); + snapdata->win_half[0] = ar->winx / 2; + snapdata->win_half[1] = ar->winy / 2; + } + if (mval) { + copy_v2_v2(snapdata->mval, mval); + } + snapdata->snap_to = snap_to; + copy_v3_v3(snapdata->ray_origin, ray_origin); + copy_v3_v3(snapdata->ray_start, ray_start); + copy_v3_v3(snapdata->ray_dir, ray_direction); + snapdata->view_proj = view_proj; + copy_v2_v2(snapdata->depth_range, depth_range); } static void copy_vert_no(const BVHTreeFromMeshType *meshdata, const int index, float r_no[3]) @@ -336,87 +334,13 @@ static void get_edge_verts( } } -#define V3_MUL_ELEM(a, b) \ - (a)[0] * (b)[0], \ - (a)[1] * (b)[1], \ - (a)[2] * (b)[2] - -static bool test_vert_dist( - const float vco[3], const float ray_co[3], const float ray_dir[3], - const float ray_depth_range[2], const float scale[3], - /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, - /* return args */ - float r_co[3]) -{ - const float vco_sc[3] = {V3_MUL_ELEM(vco, scale)}; - const float origin_sc[3] = {V3_MUL_ELEM(ray_co, scale)}; - const float dir_sc[3] = {V3_MUL_ELEM(ray_dir, scale)}; - - float depth, dist_sq; - dist_sq = dist_squared_to_ray_v3(origin_sc, dir_sc, vco_sc, &depth); - - if (depth < ray_depth_range[0]) { - return false; - } - - if ((dist_sq < *dist_to_ray_sq) && (depth < *ray_depth)) { - *dist_to_ray_sq = dist_sq; - - copy_v3_v3(r_co, vco); - - *ray_depth = depth; - return true; - } - return false; -} - -static bool test_edge_dist( - const float v1[3], const float v2[3], const float ray_co[3], const float ray_dir[3], - const float ray_depth_range[2], const float scale[3], - /* read/write args */ - float *ray_depth, float *dist_to_ray_sq, - /* return args */ - float r_co[3]) -{ - const float v1_sc[3] = {V3_MUL_ELEM(v1, scale)}; - const float v2_sc[3] = {V3_MUL_ELEM(v2, scale)}; - const float co_sc[3] = {V3_MUL_ELEM(ray_co, scale)}; - const float dir_sc[3] = {V3_MUL_ELEM(ray_dir, scale)}; - - float tmp_co[3], depth, dist_sq; - dist_sq = dist_squared_ray_to_seg_v3(co_sc, dir_sc, v1_sc, v2_sc, tmp_co, &depth); - - if (depth < ray_depth_range[0]) { - return false; - } - - if ((dist_sq < *dist_to_ray_sq) && (depth < *ray_depth)) { - *dist_to_ray_sq = dist_sq; - - tmp_co[0] /= scale[0]; - tmp_co[1] /= scale[1]; - tmp_co[2] /= scale[2]; - - copy_v3_v3(r_co, tmp_co); - - *ray_depth = depth; - return true; - } - return false; -} - -#undef V3_MUL_ELEM - static bool test_projected_vert_dist( - PreDefProject *projectdefs, - const float co[3], const enum eViewProj view_proj, - const float mval[2], const float depth_range[2], - float r_co[3]) + const float depth_range[2], const float mval[2], const float co[3], + float pmat[4][4], const float win_half[2], const bool is_persp, + float *dist_px_sq, float r_co[3]) { float depth; - float(*pmat)[4] = projectdefs->pmat; - if (view_proj == VIEW_PROJ_PERSP) { + if (is_persp) { depth = mul_project_m4_v3_zfac(pmat, co); if (depth < depth_range[0] || depth > depth_range[1]) { return false; @@ -428,109 +352,105 @@ static bool test_projected_vert_dist( (dot_m4_v3_row_y(pmat, co) + pmat[3][1]), }; - if (view_proj == VIEW_PROJ_PERSP) { + if (is_persp) { mul_v2_fl(co2d, 1 / depth); } co2d[0] += 1.0f; co2d[1] += 1.0f; - co2d[0] *= projectdefs->win_half[0]; - co2d[1] *= projectdefs->win_half[1]; + co2d[0] *= win_half[0]; + co2d[1] *= win_half[1]; const float dist_sq = len_squared_v2v2(mval, co2d); - if (dist_sq < projectdefs->dist_px_sq) { + if (dist_sq < *dist_px_sq) { copy_v3_v3(r_co, co); - projectdefs->dist_px_sq = dist_sq; + *dist_px_sq = dist_sq; return true; } return false; } static bool test_projected_edge_dist( - PreDefProject *projectdefs, - const float va[3], const float vb[3], const float ray_start[3], const float ray_normal[3], - const enum eViewProj view_proj, const float mval[2], const float depth_range[2], - float r_co[3]) + const float depth_range[2], const float mval[2], + float pmat[4][4], const float win_half[2], const bool is_persp, + const float ray_start[3], const float ray_dir[3], + const float va[3], const float vb[3], + float *dist_px_sq, float r_co[3]) { float tmp_co[3], depth; - dist_squared_ray_to_seg_v3(ray_start, ray_normal, va, vb, tmp_co, &depth); - return test_projected_vert_dist(projectdefs, tmp_co, view_proj, mval, depth_range, r_co); + dist_squared_ray_to_seg_v3(ray_start, ray_dir, va, vb, tmp_co, &depth); + return test_projected_vert_dist(depth_range, mval, tmp_co, pmat, win_half, is_persp, dist_px_sq, r_co); } - -/** \} */ - - -/* -------------------------------------------------------------------- */ - -/** \Walk DFS - * \{ */ -typedef struct Object_Nearest2dPrecalc { +typedef struct Nearest2dPrecalc { float ray_origin_local[3]; float ray_direction_local[3]; float ray_inv_dir[3]; - PreDefProject projectdefs; + float ray_min_dist; + float pmat[4][4]; /* perspective matrix multiplied by object matrix */ + bool is_persp; + float win_half[2]; + float mval[2]; bool sign[3]; - bool r_axis_closest[3]; - float depth_range[2]; +} Nearest2dPrecalc; - void *userdata; - int index; - float co[3]; - float no[3]; -} Object_Nearest2dPrecalc; - - -static void nearest2d_precalc( - Object_Nearest2dPrecalc *neasrest_precalc, const ARegion *ar, - const float dist_px, float obmat[4][4], - const float ray_origin_local[3], const float ray_direction_local[3], - const float mval[2], const float depth_range[2]) +/** + * \param lpmat: Perspective matrix multiplied by object matrix + */ +static void dist_squared_to_projected_aabb_precalc( + struct Nearest2dPrecalc *neasrest_precalc, + float lpmat[4][4], bool is_persp, const float win_half[2], + const float ray_min_dist, const float mval[2], + const float ray_origin_local[3], const float ray_direction_local[3]) { - precalc_project(&neasrest_precalc->projectdefs, ar, dist_px, obmat); + copy_m4_m4(neasrest_precalc->pmat, lpmat); + neasrest_precalc->is_persp = is_persp; + copy_v2_v2(neasrest_precalc->win_half, win_half); + neasrest_precalc->ray_min_dist = ray_min_dist; + copy_v3_v3(neasrest_precalc->ray_origin_local, ray_origin_local); copy_v3_v3(neasrest_precalc->ray_direction_local, ray_direction_local); copy_v2_v2(neasrest_precalc->mval, mval); - copy_v2_v2(neasrest_precalc->depth_range, depth_range); for (int i = 0; i < 3; i++) { - neasrest_precalc->ray_inv_dir[i] = + neasrest_precalc->ray_inv_dir[i] = (neasrest_precalc->ray_direction_local[i] != 0.0f) ? (1.0f / neasrest_precalc->ray_direction_local[i]) : FLT_MAX; neasrest_precalc->sign[i] = (neasrest_precalc->ray_inv_dir[i] < 0.0f); - neasrest_precalc->r_axis_closest[i] = true; } } -static bool cb_walk_parent_snap_project(const BVHTreeAxisRange *bounds, void *user_data) +static float dist_squared_to_projected_aabb( + struct Nearest2dPrecalc *data, + const float bbmin[3], const float bbmax[3], + bool r_axis_closest[3]) { - Object_Nearest2dPrecalc *data = user_data; float local_bvmin[3], local_bvmax[3]; if (data->sign[0]) { - local_bvmin[0] = bounds[0].max; - local_bvmax[0] = bounds[0].min; + local_bvmin[0] = bbmax[0]; + local_bvmax[0] = bbmin[0]; } else { - local_bvmin[0] = bounds[0].min; - local_bvmax[0] = bounds[0].max; + local_bvmin[0] = bbmin[0]; + local_bvmax[0] = bbmax[0]; } if (data->sign[1]) { - local_bvmin[1] = bounds[1].max; - local_bvmax[1] = bounds[1].min; + local_bvmin[1] = bbmax[1]; + local_bvmax[1] = bbmin[1]; } else { - local_bvmin[1] = bounds[1].min; - local_bvmax[1] = bounds[1].max; + local_bvmin[1] = bbmin[1]; + local_bvmax[1] = bbmax[1]; } if (data->sign[2]) { - local_bvmin[2] = bounds[2].max; - local_bvmax[2] = bounds[2].min; + local_bvmin[2] = bbmax[2]; + local_bvmax[2] = bbmin[2]; } else { - local_bvmin[2] = bounds[2].min; - local_bvmax[2] = bounds[2].max; + local_bvmin[2] = bbmin[2]; + local_bvmax[2] = bbmax[2]; } const float tmin[3] = { @@ -543,7 +463,9 @@ static bool cb_walk_parent_snap_project(const BVHTreeAxisRange *bounds, void *us (local_bvmax[1] - data->ray_origin_local[1]) * data->ray_inv_dir[1], (local_bvmax[2] - data->ray_origin_local[2]) * data->ray_inv_dir[2], }; + /* `va` and `vb` are the coordinates of the AABB edge closest to the ray */ float va[3], vb[3]; + /* `rtmin` and `rtmax` are the minimum and maximum distances of the ray hits on the AABB */ float rtmin, rtmax; int main_axis; @@ -551,38 +473,38 @@ static bool cb_walk_parent_snap_project(const BVHTreeAxisRange *bounds, void *us rtmax = tmax[0]; va[0] = vb[0] = local_bvmax[0]; main_axis = 3; - data->r_axis_closest[0] = data->sign[0]; + r_axis_closest[0] = data->sign[0]; } else if ((tmax[1] <= tmax[0]) && (tmax[1] <= tmax[2])) { rtmax = tmax[1]; va[1] = vb[1] = local_bvmax[1]; main_axis = 2; - data->r_axis_closest[1] = data->sign[1]; + r_axis_closest[1] = data->sign[1]; } else { rtmax = tmax[2]; va[2] = vb[2] = local_bvmax[2]; main_axis = 1; - data->r_axis_closest[2] = data->sign[2]; + r_axis_closest[2] = data->sign[2]; } if ((tmin[0] >= tmin[1]) && (tmin[0] >= tmin[2])) { rtmin = tmin[0]; va[0] = vb[0] = local_bvmin[0]; main_axis -= 3; - data->r_axis_closest[0] = !data->sign[0]; + r_axis_closest[0] = !data->sign[0]; } else if ((tmin[1] >= tmin[0]) && (tmin[1] >= tmin[2])) { rtmin = tmin[1]; va[1] = vb[1] = local_bvmin[1]; main_axis -= 1; - data->r_axis_closest[1] = !data->sign[1]; + r_axis_closest[1] = !data->sign[1]; } else { rtmin = tmin[2]; va[2] = vb[2] = local_bvmin[2]; main_axis -= 2; - data->r_axis_closest[2] = !data->sign[2]; + r_axis_closest[2] = !data->sign[2]; } if (main_axis < 0) { main_axis += 3; @@ -590,22 +512,34 @@ static bool cb_walk_parent_snap_project(const BVHTreeAxisRange *bounds, void *us /* if rtmin < rtmax, ray intersect `AABB` */ if (rtmin <= rtmax) { +#define IGNORE_BEHIND_RAY #ifdef IGNORE_BEHIND_RAY - /* `if rtmax < depth_min`, the whole `AABB` is behind us */ - if (rtmax < min_depth) { - return fallback; + /* `if rtmax < depth_min`, the hit is behind us */ + if (rtmax < data->ray_min_dist) { + /* Test if the entire AABB is behind us */ + float depth = depth_get( + local_bvmax, data->ray_origin_local, data->ray_direction_local); + if (depth < (data->ray_min_dist)) { + return FLT_MAX; + } } #endif const float proj = rtmin * data->ray_direction_local[main_axis]; - data->r_axis_closest[main_axis] = (proj - va[main_axis]) < (vb[main_axis] - proj); - return true; + r_axis_closest[main_axis] = (proj - va[main_axis]) < (vb[main_axis] - proj); + return 0.0f; } #ifdef IGNORE_BEHIND_RAY - /* `if rtmin < depth_min`, the whole `AABB` is behing us */ - else if (rtmin < min_depth) { - return fallback; + /* `if rtmin < depth_min`, the hit is behing us */ + else if (rtmin < data->ray_min_dist) { + /* Test if the entire AABB is behind us */ + float depth = depth_get( + local_bvmax, data->ray_origin_local, data->ray_direction_local); + if (depth < (data->ray_min_dist)) { + return FLT_MAX; + } } #endif +#undef IGNORE_BEHIND_RAY if (data->sign[main_axis]) { va[main_axis] = local_bvmax[main_axis]; vb[main_axis] = local_bvmin[main_axis]; @@ -616,31 +550,35 @@ static bool cb_walk_parent_snap_project(const BVHTreeAxisRange *bounds, void *us } float scale = fabsf(local_bvmax[main_axis] - local_bvmin[main_axis]); - float (*pmat)[4] = data->projectdefs.pmat; - float depth_a = mul_project_m4_v3_zfac(pmat, va); - float depth_b = depth_a + pmat[main_axis][3] * scale; + float (*pmat)[4] = data->pmat; float va2d[2] = { (dot_m4_v3_row_x(pmat, va) + pmat[3][0]), (dot_m4_v3_row_y(pmat, va) + pmat[3][1]), }; float vb2d[2] = { - (va2d[0] + pmat[main_axis][0] * scale) / depth_b, - (va2d[1] + pmat[main_axis][1] * scale) / depth_b, + (va2d[0] + pmat[main_axis][0] * scale), + (va2d[1] + pmat[main_axis][1] * scale), }; - va2d[0] /= depth_a; - va2d[1] /= depth_a; + if (data->is_persp) { + float depth_a = mul_project_m4_v3_zfac(pmat, va); + float depth_b = depth_a + pmat[main_axis][3] * scale; + va2d[0] /= depth_a; + va2d[1] /= depth_a; + vb2d[0] /= depth_b; + vb2d[1] /= depth_b; + } va2d[0] += 1.0f; va2d[1] += 1.0f; vb2d[0] += 1.0f; vb2d[1] += 1.0f; - va2d[0] *= data->projectdefs.win_half[0]; - va2d[1] *= data->projectdefs.win_half[1]; - vb2d[0] *= data->projectdefs.win_half[0]; - vb2d[1] *= data->projectdefs.win_half[1]; + va2d[0] *= data->win_half[0]; + va2d[1] *= data->win_half[1]; + vb2d[0] *= data->win_half[0]; + vb2d[1] *= data->win_half[1]; //float dvec[2], edge[2], rdist; //sub_v2_v2v2(dvec, data->mval, va2d); @@ -653,73 +591,138 @@ static bool cb_walk_parent_snap_project(const BVHTreeAxisRange *bounds, void *us lambda /= edge[0] * edge[0] + edge[1] * edge[1]; if (lambda <= 0.0f) { rdist = len_squared_v2v2(data->mval, va2d); - data->r_axis_closest[main_axis] = true; + r_axis_closest[main_axis] = true; } else if (lambda >= 1.0f) { rdist = len_squared_v2v2(data->mval, vb2d); - data->r_axis_closest[main_axis] = false; + r_axis_closest[main_axis] = false; } else { va2d[0] += edge[0] * lambda; va2d[1] += edge[1] * lambda; rdist = len_squared_v2v2(data->mval, va2d); - data->r_axis_closest[main_axis] = lambda < 0.5f; + r_axis_closest[main_axis] = lambda < 0.5f; } } else { rdist = len_squared_v2v2(data->mval, va2d); } - return rdist < data->projectdefs.dist_px_sq; + return rdist; +} + +static float dist_squared_to_projected_aabb_simple( + float lpmat[4][4], const float win_half[2], + const float ray_min_dist, const float mval[2], + const float ray_origin_local[3], const float ray_direction_local[3], + const float bbmin[3], const float bbmax[3]) +{ + struct Nearest2dPrecalc data; + dist_squared_to_projected_aabb_precalc( + &data, lpmat, true, win_half, ray_min_dist, + mval, ray_origin_local, ray_direction_local); + + bool dummy[3] = {true, true, true}; + return dist_squared_to_projected_aabb(&data, bbmin, bbmax, dummy); +} + +static float dist_aabb_to_plane( + const float bbmin[3], const float bbmax[3], + const float plane_co[3], const float plane_no[3]) +{ + const float local_bvmin[3] = { + (plane_no[0] < 0) ? bbmax[0] : bbmin[0], + (plane_no[1] < 0) ? bbmax[1] : bbmin[1], + (plane_no[2] < 0) ? bbmax[2] : bbmin[2], + }; + return depth_get(local_bvmin, plane_co, plane_no); +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \Walk DFS + * \{ */ +typedef struct Nearest2dUserData { + struct Nearest2dPrecalc data_precalc; + + float dist_px_sq; + + bool r_axis_closest[3]; + + float depth_range[2]; + void *userdata; + int index; + float co[3]; + float no[3]; +} Nearest2dUserData; + + +static bool cb_walk_parent_snap_project(const BVHTreeAxisRange *bounds, void *user_data) +{ + Nearest2dUserData *data = user_data; + const float bbmin[3] = {bounds[0].min, bounds[1].min, bounds[2].min}; + const float bbmax[3] = {bounds[0].max, bounds[1].max, bounds[2].max}; + const float rdist = dist_squared_to_projected_aabb( + &data->data_precalc, bbmin, bbmax, data->r_axis_closest); + return rdist < data->dist_px_sq; } static bool cb_walk_leaf_snap_vert(const BVHTreeAxisRange *bounds, int index, void *userdata) { - struct Object_Nearest2dPrecalc *neasrest_precalc = userdata; + struct Nearest2dUserData *data = userdata; + struct Nearest2dPrecalc *neasrest_precalc = &data->data_precalc; const float co[3] = { (bounds[0].min + bounds[0].max) / 2, (bounds[1].min + bounds[1].max) / 2, (bounds[2].min + bounds[2].max) / 2, }; - /* Currently the `BLI_bvhtree_walk_dfs` is being used only in the perspective view mode (VIEW_PROJ_PERSP) - * It could be used in orthographic view mode too (VIEW_PROJ_ORTHO), - * but in this case the `BLI_bvhtree_find_nearest_to_ray` is more efficient.*/ if (test_projected_vert_dist( - &neasrest_precalc->projectdefs, co, VIEW_PROJ_PERSP, - neasrest_precalc->mval, neasrest_precalc->depth_range, - neasrest_precalc->co)) + data->depth_range, + neasrest_precalc->mval, co, + neasrest_precalc->pmat, + neasrest_precalc->win_half, + neasrest_precalc->is_persp, + &data->dist_px_sq, + data->co)) { - copy_vert_no(neasrest_precalc->userdata, index, neasrest_precalc->no); - neasrest_precalc->index = index; + copy_vert_no(data->userdata, index, data->no); + data->index = index; } return true; } static bool cb_walk_leaf_snap_edge(const BVHTreeAxisRange *UNUSED(bounds), int index, void *userdata) { - struct Object_Nearest2dPrecalc *neasrest_precalc = userdata; + struct Nearest2dUserData *data = userdata; + struct Nearest2dPrecalc *neasrest_precalc = &data->data_precalc; const float *v_pair[2]; - get_edge_verts(neasrest_precalc->userdata, index, v_pair); + get_edge_verts(data->userdata, index, v_pair); - /* Currently the `BLI_bvhtree_walk_dfs` is being used only in the perspective view mode (VIEW_PROJ_PERSP) - * It could be used in orthographic view mode too (VIEW_PROJ_ORTHO), - * but in this case the `BLI_bvhtree_find_nearest_to_ray` is more efficient.*/ if (test_projected_edge_dist( - &neasrest_precalc->projectdefs, v_pair[0], v_pair[1], - neasrest_precalc->ray_origin_local, neasrest_precalc->ray_direction_local, - VIEW_PROJ_PERSP, neasrest_precalc->mval, neasrest_precalc->depth_range, - neasrest_precalc->co)) + data->depth_range, + neasrest_precalc->mval, + neasrest_precalc->pmat, + neasrest_precalc->win_half, + neasrest_precalc->is_persp, + neasrest_precalc->ray_origin_local, + neasrest_precalc->ray_direction_local, + v_pair[0], v_pair[1], + &data->dist_px_sq, + data->co)) { - sub_v3_v3v3(neasrest_precalc->no, v_pair[0], v_pair[1]); - neasrest_precalc->index = index; + sub_v3_v3v3(data->no, v_pair[0], v_pair[1]); + data->index = index; } return true; } static bool cb_nearest_walk_order(const BVHTreeAxisRange *UNUSED(bounds), char axis, void *userdata) { - const bool *r_axis_closest = ((struct Object_Nearest2dPrecalc *)userdata)->r_axis_closest; + const bool *r_axis_closest = ((struct Nearest2dUserData *)userdata)->r_axis_closest; return r_axis_closest[axis]; } @@ -731,46 +734,56 @@ static bool cb_nearest_walk_order(const BVHTreeAxisRange *UNUSED(bounds), char a * \{ */ static bool snapArmature( - const ARegion *ar, Object *ob, bArmature *arm, float obmat[4][4], - const short snap_to, const float origin[3], const float dir[3], - const float mval[2], const enum eViewProj view_proj, const float depth_range[2], + SnapData *snapdata, + Object *ob, bArmature *arm, float obmat[4][4], /* read/write args */ - float *dist_px, + float *ray_depth, float *dist_px, /* return args */ float r_loc[3], float *UNUSED(r_no)) { bool retval = false; - float ray_start_local[3], ray_normal_local[3]; - if (snap_to != SCE_SNAP_MODE_VERTEX) { + float ray_start_local[3], ray_normal_local[3]; /* Used only in the snap to edges */ + if (snapdata->snap_to == SCE_SNAP_MODE_EDGE) { float imat[4][4]; invert_m4_m4(imat, obmat); - copy_v3_v3(ray_start_local, origin); - copy_v3_v3(ray_normal_local, dir); + copy_v3_v3(ray_start_local, snapdata->ray_origin); + copy_v3_v3(ray_normal_local, snapdata->ray_dir); mul_m4_v3(imat, ray_start_local); mul_mat3_m4_v3(imat, ray_normal_local); } + else if (snapdata->snap_to != SCE_SNAP_MODE_VERTEX) { /* Currently only edge and vert */ + return retval; + } - PreDefProject projectdefs; - precalc_project(&projectdefs, ar, *dist_px, obmat); + bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + float lpmat[4][4], dist_px_sq; + mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + dist_px_sq = SQUARE(*dist_px); if (arm->edbo) { for (EditBone *eBone = arm->edbo->first; eBone; eBone = eBone->next) { if (eBone->layer & arm->layer) { /* skip hidden or moving (selected) bones */ if ((eBone->flag & (BONE_HIDDEN_A | BONE_ROOTSEL | BONE_TIPSEL)) == 0) { - switch (snap_to) { + switch (snapdata->snap_to) { case SCE_SNAP_MODE_VERTEX: retval |= test_projected_vert_dist( - &projectdefs, eBone->head, view_proj, mval, depth_range, r_loc); + snapdata->depth_range, snapdata->mval, eBone->head, + lpmat, snapdata->win_half, is_persp, &dist_px_sq, + r_loc); retval |= test_projected_vert_dist( - &projectdefs, eBone->tail, view_proj, mval, depth_range, r_loc); + snapdata->depth_range, snapdata->mval, eBone->tail, + lpmat, snapdata->win_half, is_persp, &dist_px_sq, + r_loc); break; case SCE_SNAP_MODE_EDGE: retval |= test_projected_edge_dist( - &projectdefs, eBone->head, eBone->tail, ray_start_local, ray_normal_local, - view_proj, mval, depth_range, r_loc); + snapdata->depth_range, snapdata->mval, lpmat, + snapdata->win_half, is_persp, ray_start_local, ray_normal_local, + eBone->head, eBone->tail, + &dist_px_sq, r_loc); break; } } @@ -785,52 +798,60 @@ static bool snapArmature( const float *head_vec = pchan->pose_head; const float *tail_vec = pchan->pose_tail; - switch (snap_to) { + switch (snapdata->snap_to) { case SCE_SNAP_MODE_VERTEX: retval |= test_projected_vert_dist( - &projectdefs, head_vec, view_proj, mval, depth_range, r_loc); + snapdata->depth_range, snapdata->mval, head_vec, + lpmat, snapdata->win_half, is_persp, &dist_px_sq, + r_loc); retval |= test_projected_vert_dist( - &projectdefs, tail_vec, view_proj, mval, depth_range, r_loc); + snapdata->depth_range, snapdata->mval, tail_vec, + lpmat, snapdata->win_half, is_persp, &dist_px_sq, + r_loc); break; case SCE_SNAP_MODE_EDGE: retval |= test_projected_edge_dist( - &projectdefs, head_vec, tail_vec, ray_start_local, ray_normal_local, - view_proj, mval, depth_range, r_loc); + snapdata->depth_range, snapdata->mval, lpmat, + snapdata->win_half, is_persp, ray_start_local, ray_normal_local, + head_vec, tail_vec, + &dist_px_sq, r_loc); break; } } } } if (retval) { - *dist_px = sqrtf(projectdefs.dist_px_sq); + *dist_px = sqrtf(dist_px_sq); mul_m4_v3(obmat, r_loc); + *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); return true; } return false; } static bool snapCurve( - const ARegion *ar, Object *ob, Curve *cu, float obmat[4][4], - const short snap_to, const float mval[2], const enum eViewProj view_proj, - const float depth_range[2], + SnapData *snapdata, + Object *ob, Curve *cu, float obmat[4][4], /* read/write args */ - float *dist_px, + float *ray_depth, float *dist_px, /* return args */ float r_loc[3], float *UNUSED(r_no)) { bool retval = false; /* only vertex snapping mode (eg control points and handles) supported for now) */ - if (snap_to != SCE_SNAP_MODE_VERTEX) { + if (snapdata->snap_to != SCE_SNAP_MODE_VERTEX) { return retval; } - PreDefProject projectdefs; - precalc_project(&projectdefs, ar, *dist_px, obmat); + bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + float lpmat[4][4], dist_px_sq; + mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + dist_px_sq = SQUARE(*dist_px); for (Nurb *nu = (ob->mode == OB_MODE_EDIT ? cu->editnurb->nurbs.first : cu->nurb.first); nu; nu = nu->next) { for (int u = 0; u < nu->pntsu; u++) { - switch (snap_to) { + switch (snapdata->snap_to) { case SCE_SNAP_MODE_VERTEX: { if (ob->mode == OB_MODE_EDIT) { @@ -840,19 +861,25 @@ static bool snapCurve( break; } retval |= test_projected_vert_dist( - &projectdefs, nu->bezt[u].vec[1], view_proj, mval, depth_range, r_loc); + snapdata->depth_range, snapdata->mval, nu->bezt[u].vec[1], + lpmat, snapdata->win_half, is_persp, &dist_px_sq, + r_loc); /* don't snap if handle is selected (moving), or if it is aligning to a moving handle */ if (!(nu->bezt[u].f1 & SELECT) && !(nu->bezt[u].h1 & HD_ALIGN && nu->bezt[u].f3 & SELECT)) { retval |= test_projected_vert_dist( - &projectdefs, nu->bezt[u].vec[0], view_proj, mval, depth_range, r_loc); + snapdata->depth_range, snapdata->mval, nu->bezt[u].vec[0], + lpmat, snapdata->win_half, is_persp, &dist_px_sq, + r_loc); } if (!(nu->bezt[u].f3 & SELECT) && !(nu->bezt[u].h2 & HD_ALIGN && nu->bezt[u].f1 & SELECT)) { retval |= test_projected_vert_dist( - &projectdefs, nu->bezt[u].vec[2], view_proj, mval, depth_range, r_loc); + snapdata->depth_range, snapdata->mval, nu->bezt[u].vec[2], + lpmat, snapdata->win_half, is_persp, &dist_px_sq, + r_loc); } } else { @@ -861,7 +888,9 @@ static bool snapCurve( break; } retval |= test_projected_vert_dist( - &projectdefs, nu->bp[u].vec, view_proj, mval, depth_range, r_loc); + snapdata->depth_range, snapdata->mval, nu->bp[u].vec, + lpmat, snapdata->win_half, is_persp, &dist_px_sq, + r_loc); } } else { @@ -869,11 +898,15 @@ static bool snapCurve( if (nu->pntsu > 1) { if (nu->bezt) { retval |= test_projected_vert_dist( - &projectdefs, nu->bezt[u].vec[1], view_proj, mval, depth_range, r_loc); + snapdata->depth_range, snapdata->mval, nu->bezt[u].vec[1], + lpmat, snapdata->win_half, is_persp, &dist_px_sq, + r_loc); } else { retval |= test_projected_vert_dist( - &projectdefs, nu->bp[u].vec, view_proj, mval, depth_range, r_loc); + snapdata->depth_range, snapdata->mval, nu->bp[u].vec, + lpmat, snapdata->win_half, is_persp, &dist_px_sq, + r_loc); } } } @@ -885,8 +918,9 @@ static bool snapCurve( } } if (retval) { - *dist_px = sqrtf(projectdefs.dist_px_sq); + *dist_px = sqrtf(dist_px_sq); mul_m4_v3(obmat, r_loc); + *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); return true; } return false; @@ -894,11 +928,10 @@ static bool snapCurve( /* may extend later (for now just snaps to empty center) */ static bool snapEmpty( - const ARegion *ar, Object *ob, float obmat[4][4], - const short snap_to, const float mval[2], const enum eViewProj view_proj, - const float depth_range[2], + SnapData *snapdata, + Object *ob, float obmat[4][4], /* read/write args */ - float *dist_px, + float *ray_depth, float *dist_px, /* return args */ float r_loc[3], float *UNUSED(r_no)) { @@ -909,15 +942,19 @@ static bool snapEmpty( } /* for now only vertex supported */ - switch (snap_to) { + switch (snapdata->snap_to) { case SCE_SNAP_MODE_VERTEX: { - PreDefProject projectdefs; - precalc_project(&projectdefs, ar, *dist_px, NULL); + bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + float dist_px_sq = SQUARE(*dist_px); float tmp_co[3]; copy_v3_v3(tmp_co, obmat[3]); - if (test_projected_vert_dist(&projectdefs, tmp_co, view_proj, mval, depth_range, r_loc)) { - *dist_px = sqrtf(projectdefs.dist_px_sq); + if (test_projected_vert_dist( + snapdata->depth_range, snapdata->mval, tmp_co, + snapdata->pmat, snapdata->win_half, is_persp, &dist_px_sq, + r_loc)) { + *dist_px = sqrtf(dist_px_sq); + *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); retval = true; } break; @@ -930,18 +967,17 @@ static bool snapEmpty( } static bool snapCamera( - const SnapObjectContext *sctx, Object *object, float obmat[4][4], - const short snap_to, const float mval[2], const enum eViewProj view_proj, - const float depth_range[2], + const SnapObjectContext *sctx, SnapData *snapdata, + Object *object, float obmat[4][4], /* read/write args */ - float *dist_px, + float *ray_depth, float *dist_px, /* return args */ float r_loc[3], float *UNUSED(r_no)) { Scene *scene = sctx->scene; - PreDefProject projectdefs; - precalc_project(&projectdefs, sctx->v3d_data.ar, *dist_px, NULL); + bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + float dist_px_sq = SQUARE(*dist_px); float orig_camera_mat[4][4], orig_camera_imat[4][4], imat[4][4]; bool retval = false; @@ -962,7 +998,7 @@ static bool snapCamera( invert_m4_m4(orig_camera_imat, orig_camera_mat); invert_m4_m4(imat, obmat); - switch (snap_to) { + switch (snapdata->snap_to) { case SCE_SNAP_MODE_VERTEX: { MovieTrackingObject *tracking_object; @@ -1002,7 +1038,9 @@ static bool snapCamera( mul_m4_v3(vertex_obmat, bundle_pos); retval |= test_projected_vert_dist( - &projectdefs, bundle_pos, view_proj, mval, depth_range, r_loc); + snapdata->depth_range, snapdata->mval, bundle_pos, + snapdata->pmat, snapdata->win_half, is_persp, &dist_px_sq, + r_loc); } } @@ -1013,7 +1051,8 @@ static bool snapCamera( } if (retval) { - *dist_px = sqrtf(projectdefs.dist_px_sq); + *dist_px = sqrtf(dist_px_sq); + *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); return true; } return false; @@ -1025,57 +1064,10 @@ static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt) return index_mp_to_orig ? index_mp_to_orig[lt->poly] : lt->poly; } -struct NearestDM_Data { - void *bvhdata; - const float *depth_range; - float *ray_depth; -}; - -static void test_vert_ray_dist_cb( - void *userdata, const float origin[3], const float dir[3], - const float scale[3], int index, BVHTreeNearest *nearest) -{ - struct NearestDM_Data *ndata = userdata; - const struct BVHTreeFromMeshType *data = ndata->bvhdata; - - const float *co = get_vert_co(data, index); - - if (test_vert_dist( - co, origin, dir, ndata->depth_range, - scale, ndata->ray_depth, &nearest->dist_sq, - nearest->co)) - { - copy_vert_no(data, index, nearest->no); - nearest->index = index; - } -} - -static void test_edge_ray_dist_cb( - void *userdata, const float origin[3], const float dir[3], - const float scale[3], int index, BVHTreeNearest *nearest) -{ - struct NearestDM_Data *ndata = userdata; - BVHTreeFromMeshType *data = ndata->bvhdata; - - const float *v_pair[2]; - get_edge_verts(data, index, v_pair); - - if (test_edge_dist( - v_pair[0], v_pair[1], origin, dir, ndata->depth_range, - scale, ndata->ray_depth, &nearest->dist_sq, - nearest->co)) - { - sub_v3_v3v3(nearest->no, v_pair[0], v_pair[1]); - nearest->index = index; - } -} - static bool snapDerivedMesh( - SnapObjectContext *sctx, + SnapObjectContext *sctx, SnapData *snapdata, Object *ob, DerivedMesh *dm, float obmat[4][4], const unsigned int ob_index, - const short snap_to, const float mval[2], const enum eViewProj view_proj, bool do_bb, - const float ray_origin[3], const float ray_start[3], const float ray_normal[3], - const float depth_range[2], + bool do_bb, /* read/write args */ float *ray_depth, float *dist_px, /* return args */ @@ -1084,12 +1076,12 @@ static bool snapDerivedMesh( { bool retval = false; - if (snap_to == SCE_SNAP_MODE_FACE) { + if (snapdata->snap_to == SCE_SNAP_MODE_FACE) { if (dm->getNumPolys(dm) == 0) { return retval; } } - else if (snap_to == SCE_SNAP_MODE_EDGE) { + else if (snapdata->snap_to == SCE_SNAP_MODE_EDGE) { if (dm->getNumEdges(dm) == 0) { return retval; } @@ -1101,7 +1093,9 @@ static bool snapDerivedMesh( } { - bool need_ray_start_correction_init = (snap_to == SCE_SNAP_MODE_FACE) && (view_proj == VIEW_PROJ_ORTHO); + bool need_ray_start_correction_init = + (snapdata->snap_to == SCE_SNAP_MODE_FACE) && + (snapdata->view_proj == VIEW_PROJ_ORTHO); float imat[4][4]; float timat[3][3]; /* transpose inverse matrix for normals */ @@ -1111,8 +1105,8 @@ static bool snapDerivedMesh( invert_m4_m4(imat, obmat); transpose_m3_m4(timat, imat); - copy_v3_v3(ray_start_local, ray_start); - copy_v3_v3(ray_normal_local, ray_normal); + copy_v3_v3(ray_start_local, snapdata->ray_start); + copy_v3_v3(ray_normal_local, snapdata->ray_dir); mul_m4_v3(imat, ray_start_local); mul_mat3_m4_v3(imat, ray_normal_local); @@ -1124,6 +1118,17 @@ static bool snapDerivedMesh( local_depth *= local_scale; } + float lpmat[4][4]; + float ray_org_local[3]; + float ray_min_dist; + if (ELEM(snapdata->snap_to, SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE)) { + mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + ray_min_dist = snapdata->depth_range[0] * local_scale; + } + + copy_v3_v3(ray_org_local, snapdata->ray_origin); + mul_m4_v3(imat, ray_org_local); + if (do_bb) { BoundBox *bb = BKE_object_boundbox_get(ob); @@ -1134,19 +1139,27 @@ static bool snapDerivedMesh( * Threshold is rather high, but seems to be needed to get good behavior, see T46099. */ bb = BKE_boundbox_ensure_minimum_dimensions(bb, &bb_temp, 1e-1f); - /* Exact value here is arbitrary (ideally we would scale in pixel-space based on 'dist_px'), - * scale up so we can snap against verts & edges on the boundbox, see T46816. */ - if (ELEM(snap_to, SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE)) { - BKE_boundbox_scale(&bb_temp, bb, 1.0f + 1e-1f); - bb = &bb_temp; + /* In vertex and edges you need to get the pixel distance from ray to BoundBox, see T46816. */ + if (ELEM(snapdata->snap_to, SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE)) { + float dist_px_sq = dist_squared_to_projected_aabb_simple( + lpmat, snapdata->win_half, ray_min_dist, snapdata->mval, + ray_org_local, ray_normal_local, bb->vec[0], bb->vec[6]); + if (dist_px_sq > SQUARE(*dist_px)) + { + return retval; + } } - - /* was local_depth, see: T47838 */ - len_diff = BVH_RAYCAST_DIST_MAX; - - if (!BKE_boundbox_ray_hit_check(bb, ray_start_local, ray_normal_local, &len_diff)) { - return retval; + else { + /* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */ + if (!isect_ray_aabb_v3_simple( + ray_start_local, ray_normal_local, bb->vec[0], bb->vec[6], NULL, NULL)) + { + return retval; + } } + /* was local_depth, see: T47838 */ + len_diff = dist_aabb_to_plane(bb->vec[0], bb->vec[6], ray_start_local, ray_normal_local); + if (len_diff < 0) len_diff = 0.0f; need_ray_start_correction_init = false; } } @@ -1165,7 +1178,7 @@ static bool snapDerivedMesh( } int tree_index = -1; - switch (snap_to) { + switch (snapdata->snap_to) { case SCE_SNAP_MODE_FACE: tree_index = 2; break; @@ -1196,7 +1209,7 @@ static bool snapDerivedMesh( } if (treedata && treedata->tree == NULL) { - switch (snap_to) { + switch (snapdata->snap_to) { case SCE_SNAP_MODE_FACE: bvhtree_from_mesh_looptri(treedata, dm, 0.0f, 4, 6); break; @@ -1213,12 +1226,12 @@ static bool snapDerivedMesh( return retval; } - if (snap_to == SCE_SNAP_MODE_FACE) { + if (snapdata->snap_to == SCE_SNAP_MODE_FACE) { /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already * been *inside* boundbox, leading to snap failures (see T38409). * Note also ar might be null (see T38435), in this case we assume ray_start is ok! */ - if (view_proj == VIEW_PROJ_ORTHO) { /* do_ray_start_correction */ + if (snapdata->view_proj == VIEW_PROJ_ORTHO) { /* do_ray_start_correction */ if (need_ray_start_correction_init) { /* We *need* a reasonably valid len_diff in this case. * Use BHVTree to find the closest face from ray_start_local. @@ -1235,18 +1248,20 @@ static bool snapDerivedMesh( len_diff = dot_v3v3(dvec, ray_normal_local); } } - float ray_org_local[3]; - - copy_v3_v3(ray_org_local, ray_origin); - mul_m4_v3(imat, ray_org_local); - - /* We pass a temp ray_start, set from object's boundbox, to avoid precision issues with very far - * away ray_start values (as returned in case of ortho view3d), see T38358. - */ - len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ - madd_v3_v3v3fl( - ray_start_local, ray_org_local, ray_normal_local, len_diff + depth_range[0] * local_scale); - local_depth -= len_diff; + /* You need to make sure that ray_start is really far away, + * because even in the Orthografic view, in some cases, + * the ray can start inside the object (see T50486) */ + if (len_diff > 400.0f) { + /* We pass a temp ray_start, set from object's boundbox, to avoid precision issues with + * very far away ray_start values (as returned in case of ortho view3d), see T38358. + */ + len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ + madd_v3_v3v3fl( + ray_start_local, ray_org_local, ray_normal_local, + len_diff + snapdata->depth_range[0] * local_scale); + local_depth -= len_diff; + } + else len_diff = 0.0f; } else { len_diff = 0.0f; @@ -1304,78 +1319,40 @@ static bool snapDerivedMesh( } /* SCE_SNAP_MODE_VERTEX or SCE_SNAP_MODE_EDGE */ else { - const ARegion *ar = sctx->v3d_data.ar; - - float ray_org_local[3]; - copy_v3_v3(ray_org_local, ray_origin); - mul_m4_v3(imat, ray_org_local); - BVHTreeFromMeshType treedata_type = {.userdata = treedata, .type = SNAP_MESH}; - if (view_proj == VIEW_PROJ_PERSP) { - Object_Nearest2dPrecalc neasrest_precalc; - neasrest_precalc.userdata = &treedata_type; - neasrest_precalc.index = -1; - - nearest2d_precalc(&neasrest_precalc, ar, *dist_px, obmat, - ray_org_local, ray_normal_local, mval, depth_range); - - BVHTree_WalkLeafCallback cb_walk_leaf = - (snap_to == SCE_SNAP_MODE_VERTEX) ? - cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge; - - BLI_bvhtree_walk_dfs( - treedata->tree, - cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest_precalc); - - if (neasrest_precalc.index != -1) { - copy_v3_v3(r_loc, neasrest_precalc.co); - mul_m4_v3(obmat, r_loc); - if (r_no) { - copy_v3_v3(r_no, neasrest_precalc.no); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); - } - *dist_px = sqrtf(neasrest_precalc.projectdefs.dist_px_sq); - - retval = true; + Nearest2dUserData neasrest2d = { + .dist_px_sq = SQUARE(*dist_px), + .r_axis_closest = {1.0f, 1.0f, 1.0f}, + .depth_range = {snapdata->depth_range[0], *ray_depth + snapdata->depth_range[0]}, + .userdata = &treedata_type, + .index = -1}; + + dist_squared_to_projected_aabb_precalc( + &neasrest2d.data_precalc, lpmat, + snapdata->view_proj == VIEW_PROJ_PERSP, snapdata->win_half, + ray_min_dist, snapdata->mval, ray_org_local, ray_normal_local); + + BVHTree_WalkLeafCallback cb_walk_leaf = + (snapdata->snap_to == SCE_SNAP_MODE_VERTEX) ? + cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge; + + BLI_bvhtree_walk_dfs( + treedata->tree, + cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest2d); + + if (neasrest2d.index != -1) { + copy_v3_v3(r_loc, neasrest2d.co); + mul_m4_v3(obmat, r_loc); + if (r_no) { + copy_v3_v3(r_no, neasrest2d.no); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); } - } - else { - BVHTreeNearest nearest; - - nearest.index = -1; - float dist_3d = dist_px_to_dist3d_or_tangent(ar, *dist_px); - nearest.dist_sq = SQUARE(dist_3d); - - - float ob_scale[3]; - mat4_to_size(ob_scale, obmat); - - struct NearestDM_Data userdata; - userdata.bvhdata = &treedata_type; - userdata.depth_range = depth_range; - userdata.ray_depth = ray_depth; - - BVHTree_NearestToRayCallback cb_test_ray_dist = - (snap_to == SCE_SNAP_MODE_VERTEX) ? - test_vert_ray_dist_cb : test_edge_ray_dist_cb; + *dist_px = sqrtf(neasrest2d.dist_px_sq); + *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); - if (BLI_bvhtree_find_nearest_to_ray( - treedata->tree, ray_org_local, ray_normal_local, - true, ob_scale, &nearest, cb_test_ray_dist, &userdata) != -1) - { - copy_v3_v3(r_loc, nearest.co); - mul_m4_v3(obmat, r_loc); - if (r_no) { - copy_v3_v3(r_no, nearest.no); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); - } - *dist_px *= sqrtf(nearest.dist_sq) / dist_3d; - - retval = true; - } + retval = true; } } @@ -1390,11 +1367,8 @@ static bool snapDerivedMesh( } static bool snapEditMesh( - SnapObjectContext *sctx, + SnapObjectContext *sctx, SnapData *snapdata, Object *ob, BMEditMesh *em, float obmat[4][4], const unsigned int ob_index, - const short snap_to, const float mval[2], const enum eViewProj view_proj, - const float ray_origin[3], const float ray_start[3], const float ray_normal[3], - const float depth_range[2], /* read/write args */ float *ray_depth, float *dist_px, /* return args */ @@ -1403,12 +1377,12 @@ static bool snapEditMesh( { bool retval = false; - if (snap_to == SCE_SNAP_MODE_FACE) { + if (snapdata->snap_to == SCE_SNAP_MODE_FACE) { if (em->bm->totface == 0) { return retval; } } - if (snap_to == SCE_SNAP_MODE_EDGE) { + if (snapdata->snap_to == SCE_SNAP_MODE_EDGE) { if (em->bm->totedge == 0) { return retval; } @@ -1427,10 +1401,17 @@ static bool snapEditMesh( invert_m4_m4(imat, obmat); transpose_m3_m4(timat, imat); - copy_v3_v3(ray_normal_local, ray_normal); + copy_v3_v3(ray_normal_local, snapdata->ray_dir); mul_mat3_m4_v3(imat, ray_normal_local); + /* local scale in normal direction */ + float local_scale = normalize_v3(ray_normal_local); + float local_depth = *ray_depth; + if (local_depth != BVH_RAYCAST_DIST_MAX) { + local_depth *= local_scale; + } + SnapObjectData_EditMesh *sod = NULL; BVHTreeFromEditMesh *treedata = NULL, treedata_stack; @@ -1446,7 +1427,7 @@ static bool snapEditMesh( } int tree_index = -1; - switch (snap_to) { + switch (snapdata->snap_to) { case SCE_SNAP_MODE_FACE: tree_index = 2; break; @@ -1470,7 +1451,7 @@ static bool snapEditMesh( } if (treedata && treedata->tree == NULL) { - switch (snap_to) { + switch (snapdata->snap_to) { case SCE_SNAP_MODE_FACE: { BLI_bitmap *looptri_mask = NULL; @@ -1528,24 +1509,17 @@ static bool snapEditMesh( return retval; } - if (snap_to == SCE_SNAP_MODE_FACE) { + if (snapdata->snap_to == SCE_SNAP_MODE_FACE) { float ray_start_local[3]; - copy_v3_v3(ray_start_local, ray_start); + copy_v3_v3(ray_start_local, snapdata->ray_start); mul_m4_v3(imat, ray_start_local); - /* local scale in normal direction */ - float local_scale = normalize_v3(ray_normal_local); - float local_depth = *ray_depth; - if (local_depth != BVH_RAYCAST_DIST_MAX) { - local_depth *= local_scale; - } - - /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already - * been *inside* boundbox, leading to snap failures (see T38409). + /* Only use closer ray_start in case of ortho view! In perspective one, ray_start + * may already been *inside* boundbox, leading to snap failures (see T38409). * Note also ar might be null (see T38435), in this case we assume ray_start is ok! */ float len_diff = 0.0f; - if (view_proj == VIEW_PROJ_ORTHO) { /* do_ray_start_correction */ + if (snapdata->view_proj == VIEW_PROJ_ORTHO) { /* do_ray_start_correction */ /* We *need* a reasonably valid len_diff in this case. * Use BHVTree to find the closest face from ray_start_local. */ @@ -1559,19 +1533,26 @@ static bool snapEditMesh( float dvec[3]; sub_v3_v3v3(dvec, nearest.co, ray_start_local); len_diff = dot_v3v3(dvec, ray_normal_local); - float ray_org_local[3]; - - copy_v3_v3(ray_org_local, ray_origin); - mul_m4_v3(imat, ray_org_local); - - /* We pass a temp ray_start, set from object's boundbox, - * to avoid precision issues with very far away ray_start values - * (as returned in case of ortho view3d), see T38358. - */ - len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ - madd_v3_v3v3fl( - ray_start_local, ray_org_local, ray_normal_local, len_diff + depth_range[0] * local_scale); - local_depth -= len_diff; + /* You need to make sure that ray_start is really far away, + * because even in the Orthografic view, in some cases, + * the ray can start inside the object (see T50486) */ + if (len_diff > 400.0f) { + float ray_org_local[3]; + + copy_v3_v3(ray_org_local, snapdata->ray_origin); + mul_m4_v3(imat, ray_org_local); + + /* We pass a temp ray_start, set from object's boundbox, + * to avoid precision issues with very far away ray_start values + * (as returned in case of ortho view3d), see T38358. + */ + len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */ + madd_v3_v3v3fl( + ray_start_local, ray_org_local, ray_normal_local, + len_diff + snapdata->depth_range[0] * local_scale); + local_depth -= len_diff; + } + else len_diff = 0.0f; } } if (r_hit_list) { @@ -1626,78 +1607,47 @@ static bool snapEditMesh( } } else { - const ARegion *ar = sctx->v3d_data.ar; - float ray_org_local[3]; - copy_v3_v3(ray_org_local, ray_origin); + copy_v3_v3(ray_org_local, snapdata->ray_origin); mul_m4_v3(imat, ray_org_local); BVHTreeFromMeshType treedata_type = {.userdata = treedata, .type = SNAP_EDIT_MESH}; - if (view_proj == VIEW_PROJ_PERSP) { - Object_Nearest2dPrecalc neasrest_precalc; - neasrest_precalc.userdata = &treedata_type; - neasrest_precalc.index = -1; - - nearest2d_precalc(&neasrest_precalc, ar, *dist_px, obmat, - ray_org_local, ray_normal_local, mval, depth_range); - - BVHTree_WalkLeafCallback cb_walk_leaf = - (snap_to == SCE_SNAP_MODE_VERTEX) ? - cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge; - - BLI_bvhtree_walk_dfs( - treedata->tree, - cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest_precalc); - - if (neasrest_precalc.index != -1) { - copy_v3_v3(r_loc, neasrest_precalc.co); - mul_m4_v3(obmat, r_loc); - if (r_no) { - copy_v3_v3(r_no, neasrest_precalc.no); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); - } - *dist_px = sqrtf(neasrest_precalc.projectdefs.dist_px_sq); - - retval = true; + Nearest2dUserData neasrest2d = { + .dist_px_sq = SQUARE(*dist_px), + .r_axis_closest = {1.0f, 1.0f, 1.0f}, + .depth_range = {snapdata->depth_range[0], *ray_depth + snapdata->depth_range[0]}, + .userdata = &treedata_type, + .index = -1}; + + float lpmat[4][4]; + mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + dist_squared_to_projected_aabb_precalc( + &neasrest2d.data_precalc, lpmat, + snapdata->view_proj == VIEW_PROJ_PERSP, snapdata->win_half, + (snapdata->depth_range[0] * local_scale), snapdata->mval, + ray_org_local, ray_normal_local); + + BVHTree_WalkLeafCallback cb_walk_leaf = + (snapdata->snap_to == SCE_SNAP_MODE_VERTEX) ? + cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge; + + BLI_bvhtree_walk_dfs( + treedata->tree, + cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest2d); + + if (neasrest2d.index != -1) { + copy_v3_v3(r_loc, neasrest2d.co); + mul_m4_v3(obmat, r_loc); + if (r_no) { + copy_v3_v3(r_no, neasrest2d.no); + mul_m3_v3(timat, r_no); + normalize_v3(r_no); } - } - else { - BVHTreeNearest nearest; - - nearest.index = -1; - float dist_3d = dist_px_to_dist3d_or_tangent(ar, *dist_px); - nearest.dist_sq = SQUARE(dist_3d); - - - float ob_scale[3]; - mat4_to_size(ob_scale, obmat); - - struct NearestDM_Data userdata; - userdata.bvhdata = &treedata_type; - userdata.depth_range = depth_range; - userdata.ray_depth = ray_depth; - - BVHTree_NearestToRayCallback cb_test_ray_dist = - (snap_to == SCE_SNAP_MODE_VERTEX) ? - test_vert_ray_dist_cb : test_edge_ray_dist_cb; + *dist_px = sqrtf(neasrest2d.dist_px_sq); + *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); - if (BLI_bvhtree_find_nearest_to_ray( - treedata->tree, ray_org_local, ray_normal_local, - false, ob_scale, &nearest, cb_test_ray_dist, &userdata) != -1) - { - copy_v3_v3(r_loc, nearest.co); - mul_m4_v3(obmat, r_loc); - if (r_no) { - copy_v3_v3(r_no, nearest.no); - mul_m3_v3(timat, r_no); - normalize_v3(r_no); - } - *dist_px *= sqrtf(nearest.dist_sq) / dist_3d; - - retval = true; - } + retval = true; } } @@ -1717,11 +1667,9 @@ static bool snapEditMesh( * \note Duplicate args here are documented at #snapObjectsRay */ static bool snapObject( - SnapObjectContext *sctx, + SnapObjectContext *sctx, SnapData *snapdata, Object *ob, float obmat[4][4], const unsigned int ob_index, - bool use_obedit, const short snap_to, const float mval[2], - const float ray_origin[3], const float ray_start[3], const float ray_normal[3], - const float depth_range[2], + bool use_obedit, /* read/write args */ float *ray_depth, float *dist_px, /* return args */ @@ -1729,12 +1677,6 @@ static bool snapObject( Object **r_ob, float r_obmat[4][4], ListBase *r_hit_list) { - const enum eViewProj view_proj = - ((sctx->use_v3d == false) || (mval == NULL)) ? VIEW_PROJ_NONE : - (((RegionView3D *)sctx->v3d_data.ar->regiondata)->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO); - - const ARegion *ar = sctx->v3d_data.ar; - bool retval = false; if (ob->type == OB_MESH) { @@ -1743,9 +1685,7 @@ static bool snapObject( if (use_obedit) { em = BKE_editmesh_from_object(ob); retval = snapEditMesh( - sctx, ob, em, obmat, ob_index, - snap_to, mval, view_proj, - ray_origin, ray_start, ray_normal, depth_range, + sctx, snapdata, ob, em, obmat, ob_index, ray_depth, dist_px, r_loc, r_no, r_index, r_hit_list); @@ -1762,9 +1702,8 @@ static bool snapObject( dm = mesh_get_derived_final(sctx->scene, ob, CD_MASK_BAREMESH); } retval = snapDerivedMesh( - sctx, ob, dm, obmat, ob_index, - snap_to, mval, view_proj, true, - ray_origin, ray_start, ray_normal, depth_range, + sctx, snapdata, ob, dm, obmat, ob_index, + true, ray_depth, dist_px, r_loc, r_no, r_index, r_hit_list); @@ -1772,32 +1711,32 @@ static bool snapObject( dm->release(dm); } } - else if (snap_to != SCE_SNAP_MODE_FACE) { + else if (snapdata->snap_to != SCE_SNAP_MODE_FACE) { if (ob->type == OB_ARMATURE) { retval = snapArmature( - ar, ob, ob->data, obmat, snap_to, ray_origin, ray_normal, - mval, view_proj, depth_range, dist_px, + snapdata, + ob, ob->data, obmat, + ray_depth, dist_px, r_loc, r_no); } else if (ob->type == OB_CURVE) { retval = snapCurve( - ar, ob, ob->data, obmat, snap_to, mval, view_proj, - depth_range, - dist_px, + snapdata, + ob, ob->data, obmat, + ray_depth, dist_px, r_loc, r_no); } else if (ob->type == OB_EMPTY) { retval = snapEmpty( - ar, ob, obmat, snap_to, mval, view_proj, - depth_range, - dist_px, + snapdata, + ob, obmat, + ray_depth, dist_px, r_loc, r_no); } else if (ob->type == OB_CAMERA) { retval = snapCamera( - sctx, ob, obmat, snap_to, mval, view_proj, - depth_range, - dist_px, + sctx, snapdata, ob, obmat, + ray_depth, dist_px, r_loc, r_no); } } @@ -1819,18 +1758,9 @@ static bool snapObject( * Walks through all objects in the scene to find the closest snap element ray. * * \param sctx: Snap context to store data. - * \param snap_to: Element to snap, Vertice, Edge or Face. - * Currently only works one at a time, but can eventually operate as flag. - * + * \param snapdata: struct generated in `get_snapdata`. * \param snap_select: from enum SnapSelect. - * * \param use_object_edit_cage: Uses the coordinates of BMesh (if any) to do the snapping. - * \param mval: Mouse coords. - * When NULL, ray-casting is handled without any projection matrix correction. - * \param ray_origin: ray_start before being moved toward the ray_normal at the distance from vew3d clip_min. - * \param ray_start: ray_origin moved for the start clipping plane (clip_min). - * \param ray_normal: Unit length direction of the ray. - * \param depth_range: distances of clipe plane min and clip plane max; * * Read/Write Args * --------------- @@ -1851,11 +1781,9 @@ static bool snapObject( * */ static bool snapObjectsRay( - SnapObjectContext *sctx, - const unsigned short snap_to, const SnapSelect snap_select, - const bool use_object_edit_cage, const float mval[2], - const float ray_origin[3], const float ray_start[3], const float ray_normal[3], - const float depth_range[2], + SnapObjectContext *sctx, SnapData *snapdata, + const SnapSelect snap_select, + const bool use_object_edit_cage, /* read/write args */ float *ray_depth, float *dist_px, /* return args */ @@ -1878,9 +1806,7 @@ static bool snapObjectsRay( Object *ob = base_act->object; retval |= snapObject( - sctx, ob, ob->obmat, ob_index++, - false, snap_to, mval, - ray_origin, ray_start, ray_normal, depth_range, + sctx, snapdata, ob, ob->obmat, ob_index++, false, ray_depth, dist_px, r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); } @@ -1914,9 +1840,8 @@ static bool snapObjectsRay( Object *dupli_snap = (use_obedit_dupli) ? obedit : dupli_ob->ob; retval |= snapObject( - sctx, dupli_snap, dupli_ob->mat, ob_index++, - use_obedit_dupli, snap_to, mval, - ray_origin, ray_start, ray_normal, depth_range, + sctx, snapdata, dupli_snap, dupli_ob->mat, + ob_index++, use_obedit_dupli, ray_depth, dist_px, r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); } @@ -1928,9 +1853,7 @@ static bool snapObjectsRay( Object *ob_snap = use_obedit ? obedit : ob; retval |= snapObject( - sctx, ob_snap, ob->obmat, ob_index++, - use_obedit, snap_to, mval, - ray_origin, ray_start, ray_normal, depth_range, + sctx, snapdata, ob_snap, ob->obmat, ob_index++, use_obedit, ray_depth, dist_px, r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list); } @@ -2033,16 +1956,19 @@ bool ED_transform_snap_object_project_ray_ex( SnapObjectContext *sctx, const unsigned short snap_to, const struct SnapObjectParams *params, - const float ray_start[3], const float ray_normal[3], + const float UNUSED(ray_start[3]), const float UNUSED(ray_normal[3]), float *ray_depth, float r_loc[3], float r_no[3], int *r_index, Object **r_ob, float r_obmat[4][4]) { const float depth_range[2] = {0.0f, FLT_MAX}; + + SnapData snapdata; + snap_data_set(&snapdata, sctx->v3d_data.ar, snap_to, VIEW_PROJ_NONE, NULL, r_loc, r_loc, r_no, depth_range); + return snapObjectsRay( - sctx, - snap_to, params->snap_select, params->use_object_edit_cage, NULL, - ray_start, ray_start, ray_normal, depth_range, + sctx, &snapdata, + params->snap_select, params->use_object_edit_cage, ray_depth, NULL, r_loc, r_no, r_index, r_ob, r_obmat, NULL); } @@ -2071,10 +1997,13 @@ bool ED_transform_snap_object_project_ray_all( float ray_depth_prev = ray_depth; #endif + SnapData snapdata; + snap_data_set(&snapdata, sctx->v3d_data.ar, snap_to, VIEW_PROJ_NONE, NULL, + ray_start, ray_start, ray_normal, depth_range); + bool retval = snapObjectsRay( - sctx, - snap_to, params->snap_select, params->use_object_edit_cage, NULL, - ray_start, ray_start, ray_normal, depth_range, + sctx, &snapdata, + params->snap_select, params->use_object_edit_cage, &ray_depth, NULL, NULL, NULL, NULL, NULL, NULL, r_hit_list); @@ -2153,19 +2082,38 @@ static bool transform_snap_context_project_view3d_mixed_impl( BLI_assert(snap_to_flag != 0); BLI_assert((snap_to_flag & ~(1 | 2 | 4)) == 0); - for (int i = 0; i < 3; i++) { - if ((snap_to_flag & (1 << i)) && (is_hit == false || use_depth)) { - if (use_depth == false) { - ray_depth = BVH_RAYCAST_DIST_MAX; + if (use_depth) { + const float dist_px_orig = *dist_px; + for (int i = 2; i >= 0; i--) { + if (snap_to_flag & (1 << i)) { + if (i == 0) + *dist_px = dist_px_orig; + if (ED_transform_snap_object_project_view3d( + sctx, + elem_type[i], params, + mval, dist_px, &ray_depth, + r_co, r_no)) + { + /* 0.01 is a random but small value to prioritizing + * the first elements of the loop */ + ray_depth += 0.01f; + is_hit = true; + } } - - if (ED_transform_snap_object_project_view3d( - sctx, - elem_type[i], params, - mval, dist_px, &ray_depth, - r_co, r_no)) - { - is_hit = true; + } + } + else { + for (int i = 0; i < 3; i++) { + if (snap_to_flag & (1 << i)) { + if (ED_transform_snap_object_project_view3d( + sctx, + elem_type[i], params, + mval, dist_px, &ray_depth, + r_co, r_no)) + { + is_hit = true; + break; + } } } } @@ -2234,10 +2182,14 @@ bool ED_transform_snap_object_project_view3d_ex( ray_depth = &ray_depth_fallback; } + SnapData snapdata; + const enum eViewProj view_proj = ((RegionView3D *)ar->regiondata)->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO; + snap_data_set(&snapdata, ar, snap_to, view_proj, mval, + ray_origin, ray_start, ray_normal, depth_range); + return snapObjectsRay( - sctx, - snap_to, params->snap_select, params->use_object_edit_cage, - mval, ray_origin, ray_start, ray_normal, depth_range, + sctx, &snapdata, + params->snap_select, params->use_object_edit_cage, ray_depth, dist_px, r_loc, r_no, r_index, NULL, NULL, NULL); } diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 4416b6494f9..0f3ffa8244b 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -1358,7 +1358,7 @@ void mtex_cube_map_refl_from_refldir( samplerCube ima, vec3 reflecteddirection, out float value, out vec4 color) { color = textureCube(ima, reflecteddirection); - value = 1.0; + value = color.a; } void mtex_cube_map_refl( @@ -3536,6 +3536,8 @@ void node_light_path( out float is_transmission_ray, out float ray_length, out float ray_depth, + out float diffuse_depth, + out float glossy_depth, out float transparent_depth, out float transmission_depth) { @@ -3548,6 +3550,8 @@ void node_light_path( is_transmission_ray = 0.0; ray_length = 1.0; ray_depth = 1.0; + diffuse_depth = 1.0; + glossy_depth = 1.0; transparent_depth = 1.0; transmission_depth = 1.0; } diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index f3df9090d41..1083400ece2 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -76,6 +76,8 @@ typedef struct bMotionPath { int start_frame; /* for drawing paths, the start frame number */ int end_frame; /* for drawing paths, the end frame number */ + float color[3]; /* optional custom color */ + int line_thickness; /* line thickness */ int flag; /* baking settings - eMotionPath_Flag */ } bMotionPath; @@ -84,7 +86,11 @@ typedef enum eMotionPath_Flag { /* (for bones) path represents the head of the bone */ MOTIONPATH_FLAG_BHEAD = (1 << 0), /* motion path is being edited */ - MOTIONPATH_FLAG_EDIT = (1 << 1) + MOTIONPATH_FLAG_EDIT = (1 << 1), + /* Custom colors */ + MOTIONPATH_FLAG_CUSTOM = (1 << 2), + /* Draw lines or only points */ + MOTIONPATH_FLAG_LINES = (1 << 3) } eMotionPath_Flag; /* Visualization General --------------------------- */ diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 03794bdc9d5..1e2ef0cc7d3 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -596,6 +596,7 @@ typedef struct NodeBilateralBlurData { short iter, pad; } NodeBilateralBlurData; +/* NOTE: Only for do-version code. */ typedef struct NodeHueSat { float hue, sat, val; } NodeHueSat; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 85da31667d5..0f6b1276eb3 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1268,12 +1268,51 @@ typedef enum eGP_BrushEdit_SettingsFlag { GP_BRUSHEDIT_FLAG_APPLY_STRENGTH = (1 << 2), /* apply brush to thickness */ GP_BRUSHEDIT_FLAG_APPLY_THICKNESS = (1 << 3), +} eGP_BrushEdit_SettingsFlag; + + +/* Settings for GP Interpolation Operators */ +typedef struct GP_Interpolate_Settings { + short flag; /* eGP_Interpolate_SettingsFlag */ + + char type; /* eGP_Interpolate_Type - Interpolation Mode */ + char easing; /* eBezTriple_Easing - Easing mode (if easing equation used) */ + + float back; /* BEZT_IPO_BACK */ + float amplitude, period; /* BEZT_IPO_ELASTIC */ + + struct CurveMapping *custom_ipo; /* custom interpolation curve (for use with GP_IPO_CURVEMAP) */ +} GP_Interpolate_Settings; + +/* GP_Interpolate_Settings.flag */ +typedef enum eGP_Interpolate_SettingsFlag { /* apply interpolation to all layers */ - GP_BRUSHEDIT_FLAG_INTERPOLATE_ALL_LAYERS = (1 << 4), + GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS = (1 << 0), /* apply interpolation to only selected */ - GP_BRUSHEDIT_FLAG_INTERPOLATE_ONLY_SELECTED = (1 << 5) + GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED = (1 << 1), +} eGP_Interpolate_SettingsFlag; + +/* GP_Interpolate_Settings.type */ +typedef enum eGP_Interpolate_Type { + /* Traditional Linear Interpolation */ + GP_IPO_LINEAR = 0, + + /* CurveMap Defined Interpolation */ + GP_IPO_CURVEMAP = 1, + + /* Easing Equations */ + GP_IPO_BACK = 3, + GP_IPO_BOUNCE = 4, + GP_IPO_CIRC = 5, + GP_IPO_CUBIC = 6, + GP_IPO_ELASTIC = 7, + GP_IPO_EXPO = 8, + GP_IPO_QUAD = 9, + GP_IPO_QUART = 10, + GP_IPO_QUINT = 11, + GP_IPO_SINE = 12, +} eGP_Interpolate_Type; -} eGP_BrushEdit_SettingsFlag; /* *************************************************************** */ /* Transform Orientations */ @@ -1491,7 +1530,10 @@ typedef struct ToolSettings { /* Grease Pencil Sculpt */ struct GP_BrushEdit_Settings gp_sculpt; - + + /* Grease Pencil Interpolation Tool(s) */ + struct GP_Interpolate_Settings gp_interpolate; + /* Grease Pencil Drawing Brushes (bGPDbrush) */ ListBase gp_brushes; diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index f97a5735c94..66e6f30feeb 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -262,6 +262,7 @@ extern StructRNA RNA_GPencilLayer; extern StructRNA RNA_GPencilPalette; extern StructRNA RNA_GPencilPaletteColor; extern StructRNA RNA_GPencilBrush; +extern StructRNA RNA_GPencilInterpolateSettings; extern StructRNA RNA_GPencilStroke; extern StructRNA RNA_GPencilStrokePoint; extern StructRNA RNA_GPencilSculptSettings; diff --git a/source/blender/makesrna/intern/rna_actuator.c b/source/blender/makesrna/intern/rna_actuator.c index a09853eaddc..004acbe4dbd 100644 --- a/source/blender/makesrna/intern/rna_actuator.c +++ b/source/blender/makesrna/intern/rna_actuator.c @@ -32,8 +32,9 @@ #include "DNA_actuator_types.h" #include "DNA_scene_types.h" /* for MAXFRAME */ -#include "BLI_utildefines.h" #include "BLI_math.h" +#include "BLI_string_utils.h" +#include "BLI_utildefines.h" #include "BLT_translation.h" diff --git a/source/blender/makesrna/intern/rna_animviz.c b/source/blender/makesrna/intern/rna_animviz.c index 8e42e68ed1e..eea24bfb1e0 100644 --- a/source/blender/makesrna/intern/rna_animviz.c +++ b/source/blender/makesrna/intern/rna_animviz.c @@ -153,7 +153,20 @@ static void rna_def_animviz_motion_path(BlenderRNA *brna) prop = RNA_def_property(srna, "length", PROP_INT, PROP_TIME); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Length", "Number of frames cached"); - + + /* Custom Color */ + prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Color", "Custom color for motion path"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + /* Line width */ + prop = RNA_def_property(srna, "line_thickness", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "line_thickness"); + RNA_def_property_range(prop, 1, 6); + RNA_def_property_ui_text(prop, "Line thickness", "Line thickness for drawing path"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + /* Settings */ prop = RNA_def_property(srna, "use_bone_head", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", MOTIONPATH_FLAG_BHEAD); @@ -164,6 +177,19 @@ static void rna_def_animviz_motion_path(BlenderRNA *brna) prop = RNA_def_property(srna, "is_modified", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", MOTIONPATH_FLAG_EDIT); RNA_def_property_ui_text(prop, "Edit Path", "Path is being edited"); + + /* Use custom color */ + prop = RNA_def_property(srna, "use_custom_color", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOTIONPATH_FLAG_CUSTOM); + RNA_def_property_ui_text(prop, "Custom colors", "Use custom color for this motion path"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + /* Draw lines between keyframes */ + prop = RNA_def_property(srna, "lines", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOTIONPATH_FLAG_LINES); + RNA_def_property_ui_text(prop, "Lines", "Draw straight lines between keyframe points"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + } /* --- */ @@ -337,6 +363,7 @@ static void rna_def_animviz_paths(BlenderRNA *brna) "Number of frames to show after the current frame " "(only for 'Around Current Frame' Onion-skinning method)"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); /* XXX since this is only for 3d-view drawing */ + } /* --- */ diff --git a/source/blender/makesrna/intern/rna_controller.c b/source/blender/makesrna/intern/rna_controller.c index ed700916584..3fa9d7ef270 100644 --- a/source/blender/makesrna/intern/rna_controller.c +++ b/source/blender/makesrna/intern/rna_controller.c @@ -29,6 +29,7 @@ #include "DNA_object_types.h" #include "DNA_controller_types.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BLT_translation.h" diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 14da990278c..6e0d6ad2bcd 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -31,6 +31,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -59,6 +60,7 @@ static EnumPropertyItem parent_type_items[] = { #include "WM_api.h" +#include "BKE_animsys.h" #include "BKE_gpencil.h" #include "BKE_action.h" @@ -352,10 +354,16 @@ static void rna_GPencilLayer_info_set(PointerRNA *ptr, const char *value) bGPdata *gpd = ptr->id.data; bGPDlayer *gpl = ptr->data; + char oldname[128] = ""; + BLI_strncpy(oldname, gpl->info, sizeof(oldname)); + /* copy the new name into the name slot */ BLI_strncpy_utf8(gpl->info, value, sizeof(gpl->info)); BLI_uniquename(&gpd->layers, gpl, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl->info)); + + /* now fix animation paths */ + BKE_animdata_fix_paths_rename_all(&gpd->id, "layers", oldname, gpl->info); } static void rna_GPencil_use_onion_skinning_set(PointerRNA *ptr, const int value) @@ -776,11 +784,16 @@ static void rna_GPencilPalette_info_set(PointerRNA *ptr, const char *value) bGPdata *gpd = ptr->id.data; bGPDpalette *palette = ptr->data; + char oldname[64] = ""; + BLI_strncpy(oldname, palette->info, sizeof(oldname)); + /* copy the new name into the name slot */ BLI_strncpy_utf8(palette->info, value, sizeof(palette->info)); BLI_uniquename(&gpd->palettes, palette, DATA_("GP_Palette"), '.', offsetof(bGPDpalette, info), sizeof(palette->info)); + /* now fix animation paths */ + BKE_animdata_fix_paths_rename_all(&gpd->id, "palettes", oldname, palette->info); } static char *rna_GPencilPalette_path(PointerRNA *ptr) @@ -813,14 +826,20 @@ static void rna_GPencilPaletteColor_info_set(PointerRNA *ptr, const char *value) bGPdata *gpd = ptr->id.data; bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); bGPDpalettecolor *palcolor = ptr->data; - - /* rename all strokes */ - BKE_gpencil_palettecolor_changename(gpd, palcolor->info, value); + + char oldname[64] = ""; + BLI_strncpy(oldname, palcolor->info, sizeof(oldname)); /* copy the new name into the name slot */ BLI_strncpy_utf8(palcolor->info, value, sizeof(palcolor->info)); BLI_uniquename(&palette->colors, palcolor, DATA_("Color"), '.', offsetof(bGPDpalettecolor, info), sizeof(palcolor->info)); + + /* rename all strokes */ + BKE_gpencil_palettecolor_changename(gpd, oldname, palcolor->info); + + /* now fix animation paths */ + BKE_animdata_fix_paths_rename_all(&gpd->id, "colors", oldname, palcolor->info); } static void rna_GPencilStrokeColor_info_set(PointerRNA *ptr, const char *value) diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c index 2d669986485..a5abc8a3be2 100644 --- a/source/blender/makesrna/intern/rna_key.c +++ b/source/blender/makesrna/intern/rna_key.c @@ -33,6 +33,7 @@ #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "BLT_translation.h" diff --git a/source/blender/makesrna/intern/rna_linestyle.c b/source/blender/makesrna/intern/rna_linestyle.c index 622c61beaeb..1199cccc4e6 100644 --- a/source/blender/makesrna/intern/rna_linestyle.c +++ b/source/blender/makesrna/intern/rna_linestyle.c @@ -27,6 +27,9 @@ #include <stdio.h> #include <stdlib.h> +#include "BLI_utildefines.h" +#include "BLI_string_utils.h" + #include "RNA_define.h" #include "RNA_enum_types.h" diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c index cd48bc1a3df..ff9873fb3d1 100644 --- a/source/blender/makesrna/intern/rna_mesh_api.c +++ b/source/blender/makesrna/intern/rna_mesh_api.c @@ -241,7 +241,7 @@ void RNA_api_mesh(StructRNA *srna) RNA_def_function_ui_description(func, "Free split vertex normals"); func = RNA_def_function(srna, "split_faces", "BKE_mesh_split_faces"); - RNA_def_function_ui_description(func, "Spli faces based on the edge angle"); + RNA_def_function_ui_description(func, "Split faces based on the edge angle"); func = RNA_def_function(srna, "calc_tangents", "rna_Mesh_calc_tangents"); RNA_def_function_flag(func, FUNC_USE_REPORTS); @@ -320,4 +320,3 @@ void RNA_api_mesh(StructRNA *srna) } #endif - diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 15fbea8381f..18f053f6d2a 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -4431,34 +4431,6 @@ static void def_cmp_alpha_over(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } -static void def_cmp_hue_saturation(StructRNA *srna) -{ - PropertyRNA *prop; - - RNA_def_struct_sdna_from(srna, "NodeHueSat", "storage"); - - prop = RNA_def_property(srna, "color_hue", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "hue"); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_float_default(prop, 0.5f); - RNA_def_property_ui_text(prop, "Hue", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); - - prop = RNA_def_property(srna, "color_saturation", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "sat"); - RNA_def_property_range(prop, 0.0f, 2.0f); - RNA_def_property_ui_text(prop, "Saturation", ""); - RNA_def_property_float_default(prop, 1.0f); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); - - prop = RNA_def_property(srna, "color_value", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "val"); - RNA_def_property_range(prop, 0.0f, 2.0f); - RNA_def_property_ui_text(prop, "Value", ""); - RNA_def_property_float_default(prop, 1.0f); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); -} - static void def_cmp_blur(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index 362baed1e7c..95dab13571d 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -45,6 +45,8 @@ #include "RNA_define.h" #include "RNA_enum_types.h" +#include "BLI_string_utils.h" + #include "BLT_translation.h" #include "rna_internal.h" diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index 99d57147248..28ce63a61bd 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -40,6 +40,7 @@ #include "DNA_scene_types.h" #include "BLI_math.h" +#include "BLI_string_utils.h" #include "BLT_translation.h" diff --git a/source/blender/makesrna/intern/rna_property.c b/source/blender/makesrna/intern/rna_property.c index 07bdbb03357..fb70870f49b 100644 --- a/source/blender/makesrna/intern/rna_property.c +++ b/source/blender/makesrna/intern/rna_property.c @@ -31,6 +31,7 @@ #include "DNA_object_types.h" #include "BLI_path_util.h" +#include "BLI_string_utils.h" #include "BLT_translation.h" diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 7fe8155cd8c..1efef2b4099 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -40,6 +40,7 @@ #include "IMB_imbuf_types.h" #include "BLI_math.h" +#include "BLI_string_utils.h" #include "BLT_translation.h" @@ -406,9 +407,34 @@ EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = { {0, NULL, 0, NULL, NULL} }; +EnumPropertyItem rna_enum_gpencil_interpolation_mode_items[] = { + /* interpolation */ + {0, "", 0, N_("Interpolation"), "Standard transitions between keyframes"}, + {GP_IPO_LINEAR, "LINEAR", ICON_IPO_LINEAR, "Linear", "Straight-line interpolation between A and B (i.e. no ease in/out)"}, + {GP_IPO_CURVEMAP, "CUSTOM", ICON_IPO_BEZIER, "Custom", "Custom interpolation defined using a curvemap"}, + + /* easing */ + {0, "", 0, N_("Easing (by strength)"), "Predefined inertial transitions, useful for motion graphics (from least to most ''dramatic'')"}, + {GP_IPO_SINE, "SINE", ICON_IPO_SINE, "Sinusoidal", "Sinusoidal easing (weakest, almost linear but with a slight curvature)"}, + {GP_IPO_QUAD, "QUAD", ICON_IPO_QUAD, "Quadratic", "Quadratic easing"}, + {GP_IPO_CUBIC, "CUBIC", ICON_IPO_CUBIC, "Cubic", "Cubic easing"}, + {GP_IPO_QUART, "QUART", ICON_IPO_QUART, "Quartic", "Quartic easing"}, + {GP_IPO_QUINT, "QUINT", ICON_IPO_QUINT, "Quintic", "Quintic easing"}, + {GP_IPO_EXPO, "EXPO", ICON_IPO_EXPO, "Exponential", "Exponential easing (dramatic)"}, + {GP_IPO_CIRC, "CIRC", ICON_IPO_CIRC, "Circular", "Circular easing (strongest and most dynamic)"}, + + {0, "", 0, N_("Dynamic Effects"), "Simple physics-inspired easing effects"}, + {GP_IPO_BACK, "BACK", ICON_IPO_BACK, "Back", "Cubic easing with overshoot and settle"}, + {GP_IPO_BOUNCE, "BOUNCE", ICON_IPO_BOUNCE, "Bounce", "Exponentially decaying parabolic bounce, like when objects collide"}, + {GP_IPO_ELASTIC, "ELASTIC", ICON_IPO_ELASTIC, "Elastic", "Exponentially decaying sine wave, like an elastic band"}, + + {0, NULL, 0, NULL, NULL} +}; + #ifdef RNA_RUNTIME #include "DNA_anim_types.h" +#include "DNA_color_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_mesh_types.h" @@ -419,6 +445,7 @@ EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = { #include "MEM_guardedalloc.h" #include "BKE_brush.h" +#include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_idprop.h" @@ -447,6 +474,29 @@ EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = { #include "FRS_freestyle.h" #endif +/* Grease Pencil Interpolation settings */ +static char *rna_GPencilInterpolateSettings_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("tool_settings.gpencil_interpolate"); +} + +static void rna_GPencilInterpolateSettings_type_set(PointerRNA *ptr, int value) +{ + GP_Interpolate_Settings *settings = (GP_Interpolate_Settings *)ptr->data; + + /* NOTE: This cast should be fine, as we have a small + finite set of values (eGP_Interpolate_Type) + * that should fit well within a char + */ + settings->type = (char)value; + + /* init custom interpolation curve here now the first time it's used */ + if ((settings->type == GP_IPO_CURVEMAP) && + (settings->custom_ipo == NULL)) + { + settings->custom_ipo = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + } +} + /* Grease pencil Drawing Brushes */ static bGPDbrush *rna_GPencil_brush_new(ToolSettings *ts, const char *name, int setactive) { @@ -2146,6 +2196,73 @@ static int rna_gpu_is_hq_supported_get(PointerRNA *UNUSED(ptr)) #else +/* Grease Pencil Interpolation tool settings */ +static void rna_def_gpencil_interpolate(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "GPencilInterpolateSettings", NULL); + RNA_def_struct_sdna(srna, "GP_Interpolate_Settings"); + RNA_def_struct_path_func(srna, "rna_GPencilInterpolateSettings_path"); + RNA_def_struct_ui_text(srna, "Grease Pencil Interpolate Settings", + "Settings for Grease Pencil interpolation tools"); + + /* flags */ + prop = RNA_def_property(srna, "interpolate_all_layers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_TOOLFLAG_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_TOOLFLAG_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); + + /* interpolation type */ + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "type"); + RNA_def_property_enum_items(prop, rna_enum_gpencil_interpolation_mode_items); + RNA_def_property_enum_funcs(prop, NULL, "rna_GPencilInterpolateSettings_type_set", NULL); + RNA_def_property_ui_text(prop, "Type", + "Interpolation method to use the next time 'Interpolate Sequence' is run"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + /* easing */ + prop = RNA_def_property(srna, "easing", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "easing"); + RNA_def_property_enum_items(prop, rna_enum_beztriple_interpolation_easing_items); + RNA_def_property_ui_text(prop, "Easing", + "Which ends of the segment between the preceding and following grease pencil frames " + "easing interpolation is applied to"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + /* easing options */ + prop = RNA_def_property(srna, "back", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "back"); + RNA_def_property_ui_text(prop, "Back", "Amount of overshoot for 'back' easing"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "amplitude"); + RNA_def_property_range(prop, 0.0f, FLT_MAX); /* only positive values... */ + RNA_def_property_ui_text(prop, "Amplitude", "Amount to boost elastic bounces for 'elastic' easing"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "period", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "period"); + RNA_def_property_ui_text(prop, "Period", "Time between bounces for elastic easing"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + /* custom curvemap */ + prop = RNA_def_property(srna, "interpolation_curve", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "custom_ipo"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Interpolation Curve", + "Custom curve to control 'sequence' interpolation between Grease Pencil frames"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); +} + /* Grease Pencil Drawing Brushes */ static void rna_def_gpencil_brush(BlenderRNA *brna) { @@ -2681,7 +2798,14 @@ static void rna_def_tool_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "gpencil_sculpt", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "gp_sculpt"); RNA_def_property_struct_type(prop, "GPencilSculptSettings"); - RNA_def_property_ui_text(prop, "Grease Pencil Sculpt", ""); + RNA_def_property_ui_text(prop, "Grease Pencil Sculpt", + "Settings for stroke sculpting tools and brushes"); + + prop = RNA_def_property(srna, "gpencil_interpolate", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "gp_interpolate"); + RNA_def_property_struct_type(prop, "GPencilInterpolateSettings"); + RNA_def_property_ui_text(prop, "Grease Pencil Interpolate", + "Settings for Grease Pencil Interpolation tools"); /* Grease Pencil - Drawing brushes */ prop = RNA_def_property(srna, "gpencil_brushes", PROP_COLLECTION, PROP_NONE); @@ -7352,6 +7476,7 @@ void RNA_def_scene(BlenderRNA *brna) RNA_define_animate_sdna(false); rna_def_tool_settings(brna); rna_def_gpencil_brush(brna); + rna_def_gpencil_interpolate(brna); rna_def_unified_paint_settings(brna); rna_def_curve_paint_settings(brna); rna_def_statvis(brna); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 7f405f0fb1f..40aea37d9d2 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -1047,15 +1047,6 @@ 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"); diff --git a/source/blender/makesrna/intern/rna_sensor.c b/source/blender/makesrna/intern/rna_sensor.c index ee24a434486..d0afab7a1e3 100644 --- a/source/blender/makesrna/intern/rna_sensor.c +++ b/source/blender/makesrna/intern/rna_sensor.c @@ -32,6 +32,7 @@ #include "BLI_utildefines.h" #include "BLI_math.h" +#include "BLI_string_utils.h" #include "BLT_translation.h" diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index e68e67586e9..0eab38faca8 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -3937,7 +3937,7 @@ static void rna_def_userdef_system(BlenderRNA *brna) prop = RNA_def_property(srna, "dpi", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "dpi"); - RNA_def_property_range(prop, 48, 144); + RNA_def_property_range(prop, 16, 256); RNA_def_property_ui_text(prop, "DPI", "Font size and resolution for display"); RNA_def_property_update(prop, 0, "rna_userdef_dpi_update"); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index b8ebb375a48..bacfc177432 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -113,6 +113,10 @@ set(SRC intern/MOD_weightvg_util.h ) +if(WITH_LEGACY_DEPSGRAPH) + add_definitions(-DWITH_LEGACY_DEPSGRAPH) +endif() + if(WITH_ALEMBIC) add_definitions(-DWITH_ALEMBIC) list(APPEND INC diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c index 567505ea45d..2400dbcb898 100644 --- a/source/blender/modifiers/intern/MOD_armature.c +++ b/source/blender/modifiers/intern/MOD_armature.c @@ -96,7 +96,7 @@ static void foreachObjectLink( { ArmatureModifierData *amd = (ArmatureModifierData *) md; - walk(userData, ob, &amd->object, IDWALK_NOP); + walk(userData, ob, &amd->object, IDWALK_CB_NOP); } static void updateDepgraph(ModifierData *md, DagForest *forest, diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index b049457e640..2b739dc0093 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -95,10 +95,10 @@ static void foreachObjectLink( { ArrayModifierData *amd = (ArrayModifierData *) md; - walk(userData, ob, &amd->start_cap, IDWALK_NOP); - walk(userData, ob, &amd->end_cap, IDWALK_NOP); - walk(userData, ob, &amd->curve_ob, IDWALK_NOP); - walk(userData, ob, &amd->offset_ob, IDWALK_NOP); + walk(userData, ob, &amd->start_cap, IDWALK_CB_NOP); + walk(userData, ob, &amd->end_cap, IDWALK_CB_NOP); + walk(userData, ob, &amd->curve_ob, IDWALK_CB_NOP); + walk(userData, ob, &amd->offset_ob, IDWALK_CB_NOP); } static void updateDepgraph(ModifierData *md, DagForest *forest, diff --git a/source/blender/modifiers/intern/MOD_boolean.c b/source/blender/modifiers/intern/MOD_boolean.c index da0f5aa3923..f828bc68857 100644 --- a/source/blender/modifiers/intern/MOD_boolean.c +++ b/source/blender/modifiers/intern/MOD_boolean.c @@ -101,7 +101,7 @@ static void foreachObjectLink( { BooleanModifierData *bmd = (BooleanModifierData *) md; - walk(userData, ob, &bmd->object, IDWALK_NOP); + walk(userData, ob, &bmd->object, IDWALK_CB_NOP); } static void updateDepgraph(ModifierData *md, DagForest *forest, diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c index 2fe27730370..ddcf1852dc6 100644 --- a/source/blender/modifiers/intern/MOD_cast.c +++ b/source/blender/modifiers/intern/MOD_cast.c @@ -102,7 +102,7 @@ static void foreachObjectLink( { CastModifierData *cmd = (CastModifierData *) md; - walk(userData, ob, &cmd->object, IDWALK_NOP); + walk(userData, ob, &cmd->object, IDWALK_CB_NOP); } static void updateDepgraph(ModifierData *md, DagForest *forest, diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index d15a6fcb1c8..e8c39770a14 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -125,9 +125,15 @@ static void updateDepgraph(ModifierData *md, DagForest *forest, if (clmd) { /* Actual code uses get_collisionobjects */ +#ifdef WITH_LEGACY_DEPSGRAPH dag_add_collision_relations(forest, scene, ob, obNode, clmd->coll_parms->group, ob->lay|scene->lay, eModifierType_Collision, NULL, true, "Cloth Collision"); - dag_add_forcefield_relations(forest, scene, ob, obNode, clmd->sim_parms->effector_weights, true, 0, "Cloth Field"); +#else + (void)forest; + (void)scene; + (void)ob; + (void)obNode; +#endif } } @@ -228,11 +234,11 @@ static void foreachIDLink(ModifierData *md, Object *ob, ClothModifierData *clmd = (ClothModifierData *) md; if (clmd->coll_parms) { - walk(userData, ob, (ID **)&clmd->coll_parms->group, IDWALK_NOP); + walk(userData, ob, (ID **)&clmd->coll_parms->group, IDWALK_CB_NOP); } if (clmd->sim_parms && clmd->sim_parms->effector_weights) { - walk(userData, ob, (ID **)&clmd->sim_parms->effector_weights->group, IDWALK_NOP); + walk(userData, ob, (ID **)&clmd->sim_parms->effector_weights->group, IDWALK_CB_NOP); } } diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c index 9bc96e593fd..09444476bfe 100644 --- a/source/blender/modifiers/intern/MOD_curve.c +++ b/source/blender/modifiers/intern/MOD_curve.c @@ -89,7 +89,7 @@ static void foreachObjectLink( { CurveModifierData *cmd = (CurveModifierData *) md; - walk(userData, ob, &cmd->object, IDWALK_NOP); + walk(userData, ob, &cmd->object, IDWALK_CB_NOP); } static void updateDepgraph(ModifierData *md, DagForest *forest, diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c index f5ab28d3d88..a2210893602 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.c +++ b/source/blender/modifiers/intern/MOD_datatransfer.c @@ -124,7 +124,7 @@ static void foreachObjectLink( ObjectWalkFunc walk, void *userData) { DataTransferModifierData *dtmd = (DataTransferModifierData *) md; - walk(userData, ob, &dtmd->ob_source, IDWALK_NOP); + walk(userData, ob, &dtmd->ob_source, IDWALK_CB_NOP); } static void updateDepgraph(ModifierData *md, DagForest *forest, @@ -179,7 +179,6 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *der /* Only used to check wehther we are operating on org data or not... */ Mesh *me = ob->data; - MVert *mvert; const bool invert_vgroup = (dtmd->flags & MOD_DATATRANSFER_INVERT_VGROUP) != 0; @@ -192,8 +191,9 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *der BLI_SPACE_TRANSFORM_SETUP(space_transform, ob, dtmd->ob_source); } - mvert = dm->getVertArray(dm); - if ((me->mvert == mvert) && (dtmd->data_types & DT_TYPES_AFFECT_MESH)) { + MVert *mvert = dm->getVertArray(dm); + MEdge *medge = dm->getEdgeArray(dm); + if (((me->mvert == mvert) || (me->medge == medge)) && (dtmd->data_types & DT_TYPES_AFFECT_MESH)) { /* We need to duplicate data here, otherwise setting custom normals, edges' shaprness, etc., could * modify org mesh, see T43671. */ dm = CDDM_copy(dm); @@ -211,6 +211,9 @@ static DerivedMesh *applyModifier(ModifierData *md, Object *ob, DerivedMesh *der if (BKE_reports_contain(&reports, RPT_ERROR)) { modifier_setError(md, "%s", BKE_reports_string(&reports, RPT_ERROR)); } + else if ((dtmd->data_types & DT_TYPE_LNOR) && !(me->flag & ME_AUTOSMOOTH)) { + modifier_setError((ModifierData *)dtmd, "Enable 'Auto Smooth' option in mesh settings"); + } else if (dm->getNumVerts(dm) > HIGH_POLY_WARNING || ((Mesh *)(dtmd->ob_source->data))->totvert > HIGH_POLY_WARNING) { modifier_setError(md, "You are using a rather high poly as source or destination, computation might be slow"); } diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index 059e096ddb4..08d118a2026 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -132,7 +132,7 @@ static void foreachObjectLink(ModifierData *md, Object *ob, { DisplaceModifierData *dmd = (DisplaceModifierData *) md; - walk(userData, ob, &dmd->map_object, IDWALK_NOP); + walk(userData, ob, &dmd->map_object, IDWALK_CB_NOP); } static void foreachIDLink(ModifierData *md, Object *ob, @@ -140,7 +140,7 @@ static void foreachIDLink(ModifierData *md, Object *ob, { DisplaceModifierData *dmd = (DisplaceModifierData *) md; - walk(userData, ob, (ID **)&dmd->texture, IDWALK_USER); + walk(userData, ob, (ID **)&dmd->texture, IDWALK_CB_USER); foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData); } diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c index bde20e56748..05068b9b597 100644 --- a/source/blender/modifiers/intern/MOD_dynamicpaint.c +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -129,6 +129,7 @@ static void updateDepgraph(ModifierData *md, DagForest *forest, /* add relation from canvases to all brush objects */ if (pmd && pmd->canvas) { +#ifdef WITH_LEGACY_DEPSGRAPH for (DynamicPaintSurface *surface = pmd->canvas->surfaces.first; surface; surface = surface->next) { if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) { dag_add_forcefield_relations(forest, scene, ob, obNode, surface->effector_weights, true, 0, "Dynamic Paint Field"); @@ -137,6 +138,12 @@ static void updateDepgraph(ModifierData *md, DagForest *forest, /* Actual code uses custom loop over group/scene without layer checks in dynamicPaint_doStep */ dag_add_collision_relations(forest, scene, ob, obNode, surface->brush_group, -1, eModifierType_DynamicPaint, is_brush_cb, false, "Dynamic Paint Brush"); } +#else + (void)forest; + (void)scene; + (void)ob; + (void)obNode; +#endif } } @@ -174,15 +181,15 @@ static void foreachIDLink(ModifierData *md, Object *ob, DynamicPaintSurface *surface = pmd->canvas->surfaces.first; for (; surface; surface = surface->next) { - walk(userData, ob, (ID **)&surface->brush_group, IDWALK_NOP); - walk(userData, ob, (ID **)&surface->init_texture, IDWALK_USER); + walk(userData, ob, (ID **)&surface->brush_group, IDWALK_CB_NOP); + walk(userData, ob, (ID **)&surface->init_texture, IDWALK_CB_USER); if (surface->effector_weights) { - walk(userData, ob, (ID **)&surface->effector_weights->group, IDWALK_NOP); + walk(userData, ob, (ID **)&surface->effector_weights->group, IDWALK_CB_NOP); } } } if (pmd->brush) { - walk(userData, ob, (ID **)&pmd->brush->mat, IDWALK_USER); + walk(userData, ob, (ID **)&pmd->brush->mat, IDWALK_CB_USER); } } diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index 9186b10d8ca..56c494ff3c0 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -115,7 +115,7 @@ static void foreachObjectLink( { HookModifierData *hmd = (HookModifierData *) md; - walk(userData, ob, &hmd->object, IDWALK_NOP); + walk(userData, ob, &hmd->object, IDWALK_CB_NOP); } static void updateDepgraph(ModifierData *md, DagForest *forest, diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c index 7d4701e3ef2..dc246084dca 100644 --- a/source/blender/modifiers/intern/MOD_lattice.c +++ b/source/blender/modifiers/intern/MOD_lattice.c @@ -88,7 +88,7 @@ static void foreachObjectLink( { LatticeModifierData *lmd = (LatticeModifierData *) md; - walk(userData, ob, &lmd->object, IDWALK_NOP); + walk(userData, ob, &lmd->object, IDWALK_CB_NOP); } static void updateDepgraph(ModifierData *md, DagForest *forest, diff --git a/source/blender/modifiers/intern/MOD_mask.c b/source/blender/modifiers/intern/MOD_mask.c index 5e01a20d93b..dcc7fe4d4f1 100644 --- a/source/blender/modifiers/intern/MOD_mask.c +++ b/source/blender/modifiers/intern/MOD_mask.c @@ -74,7 +74,7 @@ static void foreachObjectLink( ObjectWalkFunc walk, void *userData) { MaskModifierData *mmd = (MaskModifierData *)md; - walk(userData, ob, &mmd->ob_arm, IDWALK_NOP); + walk(userData, ob, &mmd->ob_arm, IDWALK_CB_NOP); } static void updateDepgraph(ModifierData *md, DagForest *forest, diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index 3f34319d25f..406ce398ee0 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -119,7 +119,7 @@ static void foreachObjectLink( { MeshDeformModifierData *mmd = (MeshDeformModifierData *) md; - walk(userData, ob, &mmd->object, IDWALK_NOP); + walk(userData, ob, &mmd->object, IDWALK_CB_NOP); } static void updateDepgraph(ModifierData *md, DagForest *forest, diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.c b/source/blender/modifiers/intern/MOD_meshsequencecache.c index 2f00a7c71b0..d956763fc1f 100644 --- a/source/blender/modifiers/intern/MOD_meshsequencecache.c +++ b/source/blender/modifiers/intern/MOD_meshsequencecache.c @@ -151,7 +151,7 @@ static void foreachIDLink(ModifierData *md, Object *ob, { MeshSeqCacheModifierData *mcmd = (MeshSeqCacheModifierData *) md; - walk(userData, ob, (ID **)&mcmd->cache_file, IDWALK_USER); + walk(userData, ob, (ID **)&mcmd->cache_file, IDWALK_CB_USER); } diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c index 88facb22e0e..dd127cc255c 100644 --- a/source/blender/modifiers/intern/MOD_mirror.c +++ b/source/blender/modifiers/intern/MOD_mirror.c @@ -72,7 +72,7 @@ static void foreachObjectLink( { MirrorModifierData *mmd = (MirrorModifierData *) md; - walk(userData, ob, &mmd->mirror_ob, IDWALK_NOP); + walk(userData, ob, &mmd->mirror_ob, IDWALK_CB_NOP); } static void updateDepgraph(ModifierData *md, DagForest *forest, diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index 454c97451a8..20eae751ca4 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -501,7 +501,7 @@ static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, { NormalEditModifierData *enmd = (NormalEditModifierData *) md; - walk(userData, ob, &enmd->target, IDWALK_NOP); + walk(userData, ob, &enmd->target, IDWALK_CB_NOP); } static bool isDisabled(ModifierData *md, int UNUSED(useRenderParams)) diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c index 4e78e758dc3..fbf5a958254 100644 --- a/source/blender/modifiers/intern/MOD_particleinstance.c +++ b/source/blender/modifiers/intern/MOD_particleinstance.c @@ -145,7 +145,7 @@ static void foreachObjectLink(ModifierData *md, Object *ob, { ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *) md; - walk(userData, ob, &pimd->ob, IDWALK_NOP); + walk(userData, ob, &pimd->ob, IDWALK_CB_NOP); } static int particle_skip(ParticleInstanceModifierData *pimd, ParticleSystem *psys, int p) diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 290e19736bb..87859cd240a 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -1093,7 +1093,7 @@ static void foreachObjectLink( { ScrewModifierData *ltmd = (ScrewModifierData *) md; - walk(userData, ob, <md->ob_axis, IDWALK_NOP); + walk(userData, ob, <md->ob_axis, IDWALK_CB_NOP); } ModifierTypeInfo modifierType_Screw = { diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c index 633311c2b87..a14747bc153 100644 --- a/source/blender/modifiers/intern/MOD_shrinkwrap.c +++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c @@ -101,8 +101,8 @@ static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, { ShrinkwrapModifierData *smd = (ShrinkwrapModifierData *) md; - walk(userData, ob, &smd->target, IDWALK_NOP); - walk(userData, ob, &smd->auxTarget, IDWALK_NOP); + walk(userData, ob, &smd->target, IDWALK_CB_NOP); + walk(userData, ob, &smd->auxTarget, IDWALK_CB_NOP); } static void deformVerts(ModifierData *md, Object *ob, diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c index c68f2651191..379eaea5b0c 100644 --- a/source/blender/modifiers/intern/MOD_simpledeform.c +++ b/source/blender/modifiers/intern/MOD_simpledeform.c @@ -290,7 +290,7 @@ static void foreachObjectLink( ObjectWalkFunc walk, void *userData) { SimpleDeformModifierData *smd = (SimpleDeformModifierData *)md; - walk(userData, ob, &smd->origin, IDWALK_NOP); + walk(userData, ob, &smd->origin, IDWALK_CB_NOP); } static void updateDepgraph(ModifierData *md, DagForest *forest, diff --git a/source/blender/modifiers/intern/MOD_smoke.c b/source/blender/modifiers/intern/MOD_smoke.c index f04d7432a8f..c105a3ab3a7 100644 --- a/source/blender/modifiers/intern/MOD_smoke.c +++ b/source/blender/modifiers/intern/MOD_smoke.c @@ -138,10 +138,16 @@ static void updateDepgraph(ModifierData *md, DagForest *forest, if (smd && (smd->type & MOD_SMOKE_TYPE_DOMAIN) && smd->domain) { /* Actual code uses get_collisionobjects */ +#ifdef WITH_LEGACY_DEPSGRAPH dag_add_collision_relations(forest, scene, ob, obNode, smd->domain->fluid_group, ob->lay|scene->lay, eModifierType_Smoke, is_flow_cb, true, "Smoke Flow"); dag_add_collision_relations(forest, scene, ob, obNode, smd->domain->coll_group, ob->lay|scene->lay, eModifierType_Smoke, is_coll_cb, true, "Smoke Coll"); - dag_add_forcefield_relations(forest, scene, ob, obNode, smd->domain->effector_weights, true, PFIELD_SMOKEFLOW, "Smoke Force Field"); +#else + (void)forest; + (void)scene; + (void)ob; + (void)obNode; +#endif } } @@ -168,17 +174,17 @@ static void foreachIDLink(ModifierData *md, Object *ob, SmokeModifierData *smd = (SmokeModifierData *) md; if (smd->type == MOD_SMOKE_TYPE_DOMAIN && smd->domain) { - walk(userData, ob, (ID **)&smd->domain->coll_group, IDWALK_NOP); - walk(userData, ob, (ID **)&smd->domain->fluid_group, IDWALK_NOP); - walk(userData, ob, (ID **)&smd->domain->eff_group, IDWALK_NOP); + walk(userData, ob, (ID **)&smd->domain->coll_group, IDWALK_CB_NOP); + walk(userData, ob, (ID **)&smd->domain->fluid_group, IDWALK_CB_NOP); + walk(userData, ob, (ID **)&smd->domain->eff_group, IDWALK_CB_NOP); if (smd->domain->effector_weights) { - walk(userData, ob, (ID **)&smd->domain->effector_weights->group, IDWALK_NOP); + walk(userData, ob, (ID **)&smd->domain->effector_weights->group, IDWALK_CB_NOP); } } if (smd->type == MOD_SMOKE_TYPE_FLOW && smd->flow) { - walk(userData, ob, (ID **)&smd->flow->noise_texture, IDWALK_USER); + walk(userData, ob, (ID **)&smd->flow->noise_texture, IDWALK_CB_USER); } } diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index 17adc7f1520..a0bbe5da04a 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -67,10 +67,17 @@ static void updateDepgraph(ModifierData *UNUSED(md), DagForest *forest, Scene *scene, Object *ob, DagNode *obNode) { if (ob->soft) { +#ifdef WITH_LEGACY_DEPSGRAPH /* Actual code uses ccd_build_deflector_hash */ dag_add_collision_relations(forest, scene, ob, obNode, ob->soft->collision_group, ob->lay, eModifierType_Collision, NULL, false, "Softbody Collision"); dag_add_forcefield_relations(forest, scene, ob, obNode, ob->soft->effector_weights, true, 0, "Softbody Field"); +#else + (void)forest; + (void)scene; + (void)ob; + (void)obNode; +#endif } } diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c index fb7668d16e0..78dc1ea8bcb 100644 --- a/source/blender/modifiers/intern/MOD_uvproject.c +++ b/source/blender/modifiers/intern/MOD_uvproject.c @@ -92,7 +92,7 @@ static void foreachObjectLink(ModifierData *md, Object *ob, int i; for (i = 0; i < MOD_UVPROJECT_MAXPROJECTORS; ++i) - walk(userData, ob, &umd->projectors[i], IDWALK_NOP); + walk(userData, ob, &umd->projectors[i], IDWALK_CB_NOP); } static void foreachIDLink(ModifierData *md, Object *ob, @@ -100,7 +100,7 @@ static void foreachIDLink(ModifierData *md, Object *ob, { UVProjectModifierData *umd = (UVProjectModifierData *) md; - walk(userData, ob, (ID **)&umd->image, IDWALK_USER); + walk(userData, ob, (ID **)&umd->image, IDWALK_CB_USER); foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData); } diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c index 89994c3ae22..b3fc3842635 100644 --- a/source/blender/modifiers/intern/MOD_uvwarp.c +++ b/source/blender/modifiers/intern/MOD_uvwarp.c @@ -221,8 +221,8 @@ static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, { UVWarpModifierData *umd = (UVWarpModifierData *) md; - walk(userData, ob, &umd->object_dst, IDWALK_NOP); - walk(userData, ob, &umd->object_src, IDWALK_NOP); + walk(userData, ob, &umd->object_dst, IDWALK_CB_NOP); + walk(userData, ob, &umd->object_src, IDWALK_CB_NOP); } static void uv_warp_deps_object_bone(DagForest *forest, DagNode *obNode, diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index b38de140a91..92d44257d03 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -116,16 +116,16 @@ static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, { WarpModifierData *wmd = (WarpModifierData *) md; - walk(userData, ob, &wmd->object_from, IDWALK_NOP); - walk(userData, ob, &wmd->object_to, IDWALK_NOP); - walk(userData, ob, &wmd->map_object, IDWALK_NOP); + walk(userData, ob, &wmd->object_from, IDWALK_CB_NOP); + walk(userData, ob, &wmd->object_to, IDWALK_CB_NOP); + walk(userData, ob, &wmd->map_object, IDWALK_CB_NOP); } static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) { WarpModifierData *wmd = (WarpModifierData *) md; - walk(userData, ob, (ID **)&wmd->texture, IDWALK_USER); + walk(userData, ob, (ID **)&wmd->texture, IDWALK_CB_USER); foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData); } diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index 683649ed1c0..f0c4940816e 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -111,8 +111,8 @@ static void foreachObjectLink( { WaveModifierData *wmd = (WaveModifierData *) md; - walk(userData, ob, &wmd->objectcenter, IDWALK_NOP); - walk(userData, ob, &wmd->map_object, IDWALK_NOP); + walk(userData, ob, &wmd->objectcenter, IDWALK_CB_NOP); + walk(userData, ob, &wmd->map_object, IDWALK_CB_NOP); } static void foreachIDLink(ModifierData *md, Object *ob, @@ -120,7 +120,7 @@ static void foreachIDLink(ModifierData *md, Object *ob, { WaveModifierData *wmd = (WaveModifierData *) md; - walk(userData, ob, (ID **)&wmd->texture, IDWALK_USER); + walk(userData, ob, (ID **)&wmd->texture, IDWALK_CB_USER); foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData); } diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index 93567aed2c4..2a822ac07b5 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -126,14 +126,14 @@ static bool dependsOnTime(ModifierData *md) static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, void *userData) { WeightVGEditModifierData *wmd = (WeightVGEditModifierData *) md; - walk(userData, ob, &wmd->mask_tex_map_obj, IDWALK_NOP); + walk(userData, ob, &wmd->mask_tex_map_obj, IDWALK_CB_NOP); } static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) { WeightVGEditModifierData *wmd = (WeightVGEditModifierData *) md; - walk(userData, ob, (ID **)&wmd->mask_texture, IDWALK_USER); + walk(userData, ob, (ID **)&wmd->mask_texture, IDWALK_CB_USER); foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData); } diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index 170fbbed7f1..9bff7bf11f1 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -175,14 +175,14 @@ static bool dependsOnTime(ModifierData *md) static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, void *userData) { WeightVGMixModifierData *wmd = (WeightVGMixModifierData *) md; - walk(userData, ob, &wmd->mask_tex_map_obj, IDWALK_NOP); + walk(userData, ob, &wmd->mask_tex_map_obj, IDWALK_CB_NOP); } static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) { WeightVGMixModifierData *wmd = (WeightVGMixModifierData *) md; - walk(userData, ob, (ID **)&wmd->mask_texture, IDWALK_USER); + walk(userData, ob, (ID **)&wmd->mask_texture, IDWALK_CB_USER); foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData); } diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index af59f11ba15..2c87838aeab 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -325,15 +325,15 @@ static bool dependsOnTime(ModifierData *md) static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, void *userData) { WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *) md; - walk(userData, ob, &wmd->proximity_ob_target, IDWALK_NOP); - walk(userData, ob, &wmd->mask_tex_map_obj, IDWALK_NOP); + walk(userData, ob, &wmd->proximity_ob_target, IDWALK_CB_NOP); + walk(userData, ob, &wmd->mask_tex_map_obj, IDWALK_CB_NOP); } static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) { WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *) md; - walk(userData, ob, (ID **)&wmd->mask_texture, IDWALK_USER); + walk(userData, ob, (ID **)&wmd->mask_texture, IDWALK_CB_USER); foreachObjectLink(md, ob, (ObjectWalkFunc)walk, userData); } diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 171d5313c1d..a9c0df7d7e8 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -145,7 +145,7 @@ DefNode( CompositorNode, CMP_NODE_VECBLUR, def_cmp_vector_blur, "VECBL DefNode( CompositorNode, CMP_NODE_SEPRGBA, 0, "SEPRGBA", SepRGBA, "Separate RGBA", "" ) DefNode( CompositorNode, CMP_NODE_SEPHSVA, 0, "SEPHSVA", SepHSVA, "Separate HSVA", "" ) DefNode( CompositorNode, CMP_NODE_SETALPHA, 0, "SETALPHA", SetAlpha, "Set Alpha", "" ) -DefNode( CompositorNode, CMP_NODE_HUE_SAT, def_cmp_hue_saturation, "HUE_SAT", HueSat, "Hue Saturation Value","" ) +DefNode( CompositorNode, CMP_NODE_HUE_SAT, 0, "HUE_SAT", HueSat, "Hue Saturation Value","" ) DefNode( CompositorNode, CMP_NODE_IMAGE, def_cmp_image, "IMAGE", Image, "Image", "" ) DefNode( CompositorNode, CMP_NODE_R_LAYERS, def_cmp_render_layers, "R_LAYERS", RLayers, "Render Layers", "" ) DefNode( CompositorNode, CMP_NODE_COMPOSITE, def_cmp_composite, "COMPOSITE", Composite, "Composite", "" ) diff --git a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.c b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.c index 1c31f4863ca..952ba78aff3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.c +++ b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.c @@ -35,8 +35,11 @@ /* **************** Hue Saturation ******************** */ static bNodeSocketTemplate cmp_node_hue_sat_in[] = { - { SOCK_FLOAT, 1, N_("Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, { SOCK_RGBA, 1, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, + { SOCK_FLOAT, 1, N_("Hue"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Saturation"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Value"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + { SOCK_FLOAT, 1, N_("Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, { -1, 0, "" } }; static bNodeSocketTemplate cmp_node_hue_sat_out[] = { @@ -44,23 +47,12 @@ static bNodeSocketTemplate cmp_node_hue_sat_out[] = { { -1, 0, "" } }; -static void node_composit_init_hue_sat(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeHueSat *nhs = MEM_callocN(sizeof(NodeHueSat), "node hue sat"); - node->storage = nhs; - nhs->hue = 0.5f; - nhs->sat = 1.0f; - nhs->val = 1.0f; -} - void register_node_type_cmp_hue_sat(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR, 0); node_type_socket_templates(&ntype, cmp_node_hue_sat_in, cmp_node_hue_sat_out); - node_type_init(&ntype, node_composit_init_hue_sat); - node_type_storage(&ntype, "NodeHueSat", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_outputFile.c b/source/blender/nodes/composite/nodes/node_composite_outputFile.c index 7d1087435c2..6a96f1b30d8 100644 --- a/source/blender/nodes/composite/nodes/node_composite_outputFile.c +++ b/source/blender/nodes/composite/nodes/node_composite_outputFile.c @@ -32,7 +32,7 @@ #include <string.h> #include "BLI_utildefines.h" -#include "BLI_path_util.h" +#include "BLI_string_utils.h" #include "BKE_context.h" diff --git a/source/blender/nodes/shader/nodes/node_shader_light_path.c b/source/blender/nodes/shader/nodes/node_shader_light_path.c index b1001cd3937..052f2a66ec8 100644 --- a/source/blender/nodes/shader/nodes/node_shader_light_path.c +++ b/source/blender/nodes/shader/nodes/node_shader_light_path.c @@ -39,6 +39,8 @@ static bNodeSocketTemplate sh_node_light_path_out[] = { { SOCK_FLOAT, 0, N_("Is Transmission Ray"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { SOCK_FLOAT, 0, N_("Ray Length"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { SOCK_FLOAT, 0, N_("Ray Depth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + { SOCK_FLOAT, 0, N_("Diffuse Depth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + { SOCK_FLOAT, 0, N_("Glossy Depth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { SOCK_FLOAT, 0, N_("Transparent Depth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { SOCK_FLOAT, 0, N_("Transmission Depth"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, { -1, 0, "" } diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c index 1037c83815c..72705ffb3fb 100644 --- a/source/blender/python/intern/bpy_rna_id_collection.c +++ b/source/blender/python/intern/bpy_rna_id_collection.c @@ -263,7 +263,7 @@ static PyObject *bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject * } data_cb.id_curr = id; - BKE_library_foreach_ID_link(id, foreach_libblock_id_user_map_callback, &data_cb, IDWALK_NOP); + BKE_library_foreach_ID_link(NULL, id, foreach_libblock_id_user_map_callback, &data_cb, IDWALK_CB_NOP); if (data_cb.py_id_curr) { Py_DECREF(data_cb.py_id_curr); diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index a357145ff39..903a65b20b7 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -3445,7 +3445,7 @@ bool RE_WriteRenderViewsMovie( ok = mh->append_movie(movie_ctx_arr[0], rd, preview ? scene->r.psfra : scene->r.sfra, scene->r.cfra, (int *) ibuf_arr[2]->rect, ibuf_arr[2]->x, ibuf_arr[2]->y, "", reports); - for (i = 0; i < 2; i++) { + for (i = 0; i < 3; i++) { /* imbuf knows which rects are not part of ibuf */ IMB_freeImBuf(ibuf_arr[i]); } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index b9fd4d2e762..cef5e7e4a8e 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -1846,7 +1846,7 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar BLENDER_VERSION / 100, BLENDER_VERSION % 100); uiItemStringO(col, IFACE_("Release Log"), ICON_URL, "WM_OT_url_open", "url", url); uiItemStringO(col, IFACE_("Manual"), ICON_URL, "WM_OT_url_open", "url", - "http://www.blender.org/manual"); + "https://docs.blender.org/manual/en/dev/"); uiItemStringO(col, IFACE_("Blender Website"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org"); if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "release")) { BLI_snprintf(url, sizeof(url), "http://www.blender.org/documentation/blender_python_api_%d_%d" @@ -3910,8 +3910,12 @@ static void previews_id_ensure(bContext *C, Scene *scene, ID *id) } } -static int previews_id_ensure_callback(void *userdata, ID *UNUSED(self_id), ID **idptr, int UNUSED(cd_flag)) +static int previews_id_ensure_callback(void *userdata, ID *UNUSED(self_id), ID **idptr, int cb_flag) { + if (cb_flag & IDWALK_CB_PRIVATE) { + return IDWALK_RET_NOP; + } + PreviewsIDEnsureData *data = userdata; ID *id = *idptr; @@ -3944,7 +3948,7 @@ static int previews_ensure_exec(bContext *C, wmOperator *UNUSED(op)) preview_id_data.scene = scene; id = (ID *)scene; - BKE_library_foreach_ID_link(id, previews_id_ensure_callback, &preview_id_data, IDWALK_RECURSE); + BKE_library_foreach_ID_link(NULL, id, previews_id_ensure_callback, &preview_id_data, IDWALK_RECURSE); } /* Check a last time for ID not used (fake users only, in theory), and @@ -4558,4 +4562,3 @@ EnumPropertyItem *RNA_mask_local_itemf(bContext *C, PointerRNA *ptr, PropertyRNA { return rna_id_itemf(C, ptr, r_free, C ? (ID *)CTX_data_main(C)->mask.first : NULL, true); } - diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 4eb72fb1619..27579e58dba 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -438,7 +438,7 @@ static void arg_py_context_restore( * see: `doc/manpage/blender.1.py` * - Parsed and extracted for the manual, * which converts our ad-hoc formatting to reStructuredText. - * see: http://www.blender.org/manual/advanced/command_line.html + * see: https://docs.blender.org/manual/en/dev/advanced/command_line.html * * \{ */ diff --git a/source/tools b/source/tools -Subproject 896c5f78952adb2d091d28c65086d46992dabda +Subproject b11375e89061303401376f7aeae42ac2fd64692 diff --git a/tests/gtests/CMakeLists.txt b/tests/gtests/CMakeLists.txt index a3860ce3e67..1d363f31119 100644 --- a/tests/gtests/CMakeLists.txt +++ b/tests/gtests/CMakeLists.txt @@ -4,6 +4,9 @@ if(WITH_GTESTS) Include(GTestTesting) + add_definitions(${GFLAGS_DEFINES}) + add_definitions(${GLOG_DEFINES}) + # Otherwise we get warnings here that we cant fix in external projects remove_strict_flags() diff --git a/tests/gtests/blenlib/BLI_string_test.cc b/tests/gtests/blenlib/BLI_string_test.cc index 17a4b5e82b9..08f2a745bdb 100644 --- a/tests/gtests/blenlib/BLI_string_test.cc +++ b/tests/gtests/blenlib/BLI_string_test.cc @@ -374,7 +374,7 @@ TEST(string, StrFormatIntGrouped) const int word_num = BLI_string_find_split_words( \ word_str_src, word_str_src_len, ' ', word_info, word_cmp_size_input); \ EXPECT_EQ(word_num, word_cmp_size - 1); \ - EXPECT_EQ_ARRAY_ND(word_cmp, word_info, word_cmp_size, 2); \ + EXPECT_EQ_ARRAY_ND<const int[2]>(word_cmp, word_info, word_cmp_size, 2); \ } ((void)0) #define STRING_FIND_SPLIT_WORDS(word_str_src, ...) \ diff --git a/tests/gtests/testing/CMakeLists.txt b/tests/gtests/testing/CMakeLists.txt index 1eb60e7f3b5..796f975ea65 100644 --- a/tests/gtests/testing/CMakeLists.txt +++ b/tests/gtests/testing/CMakeLists.txt @@ -47,7 +47,4 @@ set(SRC testing.h ) -add_definitions(${GFLAGS_DEFINES}) -add_definitions(${GLOG_DEFINES}) - blender_add_lib(bf_testing_main "${SRC}" "${INC}" "${INC_SYS}") diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index a29b612e0ef..4b5666e9b5a 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -399,24 +399,28 @@ endif() if(WITH_CYCLES) if(OPENIMAGEIO_IDIFF AND EXISTS "${TEST_SRC_DIR}/cycles/ctests/shader") - add_test(cycles_reports_test - ${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py - -blender "${TEST_BLENDER_EXE_BARE}" - -testdir "${TEST_SRC_DIR}/cycles/ctests/reports" - -idiff "${OPENIMAGEIO_IDIFF}" - ) - add_test(cycles_render_test - ${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py - -blender "${TEST_BLENDER_EXE_BARE}" - -testdir "${TEST_SRC_DIR}/cycles/ctests/render" - -idiff "${OPENIMAGEIO_IDIFF}" - ) - add_test(cycles_shaders_test - ${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py - -blender "${TEST_BLENDER_EXE_BARE}" - -testdir "${TEST_SRC_DIR}/cycles/ctests/shader" - -idiff "${OPENIMAGEIO_IDIFF}" - ) + macro(add_cycles_render_test subject) + if(MSVC) + add_test(NAME cycles_${subject}_test + COMMAND + "$<TARGET_FILE_DIR:blender>/${BLENDER_VERSION_MAJOR}.${BLENDER_VERSION_MINOR}/python/bin/python$<$<CONFIG:Debug>:_d>" + ${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py + -blender "$<TARGET_FILE:blender>" + -testdir "${TEST_SRC_DIR}/cycles/ctests/${subject}" + -idiff "${OPENIMAGEIO_IDIFF}" + ) + else() + add_test(cycles_${subject}_test + ${CMAKE_CURRENT_LIST_DIR}/cycles_render_tests.py + -blender "${TEST_BLENDER_EXE_BARE}" + -testdir "${TEST_SRC_DIR}/cycles/ctests/${subject}" + -idiff "${OPENIMAGEIO_IDIFF}" + ) + endif() + endmacro() + add_cycles_render_test(reports) + add_cycles_render_test(render) + add_cycles_render_test(shader) else() MESSAGE(STATUS "Disabling Cycles tests because tests folder does not exist") endif() diff --git a/tests/python/cycles_render_tests.py b/tests/python/cycles_render_tests.py index 78b4b346f24..ae8848570f0 100755 --- a/tests/python/cycles_render_tests.py +++ b/tests/python/cycles_render_tests.py @@ -6,9 +6,46 @@ import os import shutil import subprocess import sys +import time import tempfile +class COLORS_ANSI: + RED = '\033[00;31m' + GREEN = '\033[00;32m' + ENDC = '\033[0m' + + +class COLORS_DUMMY: + RED = '' + GREEN = '' + ENDC = '' + +COLORS = COLORS_DUMMY + + +def printMessage(type, status, message): + if type == 'SUCCESS': + print(COLORS.GREEN, end="") + elif type == 'FAILURE': + print(COLORS.RED, end="") + status_text = ... + if status == 'RUN': + status_text = " RUN " + elif status == 'OK': + status_text = " OK " + elif status == 'PASSED': + status_text = " PASSED " + elif status == 'FAILED': + status_text = " FAILED " + else: + status_text = status + print("[{}]" . format(status_text), end="") + print(COLORS.ENDC, end="") + print(" {}" . format(message)) + sys.stdout.flush() + + def render_file(filepath): command = ( BLENDER, @@ -83,16 +120,32 @@ def verify_output(filepath): def run_test(filepath): testname = test_get_name(filepath) spacer = "." * (32 - len(testname)) - print(testname, spacer, end="") - sys.stdout.flush() + printMessage('SUCCESS', 'RUN', testname) + time_start = time.time() error = render_file(filepath) + status = "FAIL" if not error: - if verify_output(filepath): - print("PASS") - else: + if not verify_output(filepath): error = "VERIFY" - if error: - print("FAIL", error) + time_end = time.time() + elapsed_ms = int((time_end - time_start) * 1000) + if not error: + printMessage('SUCCESS', 'OK', "{} ({} ms)" . + format(testname, elapsed_ms)) + else: + if error == "NO_CYCLES": + print("Can't perform tests because Cycles failed to load!") + return False + elif error == "NO_START": + print('Can not perform tests because blender fails to start.', + 'Make sure INSTALL target was run.') + return False + elif error == 'VERIFY': + print("Rendered result is different from reference image") + else: + print("Unknown error %r" % error) + printMessage('FAILURE', 'FAILED', "{} ({} ms)" . + format(testname, elapsed_ms)) return error @@ -105,30 +158,38 @@ def blend_list(path): def run_all_tests(dirpath): + passed_tests = [] failed_tests = [] all_files = list(blend_list(dirpath)) all_files.sort() + printMessage('SUCCESS', "==========", + "Running {} tests from 1 test case." . format(len(all_files))) + time_start = time.time() for filepath in all_files: error = run_test(filepath) + testname = test_get_name(filepath) if error: if error == "NO_CYCLES": - print("Can't perform tests because Cycles failed to load!") return False elif error == "NO_START": - print('Can not perform tests because blender fails to start.', - 'Make sure INSTALL target was run.') return False - elif error == 'VERIFY': - pass - else: - print("Unknown error %r" % error) - testname = test_get_name(filepath) failed_tests.append(testname) + else: + passed_tests.append(testname) + time_end = time.time() + elapsed_ms = int((time_end - time_start) * 1000) + print("") + printMessage('SUCCESS', "==========", + "{} tests from 1 test case ran. ({} ms total)" . + format(len(all_files), elapsed_ms)) + printMessage('SUCCESS', 'PASSED', "{} tests." . + format(len(passed_tests))) if failed_tests: + printMessage('FAILURE', 'FAILED', "{} tests, listed below:" . + format(len(failed_tests))) failed_tests.sort() - print("\n\nFAILED tests:") for test in failed_tests: - print(" ", test) + printMessage('FAILURE', "FAILED", "{}" . format(test)) return False return True @@ -145,10 +206,14 @@ def main(): parser = create_argparse() args = parser.parse_args() + global COLORS global BLENDER, ROOT, IDIFF global TEMP_FILE, TEMP_FILE_MASK, TEST_SCRIPT global VERBOSE + if os.environ.get("CYCLESTEST_COLOR") is not None: + COLORS = COLORS_ANSI + BLENDER = args.blender[0] ROOT = args.testdir[0] IDIFF = args.idiff[0] |