Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacques Lucke <jacques@blender.org>2020-04-09 13:02:02 +0300
committerJacques Lucke <jacques@blender.org>2020-04-09 13:02:02 +0300
commit55ee66be4d2eaf1bd4556a938c618c77985b4008 (patch)
tree85b445409637fa021c72e096d0d2133fdcf2992a
parent726cf93f8ad09b49cd6481af2c6ad7d5e51b0c20 (diff)
parentdabd59ba23f877f68aaf73e79f0d58118723d9b7 (diff)
Merge branch 'functions' into builtin-simulation-nodesbuiltin-simulation-nodes
-rw-r--r--build_files/cmake/platform/platform_unix.cmake7
-rw-r--r--build_files/cmake/platform/platform_win32.cmake2
-rw-r--r--doc/python_api/sphinx_doc_gen.py1
-rw-r--r--extern/mantaflow/UPDATE.sh2
-rw-r--r--extern/mantaflow/preprocessed/gitinfo.h2
-rw-r--r--extern/mantaflow/preprocessed/grid.cpp153
-rw-r--r--extern/mantaflow/preprocessed/grid.h60
-rw-r--r--extern/mantaflow/preprocessed/grid.h.reg.cpp172
-rw-r--r--extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp10
-rw-r--r--intern/cycles/device/device.cpp2
-rw-r--r--intern/cycles/device/device.h14
-rw-r--r--intern/cycles/device/device_cpu.cpp22
-rw-r--r--intern/cycles/device/device_cuda.cpp1
-rw-r--r--intern/cycles/device/device_network.cpp1
-rw-r--r--intern/cycles/device/device_opencl.cpp1
-rw-r--r--intern/cycles/device/device_task.cpp3
-rw-r--r--intern/cycles/kernel/kernel_adaptive_sampling.h9
-rw-r--r--intern/cycles/kernel/kernel_passes.h10
-rw-r--r--intern/cycles/kernel/kernel_types.h15
-rw-r--r--intern/cycles/render/buffers.cpp89
-rw-r--r--intern/cycles/render/film.cpp15
-rw-r--r--intern/cycles/render/film.h1
-rw-r--r--intern/cycles/render/image.cpp5
-rw-r--r--intern/cycles/render/image.h1
-rw-r--r--intern/cycles/render/integrator.cpp7
-rw-r--r--intern/cycles/render/mesh_volume.cpp51
-rw-r--r--intern/cycles/render/session.cpp1
-rw-r--r--intern/ghost/GHOST_C-api.h7
-rw-r--r--intern/ghost/GHOST_IWindow.h10
-rw-r--r--intern/ghost/GHOST_Types.h4
-rw-r--r--intern/ghost/intern/GHOST_C-api.cpp5
-rw-r--r--intern/ghost/intern/GHOST_EventButton.h10
-rw-r--r--intern/ghost/intern/GHOST_EventCursor.h6
-rw-r--r--intern/ghost/intern/GHOST_System.h2
-rw-r--r--intern/ghost/intern/GHOST_SystemCocoa.mm70
-rw-r--r--intern/ghost/intern/GHOST_SystemSDL.cpp22
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.cpp348
-rw-r--r--intern/ghost/intern/GHOST_SystemWin32.h48
-rw-r--r--intern/ghost/intern/GHOST_SystemX11.cpp28
-rw-r--r--intern/ghost/intern/GHOST_WindowCocoa.h5
-rw-r--r--intern/ghost/intern/GHOST_WindowCocoa.mm2
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.cpp610
-rw-r--r--intern/ghost/intern/GHOST_WindowWin32.h199
-rw-r--r--intern/ghost/intern/GHOST_WindowX11.cpp3
-rw-r--r--intern/ghost/intern/GHOST_WindowX11.h5
-rw-r--r--intern/ghost/intern/GHOST_XrGraphicsBinding.cpp2
-rw-r--r--intern/libmv/intern/camera_intrinsics.cc2
-rw-r--r--intern/libmv/intern/camera_intrinsics.h1
-rw-r--r--intern/libmv/libmv/simple_pipeline/camera_intrinsics_impl.h4
-rw-r--r--intern/mantaflow/extern/manta_fluid_API.h1
-rw-r--r--intern/mantaflow/intern/MANTA_main.cpp45
-rw-r--r--intern/mantaflow/intern/MANTA_main.h3
-rw-r--r--intern/mantaflow/intern/manta_fluid_API.cpp7
-rw-r--r--intern/mantaflow/intern/strings/smoke_script.h31
-rw-r--r--release/datafiles/colormanagement/filmic/filmic_desat65cube.spi3d2
-rw-r--r--release/datafiles/colormanagement/filmic/filmic_false_color.spi3d2
-rw-r--r--release/datafiles/userdef/userdef_default.c3
m---------release/scripts/addons0
-rw-r--r--release/scripts/modules/bl_i18n_utils/settings.py2
-rw-r--r--release/scripts/modules/bl_keymap_utils/io.py7
-rw-r--r--release/scripts/modules/bpy_extras/anim_utils.py24
-rw-r--r--release/scripts/modules/rna_manual_reference.py38
-rw-r--r--release/scripts/startup/bl_operators/wm.py17
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py9
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py24
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenkernel/BKE_particle.h2
-rw-r--r--source/blender/blenkernel/intern/action.c1
-rw-r--r--source/blender/blenkernel/intern/appdir.c12
-rw-r--r--source/blender/blenkernel/intern/armature.c2
-rw-r--r--source/blender/blenkernel/intern/blendfile.c4
-rw-r--r--source/blender/blenkernel/intern/bpath.c8
-rw-r--r--source/blender/blenkernel/intern/brush.c38
-rw-r--r--source/blender/blenkernel/intern/customdata.c5
-rw-r--r--source/blender/blenkernel/intern/editmesh.c4
-rw-r--r--source/blender/blenkernel/intern/fluid.c12
-rw-r--r--source/blender/blenkernel/intern/image.c6
-rw-r--r--source/blender/blenkernel/intern/image_save.c6
-rw-r--r--source/blender/blenkernel/intern/lib_id.c6
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.c12
-rw-r--r--source/blender/blenkernel/intern/movieclip.c28
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_apply_base.c5
-rw-r--r--source/blender/blenkernel/intern/object.c42
-rw-r--r--source/blender/blenkernel/intern/pointcache.c6
-rw-r--r--source/blender/blenkernel/intern/scene.c2
-rw-r--r--source/blender/blenkernel/intern/seqcache.c4
-rw-r--r--source/blender/blenkernel/intern/sound.c12
-rw-r--r--source/blender/blenkernel/intern/tracking_util.c92
-rw-r--r--source/blender/blenlib/BLI_path_util.h30
-rw-r--r--source/blender/blenlib/intern/BLI_filelist.c2
-rw-r--r--source/blender/blenlib/intern/BLI_ghash.c2
-rw-r--r--source/blender/blenlib/intern/fileops.c26
-rw-r--r--source/blender/blenlib/intern/math_geom.c5
-rw-r--r--source/blender/blenlib/intern/math_vector.c2
-rw-r--r--source/blender/blenlib/intern/path_util.c89
-rw-r--r--source/blender/blenlib/intern/storage.c2
-rw-r--r--source/blender/blenloader/intern/readfile.c841
-rw-r--r--source/blender/blenloader/intern/readfile.h5
-rw-r--r--source/blender/blenloader/intern/undofile.c5
-rw-r--r--source/blender/blenloader/intern/versioning_280.c54
-rw-r--r--source/blender/blenloader/intern/versioning_userdef.c13
-rw-r--r--source/blender/blenloader/intern/writefile.c4
-rw-r--r--source/blender/blentranslation/intern/blt_lang.c13
-rw-r--r--source/blender/blentranslation/intern/blt_translation.c2
-rw-r--r--source/blender/bmesh/intern/bmesh_construct.c4
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.c14
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.h1
-rw-r--r--source/blender/bmesh/intern/bmesh_operators.c2
-rw-r--r--source/blender/bmesh/intern/bmesh_query.c16
-rw-r--r--source/blender/bmesh/intern/bmesh_query.h2
-rw-r--r--source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp2
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc7
-rw-r--r--source/blender/draw/intern/draw_cache_extract.h1
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.c16
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.c3
-rw-r--r--source/blender/draw/intern/draw_cache_impl_particles.c4
-rw-r--r--source/blender/editors/animation/anim_filter.c12
-rw-r--r--source/blender/editors/armature/armature_add.c547
-rw-r--r--source/blender/editors/armature/armature_intern.h9
-rw-r--r--source/blender/editors/armature/armature_select.c176
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c33
-rw-r--r--source/blender/editors/include/ED_mesh.h13
-rw-r--r--source/blender/editors/interface/interface.c3
-rw-r--r--source/blender/editors/interface/interface_intern.h6
-rw-r--r--source/blender/editors/interface/interface_layout.c14
-rw-r--r--source/blender/editors/interface/interface_panel.c89
-rw-r--r--source/blender/editors/interface/interface_utils.c6
-rw-r--r--source/blender/editors/mesh/editmesh_select.c19
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c164
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c46
-rw-r--r--source/blender/editors/mesh/mesh_intern.h2
-rw-r--r--source/blender/editors/mesh/mesh_ops.c2
-rw-r--r--source/blender/editors/object/object_bake_api.c5
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_dyntopo.c14
-rw-r--r--source/blender/editors/space_buttons/buttons_ops.c6
-rw-r--r--source/blender/editors/space_clip/clip_buttons.c2
-rw-r--r--source/blender/editors/space_clip/clip_ops.c2
-rw-r--r--source/blender/editors/space_file/file_ops.c32
-rw-r--r--source/blender/editors/space_file/filelist.c18
-rw-r--r--source/blender/editors/space_file/filesel.c4
-rw-r--r--source/blender/editors/space_image/image_buttons.c2
-rw-r--r--source/blender/editors/space_image/image_ops.c3
-rw-r--r--source/blender/editors/space_image/image_sequence.c8
-rw-r--r--source/blender/editors/space_node/node_group.c10
-rw-r--r--source/blender/editors/space_outliner/outliner_select.c9
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c19
-rw-r--r--source/blender/editors/space_sequencer/sequencer_scopes.c8
-rw-r--r--source/blender/editors/transform/transform.c1
-rw-r--r--source/blender/editors/transform/transform_convert.c18
-rw-r--r--source/blender/editors/undo/memfile_undo.c34
-rw-r--r--source/blender/freestyle/intern/application/Controller.cpp2
-rw-r--r--source/blender/freestyle/intern/system/StringUtils.cpp2
-rw-r--r--source/blender/gpu/GPU_batch_presets.h4
-rw-r--r--source/blender/gpu/intern/gpu_batch_presets.c148
-rw-r--r--source/blender/imbuf/intern/anim_movie.c2
-rw-r--r--source/blender/imbuf/intern/thumbs.c105
-rw-r--r--source/blender/io/collada/ImageExporter.cpp2
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h19
-rw-r--r--source/blender/makesrna/intern/rna_brush.c2
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c31
-rw-r--r--source/blender/python/intern/bpy_interface.c2
-rw-r--r--source/blender/windowmanager/intern/wm_files.c12
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c2
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c5
-rw-r--r--source/blender/windowmanager/intern/wm_playanim.c6
-rw-r--r--source/blender/windowmanager/intern/wm_window.c2
-rw-r--r--source/creator/CMakeLists.txt6
-rw-r--r--source/creator/creator_args.c4
-rw-r--r--tests/gtests/blenlib/BLI_path_util_test.cc18
170 files changed, 3574 insertions, 1881 deletions
diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake
index cd304d65eac..050ab8f3ba1 100644
--- a/build_files/cmake/platform/platform_unix.cmake
+++ b/build_files/cmake/platform/platform_unix.cmake
@@ -606,3 +606,10 @@ endif()
set(PLATFORM_LINKFLAGS
"${PLATFORM_LINKFLAGS} -Wl,--version-script='${CMAKE_SOURCE_DIR}/source/creator/blender.map'"
)
+
+# Don't use position independent executable for portable install since file
+# browsers can't properly detect blender as an executable then. Still enabled
+# for non-portable installs as typically used by Linux distributions.
+if(WITH_INSTALL_PORTABLE)
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -no-pie")
+endif()
diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake
index 452deb497b2..1ecde8635c8 100644
--- a/build_files/cmake/platform/platform_win32.cmake
+++ b/build_files/cmake/platform/platform_win32.cmake
@@ -669,7 +669,7 @@ if(WITH_USD)
set(USD_INCLUDE_DIRS ${LIBDIR}/usd/include)
set(USD_RELEASE_LIB ${LIBDIR}/usd/lib/libusd_m.lib)
set(USD_DEBUG_LIB ${LIBDIR}/usd/lib/libusd_m_d.lib)
- set(USD_LIBRARY_DIR ${LIBDIR}/usd/lib/usd)
+ set(USD_LIBRARY_DIR ${LIBDIR}/usd/lib)
set(USD_LIBRARIES
debug ${USD_DEBUG_LIB}
optimized ${USD_RELEASE_LIB}
diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py
index 68a7a0df075..ee91bbc64b1 100644
--- a/doc/python_api/sphinx_doc_gen.py
+++ b/doc/python_api/sphinx_doc_gen.py
@@ -1052,6 +1052,7 @@ context_type_map = {
"selected_editable_fcurves": ("FCurve", True),
"selected_editable_objects": ("Object", True),
"selected_editable_sequences": ("Sequence", True),
+ "selected_nla_strips": ("NlaStrip", True),
"selected_nodes": ("Node", True),
"selected_objects": ("Object", True),
"selected_pose_bones": ("PoseBone", True),
diff --git a/extern/mantaflow/UPDATE.sh b/extern/mantaflow/UPDATE.sh
index 0db18d03094..3feb1ba9226 100644
--- a/extern/mantaflow/UPDATE.sh
+++ b/extern/mantaflow/UPDATE.sh
@@ -71,7 +71,7 @@ rm $BLENDER_INSTALLATION/blender/tmp/preprocessed/fileio/*.reg
cd $BLENDER_INSTALLATION/blender/tmp/
echo "Applying clang format to Mantaflow source files"
-find . -iname *.h -o -iname *.cpp | xargs clang-format --verbose -i -style=file
+find . -iname *.h -o -iname *.cpp | xargs clang-format --verbose -i -style=file -sort-includes=0
find . -iname *.h -o -iname *.cpp | xargs dos2unix --verbose
# ==================== 5) MOVE MANTAFLOW FILES TO EXTERN/ ================================
diff --git a/extern/mantaflow/preprocessed/gitinfo.h b/extern/mantaflow/preprocessed/gitinfo.h
index 208d8008a7e..791dd001bbe 100644
--- a/extern/mantaflow/preprocessed/gitinfo.h
+++ b/extern/mantaflow/preprocessed/gitinfo.h
@@ -1,3 +1,3 @@
-#define MANTA_GIT_VERSION "commit 5fbd3d04381b21afce4a593d1fe2d9bc7bef5424"
+#define MANTA_GIT_VERSION "commit 21303fab2eda588ec22988bf9e5762d2001c131f"
diff --git a/extern/mantaflow/preprocessed/grid.cpp b/extern/mantaflow/preprocessed/grid.cpp
index f10052349d5..0ea3afb91f4 100644
--- a/extern/mantaflow/preprocessed/grid.cpp
+++ b/extern/mantaflow/preprocessed/grid.cpp
@@ -853,6 +853,147 @@ template<class T> struct knPermuteAxes : public KernelBase {
int axis2;
};
+struct knJoinVec : public KernelBase {
+ knJoinVec(Grid<Vec3> &a, const Grid<Vec3> &b, bool keepMax)
+ : KernelBase(&a, 0), a(a), b(b), keepMax(keepMax)
+ {
+ runMessage();
+ run();
+ }
+ inline void op(IndexInt idx, Grid<Vec3> &a, const Grid<Vec3> &b, bool keepMax) const
+ {
+ Real a1 = normSquare(a[idx]);
+ Real b1 = normSquare(b[idx]);
+ a[idx] = (keepMax) ? max(a1, b1) : min(a1, b1);
+ }
+ inline Grid<Vec3> &getArg0()
+ {
+ return a;
+ }
+ typedef Grid<Vec3> type0;
+ inline const Grid<Vec3> &getArg1()
+ {
+ return b;
+ }
+ typedef Grid<Vec3> type1;
+ inline bool &getArg2()
+ {
+ return keepMax;
+ }
+ typedef bool type2;
+ void runMessage()
+ {
+ debMsg("Executing kernel knJoinVec ", 3);
+ debMsg("Kernel range"
+ << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
+ 4);
+ };
+ void operator()(const tbb::blocked_range<IndexInt> &__r) const
+ {
+ for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++)
+ op(idx, a, b, keepMax);
+ }
+ void run()
+ {
+ tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this);
+ }
+ Grid<Vec3> &a;
+ const Grid<Vec3> &b;
+ bool keepMax;
+};
+struct knJoinInt : public KernelBase {
+ knJoinInt(Grid<int> &a, const Grid<int> &b, bool keepMax)
+ : KernelBase(&a, 0), a(a), b(b), keepMax(keepMax)
+ {
+ runMessage();
+ run();
+ }
+ inline void op(IndexInt idx, Grid<int> &a, const Grid<int> &b, bool keepMax) const
+ {
+ a[idx] = (keepMax) ? max(a[idx], b[idx]) : min(a[idx], b[idx]);
+ }
+ inline Grid<int> &getArg0()
+ {
+ return a;
+ }
+ typedef Grid<int> type0;
+ inline const Grid<int> &getArg1()
+ {
+ return b;
+ }
+ typedef Grid<int> type1;
+ inline bool &getArg2()
+ {
+ return keepMax;
+ }
+ typedef bool type2;
+ void runMessage()
+ {
+ debMsg("Executing kernel knJoinInt ", 3);
+ debMsg("Kernel range"
+ << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
+ 4);
+ };
+ void operator()(const tbb::blocked_range<IndexInt> &__r) const
+ {
+ for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++)
+ op(idx, a, b, keepMax);
+ }
+ void run()
+ {
+ tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this);
+ }
+ Grid<int> &a;
+ const Grid<int> &b;
+ bool keepMax;
+};
+struct knJoinReal : public KernelBase {
+ knJoinReal(Grid<Real> &a, const Grid<Real> &b, bool keepMax)
+ : KernelBase(&a, 0), a(a), b(b), keepMax(keepMax)
+ {
+ runMessage();
+ run();
+ }
+ inline void op(IndexInt idx, Grid<Real> &a, const Grid<Real> &b, bool keepMax) const
+ {
+ a[idx] = (keepMax) ? max(a[idx], b[idx]) : min(a[idx], b[idx]);
+ }
+ inline Grid<Real> &getArg0()
+ {
+ return a;
+ }
+ typedef Grid<Real> type0;
+ inline const Grid<Real> &getArg1()
+ {
+ return b;
+ }
+ typedef Grid<Real> type1;
+ inline bool &getArg2()
+ {
+ return keepMax;
+ }
+ typedef bool type2;
+ void runMessage()
+ {
+ debMsg("Executing kernel knJoinReal ", 3);
+ debMsg("Kernel range"
+ << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
+ 4);
+ };
+ void operator()(const tbb::blocked_range<IndexInt> &__r) const
+ {
+ for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++)
+ op(idx, a, b, keepMax);
+ }
+ void run()
+ {
+ tbb::parallel_for(tbb::blocked_range<IndexInt>(0, size), *this);
+ }
+ Grid<Real> &a;
+ const Grid<Real> &b;
+ bool keepMax;
+};
+
template<class T> Grid<T> &Grid<T>::safeDivide(const Grid<T> &a)
{
knGridSafeDiv<T>(*this, a);
@@ -928,6 +1069,18 @@ void Grid<T>::permuteAxesCopyToGrid(int axis0, int axis1, int axis2, Grid<T> &ou
"Permuted grids must have the same dimensions!");
knPermuteAxes<T>(*this, out, axis0, axis1, axis2);
}
+template<> void Grid<Vec3>::join(const Grid<Vec3> &a, bool keepMax)
+{
+ knJoinVec(*this, a, keepMax);
+}
+template<> void Grid<int>::join(const Grid<int> &a, bool keepMax)
+{
+ knJoinInt(*this, a, keepMax);
+}
+template<> void Grid<Real>::join(const Grid<Real> &a, bool keepMax)
+{
+ knJoinReal(*this, a, keepMax);
+}
template<> Real Grid<Real>::getMax() const
{
diff --git a/extern/mantaflow/preprocessed/grid.h b/extern/mantaflow/preprocessed/grid.h
index 6abe3a2b08a..fe386cfc269 100644
--- a/extern/mantaflow/preprocessed/grid.h
+++ b/extern/mantaflow/preprocessed/grid.h
@@ -966,10 +966,38 @@ template<class T> class Grid : public GridBase {
}
}
+ //! join other grid by either keeping min or max value at cell
+ void join(const Grid<T> &a, bool keepMax = true);
+ static PyObject *_W_27(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ {
+ try {
+ PbArgs _args(_linargs, _kwds);
+ Grid *pbo = dynamic_cast<Grid *>(Pb::objFromPy(_self));
+ bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
+ pbPreparePlugin(pbo->getParent(), "Grid::join", !noTiming);
+ PyObject *_retval = 0;
+ {
+ ArgLocker _lock;
+ const Grid<T> &a = *_args.getPtr<Grid<T>>("a", 0, &_lock);
+ bool keepMax = _args.getOpt<bool>("keepMax", 1, true, &_lock);
+ pbo->_args.copy(_args);
+ _retval = getPyNone();
+ pbo->join(a, keepMax);
+ pbo->_args.check();
+ }
+ pbFinalizePlugin(pbo->getParent(), "Grid::join", !noTiming);
+ return _retval;
+ }
+ catch (std::exception &e) {
+ pbSetError("Grid::join", e.what());
+ return 0;
+ }
+ }
+
// common compound operators
//! get absolute max value in grid
Real getMaxAbs() const;
- static PyObject *_W_27(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static PyObject *_W_28(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -994,7 +1022,7 @@ template<class T> class Grid : public GridBase {
//! get max value in grid
Real getMax() const;
- static PyObject *_W_28(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static PyObject *_W_29(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1019,7 +1047,7 @@ template<class T> class Grid : public GridBase {
//! get min value in grid
Real getMin() const;
- static PyObject *_W_29(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static PyObject *_W_30(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1044,7 +1072,7 @@ template<class T> class Grid : public GridBase {
//! calculate L1 norm of grid content
Real getL1(int bnd = 0);
- static PyObject *_W_30(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static PyObject *_W_31(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1070,7 +1098,7 @@ template<class T> class Grid : public GridBase {
//! calculate L2 norm of grid content
Real getL2(int bnd = 0);
- static PyObject *_W_31(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static PyObject *_W_32(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1096,7 +1124,7 @@ template<class T> class Grid : public GridBase {
//! set all boundary cells to constant value (Dirichlet)
void setBound(T value, int boundaryWidth = 1);
- static PyObject *_W_32(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static PyObject *_W_33(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1124,7 +1152,7 @@ template<class T> class Grid : public GridBase {
//! set all boundary cells to last inner value (Neumann)
void setBoundNeumann(int boundaryWidth = 1);
- static PyObject *_W_33(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static PyObject *_W_34(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1151,7 +1179,7 @@ template<class T> class Grid : public GridBase {
//! get data pointer of grid
std::string getDataPointer();
- static PyObject *_W_34(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static PyObject *_W_35(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1176,7 +1204,7 @@ template<class T> class Grid : public GridBase {
//! debugging helper, print grid from python. skip boundary of width bnd
void printGrid(int zSlice = -1, bool printIndex = false, int bnd = 1);
- static PyObject *_W_35(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static PyObject *_W_36(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1244,7 +1272,7 @@ class MACGrid : public Grid<Vec3> {
{
mType = (GridType)(TypeMAC | TypeVec3);
}
- static int _W_36(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static int _W_37(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
PbClass *obj = Pb::objFromPy(_self);
if (obj)
@@ -1326,7 +1354,7 @@ class MACGrid : public Grid<Vec3> {
//! set all boundary cells of a MAC grid to certain value (Dirchlet). Respects staggered grid
//! locations optionally, only set normal components
void setBoundMAC(Vec3 value, int boundaryWidth, bool normalOnly = false);
- static PyObject *_W_37(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static PyObject *_W_38(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1367,7 +1395,7 @@ class FlagGrid : public Grid<int> {
{
mType = (GridType)(TypeFlags | TypeInt);
}
- static int _W_38(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static int _W_39(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
PbClass *obj = Pb::objFromPy(_self);
if (obj)
@@ -1547,7 +1575,7 @@ class FlagGrid : public Grid<int> {
const std::string &inflow = " ",
const std::string &outflow = " ",
Grid<Real> *phiWalls = 0x00);
- static PyObject *_W_39(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static PyObject *_W_40(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1581,7 +1609,7 @@ class FlagGrid : public Grid<int> {
//! set fluid flags inside levelset (liquids)
void updateFromLevelset(LevelsetGrid &levelset);
- static PyObject *_W_40(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static PyObject *_W_41(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1608,7 +1636,7 @@ class FlagGrid : public Grid<int> {
//! set all cells (except obs/in/outflow) to type (fluid by default)
void fillGrid(int type = TypeFluid);
- static PyObject *_W_41(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static PyObject *_W_42(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
@@ -1637,7 +1665,7 @@ class FlagGrid : public Grid<int> {
//! warning for large grids! only regular int returned (due to python interface)
//! optionally creates mask in RealGrid (1 where flag matches, 0 otherwise)
int countCells(int flag, int bnd = 0, Grid<Real> *mask = NULL);
- static PyObject *_W_42(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
+ static PyObject *_W_43(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
{
try {
PbArgs _args(_linargs, _kwds);
diff --git a/extern/mantaflow/preprocessed/grid.h.reg.cpp b/extern/mantaflow/preprocessed/grid.h.reg.cpp
index 70c4d8de453..9fe3e7298fa 100644
--- a/extern/mantaflow/preprocessed/grid.h.reg.cpp
+++ b/extern/mantaflow/preprocessed/grid.h.reg.cpp
@@ -8,11 +8,11 @@ namespace Manta {
#ifdef _C_FlagGrid
static const Pb::Register _R_26("FlagGrid", "FlagGrid", "Grid<int>");
template<> const char *Namify<FlagGrid>::S = "FlagGrid";
-static const Pb::Register _R_27("FlagGrid", "FlagGrid", FlagGrid::_W_38);
-static const Pb::Register _R_28("FlagGrid", "initDomain", FlagGrid::_W_39);
-static const Pb::Register _R_29("FlagGrid", "updateFromLevelset", FlagGrid::_W_40);
-static const Pb::Register _R_30("FlagGrid", "fillGrid", FlagGrid::_W_41);
-static const Pb::Register _R_31("FlagGrid", "countCells", FlagGrid::_W_42);
+static const Pb::Register _R_27("FlagGrid", "FlagGrid", FlagGrid::_W_39);
+static const Pb::Register _R_28("FlagGrid", "initDomain", FlagGrid::_W_40);
+static const Pb::Register _R_29("FlagGrid", "updateFromLevelset", FlagGrid::_W_41);
+static const Pb::Register _R_30("FlagGrid", "fillGrid", FlagGrid::_W_42);
+static const Pb::Register _R_31("FlagGrid", "countCells", FlagGrid::_W_43);
#endif
#ifdef _C_Grid
static const Pb::Register _R_32("Grid<int>", "Grid<int>", "GridBase");
@@ -35,92 +35,95 @@ static const Pb::Register _R_47("Grid<int>", "clamp", Grid<int>::_W_23);
static const Pb::Register _R_48("Grid<int>", "stomp", Grid<int>::_W_24);
static const Pb::Register _R_49("Grid<int>", "permuteAxes", Grid<int>::_W_25);
static const Pb::Register _R_50("Grid<int>", "permuteAxesCopyToGrid", Grid<int>::_W_26);
-static const Pb::Register _R_51("Grid<int>", "getMaxAbs", Grid<int>::_W_27);
-static const Pb::Register _R_52("Grid<int>", "getMax", Grid<int>::_W_28);
-static const Pb::Register _R_53("Grid<int>", "getMin", Grid<int>::_W_29);
-static const Pb::Register _R_54("Grid<int>", "getL1", Grid<int>::_W_30);
-static const Pb::Register _R_55("Grid<int>", "getL2", Grid<int>::_W_31);
-static const Pb::Register _R_56("Grid<int>", "setBound", Grid<int>::_W_32);
-static const Pb::Register _R_57("Grid<int>", "setBoundNeumann", Grid<int>::_W_33);
-static const Pb::Register _R_58("Grid<int>", "getDataPointer", Grid<int>::_W_34);
-static const Pb::Register _R_59("Grid<int>", "printGrid", Grid<int>::_W_35);
-static const Pb::Register _R_60("Grid<Real>", "Grid<Real>", "GridBase");
+static const Pb::Register _R_51("Grid<int>", "join", Grid<int>::_W_27);
+static const Pb::Register _R_52("Grid<int>", "getMaxAbs", Grid<int>::_W_28);
+static const Pb::Register _R_53("Grid<int>", "getMax", Grid<int>::_W_29);
+static const Pb::Register _R_54("Grid<int>", "getMin", Grid<int>::_W_30);
+static const Pb::Register _R_55("Grid<int>", "getL1", Grid<int>::_W_31);
+static const Pb::Register _R_56("Grid<int>", "getL2", Grid<int>::_W_32);
+static const Pb::Register _R_57("Grid<int>", "setBound", Grid<int>::_W_33);
+static const Pb::Register _R_58("Grid<int>", "setBoundNeumann", Grid<int>::_W_34);
+static const Pb::Register _R_59("Grid<int>", "getDataPointer", Grid<int>::_W_35);
+static const Pb::Register _R_60("Grid<int>", "printGrid", Grid<int>::_W_36);
+static const Pb::Register _R_61("Grid<Real>", "Grid<Real>", "GridBase");
template<> const char *Namify<Grid<Real>>::S = "Grid<Real>";
-static const Pb::Register _R_61("Grid<Real>", "Grid", Grid<Real>::_W_9);
-static const Pb::Register _R_62("Grid<Real>", "save", Grid<Real>::_W_10);
-static const Pb::Register _R_63("Grid<Real>", "load", Grid<Real>::_W_11);
-static const Pb::Register _R_64("Grid<Real>", "clear", Grid<Real>::_W_12);
-static const Pb::Register _R_65("Grid<Real>", "copyFrom", Grid<Real>::_W_13);
-static const Pb::Register _R_66("Grid<Real>", "getGridType", Grid<Real>::_W_14);
-static const Pb::Register _R_67("Grid<Real>", "add", Grid<Real>::_W_15);
-static const Pb::Register _R_68("Grid<Real>", "sub", Grid<Real>::_W_16);
-static const Pb::Register _R_69("Grid<Real>", "setConst", Grid<Real>::_W_17);
-static const Pb::Register _R_70("Grid<Real>", "addConst", Grid<Real>::_W_18);
-static const Pb::Register _R_71("Grid<Real>", "addScaled", Grid<Real>::_W_19);
-static const Pb::Register _R_72("Grid<Real>", "mult", Grid<Real>::_W_20);
-static const Pb::Register _R_73("Grid<Real>", "multConst", Grid<Real>::_W_21);
-static const Pb::Register _R_74("Grid<Real>", "safeDivide", Grid<Real>::_W_22);
-static const Pb::Register _R_75("Grid<Real>", "clamp", Grid<Real>::_W_23);
-static const Pb::Register _R_76("Grid<Real>", "stomp", Grid<Real>::_W_24);
-static const Pb::Register _R_77("Grid<Real>", "permuteAxes", Grid<Real>::_W_25);
-static const Pb::Register _R_78("Grid<Real>", "permuteAxesCopyToGrid", Grid<Real>::_W_26);
-static const Pb::Register _R_79("Grid<Real>", "getMaxAbs", Grid<Real>::_W_27);
-static const Pb::Register _R_80("Grid<Real>", "getMax", Grid<Real>::_W_28);
-static const Pb::Register _R_81("Grid<Real>", "getMin", Grid<Real>::_W_29);
-static const Pb::Register _R_82("Grid<Real>", "getL1", Grid<Real>::_W_30);
-static const Pb::Register _R_83("Grid<Real>", "getL2", Grid<Real>::_W_31);
-static const Pb::Register _R_84("Grid<Real>", "setBound", Grid<Real>::_W_32);
-static const Pb::Register _R_85("Grid<Real>", "setBoundNeumann", Grid<Real>::_W_33);
-static const Pb::Register _R_86("Grid<Real>", "getDataPointer", Grid<Real>::_W_34);
-static const Pb::Register _R_87("Grid<Real>", "printGrid", Grid<Real>::_W_35);
-static const Pb::Register _R_88("Grid<Vec3>", "Grid<Vec3>", "GridBase");
+static const Pb::Register _R_62("Grid<Real>", "Grid", Grid<Real>::_W_9);
+static const Pb::Register _R_63("Grid<Real>", "save", Grid<Real>::_W_10);
+static const Pb::Register _R_64("Grid<Real>", "load", Grid<Real>::_W_11);
+static const Pb::Register _R_65("Grid<Real>", "clear", Grid<Real>::_W_12);
+static const Pb::Register _R_66("Grid<Real>", "copyFrom", Grid<Real>::_W_13);
+static const Pb::Register _R_67("Grid<Real>", "getGridType", Grid<Real>::_W_14);
+static const Pb::Register _R_68("Grid<Real>", "add", Grid<Real>::_W_15);
+static const Pb::Register _R_69("Grid<Real>", "sub", Grid<Real>::_W_16);
+static const Pb::Register _R_70("Grid<Real>", "setConst", Grid<Real>::_W_17);
+static const Pb::Register _R_71("Grid<Real>", "addConst", Grid<Real>::_W_18);
+static const Pb::Register _R_72("Grid<Real>", "addScaled", Grid<Real>::_W_19);
+static const Pb::Register _R_73("Grid<Real>", "mult", Grid<Real>::_W_20);
+static const Pb::Register _R_74("Grid<Real>", "multConst", Grid<Real>::_W_21);
+static const Pb::Register _R_75("Grid<Real>", "safeDivide", Grid<Real>::_W_22);
+static const Pb::Register _R_76("Grid<Real>", "clamp", Grid<Real>::_W_23);
+static const Pb::Register _R_77("Grid<Real>", "stomp", Grid<Real>::_W_24);
+static const Pb::Register _R_78("Grid<Real>", "permuteAxes", Grid<Real>::_W_25);
+static const Pb::Register _R_79("Grid<Real>", "permuteAxesCopyToGrid", Grid<Real>::_W_26);
+static const Pb::Register _R_80("Grid<Real>", "join", Grid<Real>::_W_27);
+static const Pb::Register _R_81("Grid<Real>", "getMaxAbs", Grid<Real>::_W_28);
+static const Pb::Register _R_82("Grid<Real>", "getMax", Grid<Real>::_W_29);
+static const Pb::Register _R_83("Grid<Real>", "getMin", Grid<Real>::_W_30);
+static const Pb::Register _R_84("Grid<Real>", "getL1", Grid<Real>::_W_31);
+static const Pb::Register _R_85("Grid<Real>", "getL2", Grid<Real>::_W_32);
+static const Pb::Register _R_86("Grid<Real>", "setBound", Grid<Real>::_W_33);
+static const Pb::Register _R_87("Grid<Real>", "setBoundNeumann", Grid<Real>::_W_34);
+static const Pb::Register _R_88("Grid<Real>", "getDataPointer", Grid<Real>::_W_35);
+static const Pb::Register _R_89("Grid<Real>", "printGrid", Grid<Real>::_W_36);
+static const Pb::Register _R_90("Grid<Vec3>", "Grid<Vec3>", "GridBase");
template<> const char *Namify<Grid<Vec3>>::S = "Grid<Vec3>";
-static const Pb::Register _R_89("Grid<Vec3>", "Grid", Grid<Vec3>::_W_9);
-static const Pb::Register _R_90("Grid<Vec3>", "save", Grid<Vec3>::_W_10);
-static const Pb::Register _R_91("Grid<Vec3>", "load", Grid<Vec3>::_W_11);
-static const Pb::Register _R_92("Grid<Vec3>", "clear", Grid<Vec3>::_W_12);
-static const Pb::Register _R_93("Grid<Vec3>", "copyFrom", Grid<Vec3>::_W_13);
-static const Pb::Register _R_94("Grid<Vec3>", "getGridType", Grid<Vec3>::_W_14);
-static const Pb::Register _R_95("Grid<Vec3>", "add", Grid<Vec3>::_W_15);
-static const Pb::Register _R_96("Grid<Vec3>", "sub", Grid<Vec3>::_W_16);
-static const Pb::Register _R_97("Grid<Vec3>", "setConst", Grid<Vec3>::_W_17);
-static const Pb::Register _R_98("Grid<Vec3>", "addConst", Grid<Vec3>::_W_18);
-static const Pb::Register _R_99("Grid<Vec3>", "addScaled", Grid<Vec3>::_W_19);
-static const Pb::Register _R_100("Grid<Vec3>", "mult", Grid<Vec3>::_W_20);
-static const Pb::Register _R_101("Grid<Vec3>", "multConst", Grid<Vec3>::_W_21);
-static const Pb::Register _R_102("Grid<Vec3>", "safeDivide", Grid<Vec3>::_W_22);
-static const Pb::Register _R_103("Grid<Vec3>", "clamp", Grid<Vec3>::_W_23);
-static const Pb::Register _R_104("Grid<Vec3>", "stomp", Grid<Vec3>::_W_24);
-static const Pb::Register _R_105("Grid<Vec3>", "permuteAxes", Grid<Vec3>::_W_25);
-static const Pb::Register _R_106("Grid<Vec3>", "permuteAxesCopyToGrid", Grid<Vec3>::_W_26);
-static const Pb::Register _R_107("Grid<Vec3>", "getMaxAbs", Grid<Vec3>::_W_27);
-static const Pb::Register _R_108("Grid<Vec3>", "getMax", Grid<Vec3>::_W_28);
-static const Pb::Register _R_109("Grid<Vec3>", "getMin", Grid<Vec3>::_W_29);
-static const Pb::Register _R_110("Grid<Vec3>", "getL1", Grid<Vec3>::_W_30);
-static const Pb::Register _R_111("Grid<Vec3>", "getL2", Grid<Vec3>::_W_31);
-static const Pb::Register _R_112("Grid<Vec3>", "setBound", Grid<Vec3>::_W_32);
-static const Pb::Register _R_113("Grid<Vec3>", "setBoundNeumann", Grid<Vec3>::_W_33);
-static const Pb::Register _R_114("Grid<Vec3>", "getDataPointer", Grid<Vec3>::_W_34);
-static const Pb::Register _R_115("Grid<Vec3>", "printGrid", Grid<Vec3>::_W_35);
+static const Pb::Register _R_91("Grid<Vec3>", "Grid", Grid<Vec3>::_W_9);
+static const Pb::Register _R_92("Grid<Vec3>", "save", Grid<Vec3>::_W_10);
+static const Pb::Register _R_93("Grid<Vec3>", "load", Grid<Vec3>::_W_11);
+static const Pb::Register _R_94("Grid<Vec3>", "clear", Grid<Vec3>::_W_12);
+static const Pb::Register _R_95("Grid<Vec3>", "copyFrom", Grid<Vec3>::_W_13);
+static const Pb::Register _R_96("Grid<Vec3>", "getGridType", Grid<Vec3>::_W_14);
+static const Pb::Register _R_97("Grid<Vec3>", "add", Grid<Vec3>::_W_15);
+static const Pb::Register _R_98("Grid<Vec3>", "sub", Grid<Vec3>::_W_16);
+static const Pb::Register _R_99("Grid<Vec3>", "setConst", Grid<Vec3>::_W_17);
+static const Pb::Register _R_100("Grid<Vec3>", "addConst", Grid<Vec3>::_W_18);
+static const Pb::Register _R_101("Grid<Vec3>", "addScaled", Grid<Vec3>::_W_19);
+static const Pb::Register _R_102("Grid<Vec3>", "mult", Grid<Vec3>::_W_20);
+static const Pb::Register _R_103("Grid<Vec3>", "multConst", Grid<Vec3>::_W_21);
+static const Pb::Register _R_104("Grid<Vec3>", "safeDivide", Grid<Vec3>::_W_22);
+static const Pb::Register _R_105("Grid<Vec3>", "clamp", Grid<Vec3>::_W_23);
+static const Pb::Register _R_106("Grid<Vec3>", "stomp", Grid<Vec3>::_W_24);
+static const Pb::Register _R_107("Grid<Vec3>", "permuteAxes", Grid<Vec3>::_W_25);
+static const Pb::Register _R_108("Grid<Vec3>", "permuteAxesCopyToGrid", Grid<Vec3>::_W_26);
+static const Pb::Register _R_109("Grid<Vec3>", "join", Grid<Vec3>::_W_27);
+static const Pb::Register _R_110("Grid<Vec3>", "getMaxAbs", Grid<Vec3>::_W_28);
+static const Pb::Register _R_111("Grid<Vec3>", "getMax", Grid<Vec3>::_W_29);
+static const Pb::Register _R_112("Grid<Vec3>", "getMin", Grid<Vec3>::_W_30);
+static const Pb::Register _R_113("Grid<Vec3>", "getL1", Grid<Vec3>::_W_31);
+static const Pb::Register _R_114("Grid<Vec3>", "getL2", Grid<Vec3>::_W_32);
+static const Pb::Register _R_115("Grid<Vec3>", "setBound", Grid<Vec3>::_W_33);
+static const Pb::Register _R_116("Grid<Vec3>", "setBoundNeumann", Grid<Vec3>::_W_34);
+static const Pb::Register _R_117("Grid<Vec3>", "getDataPointer", Grid<Vec3>::_W_35);
+static const Pb::Register _R_118("Grid<Vec3>", "printGrid", Grid<Vec3>::_W_36);
#endif
#ifdef _C_GridBase
-static const Pb::Register _R_116("GridBase", "GridBase", "PbClass");
+static const Pb::Register _R_119("GridBase", "GridBase", "PbClass");
template<> const char *Namify<GridBase>::S = "GridBase";
-static const Pb::Register _R_117("GridBase", "GridBase", GridBase::_W_0);
-static const Pb::Register _R_118("GridBase", "getSizeX", GridBase::_W_1);
-static const Pb::Register _R_119("GridBase", "getSizeY", GridBase::_W_2);
-static const Pb::Register _R_120("GridBase", "getSizeZ", GridBase::_W_3);
-static const Pb::Register _R_121("GridBase", "getSize", GridBase::_W_4);
-static const Pb::Register _R_122("GridBase", "is3D", GridBase::_W_5);
-static const Pb::Register _R_123("GridBase", "is4D", GridBase::_W_6);
-static const Pb::Register _R_124("GridBase", "getSizeT", GridBase::_W_7);
-static const Pb::Register _R_125("GridBase", "getStrideT", GridBase::_W_8);
+static const Pb::Register _R_120("GridBase", "GridBase", GridBase::_W_0);
+static const Pb::Register _R_121("GridBase", "getSizeX", GridBase::_W_1);
+static const Pb::Register _R_122("GridBase", "getSizeY", GridBase::_W_2);
+static const Pb::Register _R_123("GridBase", "getSizeZ", GridBase::_W_3);
+static const Pb::Register _R_124("GridBase", "getSize", GridBase::_W_4);
+static const Pb::Register _R_125("GridBase", "is3D", GridBase::_W_5);
+static const Pb::Register _R_126("GridBase", "is4D", GridBase::_W_6);
+static const Pb::Register _R_127("GridBase", "getSizeT", GridBase::_W_7);
+static const Pb::Register _R_128("GridBase", "getStrideT", GridBase::_W_8);
#endif
#ifdef _C_MACGrid
-static const Pb::Register _R_126("MACGrid", "MACGrid", "Grid<Vec3>");
+static const Pb::Register _R_129("MACGrid", "MACGrid", "Grid<Vec3>");
template<> const char *Namify<MACGrid>::S = "MACGrid";
-static const Pb::Register _R_127("MACGrid", "MACGrid", MACGrid::_W_36);
-static const Pb::Register _R_128("MACGrid", "setBoundMAC", MACGrid::_W_37);
+static const Pb::Register _R_130("MACGrid", "MACGrid", MACGrid::_W_37);
+static const Pb::Register _R_131("MACGrid", "setBoundMAC", MACGrid::_W_38);
#endif
static const Pb::Register _R_7("GridType_TypeNone", 0);
static const Pb::Register _R_8("GridType_TypeReal", 1);
@@ -247,6 +250,9 @@ void PbRegister_file_7()
KEEP_UNUSED(_R_126);
KEEP_UNUSED(_R_127);
KEEP_UNUSED(_R_128);
+ KEEP_UNUSED(_R_129);
+ KEEP_UNUSED(_R_130);
+ KEEP_UNUSED(_R_131);
}
}
} // namespace Manta \ No newline at end of file
diff --git a/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp b/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp
index 18582d57e64..a8913a218c1 100644
--- a/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp
+++ b/extern/mantaflow/preprocessed/plugin/secondaryparticles.cpp
@@ -525,7 +525,7 @@ struct knFlipSampleSecondaryParticlesMoreCylinders : public KernelBase {
if (!(flags(i, j, k) & itype))
return;
- RandomStream mRand(9832);
+ static RandomStream mRand(9832);
Real radius =
0.25; // diameter=0.5 => sampling with two cylinders in each dimension since cell size=1
for (Real x = i - radius; x <= i + radius; x += 2 * radius) {
@@ -791,11 +791,9 @@ struct knFlipSampleSecondaryParticles : public KernelBase {
const int n = KE * (k_ta * TA + k_wc * WC) * dt; // number of secondary particles
if (n == 0)
return;
- RandomStream mRand(9832);
+ static RandomStream mRand(9832);
- Vec3 xi = Vec3(i + mRand.getReal(),
- j + mRand.getReal(),
- k + mRand.getReal()); // randomized offset uniform in cell
+ Vec3 xi = Vec3(i, j, k) + mRand.getVec3(); // randomized offset uniform in cell
Vec3 vi = v.getInterpolated(xi);
Vec3 dir = dt * vi; // direction of movement of current particle
Vec3 e1 = getNormalized(Vec3(dir.z, 0, -dir.x)); // perpendicular to dir
@@ -1834,7 +1832,7 @@ struct knFlipDeleteParticlesInObstacle : public KernelBase {
}
int gridIndex = flags.index(xidx);
// remove particles that penetrate obstacles
- if (flags[gridIndex] == FlagGrid::TypeObstacle || flags[gridIndex] == FlagGrid::TypeOutflow) {
+ if (flags.isObstacle(gridIndex) || flags.isOutflow(gridIndex)) {
pts.kill(idx);
}
}
diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp
index d94d409175b..dfbf57e8b88 100644
--- a/intern/cycles/device/device.cpp
+++ b/intern/cycles/device/device.cpp
@@ -597,6 +597,7 @@ DeviceInfo Device::get_multi_device(const vector<DeviceInfo> &subdevices,
info.has_half_images = true;
info.has_volume_decoupled = true;
+ info.has_adaptive_stop_per_sample = true;
info.has_osl = true;
info.has_profiling = true;
@@ -639,6 +640,7 @@ DeviceInfo Device::get_multi_device(const vector<DeviceInfo> &subdevices,
/* Accumulate device info. */
info.has_half_images &= device.has_half_images;
info.has_volume_decoupled &= device.has_volume_decoupled;
+ info.has_adaptive_stop_per_sample &= device.has_adaptive_stop_per_sample;
info.has_osl &= device.has_osl;
info.has_profiling &= device.has_profiling;
}
diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h
index a98ac171709..c55dfb3a83b 100644
--- a/intern/cycles/device/device.h
+++ b/intern/cycles/device/device.h
@@ -75,12 +75,13 @@ class DeviceInfo {
string description;
string id; /* used for user preferences, should stay fixed with changing hardware config */
int num;
- bool display_device; /* GPU is used as a display device. */
- bool has_half_images; /* Support half-float textures. */
- bool has_volume_decoupled; /* Decoupled volume shading. */
- bool has_osl; /* Support Open Shading Language. */
- bool use_split_kernel; /* Use split or mega kernel. */
- bool has_profiling; /* Supports runtime collection of profiling info. */
+ bool display_device; /* GPU is used as a display device. */
+ bool has_half_images; /* Support half-float textures. */
+ bool has_volume_decoupled; /* Decoupled volume shading. */
+ bool has_adaptive_stop_per_sample; /* Per-sample adaptive sampling stopping. */
+ bool has_osl; /* Support Open Shading Language. */
+ bool use_split_kernel; /* Use split or mega kernel. */
+ bool has_profiling; /* Supports runtime collection of profiling info. */
int cpu_threads;
vector<DeviceInfo> multi_devices;
vector<DeviceInfo> denoising_devices;
@@ -94,6 +95,7 @@ class DeviceInfo {
display_device = false;
has_half_images = false;
has_volume_decoupled = false;
+ has_adaptive_stop_per_sample = false;
has_osl = false;
use_split_kernel = false;
has_profiling = false;
diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp
index 57e8523e02a..c701c14318f 100644
--- a/intern/cycles/device/device_cpu.cpp
+++ b/intern/cycles/device/device_cpu.cpp
@@ -839,7 +839,7 @@ class CPUDevice : public Device {
return true;
}
- bool adaptive_sampling_filter(KernelGlobals *kg, RenderTile &tile)
+ bool adaptive_sampling_filter(KernelGlobals *kg, RenderTile &tile, int sample)
{
WorkTile wtile;
wtile.x = tile.x;
@@ -850,11 +850,24 @@ class CPUDevice : public Device {
wtile.stride = tile.stride;
wtile.buffer = (float *)tile.buffer;
+ /* For CPU we do adaptive stopping per sample so we can stop earlier, but
+ * for combined CPU + GPU rendering we match the GPU and do it per tile
+ * after a given number of sample steps. */
+ if (!kernel_data.integrator.adaptive_stop_per_sample) {
+ for (int y = wtile.y; y < wtile.y + wtile.h; ++y) {
+ for (int x = wtile.x; x < wtile.x + wtile.w; ++x) {
+ const int index = wtile.offset + x + y * wtile.stride;
+ float *buffer = wtile.buffer + index * kernel_data.film.pass_stride;
+ kernel_do_adaptive_stopping(kg, buffer, sample);
+ }
+ }
+ }
+
bool any = false;
- for (int y = tile.y; y < tile.y + tile.h; ++y) {
+ for (int y = wtile.y; y < wtile.y + wtile.h; ++y) {
any |= kernel_do_adaptive_filter_x(kg, y, &wtile);
}
- for (int x = tile.x; x < tile.x + tile.w; ++x) {
+ for (int x = wtile.x; x < wtile.x + wtile.w; ++x) {
any |= kernel_do_adaptive_filter_y(kg, x, &wtile);
}
return (!any);
@@ -917,7 +930,7 @@ class CPUDevice : public Device {
tile.sample = sample + 1;
if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(sample)) {
- const bool stop = adaptive_sampling_filter(kg, tile);
+ const bool stop = adaptive_sampling_filter(kg, tile, sample);
if (stop) {
const int num_progress_samples = end_sample - sample;
tile.sample = end_sample;
@@ -1327,6 +1340,7 @@ void device_cpu_info(vector<DeviceInfo> &devices)
info.id = "CPU";
info.num = 0;
info.has_volume_decoupled = true;
+ info.has_adaptive_stop_per_sample = true;
info.has_osl = true;
info.has_half_images = true;
info.has_profiling = true;
diff --git a/intern/cycles/device/device_cuda.cpp b/intern/cycles/device/device_cuda.cpp
index 9a703b45c0a..4a53fcd151d 100644
--- a/intern/cycles/device/device_cuda.cpp
+++ b/intern/cycles/device/device_cuda.cpp
@@ -129,6 +129,7 @@ void device_cuda_info(vector<DeviceInfo> &devices)
info.has_half_images = (major >= 3);
info.has_volume_decoupled = false;
+ info.has_adaptive_stop_per_sample = false;
int pci_location[3] = {0, 0, 0};
cuDeviceGetAttribute(&pci_location[0], CU_DEVICE_ATTRIBUTE_PCI_DOMAIN_ID, num);
diff --git a/intern/cycles/device/device_network.cpp b/intern/cycles/device/device_network.cpp
index 2742cbf53aa..0933d51f321 100644
--- a/intern/cycles/device/device_network.cpp
+++ b/intern/cycles/device/device_network.cpp
@@ -311,6 +311,7 @@ void device_network_info(vector<DeviceInfo> &devices)
/* todo: get this info from device */
info.has_volume_decoupled = false;
+ info.has_adaptive_stop_per_sample = false;
info.has_osl = false;
devices.push_back(info);
diff --git a/intern/cycles/device/device_opencl.cpp b/intern/cycles/device/device_opencl.cpp
index 891b73351a0..8a0b128697f 100644
--- a/intern/cycles/device/device_opencl.cpp
+++ b/intern/cycles/device/device_opencl.cpp
@@ -119,6 +119,7 @@ void device_opencl_info(vector<DeviceInfo> &devices)
info.display_device = true;
info.use_split_kernel = true;
info.has_volume_decoupled = false;
+ info.has_adaptive_stop_per_sample = false;
info.id = id;
/* Check OpenCL extensions */
diff --git a/intern/cycles/device/device_task.cpp b/intern/cycles/device/device_task.cpp
index c36b1344c3b..d2447eae867 100644
--- a/intern/cycles/device/device_task.cpp
+++ b/intern/cycles/device/device_task.cpp
@@ -138,8 +138,7 @@ void DeviceTask::update_progress(RenderTile *rtile, int pixel_samples)
/* Adaptive Sampling */
-AdaptiveSampling::AdaptiveSampling()
- : use(true), adaptive_step(ADAPTIVE_SAMPLE_STEP), min_samples(0)
+AdaptiveSampling::AdaptiveSampling() : use(true), adaptive_step(0), min_samples(0)
{
}
diff --git a/intern/cycles/kernel/kernel_adaptive_sampling.h b/intern/cycles/kernel/kernel_adaptive_sampling.h
index 047fe8c92ec..ee4d1507ef1 100644
--- a/intern/cycles/kernel/kernel_adaptive_sampling.h
+++ b/intern/cycles/kernel/kernel_adaptive_sampling.h
@@ -150,6 +150,7 @@ ccl_device void kernel_adaptive_post_adjust(KernelGlobals *kg,
}
#endif /* __DENOISING_FEATURES__ */
+ /* Cryptomatte. */
if (kernel_data.film.cryptomatte_passes) {
int num_slots = 0;
num_slots += (kernel_data.film.cryptomatte_passes & CRYPT_OBJECT) ? 1 : 0;
@@ -162,6 +163,14 @@ ccl_device void kernel_adaptive_post_adjust(KernelGlobals *kg,
id_buffer[slot].y *= sample_multiplier;
}
}
+
+ /* AOVs. */
+ for (int i = 0; i < kernel_data.film.pass_aov_value_num; i++) {
+ *(buffer + kernel_data.film.pass_aov_value + i) *= sample_multiplier;
+ }
+ for (int i = 0; i < kernel_data.film.pass_aov_color_num; i++) {
+ *((ccl_global float4 *)(buffer + kernel_data.film.pass_aov_color) + i) *= sample_multiplier;
+ }
}
/* This is a simple box filter in two passes.
diff --git a/intern/cycles/kernel/kernel_passes.h b/intern/cycles/kernel/kernel_passes.h
index 98136bc7047..7437e540a1f 100644
--- a/intern/cycles/kernel/kernel_passes.h
+++ b/intern/cycles/kernel/kernel_passes.h
@@ -403,9 +403,13 @@ ccl_device_inline void kernel_write_result(KernelGlobals *kg,
make_float4(L_sum.x * 2.0f, L_sum.y * 2.0f, L_sum.z * 2.0f, 0.0f));
}
#ifdef __KERNEL_CPU__
- if (sample > kernel_data.integrator.adaptive_min_samples &&
- (sample & (ADAPTIVE_SAMPLE_STEP - 1)) == (ADAPTIVE_SAMPLE_STEP - 1)) {
- kernel_do_adaptive_stopping(kg, buffer, sample);
+ if ((sample > kernel_data.integrator.adaptive_min_samples) &&
+ kernel_data.integrator.adaptive_stop_per_sample) {
+ const int step = kernel_data.integrator.adaptive_step;
+
+ if ((sample & (step - 1)) == (step - 1)) {
+ kernel_do_adaptive_stopping(kg, buffer, sample);
+ }
}
#endif
}
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index b6d319311a1..a1f8c35348d 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -63,11 +63,6 @@ CCL_NAMESPACE_BEGIN
#define VOLUME_STACK_SIZE 32
-/* Adaptive sampling constants */
-#define ADAPTIVE_SAMPLE_STEP 4
-static_assert((ADAPTIVE_SAMPLE_STEP & (ADAPTIVE_SAMPLE_STEP - 1)) == 0,
- "ADAPTIVE_SAMPLE_STEP must be power of two for bitwise operations to work");
-
/* Split kernel constants */
#define WORK_POOL_SIZE_GPU 64
#define WORK_POOL_SIZE_CPU 1
@@ -1242,7 +1237,9 @@ typedef struct KernelFilm {
int pass_aov_color;
int pass_aov_value;
- int pad1;
+ int pass_aov_color_num;
+ int pass_aov_value_num;
+ int pad1, pad2, pad3;
/* XYZ to rendering color space transform. float4 instead of float3 to
* ensure consistent padding/alignment across devices. */
@@ -1265,7 +1262,7 @@ typedef struct KernelFilm {
int use_display_exposure;
int use_display_pass_alpha;
- int pad3, pad4, pad5;
+ int pad4, pad5, pad6;
} KernelFilm;
static_assert_align(KernelFilm, 16);
@@ -1348,6 +1345,8 @@ typedef struct KernelIntegrator {
int sampling_pattern;
int aa_samples;
int adaptive_min_samples;
+ int adaptive_step;
+ int adaptive_stop_per_sample;
float adaptive_threshold;
/* volume render */
@@ -1360,7 +1359,7 @@ typedef struct KernelIntegrator {
int max_closures;
- int pad1, pad2, pad3;
+ int pad1;
} KernelIntegrator;
static_assert_align(KernelIntegrator, 16);
diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp
index 22db8e875dc..2d89fb9ffba 100644
--- a/intern/cycles/render/buffers.cpp
+++ b/intern/cycles/render/buffers.cpp
@@ -165,35 +165,6 @@ bool RenderBuffers::copy_from_device()
return true;
}
-static const float *get_sample_count_pass(const vector<Pass> &passes, device_vector<float> &buffer)
-{
- int sample_offset = 0;
-
- for (const Pass &pass : passes) {
- if (pass.type != PASS_SAMPLE_COUNT) {
- sample_offset += pass.components;
- }
- else {
- return buffer.data() + sample_offset;
- }
- }
-
- return NULL;
-}
-
-static float get_pixel_pass_scale(const float rcp_sample,
- const float *sample_count,
- const int i,
- const int pass_stride)
-{
- if (sample_count) {
- return 1.0f / fabsf(sample_count[i * pass_stride]);
- }
- else {
- return rcp_sample;
- }
-}
-
bool RenderBuffers::get_denoising_pass_rect(
int type, float exposure, int sample, int components, float *pixels)
{
@@ -289,7 +260,22 @@ bool RenderBuffers::get_pass_rect(
return false;
}
- const float *sample_count = get_sample_count_pass(params.passes, buffer);
+ float *sample_count = NULL;
+ if (name == "Combined") {
+ int sample_offset = 0;
+ for (size_t j = 0; j < params.passes.size(); j++) {
+ Pass &pass = params.passes[j];
+ if (pass.type != PASS_SAMPLE_COUNT) {
+ sample_offset += pass.components;
+ continue;
+ }
+ else {
+ sample_count = buffer.data() + sample_offset;
+ break;
+ }
+ }
+ }
+
int pass_offset = 0;
for (size_t j = 0; j < params.passes.size(); j++) {
@@ -307,8 +293,8 @@ bool RenderBuffers::get_pass_rect(
float *in = buffer.data() + pass_offset;
int pass_stride = params.get_passes_size();
- const float rcp_sample = 1.0f / (float)sample;
- const float pass_exposure = (pass.exposure) ? exposure : 1.0f;
+ float scale = (pass.filter) ? 1.0f / (float)sample : 1.0f;
+ float scale_exposure = (pass.exposure) ? scale * exposure : scale;
int size = params.width * params.height;
@@ -326,36 +312,28 @@ bool RenderBuffers::get_pass_rect(
if (type == PASS_DEPTH) {
for (int i = 0; i < size; i++, in += pass_stride, pixels++) {
float f = *in;
- pixels[0] = (f == 0.0f) ? 1e10f : f;
- }
- }
- else if (type == PASS_OBJECT_ID || type == PASS_MATERIAL_ID) {
- for (int i = 0; i < size; i++, in += pass_stride, pixels++) {
- pixels[0] = *in;
+ pixels[0] = (f == 0.0f) ? 1e10f : f * scale_exposure;
}
}
else if (type == PASS_MIST) {
for (int i = 0; i < size; i++, in += pass_stride, pixels++) {
- const float scale = get_pixel_pass_scale(rcp_sample, sample_count, i, pass_stride);
- const float f = *in;
- pixels[0] = saturate(f * scale);
+ float f = *in;
+ pixels[0] = saturate(f * scale_exposure);
}
}
#ifdef WITH_CYCLES_DEBUG
else if (type == PASS_BVH_TRAVERSED_NODES || type == PASS_BVH_TRAVERSED_INSTANCES ||
type == PASS_BVH_INTERSECTIONS || type == PASS_RAY_BOUNCES) {
for (int i = 0; i < size; i++, in += pass_stride, pixels++) {
- const float scale = get_pixel_pass_scale(rcp_sample, sample_count, i, pass_stride);
- const float f = *in;
+ float f = *in;
pixels[0] = f * scale;
}
}
#endif
else {
for (int i = 0; i < size; i++, in += pass_stride, pixels++) {
- const float scale = get_pixel_pass_scale(rcp_sample, sample_count, i, pass_stride);
- const float f = *in;
- pixels[0] = f * scale * pass_exposure;
+ float f = *in;
+ pixels[0] = f * scale_exposure;
}
}
}
@@ -389,7 +367,7 @@ bool RenderBuffers::get_pass_rect(
float3 f = make_float3(in[0], in[1], in[2]);
float3 f_divide = make_float3(in_divide[0], in_divide[1], in_divide[2]);
- f = safe_divide_even_color(f * pass_exposure, f_divide);
+ f = safe_divide_even_color(f * exposure, f_divide);
pixels[0] = f.x;
pixels[1] = f.y;
@@ -399,9 +377,7 @@ bool RenderBuffers::get_pass_rect(
else {
/* RGB/vector */
for (int i = 0; i < size; i++, in += pass_stride, pixels += 3) {
- const float scale = get_pixel_pass_scale(rcp_sample, sample_count, i, pass_stride);
- const float scale_exposure = scale * pass_exposure;
- const float3 f = make_float3(in[0], in[1], in[2]);
+ float3 f = make_float3(in[0], in[1], in[2]);
pixels[0] = f.x * scale_exposure;
pixels[1] = f.y * scale_exposure;
@@ -449,9 +425,7 @@ bool RenderBuffers::get_pass_rect(
}
else if (type == PASS_CRYPTOMATTE) {
for (int i = 0; i < size; i++, in += pass_stride, pixels += 4) {
- const float scale = get_pixel_pass_scale(rcp_sample, sample_count, i, pass_stride);
- const float4 f = make_float4(in[0], in[1], in[2], in[3]);
-
+ float4 f = make_float4(in[0], in[1], in[2], in[3]);
/* x and z contain integer IDs, don't rescale them.
y and w contain matte weights, they get scaled. */
pixels[0] = f.x;
@@ -462,9 +436,12 @@ bool RenderBuffers::get_pass_rect(
}
else {
for (int i = 0; i < size; i++, in += pass_stride, pixels += 4) {
- const float scale = get_pixel_pass_scale(rcp_sample, sample_count, i, pass_stride);
- const float scale_exposure = scale * pass_exposure;
- const float4 f = make_float4(in[0], in[1], in[2], in[3]);
+ if (sample_count && sample_count[i * pass_stride] < 0.0f) {
+ scale = (pass.filter) ? -1.0f / (sample_count[i * pass_stride]) : 1.0f;
+ scale_exposure = (pass.exposure) ? scale * exposure : scale;
+ }
+
+ float4 f = make_float4(in[0], in[1], in[2], in[3]);
pixels[0] = f.x * scale_exposure;
pixels[1] = f.y * scale_exposure;
diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp
index c29810d1494..26eda93fadd 100644
--- a/intern/cycles/render/film.cpp
+++ b/intern/cycles/render/film.cpp
@@ -76,6 +76,7 @@ void Pass::add(PassType type, vector<Pass> &passes, const char *name)
Pass pass;
pass.type = type;
+ pass.filter = true;
pass.exposure = false;
pass.divide_type = PASS_NONE;
if (name) {
@@ -92,6 +93,7 @@ void Pass::add(PassType type, vector<Pass> &passes, const char *name)
break;
case PASS_DEPTH:
pass.components = 1;
+ pass.filter = false;
break;
case PASS_MIST:
pass.components = 1;
@@ -112,6 +114,7 @@ void Pass::add(PassType type, vector<Pass> &passes, const char *name)
case PASS_OBJECT_ID:
case PASS_MATERIAL_ID:
pass.components = 1;
+ pass.filter = false;
break;
case PASS_EMISSION:
@@ -359,8 +362,10 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
kfilm->light_pass_flag = 0;
kfilm->pass_stride = 0;
kfilm->use_light_pass = use_light_visibility;
+ kfilm->pass_aov_value_num = 0;
+ kfilm->pass_aov_color_num = 0;
- bool have_cryptomatte = false, have_aov_color = false, have_aov_value = false;
+ bool have_cryptomatte = false;
for (size_t i = 0; i < passes.size(); i++) {
Pass &pass = passes[i];
@@ -495,16 +500,16 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
kfilm->pass_sample_count = kfilm->pass_stride;
break;
case PASS_AOV_COLOR:
- if (!have_aov_color) {
+ if (kfilm->pass_aov_color_num == 0) {
kfilm->pass_aov_color = kfilm->pass_stride;
- have_aov_color = true;
}
+ kfilm->pass_aov_color_num++;
break;
case PASS_AOV_VALUE:
- if (!have_aov_value) {
+ if (kfilm->pass_aov_value_num == 0) {
kfilm->pass_aov_value = kfilm->pass_stride;
- have_aov_value = true;
}
+ kfilm->pass_aov_value_num++;
break;
default:
assert(false);
diff --git a/intern/cycles/render/film.h b/intern/cycles/render/film.h
index 0fe4fe89d5e..aae8fb404b0 100644
--- a/intern/cycles/render/film.h
+++ b/intern/cycles/render/film.h
@@ -42,6 +42,7 @@ class Pass {
public:
PassType type;
int components;
+ bool filter;
bool exposure;
PassType divide_type;
string name;
diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp
index 67ed1176171..75050b66bf2 100644
--- a/intern/cycles/render/image.cpp
+++ b/intern/cycles/render/image.cpp
@@ -283,6 +283,7 @@ void ImageManager::set_osl_texture_system(void *texture_system)
bool ImageManager::set_animation_frame_update(int frame)
{
if (frame != animation_frame) {
+ thread_scoped_lock device_lock(images_mutex);
animation_frame = frame;
for (size_t slot = 0; slot < images.size(); slot++) {
@@ -377,7 +378,7 @@ int ImageManager::add_image_slot(ImageLoader *loader,
Image *img;
size_t slot;
- thread_scoped_lock device_lock(device_mutex);
+ thread_scoped_lock device_lock(images_mutex);
/* Fnd existing image. */
for (slot = 0; slot < images.size(); slot++) {
@@ -418,6 +419,7 @@ int ImageManager::add_image_slot(ImageLoader *loader,
void ImageManager::add_image_user(int slot)
{
+ thread_scoped_lock device_lock(images_mutex);
Image *image = images[slot];
assert(image && image->users >= 1);
@@ -426,6 +428,7 @@ void ImageManager::add_image_user(int slot)
void ImageManager::remove_image_user(int slot)
{
+ thread_scoped_lock device_lock(images_mutex);
Image *image = images[slot];
assert(image && image->users >= 1);
diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h
index 00ab12afd7a..2000582ce70 100644
--- a/intern/cycles/render/image.h
+++ b/intern/cycles/render/image.h
@@ -206,6 +206,7 @@ class ImageManager {
bool has_half_images;
thread_mutex device_mutex;
+ thread_mutex images_mutex;
int animation_frame;
vector<Image *> images;
diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/render/integrator.cpp
index 2f9d088899e..d4beb06e57b 100644
--- a/intern/cycles/render/integrator.cpp
+++ b/intern/cycles/render/integrator.cpp
@@ -190,6 +190,13 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
else {
kintegrator->adaptive_min_samples = max(4, adaptive_min_samples);
}
+
+ kintegrator->adaptive_step = 4;
+ kintegrator->adaptive_stop_per_sample = device->info.has_adaptive_stop_per_sample;
+
+ /* Adaptive step must be a power of two for bitwise operations to work. */
+ assert((kintegrator->adaptive_step & (kintegrator->adaptive_step - 1)) == 0);
+
if (aa_samples > 0 && adaptive_threshold == 0.0f) {
kintegrator->adaptive_threshold = max(0.001f, 1.0f / (float)aa_samples);
VLOG(1) << "Cycles adaptive sampling: automatic threshold = "
diff --git a/intern/cycles/render/mesh_volume.cpp b/intern/cycles/render/mesh_volume.cpp
index d73ba3b06dd..74b8fc9e5ba 100644
--- a/intern/cycles/render/mesh_volume.cpp
+++ b/intern/cycles/render/mesh_volume.cpp
@@ -25,18 +25,18 @@
CCL_NAMESPACE_BEGIN
-static size_t compute_voxel_index(const int3 &resolution, size_t x, size_t y, size_t z)
+const int64_t VOXEL_INDEX_NONE = -1;
+
+static int64_t compute_voxel_index(const int3 &resolution, int64_t x, int64_t y, int64_t z)
{
- if (x == -1 || x >= resolution.x) {
- return -1;
+ if (x < 0 || x >= resolution.x) {
+ return VOXEL_INDEX_NONE;
}
-
- if (y == -1 || y >= resolution.y) {
- return -1;
+ else if (y < 0 || y >= resolution.y) {
+ return VOXEL_INDEX_NONE;
}
-
- if (z == -1 || z >= resolution.z) {
- return -1;
+ else if (z < 0 || z >= resolution.z) {
+ return VOXEL_INDEX_NONE;
}
return x + y * resolution.x + z * resolution.x * resolution.y;
@@ -184,15 +184,15 @@ VolumeMeshBuilder::VolumeMeshBuilder(VolumeParams *volume_params)
params = volume_params;
number_of_nodes = 0;
- const size_t x = divide_up(params->resolution.x, CUBE_SIZE);
- const size_t y = divide_up(params->resolution.y, CUBE_SIZE);
- const size_t z = divide_up(params->resolution.z, CUBE_SIZE);
+ const int64_t x = divide_up(params->resolution.x, CUBE_SIZE);
+ const int64_t y = divide_up(params->resolution.y, CUBE_SIZE);
+ const int64_t z = divide_up(params->resolution.z, CUBE_SIZE);
/* Adding 2*pad_size since we pad in both positive and negative directions
* along the axis. */
- const size_t px = divide_up(params->resolution.x + 2 * params->pad_size, CUBE_SIZE);
- const size_t py = divide_up(params->resolution.y + 2 * params->pad_size, CUBE_SIZE);
- const size_t pz = divide_up(params->resolution.z + 2 * params->pad_size, CUBE_SIZE);
+ const int64_t px = divide_up(params->resolution.x + 2 * params->pad_size, CUBE_SIZE);
+ const int64_t py = divide_up(params->resolution.y + 2 * params->pad_size, CUBE_SIZE);
+ const int64_t pz = divide_up(params->resolution.z + 2 * params->pad_size, CUBE_SIZE);
res = make_int3(px, py, pz);
pad_offset = make_int3(px - x, py - y, pz - z);
@@ -209,7 +209,10 @@ void VolumeMeshBuilder::add_node(int x, int y, int z)
assert((index_x >= 0) && (index_y >= 0) && (index_z >= 0));
- const size_t index = compute_voxel_index(res, index_x, index_y, index_z);
+ const int64_t index = compute_voxel_index(res, index_x, index_y, index_z);
+ if (index == VOXEL_INDEX_NONE) {
+ return;
+ }
/* We already have a node here. */
if (grid[index] == 1) {
@@ -256,7 +259,7 @@ void VolumeMeshBuilder::generate_vertices_and_quads(vector<ccl::int3> &vertices_
for (int z = 0; z < res.z; ++z) {
for (int y = 0; y < res.y; ++y) {
for (int x = 0; x < res.x; ++x) {
- size_t voxel_index = compute_voxel_index(res, x, y, z);
+ int64_t voxel_index = compute_voxel_index(res, x, y, z);
if (grid[voxel_index] == 0) {
continue;
}
@@ -285,32 +288,32 @@ void VolumeMeshBuilder::generate_vertices_and_quads(vector<ccl::int3> &vertices_
*/
voxel_index = compute_voxel_index(res, x - 1, y, z);
- if (voxel_index == -1 || grid[voxel_index] == 0) {
+ if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, res, used_verts, QUAD_X_MIN);
}
voxel_index = compute_voxel_index(res, x + 1, y, z);
- if (voxel_index == -1 || grid[voxel_index] == 0) {
+ if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, res, used_verts, QUAD_X_MAX);
}
voxel_index = compute_voxel_index(res, x, y - 1, z);
- if (voxel_index == -1 || grid[voxel_index] == 0) {
+ if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Y_MIN);
}
voxel_index = compute_voxel_index(res, x, y + 1, z);
- if (voxel_index == -1 || grid[voxel_index] == 0) {
+ if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Y_MAX);
}
voxel_index = compute_voxel_index(res, x, y, z - 1);
- if (voxel_index == -1 || grid[voxel_index] == 0) {
+ if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Z_MIN);
}
voxel_index = compute_voxel_index(res, x, y, z + 1);
- if (voxel_index == -1 || grid[voxel_index] == 0) {
+ if (voxel_index == VOXEL_INDEX_NONE || grid[voxel_index] == 0) {
create_quad(corners, vertices_is, quads, res, used_verts, QUAD_Z_MAX);
}
}
@@ -455,7 +458,7 @@ void GeometryManager::create_volume_mesh(Mesh *mesh, Progress &progress)
for (int z = 0; z < resolution.z; ++z) {
for (int y = 0; y < resolution.y; ++y) {
for (int x = 0; x < resolution.x; ++x) {
- size_t voxel_index = compute_voxel_index(resolution, x, y, z);
+ int64_t voxel_index = compute_voxel_index(resolution, x, y, z);
for (size_t i = 0; i < voxel_grids.size(); ++i) {
const VoxelAttributeGrid &voxel_grid = voxel_grids[i];
diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp
index b1b30979b0e..58bcc7ccdfb 100644
--- a/intern/cycles/render/session.cpp
+++ b/intern/cycles/render/session.cpp
@@ -1110,6 +1110,7 @@ void Session::render(bool with_denoising)
task.adaptive_sampling.use = (scene->integrator->sampling_pattern == SAMPLING_PATTERN_PMJ) &&
scene->dscene.data.film.pass_adaptive_aux_buffer;
task.adaptive_sampling.min_samples = scene->dscene.data.integrator.adaptive_min_samples;
+ task.adaptive_sampling.adaptive_step = scene->dscene.data.integrator.adaptive_step;
/* Acquire render tiles by default. */
task.tile_types = RenderTile::PATH_TRACE;
diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h
index 5b45510a39f..75527d50c6a 100644
--- a/intern/ghost/GHOST_C-api.h
+++ b/intern/ghost/GHOST_C-api.h
@@ -762,13 +762,6 @@ extern unsigned int GHOST_GetDefaultOpenGLFramebuffer(GHOST_WindowHandle windwHa
extern void GHOST_SetTabletAPI(GHOST_SystemHandle systemhandle, GHOST_TTabletAPI api);
/**
- * Returns the status of the tablet
- * \param windowhandle The handle to the window
- * \return Status of tablet
- */
-extern const GHOST_TabletData *GHOST_GetTabletData(GHOST_WindowHandle windowhandle);
-
-/**
* Access to rectangle width.
* \param rectanglehandle The handle to the rectangle
* \return width of the rectangle
diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h
index c456f2b1dba..daf07b81e01 100644
--- a/intern/ghost/GHOST_IWindow.h
+++ b/intern/ghost/GHOST_IWindow.h
@@ -243,16 +243,6 @@ class GHOST_IWindow {
virtual bool isDialog() const = 0;
- /**
- * Returns the tablet data (pressure etc).
- * \return The tablet data (pressure etc).
- */
- virtual const GHOST_TabletData &GetTabletData()
- {
- /* Default state when no tablet is used, for systems without tablet support. */
- return GHOST_TABLET_DATA_DEFAULT;
- }
-
/***************************************************************************************
* Progress bar functionality
***************************************************************************************/
diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h
index eb844c9e574..b8de31df6c6 100644
--- a/intern/ghost/GHOST_Types.h
+++ b/intern/ghost/GHOST_Types.h
@@ -117,8 +117,8 @@ typedef struct GHOST_TabletData {
float Ytilt; /* as above */
} GHOST_TabletData;
-static const GHOST_TabletData GHOST_TABLET_DATA_DEFAULT = {
- GHOST_kTabletModeNone, /* No tablet connected. */
+static const GHOST_TabletData GHOST_TABLET_DATA_NONE = {
+ GHOST_kTabletModeNone, /* No cursor in range */
1.0f, /* Pressure */
0.0f, /* Xtilt */
0.0f}; /* Ytilt */
diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp
index 6ee1557122d..db9fd2e3bf9 100644
--- a/intern/ghost/intern/GHOST_C-api.cpp
+++ b/intern/ghost/intern/GHOST_C-api.cpp
@@ -717,11 +717,6 @@ void GHOST_SetTabletAPI(GHOST_SystemHandle systemhandle, GHOST_TTabletAPI api)
system->setTabletAPI(api);
}
-const GHOST_TabletData *GHOST_GetTabletData(GHOST_WindowHandle windowhandle)
-{
- return &((GHOST_IWindow *)windowhandle)->GetTabletData();
-}
-
GHOST_TInt32 GHOST_GetWidthRectangle(GHOST_RectangleHandle rectanglehandle)
{
return ((GHOST_Rect *)rectanglehandle)->getWidth();
diff --git a/intern/ghost/intern/GHOST_EventButton.h b/intern/ghost/intern/GHOST_EventButton.h
index e854bc23e5a..4247ae150a4 100644
--- a/intern/ghost/intern/GHOST_EventButton.h
+++ b/intern/ghost/intern/GHOST_EventButton.h
@@ -37,17 +37,19 @@ class GHOST_EventButton : public GHOST_Event {
* Constructor.
* \param time The time this event was generated.
* \param type The type of this event.
- * \param window: The window of this event.
- * \param button: The state of the buttons were at the time of the event.
+ * \param window The window of this event.
+ * \param button The state of the buttons were at the time of the event.
+ * \param tablet The tablet data associated with this event.
*/
GHOST_EventButton(GHOST_TUns64 time,
GHOST_TEventType type,
GHOST_IWindow *window,
- GHOST_TButtonMask button)
+ GHOST_TButtonMask button,
+ const GHOST_TabletData &tablet)
: GHOST_Event(time, type, window)
{
m_buttonEventData.button = button;
- m_buttonEventData.tablet = window->GetTabletData();
+ m_buttonEventData.tablet = tablet;
m_data = &m_buttonEventData;
}
diff --git a/intern/ghost/intern/GHOST_EventCursor.h b/intern/ghost/intern/GHOST_EventCursor.h
index 41597db216a..8ba657fd9fa 100644
--- a/intern/ghost/intern/GHOST_EventCursor.h
+++ b/intern/ghost/intern/GHOST_EventCursor.h
@@ -38,17 +38,19 @@ class GHOST_EventCursor : public GHOST_Event {
* \param type The type of this event.
* \param x The x-coordinate of the location the cursor was at the time of the event.
* \param y The y-coordinate of the location the cursor was at the time of the event.
+ * \param tablet The tablet data associated with this event.
*/
GHOST_EventCursor(GHOST_TUns64 msec,
GHOST_TEventType type,
GHOST_IWindow *window,
GHOST_TInt32 x,
- GHOST_TInt32 y)
+ GHOST_TInt32 y,
+ const GHOST_TabletData &tablet)
: GHOST_Event(msec, type, window)
{
m_cursorEventData.x = x;
m_cursorEventData.y = y;
- m_cursorEventData.tablet = window->GetTabletData();
+ m_cursorEventData.tablet = tablet;
m_data = &m_cursorEventData;
}
diff --git a/intern/ghost/intern/GHOST_System.h b/intern/ghost/intern/GHOST_System.h
index eaaa2ff6ee6..c404fe21e41 100644
--- a/intern/ghost/intern/GHOST_System.h
+++ b/intern/ghost/intern/GHOST_System.h
@@ -238,7 +238,7 @@ class GHOST_System : public GHOST_ISystem {
* Set which tablet API to use. Only affects Windows, other platforms have a single API.
* \param api Enum indicating which API to use.
*/
- void setTabletAPI(GHOST_TTabletAPI api);
+ virtual void setTabletAPI(GHOST_TTabletAPI api);
GHOST_TTabletAPI getTabletAPI(void);
#ifdef WITH_INPUT_NDOF
diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm
index 9becff40995..3d6d187587c 100644
--- a/intern/ghost/intern/GHOST_SystemCocoa.mm
+++ b/intern/ghost/intern/GHOST_SystemCocoa.mm
@@ -817,7 +817,8 @@ GHOST_TSuccess GHOST_SystemCocoa::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32
CGAssociateMouseAndMouseCursorPosition(true);
// Force mouse move event (not pushed by Cocoa)
- pushEvent(new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, x, y));
+ pushEvent(new GHOST_EventCursor(
+ getMilliSeconds(), GHOST_kEventCursorMove, window, x, y, window->GetCocoaTabletData()));
m_outsideLoopEventProcessed = true;
return GHOST_kSuccess;
@@ -1098,8 +1099,11 @@ GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType,
pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
// Mouse up event is trapped by the resizing event loop,
// so send it anyway to the window manager.
- pushEvent(new GHOST_EventButton(
- getMilliSeconds(), GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft));
+ pushEvent(new GHOST_EventButton(getMilliSeconds(),
+ GHOST_kEventButtonUp,
+ window,
+ GHOST_kButtonMaskLeft,
+ GHOST_TABLET_DATA_NONE));
// m_ignoreWindowSizedMessages = true;
}
break;
@@ -1437,7 +1441,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleTabletEvent(void *eventPtr, short eventT
case NSEventTypeTabletProximity:
/* Reset tablet data when device enters proximity or leaves. */
- ct = GHOST_TABLET_DATA_DEFAULT;
+ ct = GHOST_TABLET_DATA_NONE;
if ([event isEnteringProximity]) {
/* Pointer is entering tablet area proximity. */
switch ([event pointingDeviceType]) {
@@ -1504,38 +1508,52 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
switch ([event type]) {
case NSEventTypeLeftMouseDown:
handleTabletEvent(event); // Update window tablet state to be included in event.
- pushEvent(new GHOST_EventButton(
- [event timestamp] * 1000, GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft));
+ pushEvent(new GHOST_EventButton([event timestamp] * 1000,
+ GHOST_kEventButtonDown,
+ window,
+ GHOST_kButtonMaskLeft,
+ window -> GetCocoaTabletData()));
break;
case NSEventTypeRightMouseDown:
handleTabletEvent(event); // Update window tablet state to be included in event.
- pushEvent(new GHOST_EventButton(
- [event timestamp] * 1000, GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight));
+ pushEvent(new GHOST_EventButton([event timestamp] * 1000,
+ GHOST_kEventButtonDown,
+ window,
+ GHOST_kButtonMaskRight,
+ window -> GetCocoaTabletData()));
break;
case NSEventTypeOtherMouseDown:
handleTabletEvent(event); // Handle tablet events combined with mouse events
pushEvent(new GHOST_EventButton([event timestamp] * 1000,
GHOST_kEventButtonDown,
window,
- convertButton([event buttonNumber])));
+ convertButton([event buttonNumber]),
+ window -> GetCocoaTabletData()));
break;
case NSEventTypeLeftMouseUp:
handleTabletEvent(event); // Update window tablet state to be included in event.
- pushEvent(new GHOST_EventButton(
- [event timestamp] * 1000, GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft));
+ pushEvent(new GHOST_EventButton([event timestamp] * 1000,
+ GHOST_kEventButtonUp,
+ window,
+ GHOST_kButtonMaskLeft,
+ window -> GetCocoaTabletData()));
break;
case NSEventTypeRightMouseUp:
handleTabletEvent(event); // Update window tablet state to be included in event.
- pushEvent(new GHOST_EventButton(
- [event timestamp] * 1000, GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight));
+ pushEvent(new GHOST_EventButton([event timestamp] * 1000,
+ GHOST_kEventButtonUp,
+ window,
+ GHOST_kButtonMaskRight,
+ window -> GetCocoaTabletData()));
break;
case NSEventTypeOtherMouseUp:
handleTabletEvent(event); // Update window tablet state to be included in event.
pushEvent(new GHOST_EventButton([event timestamp] * 1000,
GHOST_kEventButtonUp,
window,
- convertButton([event buttonNumber])));
+ convertButton([event buttonNumber]),
+ window -> GetCocoaTabletData()));
break;
case NSEventTypeLeftMouseDragged:
@@ -1568,8 +1586,12 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
window->setCursorGrabAccum(x_accum, y_accum);
window->clientToScreenIntern(x_warp + x_accum, y_warp + y_accum, x, y);
- pushEvent(new GHOST_EventCursor(
- [event timestamp] * 1000, GHOST_kEventCursorMove, window, x, y));
+ pushEvent(new GHOST_EventCursor([event timestamp] * 1000,
+ GHOST_kEventCursorMove,
+ window,
+ x,
+ y,
+ window -> GetCocoaTabletData()));
break;
}
case GHOST_kGrabWrap: // Wrap cursor at area/window boundaries
@@ -1614,8 +1636,12 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
// Generate event
GHOST_TInt32 x, y;
window->clientToScreenIntern(x_mouse + x_accum, y_mouse + y_accum, x, y);
- pushEvent(new GHOST_EventCursor(
- [event timestamp] * 1000, GHOST_kEventCursorMove, window, x, y));
+ pushEvent(new GHOST_EventCursor([event timestamp] * 1000,
+ GHOST_kEventCursorMove,
+ window,
+ x,
+ y,
+ window -> GetCocoaTabletData()));
break;
}
default: {
@@ -1624,8 +1650,12 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
GHOST_TInt32 x, y;
window->clientToScreenIntern(mousePos.x, mousePos.y, x, y);
- pushEvent(new GHOST_EventCursor(
- [event timestamp] * 1000, GHOST_kEventCursorMove, window, x, y));
+ pushEvent(new GHOST_EventCursor([event timestamp] * 1000,
+ GHOST_kEventCursorMove,
+ window,
+ x,
+ y,
+ window -> GetCocoaTabletData()));
break;
}
}
diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp
index 5dae0ce504b..97a75d7a0f5 100644
--- a/intern/ghost/intern/GHOST_SystemSDL.cpp
+++ b/intern/ghost/intern/GHOST_SystemSDL.cpp
@@ -390,22 +390,31 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
SDL_WarpMouseInWindow(sdl_win, x_new - x_win, y_new - y_win);
}
- g_event = new GHOST_EventCursor(
- getMilliSeconds(), GHOST_kEventCursorMove, window, x_new, y_new);
+ g_event = new GHOST_EventCursor(getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ window,
+ x_new,
+ y_new,
+ GHOST_TABLET_DATA_NONE);
}
else {
g_event = new GHOST_EventCursor(getMilliSeconds(),
GHOST_kEventCursorMove,
window,
x_root + x_accum,
- y_root + y_accum);
+ y_root + y_accum,
+ GHOST_TABLET_DATA_NONE);
}
}
else
#endif
{
- g_event = new GHOST_EventCursor(
- getMilliSeconds(), GHOST_kEventCursorMove, window, x_root, y_root);
+ g_event = new GHOST_EventCursor(getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ window,
+ x_root,
+ y_root,
+ GHOST_TABLET_DATA_NONE);
}
break;
}
@@ -435,7 +444,8 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
else
break;
- g_event = new GHOST_EventButton(getMilliSeconds(), type, window, gbmask);
+ g_event = new GHOST_EventButton(
+ getMilliSeconds(), type, window, gbmask, GHOST_TABLET_DATA_NONE);
break;
}
case SDL_MOUSEWHEEL: {
diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp
index 9149d81e32e..b91792938d5 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.cpp
+++ b/intern/ghost/intern/GHOST_SystemWin32.cpp
@@ -115,12 +115,22 @@
# define WM_DPICHANGED 0x02E0
#endif // WM_DPICHANGED
+// WM_POINTER API messages minimum Windows 7
+#ifndef WM_POINTERENTER
+# define WM_POINTERENTER 0x0249
+#endif // WM_POINTERENTER
+#ifndef WM_POINTERDOWN
+# define WM_POINTERDOWN 0x0246
+#endif // WM_POINTERDOWN
#ifndef WM_POINTERUPDATE
# define WM_POINTERUPDATE 0x0245
#endif // WM_POINTERUPDATE
-
-#define WM_POINTERDOWN 0x0246
-#define WM_POINTERUP 0x0247
+#ifndef WM_POINTERUP
+# define WM_POINTERUP 0x0247
+#endif // WM_POINTERUP
+#ifndef WM_POINTERLEAVE
+# define WM_POINTERLEAVE 0x024A
+#endif // WM_POINTERLEAVE
/* Workaround for some laptop touchpads, some of which seems to
* have driver issues which makes it so window function receives
@@ -184,7 +194,8 @@ typedef enum MONITOR_DPI_TYPE {
typedef HRESULT(API *GHOST_WIN32_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS);
typedef BOOL(API *GHOST_WIN32_EnableNonClientDpiScaling)(HWND);
-GHOST_SystemWin32::GHOST_SystemWin32() : m_hasPerformanceCounter(false), m_freq(0), m_start(0)
+GHOST_SystemWin32::GHOST_SystemWin32()
+ : m_hasPerformanceCounter(false), m_freq(0), m_start(0), m_lfstart(0)
{
m_displayManager = new GHOST_DisplayManagerWin32();
GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::GHOST_SystemWin32(): m_displayManager==0\n");
@@ -223,22 +234,32 @@ GHOST_SystemWin32::~GHOST_SystemWin32()
toggleConsole(1);
}
+GHOST_TUns64 GHOST_SystemWin32::performanceCounterToMillis(__int64 perf_ticks) const
+{
+ // Calculate the time passed since system initialization.
+ __int64 delta = (perf_ticks - m_start) * 1000;
+
+ GHOST_TUns64 t = (GHOST_TUns64)(delta / m_freq);
+ return t;
+}
+
+GHOST_TUns64 GHOST_SystemWin32::tickCountToMillis(__int64 ticks) const
+{
+ return ticks - m_lfstart;
+}
+
GHOST_TUns64 GHOST_SystemWin32::getMilliSeconds() const
{
// Hardware does not support high resolution timers. We will use GetTickCount instead then.
if (!m_hasPerformanceCounter) {
- return ::GetTickCount();
+ return tickCountToMillis(::GetTickCount());
}
// Retrieve current count
__int64 count = 0;
::QueryPerformanceCounter((LARGE_INTEGER *)&count);
- // Calculate the time passed since system initialization.
- __int64 delta = 1000 * (count - m_start);
-
- GHOST_TUns64 t = (GHOST_TUns64)(delta / m_freq);
- return t;
+ return performanceCounterToMillis(count);
}
GHOST_TUns8 GHOST_SystemWin32::getNumDisplays() const
@@ -570,6 +591,7 @@ GHOST_TSuccess GHOST_SystemWin32::init()
FreeLibrary(user32);
initRawInput();
+ m_lfstart = ::GetTickCount();
// Determine whether this system has a high frequency performance counter. */
m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER *)&m_freq) == TRUE;
if (m_hasPerformanceCounter) {
@@ -908,73 +930,213 @@ GHOST_EventButton *GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type,
GHOST_TButtonMask mask)
{
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
- if (window->useTabletAPI(GHOST_kTabletNative)) {
- window->setTabletData(NULL);
+
+ if (type == GHOST_kEventButtonDown) {
+ window->updateMouseCapture(MousePressed);
+ }
+ else if (type == GHOST_kEventButtonUp) {
+ window->updateMouseCapture(MouseReleased);
}
- return new GHOST_EventButton(system->getMilliSeconds(), type, window, mask);
+
+ /* Check for active Wintab mouse emulation in addition to a tablet in range because a proximity
+ * leave event might have fired before the Windows mouse up event, thus there are still tablet
+ * events to grab. The described behavior was observed in a Wacom Bamboo CTE-450.
+ */
+ if (window->m_tabletInRange || window->wintabSysButPressed()) {
+ if (window->useTabletAPI(GHOST_kTabletWintab) && processWintabEvents(type, window)) {
+ // Wintab processing only handles in-contact events.
+ return NULL;
+ }
+ else if (window->useTabletAPI(GHOST_kTabletNative)) {
+ // Win32 Pointer processing handles input while in-range and in-contact events.
+ return NULL;
+ }
+
+ // If using Wintab and this was a button down event but no button event was queued while
+ // processing Wintab packets, fall through to create a button event.
+ }
+
+ return new GHOST_EventButton(
+ system->getMilliSeconds(), type, window, mask, GHOST_TABLET_DATA_NONE);
}
-GHOST_Event *GHOST_SystemWin32::processPointerEvent(GHOST_TEventType type,
- GHOST_WindowWin32 *window,
- WPARAM wParam,
- LPARAM lParam,
- bool &eventHandled)
+GHOST_TSuccess GHOST_SystemWin32::processWintabEvents(GHOST_TEventType type,
+ GHOST_WindowWin32 *window)
{
- GHOST_PointerInfoWin32 pointerInfo;
+ GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
+
+ /* Only process Wintab packets if we can correlate them to a Window's mouse button event. When a
+ * button event associated to a mouse button by Wintab occurs outside of WM_*BUTTON events,
+ * there's no way to tell if other simultaneously pressed non-mouse mapped buttons are associated
+ * to a modifier key (shift, alt, ctrl) or a system event (scroll, etc.) and thus it is not
+ * possible to determine if a mouse click event should occur.
+ */
+ if (!window->getMousePressed() && !window->wintabSysButPressed()) {
+ return GHOST_kFailure;
+ }
+
+ std::vector<GHOST_WintabInfoWin32> wintabInfo;
+ if (!window->getWintabInfo(wintabInfo)) {
+ return GHOST_kFailure;
+ }
+
+ auto wtiIter = wintabInfo.begin();
+
+ /* We only process events that correlate to a mouse button events, so there may exist Wintab
+ * button down events that were instead mapped to e.g. scroll still in the queue. We need to
+ * skip those and find the last button down mapped to mouse buttons.
+ */
+ if (!window->wintabSysButPressed()) {
+ for (auto it = wtiIter; it != wintabInfo.end(); it++) {
+ if (it->type == GHOST_kEventButtonDown) {
+ wtiIter = it;
+ }
+ }
+ }
+
+ for (; wtiIter != wintabInfo.end(); wtiIter++) {
+ auto info = *wtiIter;
+
+ switch (info.type) {
+ case GHOST_kEventButtonDown: {
+ /* While changing windows with a tablet, Window's mouse button events normally occur before
+ * tablet proximity events, so a button up event can't be differentiated as occurring from
+ * a Wintab tablet or a normal mouse and a Ghost button event will always be generated.
+ *
+ * If we were called during a button down event create a ghost button down event, otherwise
+ * don't duplicate the prior button down as it interrupts drawing immediately after
+ * changing a window.
+ */
+ if (type == GHOST_kEventButtonDown) {
+ // Move cursor to point of contact because GHOST_EventButton does not include position.
+ system->pushEvent(new GHOST_EventCursor(
+ info.time, GHOST_kEventCursorMove, window, info.x, info.y, info.tabletData));
+ system->pushEvent(
+ new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData));
+ }
+ window->updateWintabSysBut(MousePressed);
+ break;
+ }
+ case GHOST_kEventCursorMove:
+ system->pushEvent(new GHOST_EventCursor(
+ info.time, GHOST_kEventCursorMove, window, info.x, info.y, info.tabletData));
+ break;
+ case GHOST_kEventButtonUp:
+ system->pushEvent(
+ new GHOST_EventButton(info.time, info.type, window, info.button, info.tabletData));
+ window->updateWintabSysBut(MouseReleased);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return GHOST_kSuccess;
+}
+
+void GHOST_SystemWin32::processPointerEvents(
+ UINT type, GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam, bool &eventHandled)
+{
+ std::vector<GHOST_PointerInfoWin32> pointerInfo;
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
if (!window->useTabletAPI(GHOST_kTabletNative)) {
- return NULL;
+ return;
}
- if (window->getPointerInfo(&pointerInfo, wParam, lParam) != GHOST_kSuccess) {
- return NULL;
+ if (window->getPointerInfo(pointerInfo, wParam, lParam) != GHOST_kSuccess) {
+ return;
}
- if (!pointerInfo.isPrimary) {
+ if (!pointerInfo[0].isPrimary) {
eventHandled = true;
- return NULL; // For multi-touch displays we ignore these events
+ return; // For multi-touch displays we ignore these events
}
- system->setCursorPosition(pointerInfo.pixelLocation.x, pointerInfo.pixelLocation.y);
-
switch (type) {
- case GHOST_kEventButtonDown:
- /* Update window tablet data to be included in event. */
- window->setTabletData(&pointerInfo.tabletData);
- eventHandled = true;
- return new GHOST_EventButton(
- system->getMilliSeconds(), GHOST_kEventButtonDown, window, pointerInfo.buttonMask);
- case GHOST_kEventButtonUp:
- eventHandled = true;
- return new GHOST_EventButton(
- system->getMilliSeconds(), GHOST_kEventButtonUp, window, pointerInfo.buttonMask);
- case GHOST_kEventCursorMove:
- /* Update window tablet data to be included in event. */
- window->setTabletData(&pointerInfo.tabletData);
- eventHandled = true;
- return new GHOST_EventCursor(system->getMilliSeconds(),
- GHOST_kEventCursorMove,
- window,
- pointerInfo.pixelLocation.x,
- pointerInfo.pixelLocation.y);
+ case WM_POINTERENTER:
+ window->m_tabletInRange = true;
+ system->pushEvent(new GHOST_EventCursor(pointerInfo[0].time,
+ GHOST_kEventCursorMove,
+ window,
+ pointerInfo[0].pixelLocation.x,
+ pointerInfo[0].pixelLocation.y,
+ pointerInfo[0].tabletData));
+ break;
+ case WM_POINTERDOWN:
+ // Move cursor to point of contact because GHOST_EventButton does not include position.
+ system->pushEvent(new GHOST_EventCursor(pointerInfo[0].time,
+ GHOST_kEventCursorMove,
+ window,
+ pointerInfo[0].pixelLocation.x,
+ pointerInfo[0].pixelLocation.y,
+ pointerInfo[0].tabletData));
+ system->pushEvent(new GHOST_EventButton(pointerInfo[0].time,
+ GHOST_kEventButtonDown,
+ window,
+ pointerInfo[0].buttonMask,
+ pointerInfo[0].tabletData));
+ window->updateMouseCapture(MousePressed);
+ break;
+ case WM_POINTERUPDATE:
+ // Coalesced pointer events are reverse chronological order, reorder chronologically.
+ // Only contiguous move events are coalesced.
+ for (GHOST_TUns32 i = pointerInfo.size(); i-- > 0;) {
+ system->pushEvent(new GHOST_EventCursor(pointerInfo[i].time,
+ GHOST_kEventCursorMove,
+ window,
+ pointerInfo[i].pixelLocation.x,
+ pointerInfo[i].pixelLocation.y,
+ pointerInfo[i].tabletData));
+ }
+ break;
+ case WM_POINTERUP:
+ system->pushEvent(new GHOST_EventButton(pointerInfo[0].time,
+ GHOST_kEventButtonUp,
+ window,
+ pointerInfo[0].buttonMask,
+ pointerInfo[0].tabletData));
+ window->updateMouseCapture(MouseReleased);
+ break;
+ case WM_POINTERLEAVE:
+ window->m_tabletInRange = false;
+ system->pushEvent(new GHOST_EventButton(pointerInfo[0].time,
+ GHOST_kEventCursorMove,
+ window,
+ pointerInfo[0].buttonMask,
+ pointerInfo[0].tabletData));
+ break;
default:
- return NULL;
+ break;
}
+
+ eventHandled = true;
+ system->setCursorPosition(pointerInfo[0].pixelLocation.x, pointerInfo[0].pixelLocation.y);
}
-GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type,
- GHOST_WindowWin32 *window)
+GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *window)
{
GHOST_TInt32 x_screen, y_screen;
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
- system->getCursorPosition(x_screen, y_screen);
+ if (window->m_tabletInRange || window->wintabSysButPressed()) {
+ if (window->useTabletAPI(GHOST_kTabletWintab) &&
+ processWintabEvents(GHOST_kEventCursorMove, window)) {
+ return NULL;
+ }
+ else if (window->useTabletAPI(GHOST_kTabletNative)) {
+ // Tablet input handled in WM_POINTER* events. WM_MOUSEMOVE events in response to tablet
+ // input aren't normally generated when using WM_POINTER events, but manually moving the
+ // system cursor as we do in WM_POINTER handling does.
+ return NULL;
+ }
+
+ // If using Wintab but no button event is currently active, fall through to default handling
+ }
- /* TODO: CHECK IF THIS IS A TABLET EVENT */
- bool is_tablet = false;
+ system->getCursorPosition(x_screen, y_screen);
- if (is_tablet == false && window->getCursorGrabModeIsWarp()) {
+ if (window->getCursorGrabModeIsWarp() && !window->m_tabletInRange) {
GHOST_TInt32 x_new = x_screen;
GHOST_TInt32 y_new = y_screen;
GHOST_TInt32 x_accum, y_accum;
@@ -1001,12 +1163,17 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type,
GHOST_kEventCursorMove,
window,
x_screen + x_accum,
- y_screen + y_accum);
+ y_screen + y_accum,
+ GHOST_TABLET_DATA_NONE);
}
}
else {
- return new GHOST_EventCursor(
- system->getMilliSeconds(), GHOST_kEventCursorMove, window, x_screen, y_screen);
+ return new GHOST_EventCursor(system->getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ window,
+ x_screen,
+ y_screen,
+ GHOST_TABLET_DATA_NONE);
}
return NULL;
}
@@ -1117,7 +1284,6 @@ GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type,
if (type == GHOST_kEventWindowActivate) {
system->getWindowManager()->setActiveWindow(window);
- window->bringTabletContextToFront();
}
return new GHOST_Event(system->getMilliSeconds(), type, window);
@@ -1145,6 +1311,19 @@ GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(GHOST_TEventType eventType,
system->getMilliSeconds(), eventType, draggedObjectType, window, mouseX, mouseY, data));
}
+void GHOST_SystemWin32::setTabletAPI(GHOST_TTabletAPI api)
+{
+ GHOST_System::setTabletAPI(api);
+
+ GHOST_WindowManager *wm = getWindowManager();
+ GHOST_WindowWin32 *activeWindow = (GHOST_WindowWin32 *)wm->getActiveWindow();
+
+ for (GHOST_IWindow *win : wm->getWindows()) {
+ GHOST_WindowWin32 *windowsWindow = (GHOST_WindowWin32 *)win;
+ windowsWindow->updateWintab(windowsWindow == activeWindow);
+ }
+}
+
void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO *minmax)
{
minmax->ptMinTrackSize.x = 320;
@@ -1380,52 +1559,40 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
}
break;
////////////////////////////////////////////////////////////////////////
- // Tablet events, processed
+ // Wintab events, processed
////////////////////////////////////////////////////////////////////////
- case WT_PACKET:
- window->processWin32TabletEvent(wParam, lParam);
+ case WT_INFOCHANGE: {
+ window->processWintabInfoChangeEvent(lParam);
break;
- case WT_CSRCHANGE:
- case WT_PROXIMITY:
- window->processWin32TabletInitEvent();
+ }
+ case WT_PROXIMITY: {
+ bool inRange = LOWORD(lParam);
+ window->processWintabProximityEvent(inRange);
break;
+ }
////////////////////////////////////////////////////////////////////////
// Pointer events, processed
////////////////////////////////////////////////////////////////////////
+ case WM_POINTERENTER:
case WM_POINTERDOWN:
- event = processPointerEvent(
- GHOST_kEventButtonDown, window, wParam, lParam, eventHandled);
- if (event && eventHandled) {
- window->registerMouseClickEvent(0);
- }
- break;
- case WM_POINTERUP:
- event = processPointerEvent(GHOST_kEventButtonUp, window, wParam, lParam, eventHandled);
- if (event && eventHandled) {
- window->registerMouseClickEvent(1);
- }
- break;
case WM_POINTERUPDATE:
- event = processPointerEvent(
- GHOST_kEventCursorMove, window, wParam, lParam, eventHandled);
+ case WM_POINTERUP:
+ case WM_POINTERLEAVE:
+ processPointerEvents(msg, window, wParam, lParam, eventHandled);
break;
////////////////////////////////////////////////////////////////////////
// Mouse events, processed
////////////////////////////////////////////////////////////////////////
case WM_LBUTTONDOWN:
- window->registerMouseClickEvent(0);
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft);
break;
case WM_MBUTTONDOWN:
- window->registerMouseClickEvent(0);
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle);
break;
case WM_RBUTTONDOWN:
- window->registerMouseClickEvent(0);
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight);
break;
case WM_XBUTTONDOWN:
- window->registerMouseClickEvent(0);
if ((short)HIWORD(wParam) == XBUTTON1) {
event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4);
}
@@ -1434,19 +1601,15 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
}
break;
case WM_LBUTTONUP:
- window->registerMouseClickEvent(1);
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft);
break;
case WM_MBUTTONUP:
- window->registerMouseClickEvent(1);
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle);
break;
case WM_RBUTTONUP:
- window->registerMouseClickEvent(1);
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight);
break;
case WM_XBUTTONUP:
- window->registerMouseClickEvent(1);
if ((short)HIWORD(wParam) == XBUTTON1) {
event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4);
}
@@ -1455,7 +1618,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
}
break;
case WM_MOUSEMOVE:
- event = processCursorEvent(GHOST_kEventCursorMove, window);
+ event = processCursorEvent(window);
break;
case WM_MOUSEWHEEL: {
/* The WM_MOUSEWHEEL message is sent to the focus window
@@ -1545,7 +1708,9 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
* will not be dispatched to OUR active window if we minimize one of OUR windows. */
if (LOWORD(wParam) == WA_INACTIVE)
window->lostMouseCapture();
- window->processWin32TabletActivateEvent(GET_WM_ACTIVATE_STATE(wParam, lParam));
+
+ window->updateWintab(LOWORD(wParam) != WA_INACTIVE);
+
lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
break;
}
@@ -1603,6 +1768,11 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
else {
event = processWindowEvent(GHOST_kEventWindowSize, window);
}
+
+ if (msg == WM_SIZE && wParam == SIZE_MINIMIZED) {
+ window->updateWintab(false);
+ }
+
break;
case WM_CAPTURECHANGED:
window->lostMouseCapture();
@@ -1653,6 +1823,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
SWP_NOZORDER | SWP_NOACTIVATE);
}
break;
+ case WM_DISPLAYCHANGE:
+ for (GHOST_IWindow *iter_win : system->getWindowManager()->getWindows()) {
+ GHOST_WindowWin32 *iter_win32win = (GHOST_WindowWin32 *)iter_win;
+ iter_win32win->processWintabDisplayChangeEvent();
+ }
+ break;
////////////////////////////////////////////////////////////////////////
// Window events, ignored
////////////////////////////////////////////////////////////////////////
diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h
index 27f23e00ae7..05221cbdc40 100644
--- a/intern/ghost/intern/GHOST_SystemWin32.h
+++ b/intern/ghost/intern/GHOST_SystemWin32.h
@@ -66,6 +66,20 @@ class GHOST_SystemWin32 : public GHOST_System {
***************************************************************************************/
/**
+ * This method converts performance counter measurements into milliseconds since the start of the
+ * system process.
+ * \return The number of milliseconds since the start of the system process.
+ */
+ GHOST_TUns64 performanceCounterToMillis(__int64 perf_ticks) const;
+
+ /**
+ * This method converts system ticks into milliseconds since the start of the
+ * system process.
+ * \return The number of milliseconds since the start of the system process.
+ */
+ GHOST_TUns64 tickCountToMillis(__int64 ticks) const;
+
+ /**
* Returns the system time.
* Returns the number of milliseconds since the start of the system process.
* This overloaded method uses the high frequency timer if available.
@@ -252,6 +266,16 @@ class GHOST_SystemWin32 : public GHOST_System {
int mouseY,
void *data);
+ /***************************************************************************************
+ ** Modify tablet API
+ ***************************************************************************************/
+
+ /**
+ * Set which tablet API to use.
+ * \param api Enum indicating which API to use.
+ */
+ void setTabletAPI(GHOST_TTabletAPI api) override;
+
protected:
/**
* Initializes the system.
@@ -296,27 +320,29 @@ class GHOST_SystemWin32 : public GHOST_System {
GHOST_TButtonMask mask);
/**
- * Creates pointer event.
- * \param type The type of event to create.
+ * Creates tablet events from Wintab events.
+ * \param type The type of pointer event
+ * \param window The window receiving the event (the active window).
+ */
+ static GHOST_TSuccess processWintabEvents(GHOST_TEventType type, GHOST_WindowWin32 *window);
+
+ /**
+ * Creates tablet events from pointer events.
+ * \param type The type of pointer event
* \param window The window receiving the event (the active window).
* \param wParam The wParam from the wndproc
* \param lParam The lParam from the wndproc
* \param eventhandled true if the method handled the event
- * \return The event created.
*/
- static GHOST_Event *processPointerEvent(GHOST_TEventType type,
- GHOST_WindowWin32 *window,
- WPARAM wParam,
- LPARAM lParam,
- bool &eventhandled);
+ static void processPointerEvents(
+ UINT type, GHOST_WindowWin32 *window, WPARAM wParam, LPARAM lParam, bool &eventhandled);
/**
* Creates cursor event.
- * \param type The type of event to create.
* \param window The window receiving the event (the active window).
* \return The event created.
*/
- static GHOST_EventCursor *processCursorEvent(GHOST_TEventType type, GHOST_WindowWin32 *window);
+ static GHOST_EventCursor *processCursorEvent(GHOST_WindowWin32 *window);
/**
* Handles a mouse wheel event.
@@ -426,6 +452,8 @@ class GHOST_SystemWin32 : public GHOST_System {
__int64 m_freq;
/** High frequency timer variable. */
__int64 m_start;
+ /** Low frequency timer variable. */
+ __int64 m_lfstart;
/** AltGr on current keyboard layout. */
bool m_hasAltGr;
/** language identifier. */
diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp
index f076fe4c94a..825b93de36f 100644
--- a/intern/ghost/intern/GHOST_SystemX11.cpp
+++ b/intern/ghost/intern/GHOST_SystemX11.cpp
@@ -960,11 +960,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
case MotionNotify: {
XMotionEvent &xme = xe->xmotion;
-#ifdef WITH_X11_XINPUT
bool is_tablet = window->GetTabletData().Active != GHOST_kTabletModeNone;
-#else
- bool is_tablet = false;
-#endif
if (is_tablet == false && window->getCursorGrabModeIsWarp()) {
GHOST_TInt32 x_new = xme.x_root;
@@ -1000,12 +996,17 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
GHOST_kEventCursorMove,
window,
xme.x_root + x_accum,
- xme.y_root + y_accum);
+ xme.y_root + y_accum,
+ window->GetTabletData());
}
}
else {
- g_event = new GHOST_EventCursor(
- getMilliSeconds(), GHOST_kEventCursorMove, window, xme.x_root, xme.y_root);
+ g_event = new GHOST_EventCursor(getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ window,
+ xme.x_root,
+ xme.y_root,
+ window->GetTabletData());
}
break;
}
@@ -1272,7 +1273,8 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
else
break;
- g_event = new GHOST_EventButton(getMilliSeconds(), type, window, gbmask);
+ g_event = new GHOST_EventButton(
+ getMilliSeconds(), type, window, gbmask, window->GetTabletData());
break;
}
@@ -1373,8 +1375,12 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
*/
XCrossingEvent &xce = xe->xcrossing;
if (xce.mode == NotifyNormal) {
- g_event = new GHOST_EventCursor(
- getMilliSeconds(), GHOST_kEventCursorMove, window, xce.x_root, xce.y_root);
+ g_event = new GHOST_EventCursor(getMilliSeconds(),
+ GHOST_kEventCursorMove,
+ window,
+ xce.x_root,
+ xce.y_root,
+ window->GetTabletData());
}
// printf("X: %s window %d\n",
@@ -1563,7 +1569,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
GHOST_TSuccess GHOST_SystemX11::getModifierKeys(GHOST_ModifierKeys &keys) const
{
- /* Analyze the masks retuned from XQueryPointer. */
+ /* Analyze the masks returned from XQueryPointer. */
memset((void *)m_keyboard_vector, 0, sizeof(m_keyboard_vector));
diff --git a/intern/ghost/intern/GHOST_WindowCocoa.h b/intern/ghost/intern/GHOST_WindowCocoa.h
index a49949c2c8d..efa418deee2 100644
--- a/intern/ghost/intern/GHOST_WindowCocoa.h
+++ b/intern/ghost/intern/GHOST_WindowCocoa.h
@@ -222,11 +222,6 @@ class GHOST_WindowCocoa : public GHOST_Window {
bool isDialog() const;
- const GHOST_TabletData &GetTabletData()
- {
- return m_tablet;
- }
-
GHOST_TabletData &GetCocoaTabletData()
{
return m_tablet;
diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm
index c90b49c27a5..10ab05a0de1 100644
--- a/intern/ghost/intern/GHOST_WindowCocoa.mm
+++ b/intern/ghost/intern/GHOST_WindowCocoa.mm
@@ -395,7 +395,7 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa,
setTitle(title);
- m_tablet = GHOST_TABLET_DATA_DEFAULT;
+ m_tablet = GHOST_TABLET_DATA_NONE;
CocoaWindowDelegate *windowDelegate = [[CocoaWindowDelegate alloc] init];
[windowDelegate setSystemAndWindowCocoa:systemCocoa windowCocoa:this];
diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp
index 6570f27ac5a..d4fe7af8861 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.cpp
+++ b/intern/ghost/intern/GHOST_WindowWin32.cpp
@@ -72,6 +72,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
bool is_debug,
bool dialog)
: GHOST_Window(width, height, state, wantStereoVisual, false),
+ m_tabletInRange(false),
m_inLiveResize(false),
m_system(system),
m_hDC(0),
@@ -80,18 +81,15 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
m_nPressedButtons(0),
m_customCursor(0),
m_wantAlphaBackground(alphaBackground),
+ m_wintab(),
m_normal_state(GHOST_kWindowStateNormal),
m_user32(NULL),
- m_fpGetPointerInfo(NULL),
- m_fpGetPointerPenInfo(NULL),
- m_fpGetPointerTouchInfo(NULL),
+ m_fpGetPointerInfoHistory(NULL),
+ m_fpGetPointerPenInfoHistory(NULL),
+ m_fpGetPointerTouchInfoHistory(NULL),
m_parentWindowHwnd(parentwindow ? parentwindow->m_hWnd : NULL),
m_debug_context(is_debug)
{
- // Initialize tablet variables
- memset(&m_wintab, 0, sizeof(m_wintab));
- m_tabletData = GHOST_TABLET_DATA_DEFAULT;
-
// Create window
if (state != GHOST_kWindowStateFullScreen) {
RECT rect;
@@ -288,73 +286,33 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
// Initialize Windows Ink
if (m_user32) {
- m_fpGetPointerInfo = (GHOST_WIN32_GetPointerInfo)::GetProcAddress(m_user32, "GetPointerInfo");
- m_fpGetPointerPenInfo = (GHOST_WIN32_GetPointerPenInfo)::GetProcAddress(m_user32,
- "GetPointerPenInfo");
- m_fpGetPointerTouchInfo = (GHOST_WIN32_GetPointerTouchInfo)::GetProcAddress(
- m_user32, "GetPointerTouchInfo");
+ m_fpGetPointerInfoHistory = (GHOST_WIN32_GetPointerInfoHistory)::GetProcAddress(
+ m_user32, "GetPointerInfoHistory");
+ m_fpGetPointerPenInfoHistory = (GHOST_WIN32_GetPointerPenInfoHistory)::GetProcAddress(
+ m_user32, "GetPointerPenInfoHistory");
+ m_fpGetPointerTouchInfoHistory = (GHOST_WIN32_GetPointerTouchInfoHistory)::GetProcAddress(
+ m_user32, "GetPointerTouchInfoHistory");
+ }
+
+ if ((m_wintab.handle = ::LoadLibrary("Wintab32.dll")) &&
+ (m_wintab.info = (GHOST_WIN32_WTInfo)::GetProcAddress(m_wintab.handle, "WTInfoA")) &&
+ (m_wintab.open = (GHOST_WIN32_WTOpen)::GetProcAddress(m_wintab.handle, "WTOpenA")) &&
+ (m_wintab.get = (GHOST_WIN32_WTGet)::GetProcAddress(m_wintab.handle, "WTGetA")) &&
+ (m_wintab.set = (GHOST_WIN32_WTSet)::GetProcAddress(m_wintab.handle, "WTSetA")) &&
+ (m_wintab.close = (GHOST_WIN32_WTClose)::GetProcAddress(m_wintab.handle, "WTClose")) &&
+ (m_wintab.packetsGet = (GHOST_WIN32_WTPacketsGet)::GetProcAddress(m_wintab.handle,
+ "WTPacketsGet")) &&
+ (m_wintab.queueSizeGet = (GHOST_WIN32_WTQueueSizeGet)::GetProcAddress(m_wintab.handle,
+ "WTQueueSizeGet")) &&
+ (m_wintab.queueSizeSet = (GHOST_WIN32_WTQueueSizeSet)::GetProcAddress(m_wintab.handle,
+ "WTQueueSizeSet")) &&
+ (m_wintab.enable = (GHOST_WIN32_WTEnable)::GetProcAddress(m_wintab.handle, "WTEnable")) &&
+ (m_wintab.overlap = (GHOST_WIN32_WTOverlap)::GetProcAddress(m_wintab.handle, "WTOverlap"))) {
+ initializeWintab();
+ // Determine which tablet API to use and enable it.
+ updateWintab(true);
}
- // Initialize Wintab
- m_wintab.handle = ::LoadLibrary("Wintab32.dll");
- if (m_wintab.handle) {
- // Get API functions
- m_wintab.info = (GHOST_WIN32_WTInfo)::GetProcAddress(m_wintab.handle, "WTInfoA");
- m_wintab.open = (GHOST_WIN32_WTOpen)::GetProcAddress(m_wintab.handle, "WTOpenA");
- m_wintab.close = (GHOST_WIN32_WTClose)::GetProcAddress(m_wintab.handle, "WTClose");
- m_wintab.packet = (GHOST_WIN32_WTPacket)::GetProcAddress(m_wintab.handle, "WTPacket");
- m_wintab.enable = (GHOST_WIN32_WTEnable)::GetProcAddress(m_wintab.handle, "WTEnable");
- m_wintab.overlap = (GHOST_WIN32_WTOverlap)::GetProcAddress(m_wintab.handle, "WTOverlap");
-
- // Let's see if we can initialize tablet here.
- // Check if WinTab available by getting system context info.
- LOGCONTEXT lc = {0};
- lc.lcOptions |= CXO_SYSTEM;
- if (m_wintab.open && m_wintab.info && m_wintab.info(WTI_DEFSYSCTX, 0, &lc)) {
- // Now init the tablet
- /* The maximum tablet size, pressure and orientation (tilt) */
- AXIS TabletX, TabletY, Pressure, Orientation[3];
-
- // Open a Wintab context
-
- // Open the context
- lc.lcPktData = PACKETDATA;
- lc.lcPktMode = PACKETMODE;
- lc.lcOptions |= CXO_MESSAGES;
- lc.lcMoveMask = PACKETDATA;
-
- /* Set the entire tablet as active */
- m_wintab.info(WTI_DEVICES, DVC_X, &TabletX);
- m_wintab.info(WTI_DEVICES, DVC_Y, &TabletY);
-
- /* get the max pressure, to divide into a float */
- BOOL pressureSupport = m_wintab.info(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
- if (pressureSupport)
- m_wintab.maxPressure = Pressure.axMax;
- else
- m_wintab.maxPressure = 0;
-
- /* get the max tilt axes, to divide into floats */
- BOOL tiltSupport = m_wintab.info(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
- if (tiltSupport) {
- /* does the tablet support azimuth ([0]) and altitude ([1]) */
- if (Orientation[0].axResolution && Orientation[1].axResolution) {
- /* all this assumes the minimum is 0 */
- m_wintab.maxAzimuth = Orientation[0].axMax;
- m_wintab.maxAltitude = Orientation[1].axMax;
- }
- else { /* no so dont do tilt stuff */
- m_wintab.maxAzimuth = m_wintab.maxAltitude = 0;
- }
- }
-
- // The Wintab spec says we must open the context disabled if we are using cursor masks.
- m_wintab.tablet = m_wintab.open(m_hWnd, &lc, FALSE);
- if (m_wintab.enable && m_wintab.tablet) {
- m_wintab.enable(m_wintab.tablet, TRUE);
- }
- }
- }
CoCreateInstance(
CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_ITaskbarList3, (LPVOID *)&m_Bar);
}
@@ -368,8 +326,8 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
}
if (m_wintab.handle) {
- if (m_wintab.close && m_wintab.tablet) {
- m_wintab.close(m_wintab.tablet);
+ if (m_wintab.close && m_wintab.context) {
+ m_wintab.close(m_wintab.context);
}
FreeLibrary(m_wintab.handle);
@@ -379,9 +337,9 @@ GHOST_WindowWin32::~GHOST_WindowWin32()
if (m_user32) {
FreeLibrary(m_user32);
m_user32 = NULL;
- m_fpGetPointerInfo = NULL;
- m_fpGetPointerPenInfo = NULL;
- m_fpGetPointerTouchInfo = NULL;
+ m_fpGetPointerInfoHistory = NULL;
+ m_fpGetPointerPenInfoHistory = NULL;
+ m_fpGetPointerTouchInfoHistory = NULL;
}
if (m_customCursor) {
@@ -785,21 +743,20 @@ bool GHOST_WindowWin32::isDialog() const
return (parent != 0) && (style & WS_POPUPWINDOW);
}
-void GHOST_WindowWin32::registerMouseClickEvent(int press)
+void GHOST_WindowWin32::updateMouseCapture(GHOST_MouseCaptureEventWin32 event)
{
-
- switch (press) {
- case 0:
+ switch (event) {
+ case MousePressed:
m_nPressedButtons++;
break;
- case 1:
+ case MouseReleased:
if (m_nPressedButtons)
m_nPressedButtons--;
break;
- case 2:
+ case OperatorGrab:
m_hasGrabMouse = true;
break;
- case 3:
+ case OperatorUngrab:
m_hasGrabMouse = false;
break;
}
@@ -814,6 +771,32 @@ void GHOST_WindowWin32::registerMouseClickEvent(int press)
}
}
+bool GHOST_WindowWin32::getMousePressed() const
+{
+ return m_nPressedButtons;
+}
+
+bool GHOST_WindowWin32::wintabSysButPressed() const
+{
+ return m_wintab.numSysButtons;
+}
+
+void GHOST_WindowWin32::updateWintabSysBut(GHOST_MouseCaptureEventWin32 event)
+{
+ switch (event) {
+ case MousePressed:
+ m_wintab.numSysButtons++;
+ break;
+ case MouseReleased:
+ if (m_wintab.numSysButtons)
+ m_wintab.numSysButtons--;
+ break;
+ case OperatorGrab:
+ case OperatorUngrab:
+ break;
+ }
+}
+
HCURSOR GHOST_WindowWin32::getStandardCursor(GHOST_TStandardCursor shape) const
{
// Convert GHOST cursor to Windows OEM cursor
@@ -977,7 +960,7 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode
if (mode == GHOST_kGrabHide)
setWindowCursorVisibility(false);
}
- registerMouseClickEvent(2);
+ updateMouseCapture(OperatorGrab);
}
else {
if (m_cursorGrab == GHOST_kGrabHide) {
@@ -997,7 +980,7 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCursorGrab(GHOST_TGrabCursorMode mode
* can be incorrect on exit. */
setCursorGrabAccum(0, 0);
m_cursorGrabBounds.m_l = m_cursorGrabBounds.m_r = -1; /* disable */
- registerMouseClickEvent(3);
+ updateMouseCapture(OperatorUngrab);
}
return GHOST_kSuccess;
@@ -1017,106 +1000,200 @@ GHOST_TSuccess GHOST_WindowWin32::hasCursorShape(GHOST_TStandardCursor cursorSha
return (getStandardCursor(cursorShape)) ? GHOST_kSuccess : GHOST_kFailure;
}
-GHOST_TSuccess GHOST_WindowWin32::getPointerInfo(GHOST_PointerInfoWin32 *pointerInfo,
- WPARAM wParam,
- LPARAM lParam)
+void GHOST_WindowWin32::updateWintab(bool active)
{
- ZeroMemory(pointerInfo, sizeof(GHOST_PointerInfoWin32));
+ if (m_wintab.enable && m_wintab.overlap && m_wintab.context) {
+ bool useWintab = useTabletAPI(GHOST_kTabletWintab);
+ bool enable = active && useWintab;
+
+ // Disabling context while the Window is not minimized can cause issues on receiving Wintab
+ // input while changing a window for some drivers, so only disable if either Wintab had been
+ // disabled or the window is minimized.
+ m_wintab.enable(m_wintab.context, useWintab && !::IsIconic(m_hWnd));
+ m_wintab.overlap(m_wintab.context, enable);
+
+ if (!enable) {
+ // WT_PROXIMITY event doesn't occur unless tablet's cursor leaves the proximity while the
+ // window is active.
+ m_tabletInRange = false;
+ m_wintab.numSysButtons = 0;
+ m_wintab.sysButtonsPressed = 0;
+ }
+ }
+}
+
+void GHOST_WindowWin32::initializeWintab()
+{
+ // return if wintab library handle doesn't exist or wintab is already initialized
+ if (!m_wintab.handle || m_wintab.context) {
+ return;
+ }
+
+ // Let's see if we can initialize tablet here.
+ // Check if WinTab available by getting system context info.
+ LOGCONTEXT lc = {0};
+ if (m_wintab.open && m_wintab.info && m_wintab.queueSizeGet && m_wintab.queueSizeSet &&
+ m_wintab.info(WTI_DEFSYSCTX, 0, &lc)) {
+ // Now init the tablet
+ /* The pressure and orientation (tilt) */
+ AXIS Pressure, Orientation[3];
+
+ // Open a Wintab context
- // Obtain the basic information from the event
- pointerInfo->pointerId = GET_POINTERID_WPARAM(wParam);
- pointerInfo->isInContact = IS_POINTER_INCONTACT_WPARAM(wParam);
- pointerInfo->isPrimary = IS_POINTER_PRIMARY_WPARAM(wParam);
+ // Open the context
+ lc.lcPktData = PACKETDATA;
+ lc.lcPktMode = PACKETMODE;
+ lc.lcMoveMask = PACKETDATA;
+ // Wacom maps y origin to the tablet's bottom
+ // Invert to match Windows y origin mapping to the screen top
+ lc.lcOutExtY = -lc.lcOutExtY;
- // Obtain more accurate and predicted information from the Pointer API
- POINTER_INFO pointerApiInfo;
- if (!(m_fpGetPointerInfo && m_fpGetPointerInfo(pointerInfo->pointerId, &pointerApiInfo))) {
+ m_wintab.info(WTI_INTERFACE, IFC_NDEVICES, &m_wintab.numDevices);
+
+ /* get the max pressure, to divide into a float */
+ BOOL pressureSupport = m_wintab.info(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
+ m_wintab.maxPressure = pressureSupport ? Pressure.axMax : 0;
+
+ /* get the max tilt axes, to divide into floats */
+ BOOL tiltSupport = m_wintab.info(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
+ /* does the tablet support azimuth ([0]) and altitude ([1]) */
+ if (tiltSupport && Orientation[0].axResolution && Orientation[1].axResolution) {
+ /* all this assumes the minimum is 0 */
+ m_wintab.maxAzimuth = Orientation[0].axMax;
+ m_wintab.maxAltitude = Orientation[1].axMax;
+ }
+ else { /* no so dont do tilt stuff */
+ m_wintab.maxAzimuth = m_wintab.maxAltitude = 0;
+ }
+
+ // The Wintab spec says we must open the context disabled if we are using cursor masks.
+ m_wintab.context = m_wintab.open(m_hWnd, &lc, FALSE);
+
+ // Wintab provides no way to determine the maximum queue size aside from checking if attempts
+ // to change the queue size are successful.
+ const int maxQueue = 500;
+ int initialQueueSize = m_wintab.queueSizeGet(m_wintab.context);
+ int queueSize = initialQueueSize;
+
+ while (queueSize < maxQueue) {
+ int testSize = min(queueSize + initialQueueSize, maxQueue);
+ if (m_wintab.queueSizeSet(m_wintab.context, testSize)) {
+ queueSize = testSize;
+ }
+ else {
+ /* From Windows Wintab Documentation for WTQueueSizeSet:
+ * "If the return value is zero, the context has no queue because the function deletes the
+ * original queue before attempting to create a new one. The application must continue
+ * calling the function with a smaller queue size until the function returns a non - zero
+ * value."
+ *
+ * In our case we start with a known valid queue size and in the event of failure roll
+ * back to the last valid queue size.
+ */
+ m_wintab.queueSizeSet(m_wintab.context, queueSize);
+ break;
+ }
+ }
+ m_wintab.pkts.resize(queueSize);
+ }
+}
+
+GHOST_TSuccess GHOST_WindowWin32::getPointerInfo(
+ std::vector<GHOST_PointerInfoWin32> &outPointerInfo, WPARAM wParam, LPARAM lParam)
+{
+ if (!useTabletAPI(GHOST_kTabletNative)) {
return GHOST_kFailure;
}
- pointerInfo->hasButtonMask = GHOST_kSuccess;
- switch (pointerApiInfo.ButtonChangeType) {
- case POINTER_CHANGE_FIRSTBUTTON_DOWN:
- case POINTER_CHANGE_FIRSTBUTTON_UP:
- pointerInfo->buttonMask = GHOST_kButtonMaskLeft;
- break;
- case POINTER_CHANGE_SECONDBUTTON_DOWN:
- case POINTER_CHANGE_SECONDBUTTON_UP:
- pointerInfo->buttonMask = GHOST_kButtonMaskRight;
- break;
- case POINTER_CHANGE_THIRDBUTTON_DOWN:
- case POINTER_CHANGE_THIRDBUTTON_UP:
- pointerInfo->buttonMask = GHOST_kButtonMaskMiddle;
- break;
- case POINTER_CHANGE_FOURTHBUTTON_DOWN:
- case POINTER_CHANGE_FOURTHBUTTON_UP:
- pointerInfo->buttonMask = GHOST_kButtonMaskButton4;
- break;
- case POINTER_CHANGE_FIFTHBUTTON_DOWN:
- case POINTER_CHANGE_FIFTHBUTTON_UP:
- pointerInfo->buttonMask = GHOST_kButtonMaskButton5;
- break;
- default:
- pointerInfo->hasButtonMask = GHOST_kFailure;
- break;
+ GHOST_TInt32 pointerId = GET_POINTERID_WPARAM(wParam);
+ GHOST_TInt32 isPrimary = IS_POINTER_PRIMARY_WPARAM(wParam);
+ GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem();
+ GHOST_TUns32 outCount;
+
+ if (!(m_fpGetPointerInfoHistory && m_fpGetPointerInfoHistory(pointerId, &outCount, NULL))) {
+ return GHOST_kFailure;
}
- pointerInfo->pixelLocation = pointerApiInfo.ptPixelLocation;
- pointerInfo->tabletData.Active = GHOST_kTabletModeNone;
- pointerInfo->tabletData.Pressure = 1.0f;
- pointerInfo->tabletData.Xtilt = 0.0f;
- pointerInfo->tabletData.Ytilt = 0.0f;
+ auto pointerPenInfo = std::vector<POINTER_PEN_INFO>(outCount);
+ outPointerInfo.resize(outCount);
- if (pointerApiInfo.pointerType != PT_PEN) {
+ if (!(m_fpGetPointerPenInfoHistory &&
+ m_fpGetPointerPenInfoHistory(pointerId, &outCount, pointerPenInfo.data()))) {
return GHOST_kFailure;
}
- POINTER_PEN_INFO pointerPenInfo;
- if (m_fpGetPointerPenInfo && m_fpGetPointerPenInfo(pointerInfo->pointerId, &pointerPenInfo)) {
- pointerInfo->tabletData.Active = GHOST_kTabletModeStylus;
+ for (GHOST_TUns32 i = 0; i < outCount; i++) {
+ POINTER_INFO pointerApiInfo = pointerPenInfo[i].pointerInfo;
+ // Obtain the basic information from the event
+ outPointerInfo[i].pointerId = pointerId;
+ outPointerInfo[i].isPrimary = isPrimary;
+
+ switch (pointerApiInfo.ButtonChangeType) {
+ case POINTER_CHANGE_FIRSTBUTTON_DOWN:
+ case POINTER_CHANGE_FIRSTBUTTON_UP:
+ outPointerInfo[i].buttonMask = GHOST_kButtonMaskLeft;
+ break;
+ case POINTER_CHANGE_SECONDBUTTON_DOWN:
+ case POINTER_CHANGE_SECONDBUTTON_UP:
+ outPointerInfo[i].buttonMask = GHOST_kButtonMaskRight;
+ break;
+ case POINTER_CHANGE_THIRDBUTTON_DOWN:
+ case POINTER_CHANGE_THIRDBUTTON_UP:
+ outPointerInfo[i].buttonMask = GHOST_kButtonMaskMiddle;
+ break;
+ case POINTER_CHANGE_FOURTHBUTTON_DOWN:
+ case POINTER_CHANGE_FOURTHBUTTON_UP:
+ outPointerInfo[i].buttonMask = GHOST_kButtonMaskButton4;
+ break;
+ case POINTER_CHANGE_FIFTHBUTTON_DOWN:
+ case POINTER_CHANGE_FIFTHBUTTON_UP:
+ outPointerInfo[i].buttonMask = GHOST_kButtonMaskButton5;
+ break;
+ default:
+ break;
+ }
+
+ outPointerInfo[i].pixelLocation = pointerApiInfo.ptPixelLocation;
+ outPointerInfo[i].tabletData.Active = GHOST_kTabletModeStylus;
+ outPointerInfo[i].tabletData.Pressure = 1.0f;
+ outPointerInfo[i].tabletData.Xtilt = 0.0f;
+ outPointerInfo[i].tabletData.Ytilt = 0.0f;
+ outPointerInfo[i].time = system->performanceCounterToMillis(pointerApiInfo.PerformanceCount);
- if (pointerPenInfo.penMask & PEN_MASK_PRESSURE) {
- pointerInfo->tabletData.Pressure = pointerPenInfo.pressure / 1024.0f;
+ if (pointerPenInfo[i].penMask & PEN_MASK_PRESSURE) {
+ outPointerInfo[i].tabletData.Pressure = pointerPenInfo[i].pressure / 1024.0f;
}
- if (pointerPenInfo.penFlags & PEN_FLAG_ERASER) {
- pointerInfo->tabletData.Active = GHOST_kTabletModeEraser;
+ if (pointerPenInfo[i].penFlags & PEN_FLAG_ERASER) {
+ outPointerInfo[i].tabletData.Active = GHOST_kTabletModeEraser;
}
- if (pointerPenInfo.penFlags & PEN_MASK_TILT_X) {
- pointerInfo->tabletData.Xtilt = fmin(fabs(pointerPenInfo.tiltX / 90), 1.0f);
+ if (pointerPenInfo[i].penMask & PEN_MASK_TILT_X) {
+ outPointerInfo[i].tabletData.Xtilt = fmin(fabs(pointerPenInfo[i].tiltX / 90.0f), 1.0f);
}
- if (pointerPenInfo.penFlags & PEN_MASK_TILT_Y) {
- pointerInfo->tabletData.Ytilt = fmin(fabs(pointerPenInfo.tiltY / 90), 1.0f);
+ if (pointerPenInfo[i].penMask & PEN_MASK_TILT_Y) {
+ outPointerInfo[i].tabletData.Ytilt = fmin(fabs(pointerPenInfo[i].tiltY / 90.0f), 1.0f);
}
}
return GHOST_kSuccess;
}
-void GHOST_WindowWin32::setTabletData(GHOST_TabletData *pTabletData)
+void GHOST_WindowWin32::processWintabDisplayChangeEvent()
{
- if (pTabletData) {
- m_tabletData = *pTabletData;
- }
- else {
- m_tabletData = GHOST_TABLET_DATA_DEFAULT;
- }
-}
+ LOGCONTEXT lc_sys = {0}, lc_curr = {0};
-void GHOST_WindowWin32::processWin32TabletActivateEvent(WORD state)
-{
- if (!useTabletAPI(GHOST_kTabletWintab)) {
- return;
- }
+ if (m_wintab.info && m_wintab.get && m_wintab.set && m_wintab.info(WTI_DEFSYSCTX, 0, &lc_sys)) {
- if (m_wintab.enable && m_wintab.tablet) {
- m_wintab.enable(m_wintab.tablet, state);
+ m_wintab.get(m_wintab.context, &lc_curr);
- if (m_wintab.overlap && state) {
- m_wintab.overlap(m_wintab.tablet, TRUE);
- }
+ lc_curr.lcOutOrgX = lc_sys.lcOutOrgX;
+ lc_curr.lcOutOrgY = lc_sys.lcOutOrgY;
+ lc_curr.lcOutExtX = lc_sys.lcOutExtX;
+ lc_curr.lcOutExtY = -lc_sys.lcOutExtY;
+
+ m_wintab.set(m_wintab.context, &lc_curr);
}
}
@@ -1126,7 +1203,7 @@ bool GHOST_WindowWin32::useTabletAPI(GHOST_TTabletAPI api) const
return true;
}
else if (m_system->getTabletAPI() == GHOST_kTabletAutomatic) {
- if (m_wintab.tablet)
+ if (m_wintab.numDevices)
return api == GHOST_kTabletWintab;
else
return api == GHOST_kTabletNative;
@@ -1136,115 +1213,180 @@ bool GHOST_WindowWin32::useTabletAPI(GHOST_TTabletAPI api) const
}
}
-void GHOST_WindowWin32::processWin32TabletInitEvent()
+void GHOST_WindowWin32::processWintabProximityEvent(bool inRange)
{
if (!useTabletAPI(GHOST_kTabletWintab)) {
return;
}
// Let's see if we can initialize tablet here
- if (m_wintab.info && m_wintab.tablet) {
+ if (m_wintab.info && m_wintab.context) {
AXIS Pressure, Orientation[3]; /* The maximum tablet size */
BOOL pressureSupport = m_wintab.info(WTI_DEVICES, DVC_NPRESSURE, &Pressure);
- if (pressureSupport)
- m_wintab.maxPressure = Pressure.axMax;
- else
- m_wintab.maxPressure = 0;
+ m_wintab.maxPressure = pressureSupport ? Pressure.axMax : 0;
BOOL tiltSupport = m_wintab.info(WTI_DEVICES, DVC_ORIENTATION, &Orientation);
- if (tiltSupport) {
- /* does the tablet support azimuth ([0]) and altitude ([1]) */
- if (Orientation[0].axResolution && Orientation[1].axResolution) {
- m_wintab.maxAzimuth = Orientation[0].axMax;
- m_wintab.maxAltitude = Orientation[1].axMax;
- }
- else { /* no so dont do tilt stuff */
- m_wintab.maxAzimuth = m_wintab.maxAltitude = 0;
- }
+ /* does the tablet support azimuth ([0]) and altitude ([1]) */
+ if (tiltSupport && Orientation[0].axResolution && Orientation[1].axResolution) {
+ m_wintab.maxAzimuth = Orientation[0].axMax;
+ m_wintab.maxAltitude = Orientation[1].axMax;
+ }
+ else { /* no so dont do tilt stuff */
+ m_wintab.maxAzimuth = m_wintab.maxAltitude = 0;
}
}
- m_tabletData.Active = GHOST_kTabletModeNone;
+ m_tabletInRange = inRange;
+}
+
+void GHOST_WindowWin32::processWintabInfoChangeEvent(LPARAM lParam)
+{
+ GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem();
+
+ // Update number of connected Wintab digitizers
+ if (LOWORD(lParam) == WTI_INTERFACE && HIWORD(lParam) == IFC_NDEVICES) {
+ m_wintab.info(WTI_INTERFACE, IFC_NDEVICES, &m_wintab.numDevices);
+ updateWintab((GHOST_WindowWin32 *)system->getWindowManager()->getActiveWindow() == this);
+ }
+}
+
+GHOST_TSuccess GHOST_WindowWin32::wintabMouseToGhost(UINT cursor,
+ DWORD physicalButton,
+ GHOST_TButtonMask &ghostButton)
+{
+ const DWORD numButtons = 32;
+ BYTE logicalButtons[numButtons] = {0};
+ BYTE systemButtons[numButtons] = {0};
+
+ m_wintab.info(WTI_CURSORS + cursor, CSR_BUTTONMAP, &logicalButtons);
+ m_wintab.info(WTI_CURSORS + cursor, CSR_SYSBTNMAP, &systemButtons);
+
+ if (physicalButton >= numButtons) {
+ return GHOST_kFailure;
+ }
+ BYTE lb = logicalButtons[physicalButton];
+
+ if (lb >= numButtons) {
+ return GHOST_kFailure;
+ }
+ switch (systemButtons[lb]) {
+ case SBN_LCLICK:
+ ghostButton = GHOST_kButtonMaskLeft;
+ return GHOST_kSuccess;
+ case SBN_RCLICK:
+ ghostButton = GHOST_kButtonMaskRight;
+ return GHOST_kSuccess;
+ case SBN_MCLICK:
+ ghostButton = GHOST_kButtonMaskMiddle;
+ return GHOST_kSuccess;
+ default:
+ return GHOST_kFailure;
+ }
}
-void GHOST_WindowWin32::processWin32TabletEvent(WPARAM wParam, LPARAM lParam)
+GHOST_TSuccess GHOST_WindowWin32::getWintabInfo(std::vector<GHOST_WintabInfoWin32> &outWintabInfo)
{
if (!useTabletAPI(GHOST_kTabletWintab)) {
- return;
+ return GHOST_kFailure;
}
- if (m_wintab.packet && m_wintab.tablet) {
- PACKET pkt;
- if (m_wintab.packet((HCTX)lParam, wParam, &pkt)) {
- switch (pkt.pkCursor % 3) { /* % 3 for multiple devices ("DualTrack") */
- case 0:
- m_tabletData.Active = GHOST_kTabletModeNone; /* puck - not yet supported */
- break;
- case 1:
- m_tabletData.Active = GHOST_kTabletModeStylus; /* stylus */
- break;
- case 2:
- m_tabletData.Active = GHOST_kTabletModeEraser; /* eraser */
- break;
- }
+ if (!(m_wintab.packetsGet && m_wintab.context)) {
+ return GHOST_kFailure;
+ }
- if (m_wintab.maxPressure > 0) {
- m_tabletData.Pressure = (float)pkt.pkNormalPressure / (float)m_wintab.maxPressure;
- }
- else {
- m_tabletData.Pressure = 1.0f;
- }
+ GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)GHOST_System::getSystem();
+
+ const int numPackets = m_wintab.packetsGet(
+ m_wintab.context, m_wintab.pkts.size(), m_wintab.pkts.data());
+ outWintabInfo.resize(numPackets);
+
+ for (int i = 0; i < numPackets; i++) {
+ PACKET pkt = m_wintab.pkts[i];
+ GHOST_TabletData tabletData = GHOST_TABLET_DATA_NONE;
+ switch (pkt.pkCursor % 3) { /* % 3 for multiple devices ("DualTrack") */
+ case 0:
+ tabletData.Active = GHOST_kTabletModeNone; /* puck - not yet supported */
+ break;
+ case 1:
+ tabletData.Active = GHOST_kTabletModeStylus; /* stylus */
+ break;
+ case 2:
+ tabletData.Active = GHOST_kTabletModeEraser; /* eraser */
+ break;
+ }
- if ((m_wintab.maxAzimuth > 0) && (m_wintab.maxAltitude > 0)) {
- ORIENTATION ort = pkt.pkOrientation;
- float vecLen;
- float altRad, azmRad; /* in radians */
+ if (m_wintab.maxPressure > 0) {
+ tabletData.Pressure = (float)pkt.pkNormalPressure / (float)m_wintab.maxPressure;
+ }
- /*
- * from the wintab spec:
- * orAzimuth Specifies the clockwise rotation of the
- * cursor about the z axis through a full circular range.
- *
- * orAltitude Specifies the angle with the x-y plane
- * through a signed, semicircular range. Positive values
- * specify an angle upward toward the positive z axis;
- * negative values specify an angle downward toward the negative z axis.
- *
- * wintab.h defines .orAltitude as a UINT but documents .orAltitude
- * as positive for upward angles and negative for downward angles.
- * WACOM uses negative altitude values to show that the pen is inverted;
- * therefore we cast .orAltitude as an (int) and then use the absolute value.
- */
+ if ((m_wintab.maxAzimuth > 0) && (m_wintab.maxAltitude > 0)) {
+ ORIENTATION ort = pkt.pkOrientation;
+ float vecLen;
+ float altRad, azmRad; /* in radians */
+
+ /*
+ * from the wintab spec:
+ * orAzimuth Specifies the clockwise rotation of the
+ * cursor about the z axis through a full circular range.
+ *
+ * orAltitude Specifies the angle with the x-y plane
+ * through a signed, semicircular range. Positive values
+ * specify an angle upward toward the positive z axis;
+ * negative values specify an angle downward toward the negative z axis.
+ *
+ * wintab.h defines .orAltitude as a UINT but documents .orAltitude
+ * as positive for upward angles and negative for downward angles.
+ * WACOM uses negative altitude values to show that the pen is inverted;
+ * therefore we cast .orAltitude as an (int) and then use the absolute value.
+ */
+
+ /* convert raw fixed point data to radians */
+ altRad = (float)((fabs((float)ort.orAltitude) / (float)m_wintab.maxAltitude) * M_PI / 2.0);
+ azmRad = (float)(((float)ort.orAzimuth / (float)m_wintab.maxAzimuth) * M_PI * 2.0);
+
+ /* find length of the stylus' projected vector on the XY plane */
+ vecLen = cos(altRad);
+
+ /* from there calculate X and Y components based on azimuth */
+ tabletData.Xtilt = sin(azmRad) * vecLen;
+ tabletData.Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen);
+ }
+
+ outWintabInfo[i].x = pkt.pkX;
+ outWintabInfo[i].y = pkt.pkY;
- /* convert raw fixed point data to radians */
- altRad = (float)((fabs((float)ort.orAltitude) / (float)m_wintab.maxAltitude) * M_PI / 2.0);
- azmRad = (float)(((float)ort.orAzimuth / (float)m_wintab.maxAzimuth) * M_PI * 2.0);
+ // Some Wintab libraries don't handle relative button input correctly, so we track button
+ // presses manually.
+ DWORD buttonsChanged = m_wintab.sysButtonsPressed ^ pkt.pkButtons;
- /* find length of the stylus' projected vector on the XY plane */
- vecLen = cos(altRad);
+ // Find the index for the changed button from the button map.
+ DWORD physicalButton = 0;
+ for (DWORD diff = (unsigned)buttonsChanged >> 1; diff > 0; diff = (unsigned)diff >> 1) {
+ physicalButton++;
+ }
- /* from there calculate X and Y components based on azimuth */
- m_tabletData.Xtilt = sin(azmRad) * vecLen;
- m_tabletData.Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen);
+ if (buttonsChanged &&
+ wintabMouseToGhost(pkt.pkCursor, physicalButton, outWintabInfo[i].button)) {
+ if (buttonsChanged & pkt.pkButtons) {
+ outWintabInfo[i].type = GHOST_kEventButtonDown;
}
else {
- m_tabletData.Xtilt = 0.0f;
- m_tabletData.Ytilt = 0.0f;
+ outWintabInfo[i].type = GHOST_kEventButtonUp;
}
}
- }
-}
+ else {
+ outWintabInfo[i].type = GHOST_kEventCursorMove;
+ }
-void GHOST_WindowWin32::bringTabletContextToFront()
-{
- if (!useTabletAPI(GHOST_kTabletWintab)) {
- return;
- }
+ m_wintab.sysButtonsPressed = pkt.pkButtons;
- if (m_wintab.overlap && m_wintab.tablet) {
- m_wintab.overlap(m_wintab.tablet, TRUE);
+ // Wintab does not support performance counters, so use low frequency counter instead
+ outWintabInfo[i].time = system->tickCountToMillis(pkt.pkTime);
+ outWintabInfo[i].tabletData = tabletData;
}
+
+ return GHOST_kSuccess;
}
GHOST_TUns16 GHOST_WindowWin32::getDPIHint()
diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h
index ac1ec0ee852..d4427f67e9b 100644
--- a/intern/ghost/intern/GHOST_WindowWin32.h
+++ b/intern/ghost/intern/GHOST_WindowWin32.h
@@ -36,8 +36,9 @@
#endif
#include <wintab.h>
-#define PACKETDATA (PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR)
-#define PACKETMODE PK_BUTTONS
+#define PACKETDATA \
+ (PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR | PK_X | PK_Y | PK_TIME)
+#define PACKETMODE 0
#include <pktdef.h>
class GHOST_SystemWin32;
@@ -45,9 +46,13 @@ class GHOST_DropTargetWin32;
// typedefs for WinTab functions to allow dynamic loading
typedef UINT(API *GHOST_WIN32_WTInfo)(UINT, UINT, LPVOID);
+typedef BOOL(API *GHOST_WIN32_WTGet)(HCTX, LPLOGCONTEXTA);
+typedef BOOL(API *GHOST_WIN32_WTSet)(HCTX, LPLOGCONTEXTA);
typedef HCTX(API *GHOST_WIN32_WTOpen)(HWND, LPLOGCONTEXTA, BOOL);
typedef BOOL(API *GHOST_WIN32_WTClose)(HCTX);
-typedef BOOL(API *GHOST_WIN32_WTPacket)(HCTX, UINT, LPVOID);
+typedef BOOL(API *GHOST_WIN32_WTPacketsGet)(HCTX, int, LPVOID);
+typedef int(API *GHOST_WIN32_WTQueueSizeGet)(HCTX);
+typedef BOOL(API *GHOST_WIN32_WTQueueSizeSet)(HCTX, int);
typedef BOOL(API *GHOST_WIN32_WTEnable)(HCTX, BOOL);
typedef BOOL(API *GHOST_WIN32_WTOverlap)(HCTX, BOOL);
@@ -88,6 +93,26 @@ typedef enum tagPOINTER_BUTTON_CHANGE_TYPE {
typedef DWORD POINTER_INPUT_TYPE;
typedef UINT32 POINTER_FLAGS;
+#define POINTER_FLAG_NONE 0x00000000
+#define POINTER_FLAG_NEW 0x00000001
+#define POINTER_FLAG_INRANGE 0x00000002
+#define POINTER_FLAG_INCONTACT 0x00000004
+#define POINTER_FLAG_FIRSTBUTTON 0x00000010
+#define POINTER_FLAG_SECONDBUTTON 0x00000020
+#define POINTER_FLAG_THIRDBUTTON 0x00000040
+#define POINTER_FLAG_FOURTHBUTTON 0x00000080
+#define POINTER_FLAG_FIFTHBUTTON 0x00000100
+#define POINTER_FLAG_PRIMARY 0x00002000
+#define POINTER_FLAG_CONFIDENCE 0x000004000
+#define POINTER_FLAG_CANCELED 0x000008000
+#define POINTER_FLAG_DOWN 0x00010000
+#define POINTER_FLAG_UPDATE 0x00020000
+#define POINTER_FLAG_UP 0x00040000
+#define POINTER_FLAG_WHEEL 0x00080000
+#define POINTER_FLAG_HWHEEL 0x00100000
+#define POINTER_FLAG_CAPTURECHANGED 0x00200000
+#define POINTER_FLAG_HASTRANSFORM 0x00400000
+
typedef struct tagPOINTER_INFO {
POINTER_INPUT_TYPE pointerType;
UINT32 pointerId;
@@ -192,20 +217,40 @@ typedef struct tagPOINTER_TOUCH_INFO {
#define IS_POINTER_CANCELED_WPARAM(wParam) \
IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_CANCELED)
-typedef BOOL(API *GHOST_WIN32_GetPointerInfo)(UINT32 pointerId, POINTER_INFO *pointerInfo);
-typedef BOOL(API *GHOST_WIN32_GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo);
-typedef BOOL(API *GHOST_WIN32_GetPointerTouchInfo)(UINT32 pointerId, POINTER_TOUCH_INFO *penInfo);
+typedef BOOL(WINAPI *GHOST_WIN32_GetPointerInfoHistory)(UINT32 pointerId,
+ UINT32 *entriesCount,
+ POINTER_INFO *pointerInfo);
+typedef BOOL(WINAPI *GHOST_WIN32_GetPointerPenInfoHistory)(UINT32 pointerId,
+ UINT32 *entriesCount,
+ POINTER_PEN_INFO *penInfo);
+typedef BOOL(WINAPI *GHOST_WIN32_GetPointerTouchInfoHistory)(UINT32 pointerId,
+ UINT32 *entriesCount,
+ POINTER_TOUCH_INFO *touchInfo);
struct GHOST_PointerInfoWin32 {
GHOST_TInt32 pointerId;
- GHOST_TInt32 isInContact;
GHOST_TInt32 isPrimary;
- GHOST_TSuccess hasButtonMask;
GHOST_TButtonMask buttonMask;
POINT pixelLocation;
+ GHOST_TUns64 time;
+ GHOST_TabletData tabletData;
+};
+
+struct GHOST_WintabInfoWin32 {
+ GHOST_TInt32 x, y;
+ GHOST_TEventType type;
+ GHOST_TButtonMask button;
+ GHOST_TUns64 time;
GHOST_TabletData tabletData;
};
+typedef enum {
+ MousePressed,
+ MouseReleased,
+ OperatorGrab,
+ OperatorUngrab
+} GHOST_MouseCaptureEventWin32;
+
/**
* GHOST window on M$ Windows OSs.
*/
@@ -364,17 +409,14 @@ class GHOST_WindowWin32 : public GHOST_Window {
GHOST_TSuccess endProgressBar();
/**
- * Register a mouse click event (should be called
+ * Register a mouse capture state (should be called
* for any real button press, controls mouse
* capturing).
*
- * \param press
- * 0 - mouse pressed
- * 1 - mouse released
- * 2 - operator grab
- * 3 - operator ungrab
+ * \param event Whether mouse was pressed and released, or an operator grabbed or ungrabbed the
+ * mouse
*/
- void registerMouseClickEvent(int press);
+ void updateMouseCapture(GHOST_MouseCaptureEventWin32 event);
/**
* Inform the window that it has lost mouse capture,
@@ -392,20 +434,50 @@ class GHOST_WindowWin32 : public GHOST_Window {
HCURSOR getStandardCursor(GHOST_TStandardCursor shape) const;
void loadCursor(bool visible, GHOST_TStandardCursor cursorShape) const;
- const GHOST_TabletData &GetTabletData()
- {
- return m_tabletData;
- }
+ /**
+ * Handle setup and switch between Wintab and Pointer APIs
+ * \param active Whether the window is or will be in an active state
+ */
+ void updateWintab(bool active);
- void setTabletData(GHOST_TabletData *tabletData);
+ /**
+ * Query whether given tablet API should be used.
+ * \param api Tablet API to test.
+ */
bool useTabletAPI(GHOST_TTabletAPI api) const;
- void getPointerInfo(WPARAM wParam);
- void processWin32PointerEvent(WPARAM wParam);
- void processWin32TabletActivateEvent(WORD state);
- void processWin32TabletInitEvent();
- void processWin32TabletEvent(WPARAM wParam, LPARAM lParam);
- void bringTabletContextToFront();
+ /**
+ * Translate WM_POINTER events into GHOST_PointerInfoWin32 structs.
+ * \param outPointerInfo Storage to return resulting GHOST_PointerInfoWin32 structs
+ * \param wParam WPARAM of the event
+ * \param lParam LPARAM of the event
+ */
+ GHOST_TSuccess getPointerInfo(std::vector<GHOST_PointerInfoWin32> &outPointerInfo,
+ WPARAM wParam,
+ LPARAM lParam);
+
+ /**
+ * Handle Wintab coordinate changes when DisplayChange events occur.
+ */
+ void processWintabDisplayChangeEvent();
+
+ /**
+ * Set tablet details when a cursor enters range
+ */
+ void processWintabProximityEvent(bool inRange);
+
+ /**
+ * Handle Wintab info changes such as change in number of connected tablets.
+ * \param lParam LPARAM of the event
+ */
+ void processWintabInfoChangeEvent(LPARAM lParam);
+
+ /**
+ * Translate Wintab packets into GHOST_WintabInfoWin32 structs.
+ * \param outWintabInfo Storage to return resulting GHOST_WintabInfoWin32 structs
+ * \return Success if able to read packets, even if there are none
+ */
+ GHOST_TSuccess getWintabInfo(std::vector<GHOST_WintabInfoWin32> &outWintabInfo);
GHOST_TSuccess beginFullScreen() const
{
@@ -419,7 +491,27 @@ class GHOST_WindowWin32 : public GHOST_Window {
GHOST_TUns16 getDPIHint() override;
- GHOST_TSuccess getPointerInfo(GHOST_PointerInfoWin32 *pointerInfo, WPARAM wParam, LPARAM lParam);
+ /**
+ * Get whether there are currently any mouse buttons pressed
+ * \return True if there are any currently pressed mouse buttons
+ */
+ bool getMousePressed() const;
+
+ /**
+ * Get if there are currently pressed Wintab buttons associated to a Windows mouse button press
+ * \return True if there are currently any pressed Wintab buttons associated to a Windows
+ * mouse button press
+ */
+ bool wintabSysButPressed() const;
+
+ /**
+ * Register a Wintab button has been associated to a Windows mouse button press
+ * \param event Whether the button was pressed or released
+ */
+ void updateWintabSysBut(GHOST_MouseCaptureEventWin32 event);
+
+ /** Whether a tablet stylus is being tracked */
+ bool m_tabletInRange;
/** if the window currently resizing */
bool m_inLiveResize;
@@ -501,35 +593,56 @@ class GHOST_WindowWin32 : public GHOST_Window {
static const wchar_t *s_windowClassName;
static const int s_maxTitleLength;
- /** Tablet data for GHOST */
- GHOST_TabletData m_tabletData;
-
/* Wintab API */
struct {
/** WinTab dll handle */
- HMODULE handle;
+ HMODULE handle = NULL;
/** API functions */
- GHOST_WIN32_WTInfo info;
- GHOST_WIN32_WTOpen open;
- GHOST_WIN32_WTClose close;
- GHOST_WIN32_WTPacket packet;
- GHOST_WIN32_WTEnable enable;
- GHOST_WIN32_WTOverlap overlap;
+ GHOST_WIN32_WTInfo info = NULL;
+ GHOST_WIN32_WTGet get = NULL;
+ GHOST_WIN32_WTSet set = NULL;
+ GHOST_WIN32_WTOpen open = NULL;
+ GHOST_WIN32_WTClose close = NULL;
+ GHOST_WIN32_WTPacketsGet packetsGet = NULL;
+ GHOST_WIN32_WTQueueSizeGet queueSizeGet = NULL;
+ GHOST_WIN32_WTQueueSizeSet queueSizeSet = NULL;
+ GHOST_WIN32_WTEnable enable = NULL;
+ GHOST_WIN32_WTOverlap overlap = NULL;
/** Stores the Tablet context if detected Tablet features using WinTab.dll */
- HCTX tablet;
- LONG maxPressure;
- LONG maxAzimuth, maxAltitude;
+ HCTX context = NULL;
+ /** Number of connected Wintab digitizers */
+ UINT numDevices = 0;
+ /** Number of cursors currently in contact mapped to system buttons */
+ GHOST_TUns8 numSysButtons = 0;
+ /** Cursors currently in contact mapped to system buttons */
+ DWORD sysButtonsPressed = 0;
+ LONG maxPressure = 0;
+ LONG maxAzimuth = 0, maxAltitude = 0;
+ /* Queue size doesn't change once set, so reuse the same buffer */
+ std::vector<PACKET> pkts;
} m_wintab;
+ /**
+ * Wintab setup
+ */
+ void initializeWintab();
+
+ /**
+ * Convert Wintab system mapped (mouse) buttons into Ghost button mask
+ */
+ GHOST_TSuccess wintabMouseToGhost(UINT cursor,
+ DWORD physicalButton,
+ GHOST_TButtonMask &buttonMask);
+
GHOST_TWindowState m_normal_state;
/** user32 dll handle*/
HMODULE m_user32;
- GHOST_WIN32_GetPointerInfo m_fpGetPointerInfo;
- GHOST_WIN32_GetPointerPenInfo m_fpGetPointerPenInfo;
- GHOST_WIN32_GetPointerTouchInfo m_fpGetPointerTouchInfo;
+ GHOST_WIN32_GetPointerInfoHistory m_fpGetPointerInfoHistory;
+ GHOST_WIN32_GetPointerPenInfoHistory m_fpGetPointerPenInfoHistory;
+ GHOST_WIN32_GetPointerTouchInfoHistory m_fpGetPointerTouchInfoHistory;
HWND m_parentWindowHwnd;
diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp
index 3bece605d45..db9f6846b11 100644
--- a/intern/ghost/intern/GHOST_WindowX11.cpp
+++ b/intern/ghost/intern/GHOST_WindowX11.cpp
@@ -239,6 +239,7 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
#ifdef WITH_XDND
m_dropTarget(NULL),
#endif
+ m_tabletData(GHOST_TABLET_DATA_NONE),
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
m_xic(NULL),
#endif
@@ -498,8 +499,6 @@ GHOST_WindowX11::GHOST_WindowX11(GHOST_SystemX11 *system,
#ifdef WITH_X11_XINPUT
refreshXInputDevices();
-
- m_tabletData = GHOST_TABLET_DATA_DEFAULT;
#endif
/* now set up the rendering context. */
diff --git a/intern/ghost/intern/GHOST_WindowX11.h b/intern/ghost/intern/GHOST_WindowX11.h
index a9914d88340..4704cb45e58 100644
--- a/intern/ghost/intern/GHOST_WindowX11.h
+++ b/intern/ghost/intern/GHOST_WindowX11.h
@@ -144,12 +144,11 @@ class GHOST_WindowX11 : public GHOST_Window {
* Return a handle to the x11 window type.
*/
Window getXWindow();
-#ifdef WITH_X11_XINPUT
+
GHOST_TabletData &GetTabletData()
{
return m_tabletData;
}
-#endif // WITH_X11_XINPUT
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
XIC getX11_XIC()
@@ -269,9 +268,7 @@ class GHOST_WindowX11 : public GHOST_Window {
GHOST_DropTargetX11 *m_dropTarget;
#endif
-#ifdef WITH_X11_XINPUT
GHOST_TabletData m_tabletData;
-#endif
#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
XIC m_xic;
diff --git a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
index e8de5d1bed7..60b3b06f6a8 100644
--- a/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
+++ b/intern/ghost/intern/GHOST_XrGraphicsBinding.cpp
@@ -279,7 +279,7 @@ class GHOST_XrGraphicsBindingD3D : public GHOST_IXrGraphicsBinding {
swapchain_image);
# if 0
- /* Ideally we'd just create a render target view for the OpenXR swapchain image texture and
+ /* Ideally we'd just create a render target view for the OpenXR swap-chain image texture and
* blit from the OpenGL context into it. The NV_DX_interop extension doesn't want to work with
* this though. At least not with Optimus hardware. See:
* https://github.com/mpv-player/mpv/issues/2949#issuecomment-197262807.
diff --git a/intern/libmv/intern/camera_intrinsics.cc b/intern/libmv/intern/camera_intrinsics.cc
index 4bd6b1270f5..89e3d0d1178 100644
--- a/intern/libmv/intern/camera_intrinsics.cc
+++ b/intern/libmv/intern/camera_intrinsics.cc
@@ -80,6 +80,8 @@ void libmv_cameraIntrinsicsUpdate(
* are not freed.
*/
+ camera_intrinsics->SetThreads(libmv_camera_intrinsics_options->num_threads);
+
if (camera_intrinsics->focal_length() != focal_length) {
camera_intrinsics->SetFocalLength(focal_length, focal_length);
}
diff --git a/intern/libmv/intern/camera_intrinsics.h b/intern/libmv/intern/camera_intrinsics.h
index 9e900892952..40a5826a9c4 100644
--- a/intern/libmv/intern/camera_intrinsics.h
+++ b/intern/libmv/intern/camera_intrinsics.h
@@ -33,6 +33,7 @@ enum {
typedef struct libmv_CameraIntrinsicsOptions {
// Common settings of all distortion models.
+ int num_threads;
int distortion_model;
int image_width, image_height;
double focal_length;
diff --git a/intern/libmv/libmv/simple_pipeline/camera_intrinsics_impl.h b/intern/libmv/libmv/simple_pipeline/camera_intrinsics_impl.h
index 97abee7ab01..e1b53992dfd 100644
--- a/intern/libmv/libmv/simple_pipeline/camera_intrinsics_impl.h
+++ b/intern/libmv/libmv/simple_pipeline/camera_intrinsics_impl.h
@@ -63,7 +63,7 @@ void LookupWarpGrid::Compute(const CameraIntrinsics &intrinsics,
double aspx = (double) w / intrinsics.image_width();
double aspy = (double) h / intrinsics.image_height();
#if defined(_OPENMP)
-# pragma omp parallel for schedule(dynamic) num_threads(threads_) \
+# pragma omp parallel for schedule(static) num_threads(threads_) \
if (threads_ > 1 && height > 100)
#endif
for (int y = 0; y < height; y++) {
@@ -125,7 +125,7 @@ void LookupWarpGrid::Apply(const PixelType *input_buffer,
int channels,
PixelType *output_buffer) {
#if defined(_OPENMP)
-# pragma omp parallel for schedule(dynamic) num_threads(threads_) \
+# pragma omp parallel for schedule(static) num_threads(threads_) \
if (threads_ > 1 && height > 100)
#endif
for (int y = 0; y < height; y++) {
diff --git a/intern/mantaflow/extern/manta_fluid_API.h b/intern/mantaflow/extern/manta_fluid_API.h
index 939f24fbeef..85cc04b4a52 100644
--- a/intern/mantaflow/extern/manta_fluid_API.h
+++ b/intern/mantaflow/extern/manta_fluid_API.h
@@ -39,6 +39,7 @@ void manta_ensure_invelocity(struct MANTA *fluid, struct FluidModifierData *mmd)
void manta_ensure_outflow(struct MANTA *fluid, struct FluidModifierData *mmd);
int manta_write_config(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
int manta_write_data(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
+int manta_write_noise(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
int manta_read_config(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
int manta_read_data(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
int manta_read_noise(struct MANTA *fluid, struct FluidModifierData *mmd, int framenr);
diff --git a/intern/mantaflow/intern/MANTA_main.cpp b/intern/mantaflow/intern/MANTA_main.cpp
index 54b728b734e..79c8625ec48 100644
--- a/intern/mantaflow/intern/MANTA_main.cpp
+++ b/intern/mantaflow/intern/MANTA_main.cpp
@@ -1098,12 +1098,14 @@ bool MANTA::updateMeshStructures(FluidModifierData *mmd, int framenr)
int expected = 0; /* Expected number of read successes for this frame. */
/* Ensure empty data structures at start. */
- if (!mMeshNodes || !mMeshTriangles || !mMeshVelocities)
+ if (!mMeshNodes || !mMeshTriangles)
return false;
mMeshNodes->clear();
mMeshTriangles->clear();
- mMeshVelocities->clear();
+
+ if (mMeshVelocities)
+ mMeshVelocities->clear();
std::string mformat = getCacheFileEnding(mmd->domain->cache_mesh_format);
std::string dformat = getCacheFileEnding(mmd->domain->cache_data_format);
@@ -1222,19 +1224,21 @@ bool MANTA::updateSmokeStructures(FluidModifierData *mmd, int framenr)
if (mUsingColors) {
file = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_DOMAIN_FILE_COLORR, dformat, framenr);
- expected += 3;
+ expected += 1;
if (BLI_exists(file.c_str())) {
result += updateGridFromFile(file, mColorR, false);
assert(result == expected);
}
file = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_DOMAIN_FILE_COLORG, dformat, framenr);
+ expected += 1;
if (BLI_exists(file.c_str())) {
result += updateGridFromFile(file, mColorG, false);
assert(result == expected);
}
file = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_DOMAIN_FILE_COLORB, dformat, framenr);
+ expected += 1;
if (BLI_exists(file.c_str())) {
result += updateGridFromFile(file, mColorB, false);
assert(result == expected);
@@ -1243,19 +1247,21 @@ bool MANTA::updateSmokeStructures(FluidModifierData *mmd, int framenr)
if (mUsingFire) {
file = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_DOMAIN_FILE_FLAME, dformat, framenr);
- expected += 3;
+ expected += 1;
if (BLI_exists(file.c_str())) {
result += updateGridFromFile(file, mFlame, false);
assert(result == expected);
}
file = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_DOMAIN_FILE_FUEL, dformat, framenr);
+ expected += 1;
if (BLI_exists(file.c_str())) {
result += updateGridFromFile(file, mFuel, false);
assert(result == expected);
}
file = getFile(mmd, FLUID_DOMAIN_DIR_DATA, FLUID_DOMAIN_FILE_REACT, dformat, framenr);
+ expected += 1;
if (BLI_exists(file.c_str())) {
result += updateGridFromFile(file, mReact, false);
assert(result == expected);
@@ -1301,19 +1307,21 @@ bool MANTA::updateNoiseStructures(FluidModifierData *mmd, int framenr)
if (mUsingColors) {
file = getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_DOMAIN_FILE_COLORRNOISE, nformat, framenr);
- expected += 3;
+ expected += 1;
if (BLI_exists(file.c_str())) {
result += updateGridFromFile(file, mColorRHigh, true);
assert(result == expected);
}
file = getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_DOMAIN_FILE_COLORGNOISE, nformat, framenr);
+ expected += 1;
if (BLI_exists(file.c_str())) {
result += updateGridFromFile(file, mColorGHigh, true);
assert(result == expected);
}
file = getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_DOMAIN_FILE_COLORBNOISE, nformat, framenr);
+ expected += 1;
if (BLI_exists(file.c_str())) {
result += updateGridFromFile(file, mColorBHigh, true);
assert(result == expected);
@@ -1322,19 +1330,21 @@ bool MANTA::updateNoiseStructures(FluidModifierData *mmd, int framenr)
if (mUsingFire) {
file = getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_DOMAIN_FILE_FLAMENOISE, nformat, framenr);
- expected += 3;
+ expected += 1;
if (BLI_exists(file.c_str())) {
result += updateGridFromFile(file, mFlameHigh, true);
assert(result == expected);
}
file = getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_DOMAIN_FILE_FUELNOISE, nformat, framenr);
+ expected += 1;
if (BLI_exists(file.c_str())) {
result += updateGridFromFile(file, mFuelHigh, true);
assert(result == expected);
}
file = getFile(mmd, FLUID_DOMAIN_DIR_NOISE, FLUID_DOMAIN_FILE_REACTNOISE, nformat, framenr);
+ expected += 1;
if (BLI_exists(file.c_str())) {
result += updateGridFromFile(file, mReactHigh, true);
assert(result == expected);
@@ -1433,6 +1443,29 @@ bool MANTA::writeData(FluidModifierData *mmd, int framenr)
return runPythonString(pythonCommands);
}
+bool MANTA::writeNoise(FluidModifierData *mmd, int framenr)
+{
+ if (with_debug)
+ std::cout << "MANTA::writeNoise()" << std::endl;
+
+ std::ostringstream ss;
+ std::vector<std::string> pythonCommands;
+
+ std::string directory = getDirectory(mmd, FLUID_DOMAIN_DIR_NOISE);
+ std::string nformat = getCacheFileEnding(mmd->domain->cache_noise_format);
+
+ bool final_cache = (mmd->domain->cache_type == FLUID_DOMAIN_CACHE_FINAL);
+ std::string resumable_cache = (final_cache) ? "False" : "True";
+
+ if (mUsingSmoke && mUsingNoise) {
+ ss.str("");
+ ss << "smoke_save_noise_" << mCurrentID << "('" << escapeSlashes(directory) << "', " << framenr
+ << ", '" << nformat << "', " << resumable_cache << ")";
+ pythonCommands.push_back(ss.str());
+ }
+ return runPythonString(pythonCommands);
+}
+
bool MANTA::readConfiguration(FluidModifierData *mmd, int framenr)
{
if (with_debug)
diff --git a/intern/mantaflow/intern/MANTA_main.h b/intern/mantaflow/intern/MANTA_main.h
index f4699e857ff..caac63d8e39 100644
--- a/intern/mantaflow/intern/MANTA_main.h
+++ b/intern/mantaflow/intern/MANTA_main.h
@@ -79,7 +79,8 @@ struct MANTA {
// Write cache
bool writeConfiguration(FluidModifierData *mmd, int framenr);
bool writeData(FluidModifierData *mmd, int framenr);
- // write call for noise, mesh and particles were left in bake calls for now
+ bool writeNoise(FluidModifierData *mmd, int framenr);
+ // write calls for mesh and particles were left in bake calls for now
// Read cache (via Manta save/load)
bool readConfiguration(FluidModifierData *mmd, int framenr);
diff --git a/intern/mantaflow/intern/manta_fluid_API.cpp b/intern/mantaflow/intern/manta_fluid_API.cpp
index 594f7a44e7f..b4582051c3c 100644
--- a/intern/mantaflow/intern/manta_fluid_API.cpp
+++ b/intern/mantaflow/intern/manta_fluid_API.cpp
@@ -80,6 +80,13 @@ int manta_write_data(MANTA *fluid, FluidModifierData *mmd, int framenr)
return fluid->writeData(mmd, framenr);
}
+int manta_write_noise(MANTA *fluid, FluidModifierData *mmd, int framenr)
+{
+ if (!fluid || !mmd)
+ return 0;
+ return fluid->writeNoise(mmd, framenr);
+}
+
int manta_read_config(MANTA *fluid, FluidModifierData *mmd, int framenr)
{
if (!fluid || !mmd)
diff --git a/intern/mantaflow/intern/strings/smoke_script.h b/intern/mantaflow/intern/strings/smoke_script.h
index fb2eaa9b441..7a855ff2cb8 100644
--- a/intern/mantaflow/intern/strings/smoke_script.h
+++ b/intern/mantaflow/intern/strings/smoke_script.h
@@ -82,6 +82,7 @@ const std::string smoke_alloc =
"\n\
mantaMsg('Smoke alloc')\n\
shadow_s$ID$ = s$ID$.create(RealGrid)\n\
+emission_s$ID$ = s$ID$.create(RealGrid)\n\
emissionIn_s$ID$ = s$ID$.create(RealGrid)\n\
density_s$ID$ = s$ID$.create(RealGrid)\n\
densityIn_s$ID$ = s$ID$.create(RealGrid)\n\
@@ -101,7 +102,7 @@ color_b_in_s$ID$ = None\n\
\n\
# Keep track of important objects in dict to load them later on\n\
smoke_data_dict_final_s$ID$ = dict(density=density_s$ID$, shadow=shadow_s$ID$)\n\
-smoke_data_dict_resume_s$ID$ = dict(densityIn=densityIn_s$ID$, emissionIn=emissionIn_s$ID$)\n";
+smoke_data_dict_resume_s$ID$ = dict(densityIn=densityIn_s$ID$, emission=emission_s$ID$)\n";
const std::string smoke_alloc_noise =
"\n\
@@ -300,6 +301,9 @@ def smoke_adaptive_step_$ID$(framenr):\n\
setObstacleFlags(flags=flags_s$ID$, phiObs=phiObs_s$ID$, phiOut=phiOut_s$ID$, phiIn=phiIn_s$ID$, boundaryWidth=1)\n\
flags_s$ID$.fillGrid()\n\
\n\
+ # accumulate emission value per adaptive step for later use in noise computation\n\
+ emission_s$ID$.join(emissionIn_s$ID$)\n\
+ \n\
applyEmission(flags=flags_s$ID$, target=density_s$ID$, source=densityIn_s$ID$, emissionTexture=emissionIn_s$ID$, type=FlagInflow|FlagOutflow)\n\
if using_heat_s$ID$:\n\
applyEmission(flags=flags_s$ID$, target=heat_s$ID$, source=heatIn_s$ID$, emissionTexture=emissionIn_s$ID$, type=FlagInflow|FlagOutflow)\n\
@@ -329,6 +333,9 @@ const std::string smoke_step =
def smoke_step_$ID$():\n\
mantaMsg('Smoke step low')\n\
\n\
+ # save original state for later (used during noise creation)\n\
+ velTmp_s$ID$.copyFrom(vel_s$ID$)\n\
+ \n\
if using_dissolve_s$ID$:\n\
mantaMsg('Dissolving smoke')\n\
dissolveSmoke(flags=flags_s$ID$, density=density_s$ID$, heat=heat_s$ID$, red=color_r_s$ID$, green=color_g_s$ID$, blue=color_b_s$ID$, speed=dissolveSpeed_s$ID$, logFalloff=using_logdissolve_s$ID$)\n\
@@ -422,36 +429,36 @@ def smoke_step_noise_$ID$(framenr):\n\
mantaMsg('Interpolating grids')\n\
# Join big obstacle levelset after initDomain() call as it overwrites everything in phiObs\n\
if using_obstacle_s$ID$:\n\
- interpolateGrid(target=phiIn_sn$ID$, source=phiObsIn_s$ID$) # mis-use phiIn_sn\n\
+ phiIn_sn$ID$.copyFrom(phiObsIn_s$ID$) if upres_sn$ID$ <= 1 else interpolateGrid(target=phiIn_sn$ID$, source=phiObsIn_s$ID$) # mis-use phiIn_sn\n\
phiObs_sn$ID$.join(phiIn_sn$ID$)\n\
if using_outflow_s$ID$:\n\
- interpolateGrid(target=phiOut_sn$ID$, source=phiOut_s$ID$)\n\
- interpolateGrid(target=phiIn_sn$ID$, source=phiIn_s$ID$)\n\
- interpolateMACGrid(target=vel_sn$ID$, source=vel_s$ID$)\n\
+ phiOut_sn$ID$.copyFrom(phiOut_s$ID$) if upres_sn$ID$ <= 1 else interpolateGrid(target=phiOut_sn$ID$, source=phiOut_s$ID$)\n\
+ phiIn_sn$ID$.copyFrom(phiIn_s$ID$) if upres_sn$ID$ <= 1 else interpolateGrid(target=phiIn_sn$ID$, source=phiIn_s$ID$)\n\
+ vel_sn$ID$.copyFrom(velTmp_s$ID$) if upres_sn$ID$ <= 1 else interpolateMACGrid(target=vel_sn$ID$, source=velTmp_s$ID$)\n\
\n\
setObstacleFlags(flags=flags_sn$ID$, phiObs=phiObs_sn$ID$, phiOut=phiOut_sn$ID$, phiIn=phiIn_sn$ID$, boundaryWidth=1)\n\
flags_sn$ID$.fillGrid()\n\
\n\
# Interpolate emission grids and apply them to big noise grids\n\
- interpolateGrid(source=densityIn_s$ID$, target=tmpIn_sn$ID$)\n\
- interpolateGrid(source=emissionIn_s$ID$, target=emissionIn_sn$ID$)\n\
+ tmpIn_sn$ID$.copyFrom(densityIn_s$ID$) if upres_sn$ID$ <= 1 else interpolateGrid(source=densityIn_s$ID$, target=tmpIn_sn$ID$)\n\
+ emissionIn_sn$ID$.copyFrom(emission_s$ID$) if upres_sn$ID$ <= 1 else interpolateGrid(source=emission_s$ID$, target=emissionIn_sn$ID$)\n\
\n\
# Higher-res noise grid needs scaled emission values\n\
tmpIn_sn$ID$.multConst(float(upres_sn$ID$))\n\
applyEmission(flags=flags_sn$ID$, target=density_sn$ID$, source=tmpIn_sn$ID$, emissionTexture=emissionIn_sn$ID$, type=FlagInflow|FlagOutflow)\n\
\n\
if using_colors_s$ID$:\n\
- interpolateGrid(source=color_r_in_s$ID$, target=tmpIn_sn$ID$)\n\
+ tmpIn_sn$ID$.copyFrom(color_r_in_s$ID$) if upres_sn$ID$ <= 1 else interpolateGrid(source=color_r_in_s$ID$, target=tmpIn_sn$ID$)\n\
applyEmission(flags=flags_sn$ID$, target=color_r_sn$ID$, source=tmpIn_sn$ID$, emissionTexture=emissionIn_sn$ID$, type=FlagInflow|FlagOutflow)\n\
- interpolateGrid(source=color_g_in_s$ID$, target=tmpIn_sn$ID$)\n\
+ tmpIn_sn$ID$.copyFrom(color_g_in_s$ID$) if upres_sn$ID$ <= 1 else interpolateGrid(source=color_g_in_s$ID$, target=tmpIn_sn$ID$)\n\
applyEmission(flags=flags_sn$ID$, target=color_g_sn$ID$, source=tmpIn_sn$ID$, emissionTexture=emissionIn_sn$ID$, type=FlagInflow|FlagOutflow)\n\
- interpolateGrid(source=color_b_in_s$ID$, target=tmpIn_sn$ID$)\n\
+ tmpIn_sn$ID$.copyFrom(color_b_in_s$ID$) if upres_sn$ID$ <= 1 else interpolateGrid(source=color_b_in_s$ID$, target=tmpIn_sn$ID$)\n\
applyEmission(flags=flags_sn$ID$, target=color_b_sn$ID$, source=tmpIn_sn$ID$, emissionTexture=emissionIn_sn$ID$, type=FlagInflow|FlagOutflow)\n\
\n\
if using_fire_s$ID$:\n\
- interpolateGrid(source=fuelIn_s$ID$, target=tmpIn_sn$ID$)\n\
+ tmpIn_sn$ID$.copyFrom(fuelIn_s$ID$) if upres_sn$ID$ <= 1 else interpolateGrid(source=fuelIn_s$ID$, target=tmpIn_sn$ID$)\n\
applyEmission(flags=flags_sn$ID$, target=fuel_sn$ID$, source=tmpIn_sn$ID$, emissionTexture=emissionIn_sn$ID$, type=FlagInflow|FlagOutflow)\n\
- interpolateGrid(source=reactIn_s$ID$, target=tmpIn_sn$ID$)\n\
+ tmpIn_sn$ID$.copyFrom(reactIn_s$ID$) if upres_sn$ID$ <= 1 else interpolateGrid(source=reactIn_s$ID$, target=tmpIn_sn$ID$)\n\
applyEmission(flags=flags_sn$ID$, target=react_sn$ID$, source=tmpIn_sn$ID$, emissionTexture=emissionIn_sn$ID$, type=FlagInflow|FlagOutflow)\n\
\n\
mantaMsg('Noise step / sn$ID$.frame: ' + str(sn$ID$.frame))\n\
diff --git a/release/datafiles/colormanagement/filmic/filmic_desat65cube.spi3d b/release/datafiles/colormanagement/filmic/filmic_desat65cube.spi3d
index bd80dd4eae0..66778b93e4e 100644
--- a/release/datafiles/colormanagement/filmic/filmic_desat65cube.spi3d
+++ b/release/datafiles/colormanagement/filmic/filmic_desat65cube.spi3d
@@ -1,4 +1,4 @@
-SPILUT 1.0
+spilut 1.0
3 3
65 65 65
0 0 0 0.000175781 0.000175781 0.000175781
diff --git a/release/datafiles/colormanagement/filmic/filmic_false_color.spi3d b/release/datafiles/colormanagement/filmic/filmic_false_color.spi3d
index d8fcfd4e70d..b81b18ec643 100644
--- a/release/datafiles/colormanagement/filmic/filmic_false_color.spi3d
+++ b/release/datafiles/colormanagement/filmic/filmic_false_color.spi3d
@@ -1,4 +1,4 @@
-SPILUT 1.0
+spilut 1.0
3 3
65 65 65
0 0 0 0 0 0
diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c
index ec4b214c5f5..9771530ff18 100644
--- a/release/datafiles/userdef/userdef_default.c
+++ b/release/datafiles/userdef/userdef_default.c
@@ -64,7 +64,8 @@ const UserDef U_default = {
.uiflag2 = USER_REGION_OVERLAP,
.gpu_flag = USER_GPU_FLAG_OVERLAY_SMOOTH_WIRE,
.app_flag = 0,
- .language = 0,
+ /** Default language of English (1), not Automatic (0). */
+ .language = 1,
.viewzoom = USER_ZOOM_DOLLY,
.mixbufsize = 2048,
.audiodevice = 0,
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject 8e6f485cf5b160c425d7da7c743879b20f3d6a9
+Subproject d348bde0f96809e289b0514c015cafb97f2dcf7
diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py
index 4da9d515ffd..c736f78d501 100644
--- a/release/scripts/modules/bl_i18n_utils/settings.py
+++ b/release/scripts/modules/bl_i18n_utils/settings.py
@@ -108,7 +108,7 @@ IMPORT_MIN_LEVEL = 0.0
# Languages in /branches we do not want to import in /trunk currently...
IMPORT_LANGUAGES_SKIP = {
- 'am_ET', 'bg_BG', 'fi_FI', 'el_GR', 'et_EE', 'ne_NP', 'ro_RO', 'uz_UZ', 'uz_UZ@cyrillic', 'kk_KZ',
+ 'am_ET', 'bg_BG', 'fi_FI', 'el_GR', 'et_EE', 'ne_NP', 'ro_RO', 'uz_UZ', 'uz_UZ@cyrillic', 'kk_KZ', 'es_ES',
}
# Languages that need RTL pre-processing.
diff --git a/release/scripts/modules/bl_keymap_utils/io.py b/release/scripts/modules/bl_keymap_utils/io.py
index a93e86bc0a1..8cddbd37ea3 100644
--- a/release/scripts/modules/bl_keymap_utils/io.py
+++ b/release/scripts/modules/bl_keymap_utils/io.py
@@ -136,6 +136,9 @@ def keyconfig_export_as_data(wm, kc, filepath, *, all_keymaps=False):
# First add all user_modified keymaps (found in keyconfigs.user.keymaps list),
# then add all remaining keymaps from the currently active custom keyconfig.
#
+ # Sort the resulting list according to top context name,
+ # while this isn't essential, it makes comparing keymaps simpler.
+ #
# This will create a final list of keymaps that can be used as a "diff" against
# the default blender keyconfig, recreating the current setup from a fresh blender
# without needing to export keymaps which haven't been edited.
@@ -152,6 +155,10 @@ def keyconfig_export_as_data(wm, kc, filepath, *, all_keymaps=False):
else:
export_keymaps = keyconfig_merge(edited_kc, edited_kc)
+ # Sort the keymap list by top context name before exporting,
+ # not essential, just convenient to order them predictably.
+ export_keymaps.sort(key=lambda k: k[0].name)
+
with open(filepath, "w") as fh:
fw = fh.write
fw("keyconfig_data = \\\n[")
diff --git a/release/scripts/modules/bpy_extras/anim_utils.py b/release/scripts/modules/bpy_extras/anim_utils.py
index c67134ac3f6..7de919f489e 100644
--- a/release/scripts/modules/bpy_extras/anim_utils.py
+++ b/release/scripts/modules/bpy_extras/anim_utils.py
@@ -216,8 +216,6 @@ def bake_action_iter(
pose_info = []
obj_info = []
- options = {'INSERTKEY_NEEDED'}
-
# -------------------------------------------------------------------------
# Collect transformations
@@ -281,7 +279,7 @@ def bake_action_iter(
for (f, matrix, bbones) in pose_info:
pbone.matrix_basis = matrix[name].copy()
- pbone.keyframe_insert("location", index=-1, frame=f, group=name, options=options)
+ pbone.keyframe_insert("location", index=-1, frame=f, group=name)
rotation_mode = pbone.rotation_mode
if rotation_mode == 'QUATERNION':
@@ -293,9 +291,9 @@ def bake_action_iter(
del quat
else:
quat_prev = pbone.rotation_quaternion.copy()
- pbone.keyframe_insert("rotation_quaternion", index=-1, frame=f, group=name, options=options)
+ pbone.keyframe_insert("rotation_quaternion", index=-1, frame=f, group=name)
elif rotation_mode == 'AXIS_ANGLE':
- pbone.keyframe_insert("rotation_axis_angle", index=-1, frame=f, group=name, options=options)
+ pbone.keyframe_insert("rotation_axis_angle", index=-1, frame=f, group=name)
else: # euler, XYZ, ZXY etc
if euler_prev is not None:
euler = pbone.rotation_euler.copy()
@@ -305,9 +303,9 @@ def bake_action_iter(
del euler
else:
euler_prev = pbone.rotation_euler.copy()
- pbone.keyframe_insert("rotation_euler", index=-1, frame=f, group=name, options=options)
+ pbone.keyframe_insert("rotation_euler", index=-1, frame=f, group=name)
- pbone.keyframe_insert("scale", index=-1, frame=f, group=name, options=options)
+ pbone.keyframe_insert("scale", index=-1, frame=f, group=name)
# Bendy Bones
if pbone.bone.bbone_segments > 1:
@@ -315,7 +313,7 @@ def bake_action_iter(
for bb_prop in BBONE_PROPS:
# update this property with value from bbone_shape, then key it
setattr(pbone, bb_prop, bbone_shape[bb_prop])
- pbone.keyframe_insert(bb_prop, index=-1, frame=f, group=name, options=options)
+ pbone.keyframe_insert(bb_prop, index=-1, frame=f, group=name)
# object. TODO. multiple objects
if do_object:
@@ -331,7 +329,7 @@ def bake_action_iter(
name = "Action Bake" # XXX: placeholder
obj.matrix_basis = matrix
- obj.keyframe_insert("location", index=-1, frame=f, group=name, options=options)
+ obj.keyframe_insert("location", index=-1, frame=f, group=name)
rotation_mode = obj.rotation_mode
if rotation_mode == 'QUATERNION':
@@ -344,9 +342,9 @@ def bake_action_iter(
print ("quat_prev", quat_prev)
else:
quat_prev = obj.rotation_quaternion.copy()
- obj.keyframe_insert("rotation_quaternion", index=-1, frame=f, group=name, options=options)
+ obj.keyframe_insert("rotation_quaternion", index=-1, frame=f, group=name)
elif rotation_mode == 'AXIS_ANGLE':
- obj.keyframe_insert("rotation_axis_angle", index=-1, frame=f, group=name, options=options)
+ obj.keyframe_insert("rotation_axis_angle", index=-1, frame=f, group=name)
else: # euler, XYZ, ZXY etc
if euler_prev is not None:
euler = obj.rotation_euler.copy()
@@ -356,9 +354,9 @@ def bake_action_iter(
del euler
else:
euler_prev = obj.rotation_euler.copy()
- obj.keyframe_insert("rotation_euler", index=-1, frame=f, group=name, options=options)
+ obj.keyframe_insert("rotation_euler", index=-1, frame=f, group=name)
- obj.keyframe_insert("scale", index=-1, frame=f, group=name, options=options)
+ obj.keyframe_insert("scale", index=-1, frame=f, group=name)
if do_parents_clear:
obj.parent = None
diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py
index b49c2f959b0..7b266d66d29 100644
--- a/release/scripts/modules/rna_manual_reference.py
+++ b/release/scripts/modules/rna_manual_reference.py
@@ -9,27 +9,25 @@ else:
url_manual_prefix = "https://docs.blender.org/manual/en/" + manual_version + "/"
-language = ""
-if bpy.context.preferences.view.use_international_fonts:
- language = bpy.context.preferences.view.language
- if language == 'DEFAULT':
- import os
- language = os.getenv('LANG', '').split('.')[0]
+language = bpy.context.preferences.view.language
+if language == 'DEFAULT':
+ import os
+ language = os.getenv('LANG', '').split('.')[0]
LANG = {
- "de_DE": "de",
- "ru_RU": "ru",
- "uk_UA": "uk",
- "es": "es",
- "fr_FR": "fr",
- "it_IT": "it",
- "ja_JP": "ja",
- "ko_KR": "ko",
- "pt_PT": "pt",
- "pt_BR": "pt",
- "vi_VN": "vi",
- "zh_CN": "zh-hans",
- "zh_TW": "zh-hant",
+"de_DE": "de",
+"ru_RU": "ru",
+"uk_UA": "uk",
+"es": "es",
+"fr_FR": "fr",
+"it_IT": "it",
+"ja_JP": "ja",
+"ko_KR": "ko",
+"pt_PT": "pt",
+"pt_BR": "pt",
+"vi_VN": "vi",
+"zh_CN": "zh-hans",
+"zh_TW": "zh-hant",
}.get(language)
if LANG is not None:
@@ -621,7 +619,7 @@ url_manual_mapping = (
("bpy.ops.mesh.dissolve_limited*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-dissolve-limited"),
("bpy.ops.mesh.face_make_planar*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-face-make-planar"),
("bpy.ops.mesh.paint_mask_slice*", "sculpt_paint/sculpting/hide_mask.html#bpy-ops-mesh-paint-mask-slice"),
- ("bpy.ops.mesh.smoothen_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-smoothen-normals"),
+ ("bpy.ops.mesh.smooth_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-smooth-normals"),
("bpy.ops.object.duplicate_move*", "scene_layout/object/editing/duplicate.html#bpy-ops-object-duplicate-move"),
("bpy.ops.object.hook_add_selob*", "modeling/meshes/editing/vertex/hooks.html#bpy-ops-object-hook-add-selob"),
("bpy.ops.object.select_by_type*", "scene_layout/object/selecting.html#bpy-ops-object-select-by-type"),
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index 6e3b883aa8a..f0f3ba14d92 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -2423,7 +2423,14 @@ class WM_MT_splash(Menu):
col = split.column()
- col.label()
+ sub = col.split(factor=0.35)
+ row = sub.row()
+ row.alignment = 'RIGHT'
+ row.label(text="Language")
+ prefs = context.preferences
+ sub.prop(prefs.view, "language", text="")
+
+ col.separator()
sub = col.split(factor=0.35)
row = sub.row()
@@ -2465,14 +2472,6 @@ class WM_MT_splash(Menu):
label = "Blender Dark"
sub.menu("USERPREF_MT_interface_theme_presets", text=label)
- # We need to make switching to a language easier first
- #sub = col.split(factor=0.35)
- #row = sub.row()
- #row.alignment = 'RIGHT'
- # row.label(text="Language:")
- #prefs = context.preferences
- #sub.prop(prefs.system, "language", text="")
-
# Keep height constant
if not has_select_mouse:
col.label()
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 8d30e6b3651..243d187cd78 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -242,21 +242,14 @@ class USERPREF_PT_interface_translation(InterfacePanel, CenterAlignMixIn, Panel)
def poll(cls, context):
return bpy.app.build_options.international
- def draw_header(self, context):
- prefs = context.preferences
- view = prefs.view
-
- self.layout.prop(view, "use_international_fonts", text="")
-
def draw_centered(self, context, layout):
prefs = context.preferences
view = prefs.view
- layout.active = view.use_international_fonts
-
layout.prop(view, "language")
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False)
+ flow.active = (bpy.app.translations.locale != 'en_US')
flow.prop(view, "use_translate_tooltips", text="Tooltips")
flow.prop(view, "use_translate_interface", text="Interface")
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 561e5ddadbc..e7b21426d03 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -3320,13 +3320,7 @@ class VIEW3D_MT_pose(Menu):
layout.separator()
- layout.operator_context = 'EXEC_AREA'
- layout.operator("pose.autoside_names", text="AutoName Left/Right").axis = 'XAXIS'
- layout.operator("pose.autoside_names", text="AutoName Front/Back").axis = 'YAXIS'
- layout.operator("pose.autoside_names", text="AutoName Top/Bottom").axis = 'ZAXIS'
-
- layout.operator("pose.flip_names")
-
+ layout.menu("VIEW3D_MT_pose_names")
layout.operator("pose.quaternions_flip")
layout.separator()
@@ -3463,6 +3457,19 @@ class VIEW3D_MT_pose_constraints(Menu):
layout.operator("pose.constraints_clear")
+class VIEW3D_MT_pose_names(Menu):
+ bl_label = "Names"
+
+ def draw(self, _context):
+ layout = self.layout
+
+ layout.operator_context = 'EXEC_REGION_WIN'
+ layout.operator("pose.autoside_names", text="AutoName Left/Right").axis = 'XAXIS'
+ layout.operator("pose.autoside_names", text="AutoName Front/Back").axis = 'YAXIS'
+ layout.operator("pose.autoside_names", text="AutoName Top/Bottom").axis = 'ZAXIS'
+ layout.operator("pose.flip_names")
+
+
class VIEW3D_MT_pose_showhide(ShowHideMenu, Menu):
_operator_name = "pose"
@@ -4176,7 +4183,7 @@ class VIEW3D_MT_edit_mesh_normals(Menu):
layout.operator("mesh.normals_tools", text="Copy Vectors").mode = 'COPY'
layout.operator("mesh.normals_tools", text="Paste Vectors").mode = 'PASTE'
- layout.operator("mesh.smoothen_normals", text="Smoothen Vectors")
+ layout.operator("mesh.smooth_normals", text="Smooth Vectors")
layout.operator("mesh.normals_tools", text="Reset Vectors").mode = 'RESET'
layout.separator()
@@ -7447,6 +7454,7 @@ classes = (
VIEW3D_MT_pose_group,
VIEW3D_MT_pose_ik,
VIEW3D_MT_pose_constraints,
+ VIEW3D_MT_pose_names,
VIEW3D_MT_pose_showhide,
VIEW3D_MT_pose_apply,
VIEW3D_MT_pose_context_menu,
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index 78e0e12355b..b9eab004ddf 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -27,7 +27,7 @@
* \note Use #STRINGIFY() rather than defining with quotes.
*/
#define BLENDER_VERSION 283
-#define BLENDER_SUBVERSION 11
+#define BLENDER_SUBVERSION 13
/** Several breakages with 280, e.g. collections vs layers. */
#define BLENDER_MINVERSION 280
#define BLENDER_MINSUBVERSION 0
diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h
index eff546a7c55..ea81be05b03 100644
--- a/source/blender/blenkernel/BKE_particle.h
+++ b/source/blender/blenkernel/BKE_particle.h
@@ -270,7 +270,7 @@ BLI_INLINE float psys_frand(ParticleSystem *psys, unsigned int seed)
/* XXX far from ideal, this simply scrambles particle random numbers a bit
* to avoid obvious correlations.
* Can't use previous psys->frand arrays because these require initialization
- * inside psys_check_enabled, which wreaks havoc in multi-threaded depgraph updates.
+ * inside psys_check_enabled, which wreaks havoc in multi-threaded depsgraph updates.
*/
unsigned int offset = PSYS_FRAND_SEED_OFFSET[psys->seed % PSYS_FRAND_COUNT];
unsigned int multiplier = PSYS_FRAND_SEED_MULTIPLIER[psys->seed % PSYS_FRAND_COUNT];
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index 478609f2f3f..ba77538bfb6 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -925,6 +925,7 @@ void BKE_pose_channel_free_ex(bPoseChannel *pchan, bool do_id_user)
if (pchan->prop) {
IDP_FreeProperty(pchan->prop);
+ pchan->prop = NULL;
}
/* Cached data, for new draw manager rendering code. */
diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c
index 0e578250b39..4071cc024aa 100644
--- a/source/blender/blenkernel/intern/appdir.c
+++ b/source/blender/blenkernel/intern/appdir.c
@@ -232,7 +232,7 @@ static bool get_path_local(char *targetpath,
char osx_resourses[FILE_MAX];
BLI_snprintf(osx_resourses, sizeof(osx_resourses), "%s../Resources", bprogdir);
/* Remove the '/../' added above. */
- BLI_cleanup_path(NULL, osx_resourses);
+ BLI_path_normalize(NULL, osx_resourses);
return test_path(
targetpath, targetpath_len, osx_resourses, blender_version_decimal(ver), relfolder);
#else
@@ -687,12 +687,12 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name)
BLI_strncpy(fullname, name, maxlen);
if (name[0] == '.') {
- BLI_path_cwd(fullname, maxlen);
+ BLI_path_abs_from_cwd(fullname, maxlen);
#ifdef _WIN32
BLI_path_program_extensions_add_win32(fullname, maxlen);
#endif
}
- else if (BLI_last_slash(name)) {
+ else if (BLI_path_slash_rfind(name)) {
// full path
BLI_strncpy(fullname, name, maxlen);
#ifdef _WIN32
@@ -703,7 +703,7 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name)
BLI_path_program_search(fullname, maxlen, name);
}
/* Remove "/./" and "/../" so string comparisons can be used on the path. */
- BLI_cleanup_path(NULL, fullname);
+ BLI_path_normalize(NULL, fullname);
#if defined(DEBUG)
if (!STREQ(name, fullname)) {
@@ -939,7 +939,7 @@ static void where_is_temp(char *fullname, char *basename, const size_t maxlen, c
}
else {
/* add a trailing slash if needed */
- BLI_add_slash(fullname);
+ BLI_path_slash_ensure(fullname);
#ifdef WIN32
if (userdir && userdir != fullname) {
/* also set user pref to show %TEMP%. /tmp/ is just plain confusing for Windows users. */
@@ -967,7 +967,7 @@ static void where_is_temp(char *fullname, char *basename, const size_t maxlen, c
if (BLI_is_dir(tmp_name)) {
BLI_strncpy(basename, fullname, maxlen);
BLI_strncpy(fullname, tmp_name, maxlen);
- BLI_add_slash(fullname);
+ BLI_path_slash_ensure(fullname);
}
else {
CLOG_WARN(&LOG,
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index daf364432f4..af98992cc01 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -156,7 +156,7 @@ IDTypeInfo IDType_ID_AR = {
.main_listbase_index = INDEX_ID_AR,
.struct_size = sizeof(bArmature),
.name = "Armature",
- .name_plural = "armature",
+ .name_plural = "armatures",
.translation_context = BLT_I18NCONTEXT_ID_ARMATURE,
.flags = 0,
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index d862132480a..2a53a0b69a4 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -76,7 +76,7 @@
static bool clean_paths_visit_cb(void *UNUSED(userdata), char *path_dst, const char *path_src)
{
strcpy(path_dst, path_src);
- BLI_path_native_slash(path_dst);
+ BLI_path_slash_native(path_dst);
return !STREQ(path_dst, path_src);
}
@@ -88,7 +88,7 @@ static void clean_paths(Main *main)
BKE_bpath_traverse_main(main, clean_paths_visit_cb, BKE_BPATH_TRAVERSE_SKIP_MULTIFILE, NULL);
for (scene = main->scenes.first; scene; scene = scene->id.next) {
- BLI_path_native_slash(scene->r.pic);
+ BLI_path_slash_native(scene->r.pic);
}
}
diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c
index 5891a0361d8..d179bfbedfd 100644
--- a/source/blender/blenkernel/intern/bpath.c
+++ b/source/blender/blenkernel/intern/bpath.c
@@ -137,7 +137,7 @@ static bool bpath_relative_rebase_visit_cb(void *userdata, char *path_dst, const
char filepath[(FILE_MAXDIR * 2) + FILE_MAXFILE];
BLI_strncpy(filepath, path_src, FILE_MAX);
if (BLI_path_abs(filepath, data->basedir_src)) {
- BLI_cleanup_path(NULL, filepath);
+ BLI_path_normalize(NULL, filepath);
/* This may fail, if so it's fine to leave absolute since the path is still valid. */
BLI_path_rel(filepath, data->basedir_dst);
@@ -815,13 +815,13 @@ bool BKE_bpath_relocate_visitor(void *pathbase_v, char *path_dst, const char *pa
}
/* Make referenced file absolute. This would be a side-effect of
- * BLI_cleanup_path, but we do it explicitly so we know if it changed. */
+ * BLI_path_normalize, but we do it explicitly so we know if it changed. */
BLI_strncpy(filepath, path_src, FILE_MAX);
if (BLI_path_abs(filepath, base_old)) {
/* Path was relative and is now absolute. Remap.
- * Important BLI_cleanup_path runs before the path is made relative
+ * Important BLI_path_normalize runs before the path is made relative
* because it wont work for paths that start with "//../" */
- BLI_cleanup_path(base_new, filepath);
+ BLI_path_normalize(base_new, filepath);
BLI_path_rel(filepath, base_new);
BLI_strncpy(path_dst, filepath, FILE_MAX);
return true;
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index 2306b046026..e1303b8d723 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -344,6 +344,7 @@ typedef enum eGPCurveMappingPreset {
GPCURVE_PRESET_INK = 1,
GPCURVE_PRESET_INKNOISE = 2,
GPCURVE_PRESET_MARKER = 3,
+ GPCURVE_PRESET_CHISEL = 4,
} eGPCurveMappingPreset;
static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset)
@@ -375,9 +376,9 @@ static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset)
case GPCURVE_PRESET_INKNOISE:
cuma->curve[0].x = 0.0f;
cuma->curve[0].y = 0.0f;
- cuma->curve[1].x = 0.63134f;
- cuma->curve[1].y = 0.3625f;
- cuma->curve[2].x = 1.0f;
+ cuma->curve[1].x = 0.55f;
+ cuma->curve[1].y = 0.45f;
+ cuma->curve[2].x = 0.85f;
cuma->curve[2].y = 1.0f;
break;
case GPCURVE_PRESET_MARKER:
@@ -390,6 +391,12 @@ static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset)
cuma->curve[3].x = 1.0f;
cuma->curve[3].y = 1.0f;
break;
+ case GPCURVE_PRESET_CHISEL:
+ cuma->curve[0].x = 0.0f;
+ cuma->curve[0].y = 0.0f;
+ cuma->curve[1].x = 0.8f;
+ cuma->curve[1].y = 1.0f;
+ break;
}
if (cuma->table) {
@@ -519,7 +526,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->simplify_f = 0.000f;
brush->gpencil_settings->flag |= GP_BRUSH_GROUP_RANDOM;
- brush->gpencil_settings->draw_random_press = 1.0f;
+ brush->gpencil_settings->draw_random_press = 0.6f;
brush->gpencil_settings->draw_random_strength = 0.0f;
brush->gpencil_settings->draw_jitter = 0.0f;
brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
@@ -574,7 +581,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
break;
}
case GP_BRUSH_PRESET_MARKER_CHISEL: {
- brush->size = 80.0f;
+ brush->size = 100.0f;
brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE;
brush->gpencil_settings->draw_strength = 1.0f;
@@ -597,6 +604,12 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->draw_jitter = 0.0f;
brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
+ /* Curve. */
+ custom_curve = brush->gpencil_settings->curve_sensitivity;
+ BKE_curvemapping_set_defaults(custom_curve, 0, 0.0f, 0.0f, 1.0f, 1.0f);
+ BKE_curvemapping_initialize(custom_curve);
+ brush_gpencil_curvemap_reset(custom_curve->cm, 2, GPCURVE_PRESET_CHISEL);
+
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_CHISEL;
brush->gpencil_tool = GPAINT_TOOL_DRAW;
@@ -604,7 +617,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
break;
}
case GP_BRUSH_PRESET_PEN: {
- brush->size = 30.0f;
+ brush->size = 25.0f;
brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE;
brush->gpencil_settings->draw_strength = 1.0f;
@@ -662,11 +675,22 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PENCIL;
brush->gpencil_tool = GPAINT_TOOL_DRAW;
+ /* Create and link Black Dots material to brush.
+ * This material is required because the brush uses the material to define how the stroke is
+ * drawn. */
+ Material *ma = BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2);
+ if (ma == NULL) {
+ ma = BKE_gpencil_material_add(bmain, "Dots Stroke");
+ }
+ brush->gpencil_settings->material = ma;
+ /* Pin the matterial to the brush. */
+ brush->gpencil_settings->flag |= GP_BRUSH_MATERIAL_PINNED;
+
zero_v3(brush->secondary_rgb);
break;
}
case GP_BRUSH_PRESET_PENCIL: {
- brush->size = 25.0f;
+ brush->size = 20.0f;
brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE;
brush->gpencil_settings->draw_strength = 0.6f;
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index 87a5ac80bc7..754c2ce097f 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -3556,10 +3556,9 @@ void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source,
/* if we found a matching layer, copy the data */
if (dest->layers[dest_i].type == source->layers[src_i].type &&
STREQ(dest->layers[dest_i].name, source->layers[src_i].name)) {
- const void *src_data = POINTER_OFFSET(src_block, source->layers[src_i].offset);
- void *dest_data = POINTER_OFFSET(*dest_block, dest->layers[dest_i].offset);
-
if (no_mask || ((CD_TYPE_AS_MASK(dest->layers[dest_i].type) & mask_exclude) == 0)) {
+ const void *src_data = POINTER_OFFSET(src_block, source->layers[src_i].offset);
+ void *dest_data = POINTER_OFFSET(*dest_block, dest->layers[dest_i].offset);
const LayerTypeInfo *typeInfo = layerType_getInfo(source->layers[src_i].type);
if (typeInfo->copy) {
typeInfo->copy(src_data, dest_data, 1);
diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c
index 94976ed0c96..5aaae74e670 100644
--- a/source/blender/blenkernel/intern/editmesh.c
+++ b/source/blender/blenkernel/intern/editmesh.c
@@ -67,6 +67,10 @@ BMEditMesh *BKE_editmesh_copy(BMEditMesh *em)
* tessellation only when/if that copy ends up getting used. */
em_copy->looptris = NULL;
+ /* Copy various settings. */
+ em_copy->selectmode = em->selectmode;
+ em_copy->mat_nr = em->mat_nr;
+
return em_copy;
}
diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c
index cbb933725eb..e96f65751de 100644
--- a/source/blender/blenkernel/intern/fluid.c
+++ b/source/blender/blenkernel/intern/fluid.c
@@ -1250,7 +1250,8 @@ static void update_obstacles(Depsgraph *depsgraph,
/* Set scene time */
/* Handle emission subframe */
- if (subframe < subframes || time_per_frame + dt + FLT_EPSILON < frame_length) {
+ if ((subframe < subframes || time_per_frame + dt + FLT_EPSILON < frame_length) &&
+ !is_first_frame) {
scene->r.subframe = (time_per_frame + (subframe + 1.0f) * subframe_dt) / frame_length;
scene->r.cfra = frame - 1;
}
@@ -2763,7 +2764,8 @@ static void update_flowsfluids(struct Depsgraph *depsgraph,
FluidObjectBB bb_temp = {NULL};
/* Set scene time */
- if (subframe < subframes || time_per_frame + dt + FLT_EPSILON < frame_length) {
+ if ((subframe < subframes || time_per_frame + dt + FLT_EPSILON < frame_length) &&
+ !is_first_frame) {
scene->r.subframe = (time_per_frame + (subframe + 1.0f) * subframe_dt) / frame_length;
scene->r.cfra = frame - 1;
}
@@ -3999,7 +4001,11 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd,
}
if (has_data || baking_data) {
if (baking_noise && with_smoke && with_noise) {
- manta_bake_noise(mds->fluid, mmd, scene_framenr);
+ /* Ensure that no bake occurs if domain was minimized by adaptive domain. */
+ if (mds->total_cells > 1) {
+ manta_bake_noise(mds->fluid, mmd, scene_framenr);
+ }
+ manta_write_noise(mds->fluid, mmd, scene_framenr);
}
if (baking_mesh && with_liquid && with_mesh) {
manta_bake_mesh(mds->fluid, mmd, scene_framenr);
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index f58e7c06d7f..9bd1a632278 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -5316,8 +5316,8 @@ void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath)
index = (iuser && iuser->tile) ? iuser->tile : 1001;
}
- BLI_stringdec(filepath, head, tail, &numlen);
- BLI_stringenc(filepath, head, tail, numlen, index);
+ BLI_path_sequence_decode(filepath, head, tail, &numlen);
+ BLI_path_sequence_encode(filepath, head, tail, numlen, index);
}
BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id));
@@ -5458,7 +5458,7 @@ float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame, int
int BKE_image_sequence_guess_offset(Image *image)
{
- return BLI_stringdec(image->name, NULL, NULL, NULL);
+ return BLI_path_sequence_decode(image->name, NULL, NULL, NULL);
}
bool BKE_image_has_anim(Image *ima)
diff --git a/source/blender/blenkernel/intern/image_save.c b/source/blender/blenkernel/intern/image_save.c
index b2e0f234f1e..c034fe895a6 100644
--- a/source/blender/blenkernel/intern/image_save.c
+++ b/source/blender/blenkernel/intern/image_save.c
@@ -406,7 +406,7 @@ bool BKE_image_save(
if (ima->source == IMA_SRC_TILED) {
/* Verify filepath for tiles images. */
- if (BLI_stringdec(opts->filepath, NULL, NULL, NULL) != 1001) {
+ if (BLI_path_sequence_decode(opts->filepath, NULL, NULL, NULL) != 1001) {
BKE_reportf(reports,
RPT_ERROR,
"When saving a tiled image, the path '%s' must contain the UDIM tag 1001",
@@ -429,7 +429,7 @@ bool BKE_image_save(
char head[FILE_MAX], tail[FILE_MAX];
unsigned short numlen;
- BLI_stringdec(filepath, head, tail, &numlen);
+ BLI_path_sequence_decode(filepath, head, tail, &numlen);
/* Save all other tiles. */
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
@@ -439,7 +439,7 @@ bool BKE_image_save(
}
/* Build filepath of the tile. */
- BLI_stringenc(opts->filepath, head, tail, numlen, tile->tile_number);
+ BLI_path_sequence_encode(opts->filepath, head, tail, numlen, tile->tile_number);
iuser->tile = tile->tile_number;
ok = ok && image_save_single(reports, ima, iuser, opts, &colorspace_changed);
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 286ee4a8723..487ec0bf161 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -320,7 +320,11 @@ void id_us_min(ID *id)
id->lib ? id->lib->filepath : "[Main]",
id->us,
limit);
- BLI_assert(0);
+ if (GS(id->name) != ID_IP) {
+ /* Do not assert on deprecated ID types, we cannot really ensure that their ID refcounting
+ * is valid... */
+ BLI_assert(0);
+ }
id->us = limit;
}
else {
diff --git a/source/blender/blenkernel/intern/mesh_convert.c b/source/blender/blenkernel/intern/mesh_convert.c
index 74b79490d67..28a32e1573b 100644
--- a/source/blender/blenkernel/intern/mesh_convert.c
+++ b/source/blender/blenkernel/intern/mesh_convert.c
@@ -1155,9 +1155,21 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph, Object *object, bool preser
/* Happens in special cases like request of mesh for non-mother meta ball. */
return NULL;
}
+
/* The result must have 0 users, since it's just a mesh which is free-dangling data-block.
* All the conversion functions are supposed to ensure mesh is not counted. */
BLI_assert(new_mesh->id.us == 0);
+
+ /* It is possible that mesh came from modifier stack evaluation, which preserves edit_mesh
+ * pointer (which allows draw manager to access edit mesh when drawing). Normally this does
+ * not cause ownership problems because evaluated object runtime is keeping track of the real
+ * ownership.
+ *
+ * Here we are constructing a mesh which is supposed to be independent, which means no shared
+ * ownership is allowed, so we make sure edit mesh is reset to NULL (which is similar to as if
+ * one duplicates the objects and applies all the modifiers). */
+ new_mesh->edit_mesh = NULL;
+
return new_mesh;
}
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
index 1964dba7593..193fe859def 100644
--- a/source/blender/blenkernel/intern/movieclip.c
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -197,14 +197,14 @@ static void get_sequence_fname(const MovieClip *clip, const int framenr, char *n
int offset;
BLI_strncpy(name, clip->name, sizeof(clip->name));
- BLI_stringdec(name, head, tail, &numlen);
+ BLI_path_sequence_decode(name, head, tail, &numlen);
/* Movie-clips always points to first image from sequence, auto-guess offset for now.
* Could be something smarter in the future. */
offset = sequence_guess_offset(clip->name, strlen(head), numlen);
if (numlen) {
- BLI_stringenc(
+ BLI_path_sequence_encode(
name, head, tail, numlen, offset + framenr - clip->start_frame + clip->frame_offset);
}
else {
@@ -421,7 +421,7 @@ static void movieclip_calc_length(MovieClip *clip)
unsigned short numlen;
char name[FILE_MAX], head[FILE_MAX], tail[FILE_MAX];
- BLI_stringdec(clip->name, head, tail, &numlen);
+ BLI_path_sequence_decode(clip->name, head, tail, &numlen);
if (numlen == 0) {
/* there's no number group in file name, assume it's single framed sequence */
@@ -456,9 +456,10 @@ typedef struct MovieClipCache {
int flag;
/* cache for undistorted shot */
+ float focal_length;
float principal[2];
- float polynomial_k1;
- float division_k1;
+ float polynomial_k[3];
+ float division_k[2];
short distortion_model;
bool undistortion_used;
@@ -505,7 +506,7 @@ static int user_frame_to_cache_frame(MovieClip *clip, int framenr)
unsigned short numlen;
char head[FILE_MAX], tail[FILE_MAX];
- BLI_stringdec(clip->name, head, tail, &numlen);
+ BLI_path_sequence_decode(clip->name, head, tail, &numlen);
/* see comment in get_sequence_fname */
clip->cache->sequence_offset = sequence_guess_offset(clip->name, strlen(head), numlen);
@@ -648,7 +649,7 @@ static bool put_imbuf_cache(
clip->cache->sequence_offset = -1;
if (clip->source == MCLIP_SRC_SEQUENCE) {
unsigned short numlen;
- BLI_stringdec(clip->name, NULL, NULL, &numlen);
+ BLI_path_sequence_decode(clip->name, NULL, NULL, &numlen);
clip->cache->is_still_sequence = (numlen == 0);
}
}
@@ -887,6 +888,10 @@ static bool check_undistortion_cache_flags(const MovieClip *clip)
const MovieClipCache *cache = clip->cache;
const MovieTrackingCamera *camera = &clip->tracking.camera;
+ if (camera->focal != cache->postprocessed.focal_length) {
+ return false;
+ }
+
/* check for distortion model changes */
if (!equals_v2v2(camera->principal, cache->postprocessed.principal)) {
return false;
@@ -896,11 +901,11 @@ static bool check_undistortion_cache_flags(const MovieClip *clip)
return false;
}
- if (!equals_v3v3(&camera->k1, &cache->postprocessed.polynomial_k1)) {
+ if (!equals_v3v3(&camera->k1, cache->postprocessed.polynomial_k)) {
return false;
}
- if (!equals_v2v2(&camera->division_k1, &cache->postprocessed.division_k1)) {
+ if (!equals_v2v2(&camera->division_k1, cache->postprocessed.division_k)) {
return false;
}
@@ -1001,9 +1006,10 @@ static void put_postprocessed_frame_to_cache(
if (need_undistortion_postprocess(user, flag)) {
cache->postprocessed.distortion_model = camera->distortion_model;
+ cache->postprocessed.focal_length = camera->focal;
copy_v2_v2(cache->postprocessed.principal, camera->principal);
- copy_v3_v3(&cache->postprocessed.polynomial_k1, &camera->k1);
- copy_v2_v2(&cache->postprocessed.division_k1, &camera->division_k1);
+ copy_v3_v3(cache->postprocessed.polynomial_k, &camera->k1);
+ copy_v2_v2(cache->postprocessed.division_k, &camera->division_k1);
cache->postprocessed.undistortion_used = true;
}
else {
diff --git a/source/blender/blenkernel/intern/multires_reshape_apply_base.c b/source/blender/blenkernel/intern/multires_reshape_apply_base.c
index 90736de12e1..e67e553e8ed 100644
--- a/source/blender/blenkernel/intern/multires_reshape_apply_base.c
+++ b/source/blender/blenkernel/intern/multires_reshape_apply_base.c
@@ -81,6 +81,11 @@ static float v3_dist_from_plane(float v[3], float center[3], float no[3])
void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape_context)
{
+ if (reshape_context->mmd->simple) {
+ /* Simple subdivisions does not move base mesh verticies, so no refitting is needed. */
+ return;
+ }
+
Mesh *base_mesh = reshape_context->base_mesh;
MeshElemMap *pmap;
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 733f112f80c..f68b1a4d314 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -1883,7 +1883,9 @@ bool BKE_object_obdata_is_libdata(const Object *ob)
return (ob && ob->data && ID_IS_LINKED(ob->data));
}
-/* *************** PROXY **************** */
+/* -------------------------------------------------------------------- */
+/** \name Object Proxy API
+ * \{ */
/* when you make proxy, ensure the exposed layers are extern */
static void armature_set_id_extern(Object *ob)
@@ -2093,7 +2095,11 @@ void BKE_object_obdata_size_init(struct Object *ob, const float size)
}
}
-/* *************** CALC ****************** */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Object Matrix Get/Set API
+ * \{ */
void BKE_object_scale_to_mat3(Object *ob, float mat[3][3])
{
@@ -2586,6 +2592,12 @@ void BKE_object_get_parent_matrix(Object *ob, Object *par, float parentmat[4][4]
}
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Object Matrix Evaluation API
+ * \{ */
+
/**
* \param r_originmat: Optional matrix that stores the space the object is in
* (without its own matrix applied)
@@ -2790,6 +2802,12 @@ void BKE_object_apply_mat4(Object *ob,
BKE_object_apply_mat4_ex(ob, mat, use_parent ? ob->parent : NULL, ob->parentinv, use_compat);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Object Bounding Box API
+ * \{ */
+
BoundBox *BKE_boundbox_alloc_unit(void)
{
BoundBox *bb;
@@ -2911,6 +2929,8 @@ void BKE_object_boundbox_calc_from_mesh(struct Object *ob, struct Mesh *me_eval)
ob->runtime.bb->flag &= ~BOUNDBOX_DIRTY;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
/** \name Object Dimension Get/Set
*
@@ -3594,9 +3614,11 @@ void BKE_object_delete_ptcache(Object *ob, int index)
BLI_freelinkN(&ob->pc_ids, link);
}
-/* shape key utility function */
+/* -------------------------------------------------------------------- */
+/** \name Object Data Shape Key Insert
+ * \{ */
-/************************* Mesh ************************/
+/* Mesh */
static KeyBlock *insert_meshkey(Main *bmain, Object *ob, const char *name, const bool from_mix)
{
Mesh *me = ob->data;
@@ -3628,7 +3650,7 @@ static KeyBlock *insert_meshkey(Main *bmain, Object *ob, const char *name, const
return kb;
}
-/************************* Lattice ************************/
+/* Lattice */
static KeyBlock *insert_lattkey(Main *bmain, Object *ob, const char *name, const bool from_mix)
{
Lattice *lt = ob->data;
@@ -3666,7 +3688,7 @@ static KeyBlock *insert_lattkey(Main *bmain, Object *ob, const char *name, const
return kb;
}
-/************************* Curve ************************/
+/* Curve */
static KeyBlock *insert_curvekey(Main *bmain, Object *ob, const char *name, const bool from_mix)
{
Curve *cu = ob->data;
@@ -3707,6 +3729,12 @@ static KeyBlock *insert_curvekey(Main *bmain, Object *ob, const char *name, cons
return kb;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Object Shape Key API
+ * \{ */
+
KeyBlock *BKE_object_shapekey_insert(Main *bmain,
Object *ob,
const char *name,
@@ -3804,6 +3832,8 @@ bool BKE_object_shapekey_remove(Main *bmain, Object *ob, KeyBlock *kb)
return true;
}
+/** \} */
+
bool BKE_object_flag_test_recursive(const Object *ob, short flag)
{
if (ob->flag & flag) {
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index 78e7dec9f38..77b3f1580ac 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -2066,7 +2066,7 @@ static int ptcache_path(PTCacheID *pid, char *filename)
BLI_path_abs(filename, blendfilename);
}
- return BLI_add_slash(filename); /* new strlen() */
+ return BLI_path_slash_ensure(filename); /* new strlen() */
}
else if (G.relbase_valid || lib) {
char file[MAX_PTCACHE_PATH]; /* we don't want the dir, only the file */
@@ -2083,14 +2083,14 @@ static int ptcache_path(PTCacheID *pid, char *filename)
BLI_snprintf(filename, MAX_PTCACHE_PATH, "//" PTCACHE_PATH "%s", file);
BLI_path_abs(filename, blendfilename);
- return BLI_add_slash(filename); /* new strlen() */
+ return BLI_path_slash_ensure(filename); /* new strlen() */
}
/* use the temp path. this is weak but better then not using point cache at all */
/* temporary directory is assumed to exist and ALWAYS has a trailing slash */
BLI_snprintf(filename, MAX_PTCACHE_PATH, "%s" PTCACHE_PATH, BKE_tempdir_session());
- return BLI_add_slash(filename); /* new strlen() */
+ return BLI_path_slash_ensure(filename); /* new strlen() */
}
static int ptcache_filename(PTCacheID *pid, char *filename, int cfra, short do_path, short do_ext)
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 20f7add0c74..c4b6f5dafe5 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -2167,8 +2167,6 @@ GHash *BKE_scene_undo_depsgraphs_extract(Main *bmain)
void BKE_scene_undo_depsgraphs_restore(Main *bmain, GHash *depsgraph_extract)
{
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
- BLI_assert(scene->depsgraph_hash == NULL);
-
for (ViewLayer *view_layer = scene->view_layers.first; view_layer != NULL;
view_layer = view_layer->next) {
char key_full[MAX_ID_NAME + FILE_MAX + MAX_NAME] = {0};
diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c
index a2c0434a474..12c5821e858 100644
--- a/source/blender/blenkernel/intern/seqcache.c
+++ b/source/blender/blenkernel/intern/seqcache.c
@@ -243,7 +243,7 @@ static void seq_disk_cache_get_files(SeqDiskCache *disk_cache, char *path)
if (is_dir && !FILENAME_IS_CURRPAR(file)) {
char subpath[FILE_MAX];
BLI_strncpy(subpath, fl->path, sizeof(subpath));
- BLI_add_slash(subpath);
+ BLI_path_slash_ensure(subpath);
seq_disk_cache_get_files(disk_cache, subpath);
}
@@ -439,7 +439,7 @@ static void seq_disk_cache_delete_invalid_files(SeqDiskCache *disk_cache,
DiskCacheFile *next_file, *cache_file = disk_cache->files.first;
char cache_dir[FILE_MAX];
seq_disk_cache_get_dir(disk_cache, scene, seq, cache_dir, sizeof(cache_dir));
- BLI_add_slash(cache_dir);
+ BLI_path_slash_ensure(cache_dir);
while (cache_file) {
next_file = cache_file->next;
diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c
index 97a643e88b6..64fe396d6a3 100644
--- a/source/blender/blenkernel/intern/sound.c
+++ b/source/blender/blenkernel/intern/sound.c
@@ -751,12 +751,20 @@ static void sound_start_play_scene(Scene *scene)
}
}
+static float get_cur_time(Scene *scene)
+{
+ /* We divide by the current framelen to take into account time remapping.
+ * Otherwise we will get the wrong starting time which will break A/V sync.
+ * See T74111 for further details. */
+ return FRA2TIME((CFRA + SUBFRA) / scene->r.framelen);
+}
+
void BKE_sound_play_scene(Scene *scene)
{
sound_verify_evaluated_id(&scene->id);
AUD_Status status;
- const float cur_time = (float)((double)CFRA / FPS);
+ const float cur_time = get_cur_time(scene);
AUD_Device_lock(sound_device);
@@ -804,7 +812,7 @@ void BKE_sound_seek_scene(Main *bmain, Scene *scene)
int animation_playing;
const float one_frame = (float)(1.0 / FPS);
- const float cur_time = (float)((double)CFRA / FPS);
+ const float cur_time = FRA2TIME(CFRA);
AUD_Device_lock(sound_device);
diff --git a/source/blender/blenkernel/intern/tracking_util.c b/source/blender/blenkernel/intern/tracking_util.c
index 51758abdf3f..5f7452e4775 100644
--- a/source/blender/blenkernel/intern/tracking_util.c
+++ b/source/blender/blenkernel/intern/tracking_util.c
@@ -35,6 +35,7 @@
#include "BLI_math.h"
#include "BLI_string.h"
#include "BLI_string_utils.h"
+#include "BLI_threads.h"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
@@ -432,6 +433,59 @@ void tracking_marker_insert_disabled(MovieTrackingTrack *track,
}
}
+static void distortion_model_parameters_from_tracking(
+ const MovieTrackingCamera *camera, libmv_CameraIntrinsicsOptions *camera_intrinsics_options)
+{
+ switch (camera->distortion_model) {
+ case TRACKING_DISTORTION_MODEL_POLYNOMIAL:
+ camera_intrinsics_options->distortion_model = LIBMV_DISTORTION_MODEL_POLYNOMIAL;
+ camera_intrinsics_options->polynomial_k1 = camera->k1;
+ camera_intrinsics_options->polynomial_k2 = camera->k2;
+ camera_intrinsics_options->polynomial_k3 = camera->k3;
+ camera_intrinsics_options->polynomial_p1 = 0.0;
+ camera_intrinsics_options->polynomial_p2 = 0.0;
+ return;
+
+ case TRACKING_DISTORTION_MODEL_DIVISION:
+ camera_intrinsics_options->distortion_model = LIBMV_DISTORTION_MODEL_DIVISION;
+ camera_intrinsics_options->division_k1 = camera->division_k1;
+ camera_intrinsics_options->division_k2 = camera->division_k2;
+ return;
+ }
+
+ /* Unknown distortion model, which might be due to opening newer file in older Blender.
+ * Fallback to a known and supported model with 0 distortion. */
+ camera_intrinsics_options->distortion_model = LIBMV_DISTORTION_MODEL_POLYNOMIAL;
+ camera_intrinsics_options->polynomial_k1 = 0.0;
+ camera_intrinsics_options->polynomial_k2 = 0.0;
+ camera_intrinsics_options->polynomial_k3 = 0.0;
+ camera_intrinsics_options->polynomial_p1 = 0.0;
+ camera_intrinsics_options->polynomial_p2 = 0.0;
+}
+
+static void distortion_model_parameters_from_options(
+ const libmv_CameraIntrinsicsOptions *camera_intrinsics_options, MovieTrackingCamera *camera)
+{
+ switch (camera_intrinsics_options->distortion_model) {
+ case LIBMV_DISTORTION_MODEL_POLYNOMIAL:
+ camera->distortion_model = TRACKING_DISTORTION_MODEL_POLYNOMIAL;
+ camera->k1 = camera_intrinsics_options->polynomial_k1;
+ camera->k2 = camera_intrinsics_options->polynomial_k2;
+ camera->k3 = camera_intrinsics_options->polynomial_k3;
+ return;
+
+ case LIBMV_DISTORTION_MODEL_DIVISION:
+ camera->distortion_model = TRACKING_DISTORTION_MODEL_DIVISION;
+ camera->division_k1 = camera_intrinsics_options->division_k1;
+ camera->division_k2 = camera_intrinsics_options->division_k2;
+ return;
+ }
+
+ /* Libmv returned distortion model which is not known to Blender. This is a logical error in code
+ * and Blender side is to be updated to match Libmv. */
+ BLI_assert(!"Unknown distortion model");
+}
+
/* Fill in Libmv C-API camera intrinsics options from tracking structure. */
void tracking_cameraIntrinscisOptionsFromTracking(
MovieTracking *tracking,
@@ -442,29 +496,14 @@ void tracking_cameraIntrinscisOptionsFromTracking(
MovieTrackingCamera *camera = &tracking->camera;
float aspy = 1.0f / tracking->camera.pixel_aspect;
+ camera_intrinsics_options->num_threads = BLI_system_thread_count();
+
camera_intrinsics_options->focal_length = camera->focal;
camera_intrinsics_options->principal_point_x = camera->principal[0];
camera_intrinsics_options->principal_point_y = camera->principal[1] * aspy;
- switch (camera->distortion_model) {
- case TRACKING_DISTORTION_MODEL_POLYNOMIAL:
- camera_intrinsics_options->distortion_model = LIBMV_DISTORTION_MODEL_POLYNOMIAL;
- camera_intrinsics_options->polynomial_k1 = camera->k1;
- camera_intrinsics_options->polynomial_k2 = camera->k2;
- camera_intrinsics_options->polynomial_k3 = camera->k3;
- camera_intrinsics_options->polynomial_p1 = 0.0;
- camera_intrinsics_options->polynomial_p2 = 0.0;
- break;
- case TRACKING_DISTORTION_MODEL_DIVISION:
- camera_intrinsics_options->distortion_model = LIBMV_DISTORTION_MODEL_DIVISION;
- camera_intrinsics_options->division_k1 = camera->division_k1;
- camera_intrinsics_options->division_k2 = camera->division_k2;
- break;
- default:
- BLI_assert(!"Unknown distortion model");
- break;
- }
+ distortion_model_parameters_from_tracking(camera, camera_intrinsics_options);
camera_intrinsics_options->image_width = calibration_width;
camera_intrinsics_options->image_height = (int)(calibration_height * aspy);
@@ -481,22 +520,7 @@ void tracking_trackingCameraFromIntrinscisOptions(
camera->principal[0] = camera_intrinsics_options->principal_point_x;
camera->principal[1] = camera_intrinsics_options->principal_point_y / (double)aspy;
- switch (camera_intrinsics_options->distortion_model) {
- case LIBMV_DISTORTION_MODEL_POLYNOMIAL:
- camera->distortion_model = TRACKING_DISTORTION_MODEL_POLYNOMIAL;
- camera->k1 = camera_intrinsics_options->polynomial_k1;
- camera->k2 = camera_intrinsics_options->polynomial_k2;
- camera->k3 = camera_intrinsics_options->polynomial_k3;
- break;
- case LIBMV_DISTORTION_MODEL_DIVISION:
- camera->distortion_model = TRACKING_DISTORTION_MODEL_DIVISION;
- camera->division_k1 = camera_intrinsics_options->division_k1;
- camera->division_k2 = camera_intrinsics_options->division_k2;
- break;
- default:
- BLI_assert(!"Unknown distortion model");
- break;
- }
+ distortion_model_parameters_from_options(camera_intrinsics_options, camera);
}
/* Get previous keyframed marker. */
diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h
index 4376aefdc25..9a6a14547d2 100644
--- a/source/blender/blenlib/BLI_path_util.h
+++ b/source/blender/blenlib/BLI_path_util.h
@@ -56,11 +56,11 @@ bool BLI_path_name_at_index(const char *__restrict path,
int *__restrict r_offset,
int *__restrict r_len) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
-const char *BLI_last_slash(const char *string) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
-int BLI_add_slash(char *string) ATTR_NONNULL();
-void BLI_del_slash(char *string) ATTR_NONNULL();
-const char *BLI_first_slash(const char *string) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
-void BLI_path_native_slash(char *path) ATTR_NONNULL();
+const char *BLI_path_slash_rfind(const char *string) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
+int BLI_path_slash_ensure(char *string) ATTR_NONNULL();
+void BLI_path_slash_rstrip(char *string) ATTR_NONNULL();
+const char *BLI_path_slash_find(const char *string) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
+void BLI_path_slash_native(char *path) ATTR_NONNULL();
#ifdef _WIN32
bool BLI_path_program_extensions_add_win32(char *name, const size_t maxlen);
@@ -77,22 +77,22 @@ bool BLI_path_extension_check_glob(const char *str, const char *ext_fnmatch)
bool BLI_path_extension_glob_validate(char *ext_fnmatch) ATTR_NONNULL();
bool BLI_path_extension_replace(char *path, size_t maxlen, const char *ext) ATTR_NONNULL();
bool BLI_path_extension_ensure(char *path, size_t maxlen, const char *ext) ATTR_NONNULL();
-bool BLI_ensure_filename(char *filepath, size_t maxlen, const char *filename) ATTR_NONNULL();
-int BLI_stringdec(const char *string, char *head, char *start, unsigned short *numlen);
-void BLI_stringenc(
+bool BLI_path_filename_ensure(char *filepath, size_t maxlen, const char *filename) ATTR_NONNULL();
+int BLI_path_sequence_decode(const char *string, char *head, char *start, unsigned short *numlen);
+void BLI_path_sequence_encode(
char *string, const char *head, const char *tail, unsigned short numlen, int pic);
-void BLI_cleanup_path(const char *relabase, char *path) ATTR_NONNULL(2);
+void BLI_path_normalize(const char *relabase, char *path) ATTR_NONNULL(2);
/* Same as above but adds a trailing slash. */
-void BLI_cleanup_dir(const char *relabase, char *dir) ATTR_NONNULL(2);
+void BLI_path_normalize_dir(const char *relabase, char *dir) ATTR_NONNULL(2);
bool BLI_filename_make_safe(char *fname) ATTR_NONNULL(1);
bool BLI_path_make_safe(char *path) ATTR_NONNULL(1);
/* Go back one directory. */
-bool BLI_parent_dir(char *path) ATTR_NONNULL();
+bool BLI_path_parent_dir(char *path) ATTR_NONNULL();
/* Go back until the directory is found. */
-bool BLI_parent_dir_until_exists(char *path) ATTR_NONNULL();
+bool BLI_path_parent_dir_until_exists(char *path) ATTR_NONNULL();
bool BLI_path_abs(char *path, const char *basepath) ATTR_NONNULL();
bool BLI_path_frame(char *path, int frame, int digits) ATTR_NONNULL();
@@ -100,7 +100,7 @@ bool BLI_path_frame_range(char *path, int sta, int end, int digits) ATTR_NONNULL
bool BLI_path_frame_get(char *path, int *r_frame, int *numdigits) ATTR_NONNULL();
void BLI_path_frame_strip(char *path, char *ext) ATTR_NONNULL();
bool BLI_path_frame_check_chars(const char *path) ATTR_NONNULL();
-bool BLI_path_cwd(char *path, const size_t maxlen) ATTR_NONNULL();
+bool BLI_path_abs_from_cwd(char *path, const size_t maxlen) ATTR_NONNULL();
void BLI_path_rel(char *file, const char *relfile) ATTR_NONNULL();
bool BLI_path_is_rel(const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
@@ -109,8 +109,8 @@ bool BLI_path_is_unc(const char *path);
void BLI_path_to_display_name(char *display_name, int maxlen, const char *name) ATTR_NONNULL();
#if defined(WIN32)
-void BLI_cleanup_unc_16(wchar_t *path_16);
-void BLI_cleanup_unc(char *path_16, int maxlen);
+void BLI_path_normalize_unc_16(wchar_t *path_16);
+void BLI_path_normalize_unc(char *path_16, int maxlen);
#endif
bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char *sep)
diff --git a/source/blender/blenlib/intern/BLI_filelist.c b/source/blender/blenlib/intern/BLI_filelist.c
index 2e402f0c063..26f1de33aa9 100644
--- a/source/blender/blenlib/intern/BLI_filelist.c
+++ b/source/blender/blenlib/intern/BLI_filelist.c
@@ -147,7 +147,7 @@ static void bli_builddir(struct BuildDirCtx *dir_ctx, const char *dirname)
char pardir[FILE_MAXDIR];
BLI_strncpy(pardir, dirname, sizeof(pardir));
- if (BLI_parent_dir(pardir) && (BLI_access(pardir, R_OK) == 0)) {
+ if (BLI_path_parent_dir(pardir) && (BLI_access(pardir, R_OK) == 0)) {
struct dirlink *const dlink = (struct dirlink *)malloc(sizeof(struct dirlink));
if (dlink != NULL) {
dlink->name = BLI_strdup(FILENAME_PARENT);
diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c
index 0800027e520..09dbf18acd0 100644
--- a/source/blender/blenlib/intern/BLI_ghash.c
+++ b/source/blender/blenlib/intern/BLI_ghash.c
@@ -1056,7 +1056,7 @@ void BLI_ghash_flag_clear(GHash *gh, uint flag)
* #BLI_ghash_len(gh) times before becoming done.
*
* \param gh: The GHash to iterate over.
- * \return Pointer to a new DynStr.
+ * \return Pointer to a new iterator.
*/
GHashIterator *BLI_ghashIterator_new(GHash *gh)
{
diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c
index da77046547c..b9133edcae3 100644
--- a/source/blender/blenlib/intern/fileops.c
+++ b/source/blender/blenlib/intern/fileops.c
@@ -492,7 +492,7 @@ static bool delete_recursive(const char *dir)
/* dir listing produces dir path without trailing slash... */
BLI_strncpy(path, fl->path, sizeof(path));
- BLI_add_slash(path);
+ BLI_path_slash_ensure(path);
if (delete_recursive(path)) {
err = true;
@@ -562,9 +562,9 @@ int BLI_move(const char *file, const char *to)
BLI_strncpy(str, to, sizeof(str));
/* points 'to' to a directory ? */
- if (BLI_last_slash(str) == (str + strlen(str) - 1)) {
- if (BLI_last_slash(file) != NULL) {
- strcat(str, BLI_last_slash(file) + 1);
+ if (BLI_path_slash_rfind(str) == (str + strlen(str) - 1)) {
+ if (BLI_path_slash_rfind(file) != NULL) {
+ strcat(str, BLI_path_slash_rfind(file) + 1);
}
}
@@ -594,9 +594,9 @@ int BLI_copy(const char *file, const char *to)
BLI_strncpy(str, to, sizeof(str));
/* points 'to' to a directory ? */
- if (BLI_last_slash(str) == (str + strlen(str) - 1)) {
- if (BLI_last_slash(file) != NULL) {
- strcat(str, BLI_last_slash(file) + 1);
+ if (BLI_path_slash_rfind(str) == (str + strlen(str) - 1)) {
+ if (BLI_path_slash_rfind(file) != NULL) {
+ strcat(str, BLI_path_slash_rfind(file) + 1);
}
}
@@ -638,7 +638,7 @@ bool BLI_dir_create_recursive(const char *dirname)
* blah1/blah2 (without slash) */
BLI_strncpy(tmp, dirname, sizeof(tmp));
- BLI_del_slash(tmp);
+ BLI_path_slash_rstrip(tmp);
/* check special case "c:\foo", don't try create "c:", harmless but prints an error below */
if (isalpha(tmp[0]) && (tmp[1] == ':') && tmp[2] == '\0') {
@@ -652,7 +652,7 @@ bool BLI_dir_create_recursive(const char *dirname)
return false;
}
- lslash = (char *)BLI_last_slash(tmp);
+ lslash = (char *)BLI_path_slash_rfind(tmp);
if (lslash) {
/* Split about the last slash and recurse */
@@ -723,7 +723,7 @@ static void join_dirfile_alloc(char **dst, size_t *alloc_len, const char *dir, c
static char *strip_last_slash(const char *dir)
{
char *result = BLI_strdup(dir);
- BLI_del_slash(result);
+ BLI_path_slash_rstrip(result);
return result;
}
@@ -1277,7 +1277,7 @@ static const char *check_destination(const char *file, const char *to)
size_t len = 0;
str = strip_last_slash(file);
- filename = BLI_last_slash(str);
+ filename = BLI_path_slash_rfind(str);
if (!filename) {
MEM_freeN(str);
@@ -1350,9 +1350,9 @@ bool BLI_dir_create_recursive(const char *dirname)
BLI_strncpy(tmp, dirname, size);
/* Avoids one useless recursion in case of '/foo/bar/' path... */
- BLI_del_slash(tmp);
+ BLI_path_slash_rstrip(tmp);
- lslash = (char *)BLI_last_slash(tmp);
+ lslash = (char *)BLI_path_slash_rfind(tmp);
if (lslash) {
/* Split about the last slash and recurse */
*lslash = 0;
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index 4c0d7e08a3c..a26824bd2b5 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -2379,7 +2379,10 @@ bool isect_tri_tri_epsilon_v3(const float t_a0[3],
if (UNLIKELY(edge_fac == -1.0f)) {
/* pass */
}
- else if (edge_fac > 0.0f && edge_fac < 1.0f) {
+ /* Important to include 0.0f and 1.0f as one of the triangles vertices may be placed
+ * exactly on the plane. In this case both it's edges will have a factor of 0 or 1,
+ * but not be going through the plane. See T73566. */
+ else if (edge_fac >= 0.0f && edge_fac <= 1.0f) {
float ix_tri[3];
float span_fac;
diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c
index 5919b7e1dd6..9009f73a62f 100644
--- a/source/blender/blenlib/intern/math_vector.c
+++ b/source/blender/blenlib/intern/math_vector.c
@@ -778,7 +778,7 @@ void bisect_v3_v3v3v3(float out[3], const float v1[3], const float v2[3], const
* <pre>
* v
* + ^
- * \ |
+ * \ |
* \|
* + normal: axis of reflection
* /
diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c
index 0bb2ba5859b..2f51b66725b 100644
--- a/source/blender/blenlib/intern/path_util.c
+++ b/source/blender/blenlib/intern/path_util.c
@@ -80,12 +80,12 @@ static bool BLI_path_is_abs(const char *name);
* or from dot if no digits.
* \param r_num_len: Optional to return number of digits found.
*/
-int BLI_stringdec(const char *string, char *head, char *tail, ushort *r_num_len)
+int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort *r_num_len)
{
uint nums = 0, nume = 0;
int i;
bool found_digit = false;
- const char *const lslash = BLI_last_slash(string);
+ const char *const lslash = BLI_path_slash_rfind(string);
const uint string_len = strlen(string);
const uint lslash_len = lslash != NULL ? (int)(lslash - string) : 0;
uint name_end = string_len;
@@ -151,7 +151,7 @@ int BLI_stringdec(const char *string, char *head, char *tail, ushort *r_num_len)
* Returns in area pointed to by string a string of the form "<head><pic><tail>", where pic
* is formatted as numlen digits with leading zeroes.
*/
-void BLI_stringenc(
+void BLI_path_sequence_encode(
char *string, const char *head, const char *tail, unsigned short numlen, int pic)
{
sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail);
@@ -170,7 +170,7 @@ static int BLI_path_unc_prefix_len(const char *path); /* defined below in same f
*
* \note \a path isn't protected for max string names...
*/
-void BLI_cleanup_path(const char *relabase, char *path)
+void BLI_path_normalize(const char *relabase, char *path)
{
ptrdiff_t a;
char *start, *eind;
@@ -263,10 +263,10 @@ void BLI_cleanup_path(const char *relabase, char *path)
/**
* Cleanup filepath ensuring a trailing slash.
*/
-void BLI_cleanup_dir(const char *relabase, char *dir)
+void BLI_path_normalize_dir(const char *relabase, char *dir)
{
- BLI_cleanup_path(relabase, dir);
- BLI_add_slash(dir);
+ BLI_path_normalize(relabase, dir);
+ BLI_path_slash_ensure(dir);
}
/**
@@ -381,8 +381,8 @@ bool BLI_path_make_safe(char *path)
}
#endif
- for (curr_slash = (char *)BLI_first_slash(curr_path); curr_slash;
- curr_slash = (char *)BLI_first_slash(curr_path)) {
+ for (curr_slash = (char *)BLI_path_slash_find(curr_path); curr_slash;
+ curr_slash = (char *)BLI_path_slash_find(curr_path)) {
const char backup = *curr_slash;
*curr_slash = '\0';
if (!skip_first && (*curr_path != '\0') && BLI_filename_make_safe(curr_path)) {
@@ -494,14 +494,14 @@ static void BLI_path_unc_to_short(wchar_t *unc)
}
}
-void BLI_cleanup_unc(char *path, int maxlen)
+void BLI_path_normalize_unc(char *path, int maxlen)
{
wchar_t *tmp_16 = alloc_utf16_from_8(path, 1);
- BLI_cleanup_unc_16(tmp_16);
+ BLI_path_normalize_unc_16(tmp_16);
conv_utf_16_to_8(tmp_16, path, maxlen);
}
-void BLI_cleanup_unc_16(wchar_t *path_16)
+void BLI_path_normalize_unc_16(wchar_t *path_16)
{
BLI_path_unc_to_short(path_16);
BLI_path_add_slash_to_share(path_16);
@@ -578,11 +578,11 @@ void BLI_path_rel(char *file, const char *relfile)
BLI_str_replace_char(file + BLI_path_unc_prefix_len(file), '\\', '/');
/* remove /./ which confuse the following slash counting... */
- BLI_cleanup_path(NULL, file);
- BLI_cleanup_path(NULL, temp);
+ BLI_path_normalize(NULL, file);
+ BLI_path_normalize(NULL, temp);
/* the last slash in the file indicates where the path part ends */
- lslash = BLI_last_slash(temp);
+ lslash = BLI_path_slash_rfind(temp);
if (lslash) {
/* find the prefix of the filename that is equal for both filenames.
@@ -701,13 +701,13 @@ bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char
* Replaces path with the path of its parent directory, returning true if
* it was able to find a parent directory within the pathname.
*/
-bool BLI_parent_dir(char *path)
+bool BLI_path_parent_dir(char *path)
{
const char parent_dir[] = {'.', '.', SEP, '\0'}; /* "../" or "..\\" */
char tmp[FILE_MAX + 4];
BLI_join_dirfile(tmp, sizeof(tmp), path, parent_dir);
- BLI_cleanup_path(NULL, tmp); /* does all the work of normalizing the path for us */
+ BLI_path_normalize(NULL, tmp); /* does all the work of normalizing the path for us */
if (!BLI_path_extension_check(tmp, parent_dir)) {
strcpy(path, tmp); /* We assume pardir is always shorter... */
@@ -722,12 +722,12 @@ bool BLI_parent_dir(char *path)
* Strips off nonexistent (or non-accessible) subdirectories from the end of *dir,
* leaving the path of the lowest-level directory that does exist and we can read.
*/
-bool BLI_parent_dir_until_exists(char *dir)
+bool BLI_path_parent_dir_until_exists(char *dir)
{
bool valid_path = true;
/* Loop as long as cur path is not a dir, and we can get a parent path. */
- while ((BLI_access(dir, R_OK) != 0) && (valid_path = BLI_parent_dir(dir))) {
+ while ((BLI_access(dir, R_OK) != 0) && (valid_path = BLI_path_parent_dir(dir))) {
/* pass */
}
return (valid_path && dir[0]);
@@ -777,7 +777,7 @@ static bool stringframe_chars(const char *path, int *char_start, int *char_end)
*/
static void ensure_digits(char *path, int digits)
{
- char *file = (char *)BLI_last_slash(path);
+ char *file = (char *)BLI_path_slash_rfind(path);
if (file == NULL) {
file = path;
@@ -852,7 +852,7 @@ bool BLI_path_frame_range(char *path, int sta, int end, int digits)
bool BLI_path_frame_get(char *path, int *r_frame, int *r_numdigits)
{
if (*path) {
- char *file = (char *)BLI_last_slash(path);
+ char *file = (char *)BLI_path_slash_rfind(path);
char *c;
int len, numdigits;
@@ -908,7 +908,7 @@ void BLI_path_frame_strip(char *path, char *r_ext)
return;
}
- char *file = (char *)BLI_last_slash(path);
+ char *file = (char *)BLI_path_slash_rfind(path);
char *c, *suffix;
int len;
int numdigits = 0;
@@ -1075,8 +1075,8 @@ bool BLI_path_abs(char *path, const char *basepath)
BLI_strncpy(base, basepath, sizeof(base));
/* file component is ignored, so don't bother with the trailing slash */
- BLI_cleanup_path(NULL, base);
- lslash = BLI_last_slash(base);
+ BLI_path_normalize(NULL, base);
+ lslash = BLI_path_slash_rfind(base);
BLI_str_replace_char(base + BLI_path_unc_prefix_len(base), '\\', '/');
if (lslash) {
@@ -1110,19 +1110,20 @@ bool BLI_path_abs(char *path, const char *basepath)
#endif
/* ensure this is after correcting for path switch */
- BLI_cleanup_path(NULL, path);
+ BLI_path_normalize(NULL, path);
return wasrelative;
}
/**
- * Expands path relative to the current working directory, if it was relative.
- * Returns true if such expansion was done.
+ * Checks for relative path, expanding them relative to the current working directory.
+ * Returns true if the expansion was performed.
*
- * \note Should only be done with command line paths.
- * this is _not_ something blenders internal paths support like the "//" prefix
+ * \note Should only be called with command line paths.
+ * This is _not_ something Blender's internal paths support, instead they use the "//" prefix.
+ * In most cases #BLI_path_abs should be used instead.
*/
-bool BLI_path_cwd(char *path, const size_t maxlen)
+bool BLI_path_abs_from_cwd(char *path, const size_t maxlen)
{
#ifdef DEBUG_STRSIZE
memset(path, 0xff, sizeof(*path) * maxlen);
@@ -1363,7 +1364,7 @@ void BLI_make_file_string(const char *relabase, char *string, const char *dir, c
/* Get the file name, chop everything past the last slash (ie. the filename) */
strcpy(string, relabase);
- lslash = (char *)BLI_last_slash(string);
+ lslash = (char *)BLI_path_slash_rfind(string);
if (lslash) {
*(lslash + 1) = 0;
}
@@ -1418,7 +1419,7 @@ void BLI_make_file_string(const char *relabase, char *string, const char *dir, c
strcat(string, file);
/* Push all slashes to the system preferred direction */
- BLI_path_native_slash(string);
+ BLI_path_slash_native(string);
}
static bool path_extension_check_ex(const char *str,
@@ -1608,12 +1609,12 @@ bool BLI_path_extension_ensure(char *path, size_t maxlen, const char *ext)
return true;
}
-bool BLI_ensure_filename(char *filepath, size_t maxlen, const char *filename)
+bool BLI_path_filename_ensure(char *filepath, size_t maxlen, const char *filename)
{
#ifdef DEBUG_STRSIZE
memset(filepath, 0xff, sizeof(*filepath) * maxlen);
#endif
- char *c = (char *)BLI_last_slash(filepath);
+ char *c = (char *)BLI_path_slash_rfind(filepath);
if (!c || ((c - filepath) < maxlen - (strlen(filename) + 1))) {
strcpy(c ? &c[1] : filepath, filename);
return true;
@@ -1636,7 +1637,7 @@ void BLI_split_dirfile(
memset(dir, 0xff, sizeof(*dir) * dirlen);
memset(file, 0xff, sizeof(*file) * filelen);
#endif
- const char *lslash_str = BLI_last_slash(string);
+ const char *lslash_str = BLI_path_slash_rfind(string);
const size_t lslash = lslash_str ? (size_t)(lslash_str - string) + 1 : 0;
if (dir) {
@@ -1680,7 +1681,7 @@ const char *BLI_path_extension(const char *filepath)
if (extension == NULL) {
return NULL;
}
- if (BLI_first_slash(extension) != NULL) {
+ if (BLI_path_slash_find(extension) != NULL) {
/* There is a path separator in the extension, so the '.' was found in a
* directory component and not in the filename. */
return NULL;
@@ -1695,7 +1696,7 @@ void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__re
{
size_t dirlen = BLI_strnlen(dst, maxlen);
- /* inline BLI_add_slash */
+ /* inline BLI_path_slash_ensure */
if ((dirlen > 0) && (dst[dirlen - 1] != SEP)) {
dst[dirlen++] = SEP;
dst[dirlen] = '\0';
@@ -1738,7 +1739,7 @@ void BLI_join_dirfile(char *__restrict dst,
return; /* fills the path */
}
- /* inline BLI_add_slash */
+ /* inline BLI_path_slash_ensure */
if ((dirlen > 0) && !ELEM(dst[dirlen - 1], SEP, ALTSEP)) {
dst[dirlen++] = SEP;
dst[dirlen] = '\0';
@@ -1846,7 +1847,7 @@ size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *pat
*/
const char *BLI_path_basename(const char *path)
{
- const char *const filename = BLI_last_slash(path);
+ const char *const filename = BLI_path_slash_rfind(path);
return filename ? filename + 1 : path;
}
@@ -1921,7 +1922,7 @@ bool BLI_path_name_at_index(const char *__restrict path,
/**
* Returns pointer to the leftmost path separator in string. Not actually used anywhere.
*/
-const char *BLI_first_slash(const char *string)
+const char *BLI_path_slash_find(const char *string)
{
const char *const ffslash = strchr(string, '/');
const char *const fbslash = strchr(string, '\\');
@@ -1939,7 +1940,7 @@ const char *BLI_first_slash(const char *string)
/**
* Returns pointer to the rightmost path separator in string.
*/
-const char *BLI_last_slash(const char *string)
+const char *BLI_path_slash_rfind(const char *string)
{
const char *const lfslash = strrchr(string, '/');
const char *const lbslash = strrchr(string, '\\');
@@ -1958,7 +1959,7 @@ const char *BLI_last_slash(const char *string)
* Appends a slash to string if there isn't one there already.
* Returns the new length of the string.
*/
-int BLI_add_slash(char *string)
+int BLI_path_slash_ensure(char *string)
{
int len = strlen(string);
if (len == 0 || string[len - 1] != SEP) {
@@ -1972,7 +1973,7 @@ int BLI_add_slash(char *string)
/**
* Removes the last slash and everything after it to the end of string, if there is one.
*/
-void BLI_del_slash(char *string)
+void BLI_path_slash_rstrip(char *string)
{
int len = strlen(string);
while (len) {
@@ -1989,7 +1990,7 @@ void BLI_del_slash(char *string)
/**
* Changes to the path separators to the native ones for this OS.
*/
-void BLI_path_native_slash(char *path)
+void BLI_path_slash_native(char *path)
{
#ifdef WIN32
if (path && BLI_strnlen(path, 3) > 2) {
diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c
index 7274a15661a..9b437b02c0a 100644
--- a/source/blender/blenlib/intern/storage.c
+++ b/source/blender/blenlib/intern/storage.c
@@ -316,7 +316,7 @@ int BLI_exists(const char *name)
* 2. after the C:\ when the path is the volume only
*/
if ((len >= 3) && (tmp_16[0] == L'\\') && (tmp_16[1] == L'\\')) {
- BLI_cleanup_unc_16(tmp_16);
+ BLI_path_normalize_unc_16(tmp_16);
}
if ((tmp_16[1] == L':') && (tmp_16[2] == L'\0')) {
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 7d4fe2118cb..7f26019b925 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -470,8 +470,9 @@ static void *oldnewmap_liblookup(OldNewMap *onm, const void *addr, const void *l
return NULL;
}
-static void oldnewmap_free_unused(OldNewMap *onm)
+static void oldnewmap_clear(OldNewMap *onm)
{
+ /* Free unused data. */
for (int i = 0; i < onm->nentries; i++) {
OldNew *entry = &onm->entries[i];
if (entry->nr == 0) {
@@ -479,10 +480,7 @@ static void oldnewmap_free_unused(OldNewMap *onm)
entry->newp = NULL;
}
}
-}
-static void oldnewmap_clear(OldNewMap *onm)
-{
onm->capacity_exp = DEFAULT_SIZE_EXP;
oldnewmap_clear_map(onm);
onm->nentries = 0;
@@ -664,7 +662,7 @@ static Main *blo_find_main(FileData *fd, const char *filepath, const char *relab
char name1[FILE_MAX];
BLI_strncpy(name1, filepath, sizeof(name1));
- BLI_cleanup_path(relabase, name1);
+ BLI_path_normalize(relabase, name1);
// printf("blo_find_main: relabase %s\n", relabase);
// printf("blo_find_main: original in %s\n", filepath);
@@ -1680,7 +1678,7 @@ bool BLO_library_path_explode(const char *path, char *r_dir, char **r_group, cha
strcpy(r_dir, path);
- while ((slash = (char *)BLI_last_slash(r_dir))) {
+ while ((slash = (char *)BLI_path_slash_rfind(r_dir))) {
char tc = *slash;
*slash = '\0';
if (BLO_has_bfile_extension(r_dir) && BLI_is_file(r_dir)) {
@@ -2376,9 +2374,6 @@ static void *read_struct(FileData *fd, BHead *bh, const char *blockname)
}
}
- if (!BHEADN_FROM_BHEAD(bh)->is_memchunk_identical) {
- fd->are_memchunks_identical = false;
- }
#ifdef USE_BHEAD_READ_ON_DEMAND
if (bh_orig != bh) {
MEM_freeN(BHEADN_FROM_BHEAD(bh));
@@ -2389,6 +2384,15 @@ static void *read_struct(FileData *fd, BHead *bh, const char *blockname)
return temp;
}
+/* Like read_struct, but gets a pointer without allocating. Only works for
+ * undo since DNA must match. */
+static const void *peek_struct_undo(FileData *fd, BHead *bhead)
+{
+ BLI_assert(fd->memfile != NULL);
+ UNUSED_VARS_NDEBUG(fd);
+ return (bhead->len) ? (const void *)(bhead + 1) : NULL;
+}
+
typedef void (*link_list_cb)(FileData *fd, void *data);
static void link_list_ex(FileData *fd, ListBase *lb, link_list_cb callback) /* only direct data */
@@ -2768,7 +2772,7 @@ static void direct_link_id_override_property_cb(FileData *fd, void *data)
link_list_ex(fd, &op->operations, direct_link_id_override_property_operation_cb);
}
-static void direct_link_id(FileData *fd, ID *id, ID *id_old);
+static void direct_link_id_common(FileData *fd, ID *id, ID *id_old, const int tag);
static void direct_link_nodetree(FileData *fd, bNodeTree *ntree);
static void direct_link_collection(FileData *fd, Collection *collection);
@@ -2778,7 +2782,8 @@ static void direct_link_id_private_id(FileData *fd, ID *id, ID *id_old)
bNodeTree **nodetree = BKE_ntree_ptr_from_id(id);
if (nodetree != NULL && *nodetree != NULL) {
*nodetree = newdataadr(fd, *nodetree);
- direct_link_id(fd, (ID *)*nodetree, id_old != NULL ? (ID *)ntreeFromID(id_old) : NULL);
+ direct_link_id_common(
+ fd, (ID *)*nodetree, id_old != NULL ? (ID *)ntreeFromID(id_old) : NULL, 0);
direct_link_nodetree(fd, *nodetree);
}
@@ -2786,15 +2791,72 @@ static void direct_link_id_private_id(FileData *fd, ID *id, ID *id_old)
Scene *scene = (Scene *)id;
if (scene->master_collection != NULL) {
scene->master_collection = newdataadr(fd, scene->master_collection);
- direct_link_id(fd,
- &scene->master_collection->id,
- id_old != NULL ? &((Scene *)id_old)->master_collection->id : NULL);
+ direct_link_id_common(fd,
+ &scene->master_collection->id,
+ id_old != NULL ? &((Scene *)id_old)->master_collection->id : NULL,
+ 0);
direct_link_collection(fd, scene->master_collection);
}
}
}
-static void direct_link_id(FileData *fd, ID *id, ID *id_old)
+static int direct_link_id_restore_recalc_exceptions(const ID *id_current)
+{
+ /* Exception for armature objects, where the pose has direct points to the
+ * armature databolock. */
+ if (GS(id_current->name) == ID_OB && ((Object *)id_current)->pose) {
+ return ID_RECALC_GEOMETRY;
+ }
+
+ return 0;
+}
+
+static int direct_link_id_restore_recalc(const FileData *fd,
+ const ID *id_target,
+ const ID *id_current,
+ const bool is_identical)
+{
+ /* These are the evaluations that had not been performed yet at the time the
+ * target undo state was written. These need to be done again, since they may
+ * flush back changes to the original datablock. */
+ int recalc = id_target->recalc;
+
+ if (id_current == NULL) {
+ /* ID does not currently exist in the database, so also will not exist in
+ * the dependency graphs. That means it will be newly created and as a
+ * result also fully re-evaluated regardless of the recalc flag set here. */
+ recalc |= ID_RECALC_ALL;
+ }
+ else {
+ /* If the contents datablock changed, the depsgraph needs to copy the
+ * datablock again to ensure it matches the original datablock. */
+ if (!is_identical) {
+ recalc |= ID_RECALC_COPY_ON_WRITE;
+ }
+
+ /* Special exceptions. */
+ recalc |= direct_link_id_restore_recalc_exceptions(id_current);
+
+ /* Evaluations for the current state that have not been performed yet
+ * by the time we are performing this undo step. */
+ recalc |= id_current->recalc;
+
+ /* Tags that were set between the target state and the current state,
+ * that we need to perform again. */
+ if (fd->undo_direction < 0) {
+ /* Undo: tags from target to the current state. */
+ recalc |= id_current->recalc_undo_accumulated;
+ }
+ else {
+ /* Redo: tags from current to the target state. */
+ recalc |= id_target->recalc_undo_accumulated;
+ }
+ }
+
+ return recalc;
+}
+
+static void direct_link_id_common(FileData *fd, ID *id, ID *id_old, const int tag)
{
/*link direct data of ID properties*/
if (id->properties) {
@@ -2804,10 +2866,8 @@ static void direct_link_id(FileData *fd, ID *id, ID *id_old)
}
id->py_instance = NULL;
- /* That way data-lock reading not going through main read_libblock()
- * function are still in a clear tag state.
- * (glowering at certain nodetree fake data-lock here...). */
- id->tag = 0;
+ /* Initialize with provided tag. */
+ id->tag = tag;
id->flag &= ~LIB_INDIRECT_WEAK_LINK;
/* NOTE: It is important to not clear the recalc flags for undo/redo.
@@ -2823,29 +2883,8 @@ static void direct_link_id(FileData *fd, ID *id, ID *id_old)
id->recalc_undo_accumulated = 0;
}
else if ((fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0) {
- if (fd->undo_direction < 0) {
- /* We are coming from the future (i.e. do an actual undo, and not a redo), and we found an
- * old (aka existing) ID: we use its 'accumulated recalc flags since last memfile undo step
- * saving' as recalc flags of our newly read ID. */
- if (id_old != NULL) {
- id->recalc = id_old->recalc_undo_accumulated;
- }
- }
- else {
- /* We are coming from the past (i.e. do a redo), we use saved 'accumulated
- * recalc flags since last memfile undo step saving' as recalc flags of our newly read ID. */
- id->recalc = id->recalc_undo_accumulated;
- }
- /* In any case, we need to flush the depsgraph's CoWs, as even if the ID address itself did not
- * change, internal data most likely have. */
- id->recalc |= ID_RECALC_COPY_ON_WRITE;
-
- /* We need to 'accumulate' the accumulated recalc flags of all undo steps until we actually
- * perform a depsgraph update, otherwise we'd only ever use the flags from one of the steps,
- * and never get proper flags matching all others. */
- if (id_old != NULL) {
- id->recalc_undo_accumulated |= id_old->recalc_undo_accumulated;
- }
+ id->recalc = direct_link_id_restore_recalc(fd, id, id_old, false);
+ id->recalc_undo_accumulated = 0;
}
/* Link direct data of overrides. */
@@ -8416,7 +8455,7 @@ void blo_do_versions_view3d_split_250(View3D *v3d, ListBase *regions)
static bool direct_link_screen(FileData *fd, bScreen *screen)
{
- bool wrong_id = false;
+ bool success = true;
screen->regionbase.first = screen->regionbase.last = NULL;
screen->context = NULL;
@@ -8426,10 +8465,10 @@ static bool direct_link_screen(FileData *fd, bScreen *screen)
if (!direct_link_area_map(fd, AREAMAP_FROM_SCREEN(screen))) {
printf("Error reading Screen %s... removing it.\n", screen->id.name + 2);
- wrong_id = true;
+ success = false;
}
- return wrong_id;
+ return success;
}
/** \} */
@@ -8474,7 +8513,7 @@ static void direct_link_library(FileData *fd, Library *lib, Main *main)
/* make sure we have full path in lib->filepath */
BLI_strncpy(lib->filepath, lib->name, sizeof(lib->name));
- BLI_cleanup_path(fd->relabase, lib->filepath);
+ BLI_path_normalize(fd->relabase, lib->filepath);
// printf("direct_link_library: name %s\n", lib->name);
// printf("direct_link_library: filepath %s\n", lib->filepath);
@@ -9265,289 +9304,15 @@ static const char *dataname(short id_code)
return "Data from Lib Block";
}
-static BHead *read_data_into_oldnewmap(FileData *fd, BHead *bhead, const char *allocname)
-{
- bhead = blo_bhead_next(fd, bhead);
-
- while (bhead && bhead->code == DATA) {
- void *data;
-#if 0
- /* XXX DUMB DEBUGGING OPTION TO GIVE NAMES for guarded malloc errors */
- short* sp = fd->filesdna->structs[bhead->SDNAnr];
- char* tmp = malloc(100);
- allocname = fd->filesdna->types[sp[0]];
- strcpy(tmp, allocname);
- data = read_struct(fd, bhead, tmp);
-#else
- data = read_struct(fd, bhead, allocname);
-#endif
-
- if (data) {
- oldnewmap_insert(fd->datamap, bhead->old, data, 0);
- }
-
- bhead = blo_bhead_next(fd, bhead);
- }
-
- return bhead;
-}
-
-static BHead *read_libblock(FileData *fd,
- Main *main,
- BHead *bhead,
- const int tag,
- const bool placeholder_set_indirect_extern,
- ID **r_id)
+static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID *id_old)
{
- /* this routine reads a libblock and its direct data. Use link functions to connect it all
- */
- ID *id;
- ListBase *lb;
- const char *allocname;
-
- /* XXX Very weakly handled currently, see comment at the end of this function before trying to
- * use it for anything new. */
- bool wrong_id = false;
-
- /* In undo case, most libs and linked data should be kept as is from previous state
- * (see BLO_read_from_memfile).
- * However, some needed by the snapshot being read may have been removed in previous one,
- * and would go missing.
- * This leads e.g. to disappearing objects in some undo/redo case, see T34446.
- * That means we have to carefully check whether current lib or
- * libdata already exits in old main, if it does we merely copy it over into new main area,
- * otherwise we have to do a full read of that bhead... */
- if (fd->memfile && ELEM(bhead->code, ID_LI, ID_LINK_PLACEHOLDER)) {
- const char *idname = blo_bhead_id_name(fd, bhead);
-
- DEBUG_PRINTF("Checking %s...\n", idname);
-
- if (bhead->code == ID_LI) {
- Main *libmain = fd->old_mainlist->first;
- /* Skip oldmain itself... */
- for (libmain = libmain->next; libmain; libmain = libmain->next) {
- DEBUG_PRINTF("... against %s: ", libmain->curlib ? libmain->curlib->id.name : "<NULL>");
- if (libmain->curlib && STREQ(idname, libmain->curlib->id.name)) {
- Main *oldmain = fd->old_mainlist->first;
- DEBUG_PRINTF("FOUND!\n");
- /* In case of a library, we need to re-add its main to fd->mainlist,
- * because if we have later a missing ID_LINK_PLACEHOLDER,
- * we need to get the correct lib it is linked to!
- * Order is crucial, we cannot bulk-add it in BLO_read_from_memfile()
- * like it used to be. */
- BLI_remlink(fd->old_mainlist, libmain);
- BLI_remlink_safe(&oldmain->libraries, libmain->curlib);
- BLI_addtail(fd->mainlist, libmain);
- BLI_addtail(&main->libraries, libmain->curlib);
-
- if (r_id) {
- *r_id = NULL; /* Just in case... */
- }
- return blo_bhead_next(fd, bhead);
- }
- DEBUG_PRINTF("nothing...\n");
- }
- }
- else {
- DEBUG_PRINTF("... in %s (%s): ",
- main->curlib ? main->curlib->id.name : "<NULL>",
- main->curlib ? main->curlib->name : "<NULL>");
- if ((id = BKE_libblock_find_name(main, GS(idname), idname + 2))) {
- DEBUG_PRINTF("FOUND!\n");
- /* Even though we found our linked ID,
- * there is no guarantee its address is still the same. */
- if (id != bhead->old) {
- oldnewmap_insert(fd->libmap, bhead->old, id, GS(id->name));
- }
-
- /* No need to do anything else for ID_LINK_PLACEHOLDER,
- * it's assumed already present in its lib's main. */
- if (r_id) {
- *r_id = NULL; /* Just in case... */
- }
- return blo_bhead_next(fd, bhead);
- }
- DEBUG_PRINTF("nothing...\n");
- }
- }
-
- /* read libblock */
- fd->are_memchunks_identical = true;
- id = read_struct(fd, bhead, "lib block");
- const short idcode = id != NULL ? GS(id->name) : 0;
-
- BHead *id_bhead = bhead;
- /* Used when undoing from memfile, we swap changed IDs into their old addresses when found. */
- ID *id_old = NULL;
- bool do_id_swap = false;
-
- if (id != NULL) {
- const bool do_partial_undo = (fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0;
-
- if (id_bhead->code != ID_LINK_PLACEHOLDER) {
- /* need a name for the mallocN, just for debugging and sane prints on leaks */
- allocname = dataname(idcode);
-
- /* read all data into fd->datamap */
- /* TODO: instead of building oldnewmap here we could just quickly check the bheads... could
- * save some more ticks. Probably not worth it though, bottleneck is full depsgraph rebuild
- * and evaluate, not actual file reading. */
- bhead = read_data_into_oldnewmap(fd, id_bhead, allocname);
-
- DEBUG_PRINTF(
- "%s: ID %s is unchanged: %d\n", __func__, id->name, fd->are_memchunks_identical);
-
- if (fd->memfile != NULL) {
- BLI_assert(fd->old_idmap != NULL || !do_partial_undo);
- /* This code should only ever be reached for local data-blocks. */
- BLI_assert(main->curlib == NULL);
-
- /* Find the 'current' existing ID we want to reuse instead of the one we would read from
- * the undo memfile. */
- DEBUG_PRINTF("\t Looking for ID %s with uuid %u instead of newly read one\n",
- id->name,
- id->session_uuid);
- id_old = do_partial_undo ? BKE_main_idmap_lookup_uuid(fd->old_idmap, id->session_uuid) :
- NULL;
- bool can_finalize_and_return = false;
-
- if (ELEM(idcode, ID_WM, ID_SCR, ID_WS)) {
- /* Read WindowManager, Screen and WorkSpace IDs are never actually used during undo (see
- * `setup_app_data()` in `blendfile.c`).
- * So we can just abort here, just ensuring libmapping is set accordingly. */
- can_finalize_and_return = true;
- }
- else if (id_old != NULL && fd->are_memchunks_identical) {
- /* Do not add LIB_TAG_NEW here, this should not be needed/used in undo case anyway (as
- * this is only for do_version-like code), but for sake of consistency, and also because
- * it will tell us which ID is re-used from old Main, and which one is actually new. */
- id_old->tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_UNDO_OLD_ID_REUSED;
- id_old->lib = main->curlib;
- id_old->us = ID_FAKE_USERS(id_old);
- /* Do not reset id->icon_id here, memory allocated for it remains valid. */
- /* Needed because .blend may have been saved with crap value here... */
- id_old->newid = NULL;
- id_old->orig_id = NULL;
-
- /* About recalc: since that ID did not change at all, we know that its recalc fields also
- * remained unchanged, so no need to handle neither recalc nor recalc_undo_future here.
- */
-
- Main *old_bmain = fd->old_mainlist->first;
- ListBase *old_lb = which_libbase(old_bmain, idcode);
- ListBase *new_lb = which_libbase(main, idcode);
- BLI_remlink(old_lb, id_old);
- BLI_addtail(new_lb, id_old);
-
- can_finalize_and_return = true;
- }
-
- if (can_finalize_and_return) {
- DEBUG_PRINTF("Re-using existing ID %s instead of newly read one\n", id_old->name);
- oldnewmap_insert(fd->libmap, id_bhead->old, id_old, id_bhead->code);
- oldnewmap_insert(fd->libmap, id_old, id_old, id_bhead->code);
-
- if (r_id) {
- *r_id = id_old;
- }
-
- if (do_partial_undo) {
- /* Even though we re-use the old ID as-is, it does not mean that we are 100% safe from
- * needing some depsgraph updates for it (it could depend on another ID which address
- * did not change, but which actual content might have been re-read from the memfile).
- * IMPORTANT: Do not fully overwrite recalc flag here, depsgraph may not have been ran
- * yet for previous undo step(s), we do not want to erase flags set by those.
- */
- if (fd->undo_direction < 0) {
- /* We are coming from the future (i.e. do an actual undo, and not a redo), we use our
- * old reused ID's 'accumulated recalc flags since last memfile undo step saving' as
- * recalc flags. */
- id_old->recalc |= id_old->recalc_undo_accumulated;
- }
- else {
- /* We are coming from the past (i.e. do a redo), we use the saved 'accumulated recalc
- * flags since last memfile undo step saving' from the newly read ID as recalc flags.
- */
- id_old->recalc |= id->recalc_undo_accumulated;
- }
- /* There is no need to flush the depsgraph's CoWs here, since that ID's data itself did
- * not change. */
-
- /* We need to 'accumulate' the accumulated recalc flags of all undo steps until we
- * actually perform a depsgraph update, otherwise we'd only ever use the flags from one
- * of the steps, and never get proper flags matching all others. */
- id_old->recalc_undo_accumulated |= id->recalc_undo_accumulated;
- }
-
- MEM_freeN(id);
- oldnewmap_free_unused(fd->datamap);
- oldnewmap_clear(fd->datamap);
-
- return bhead;
- }
- }
- }
-
- /* do after read_struct, for dna reconstruct */
- lb = which_libbase(main, idcode);
- if (lb) {
- /* Some re-used old IDs might also use newly read ones, so we have to check for old memory
- * addresses for those as well. */
- if (fd->memfile != NULL && do_partial_undo && id->lib == NULL) {
- BLI_assert(fd->old_idmap != NULL);
- DEBUG_PRINTF("\t Looking for ID %s with uuid %u instead of newly read one\n",
- id->name,
- id->session_uuid);
- id_old = BKE_main_idmap_lookup_uuid(fd->old_idmap, id->session_uuid);
- if (id_old != NULL) {
- BLI_assert(MEM_allocN_len(id) == MEM_allocN_len(id_old));
- /* UI IDs are always re-used from old bmain at higher-level calling code, so never swap
- * those. Besides maybe custom properties, no other ID should have pointers to those
- * anyway...
- * And linked IDs are handled separately as well. */
- do_id_swap = !ELEM(idcode, ID_WM, ID_SCR, ID_WS) &&
- !(id_bhead->code == ID_LINK_PLACEHOLDER);
- }
- }
-
- /* At this point, we know we are going to keep that newly read & allocated ID, so we need to
- * reallocate it to ensure we actually get a unique memory address for it. */
- if (!do_id_swap) {
- DEBUG_PRINTF("using newly-read ID %s to a new mem address\n", id->name);
- }
- else {
- DEBUG_PRINTF("using newly-read ID %s to its old, already existing address\n", id->name);
- }
-
- /* for ID_LINK_PLACEHOLDER check */
- ID *id_target = do_id_swap ? id_old : id;
- oldnewmap_insert(fd->libmap, id_bhead->old, id_target, id_bhead->code);
- oldnewmap_insert(fd->libmap, id_old, id_target, id_bhead->code);
-
- BLI_addtail(lb, id);
-
- if (fd->memfile == NULL) {
- /* When actually reading a file , we do want to reset/re-generate session uuids.
- * In unod case, we want to re-use existing ones. */
- id->session_uuid = MAIN_ID_SESSION_UUID_UNSET;
- }
-
- BKE_lib_libblock_session_uuid_ensure(id);
- }
- else {
- /* unknown ID type */
- printf("%s: unknown id code '%c%c'\n", __func__, (idcode & 0xff), (idcode >> 8));
- MEM_freeN(id);
- id = NULL;
- }
+ if (fd->memfile == NULL) {
+ /* When actually reading a file , we do want to reset/re-generate session uuids.
+ * In undo case, we want to re-use existing ones. */
+ id->session_uuid = MAIN_ID_SESSION_UUID_UNSET;
}
- if (r_id) {
- *r_id = do_id_swap ? id_old : id;
- }
- if (!id) {
- return blo_bhead_next(fd, id_bhead);
- }
+ BKE_lib_libblock_session_uuid_ensure(id);
id->lib = main->curlib;
id->us = ID_FAKE_USERS(id);
@@ -9555,36 +9320,25 @@ static BHead *read_libblock(FileData *fd,
id->newid = NULL; /* Needed because .blend may have been saved with crap value here... */
id->orig_id = NULL;
- /* this case cannot be direct_linked: it's just the ID part */
- if (id_bhead->code == ID_LINK_PLACEHOLDER) {
- /* That way, we know which data-lock needs do_versions (required currently for linking). */
- id->tag = tag | LIB_TAG_ID_LINK_PLACEHOLDER | LIB_TAG_NEED_LINK | LIB_TAG_NEW;
-
- if (placeholder_set_indirect_extern) {
- if (id->flag & LIB_INDIRECT_WEAK_LINK) {
- id->tag |= LIB_TAG_INDIRECT;
- }
- else {
- id->tag |= LIB_TAG_EXTERN;
- }
- }
-
- return blo_bhead_next(fd, id_bhead);
+ if (tag & LIB_TAG_ID_LINK_PLACEHOLDER) {
+ /* For placeholder we only need to set the tag, no further data to read. */
+ id->tag = tag;
+ return true;
}
- /* init pointers direct data */
- direct_link_id(fd, id, id_old);
+ /* Read part of datablock that is common between real and embedded datablocks. */
+ direct_link_id_common(fd, id, id_old, tag);
- /* That way, we know which data-lock needs do_versions (required currently for linking). */
- /* Note: doing this after direct_link_id(), which resets that field. */
- id->tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_NEW;
+ /* XXX Very weakly handled currently, see comment in read_libblock() before trying to
+ * use it for anything new. */
+ bool success = true;
- switch (idcode) {
+ switch (GS(id->name)) {
case ID_WM:
direct_link_windowmanager(fd, (wmWindowManager *)id);
break;
case ID_SCR:
- wrong_id = direct_link_screen(fd, (bScreen *)id);
+ success = direct_link_screen(fd, (bScreen *)id);
break;
case ID_SCE:
direct_link_scene(fd, (Scene *)id);
@@ -9699,10 +9453,355 @@ static BHead *read_libblock(FileData *fd,
break;
}
- oldnewmap_free_unused(fd->datamap);
+ return success;
+}
+
+/* Read all data associated with a datablock into datamap. */
+static BHead *read_data_into_datamap(FileData *fd, BHead *bhead, const char *allocname)
+{
+ bhead = blo_bhead_next(fd, bhead);
+
+ while (bhead && bhead->code == DATA) {
+ void *data;
+#if 0
+ /* XXX DUMB DEBUGGING OPTION TO GIVE NAMES for guarded malloc errors */
+ short* sp = fd->filesdna->structs[bhead->SDNAnr];
+ char* tmp = malloc(100);
+ allocname = fd->filesdna->types[sp[0]];
+ strcpy(tmp, allocname);
+ data = read_struct(fd, bhead, tmp);
+#else
+ data = read_struct(fd, bhead, allocname);
+#endif
+
+ if (data) {
+ oldnewmap_insert(fd->datamap, bhead->old, data, 0);
+ }
+
+ bhead = blo_bhead_next(fd, bhead);
+ }
+
+ return bhead;
+}
+
+/* Verify if the datablock and all associated data is identical. */
+static bool read_libblock_is_identical(FileData *fd, BHead *bhead)
+{
+ /* Test ID itself. */
+ if (bhead->len && !BHEADN_FROM_BHEAD(bhead)->is_memchunk_identical) {
+ return false;
+ }
+
+ /* Test any other data that is part of ID (logic must match read_data_into_datamap). */
+ bhead = blo_bhead_next(fd, bhead);
+
+ while (bhead && bhead->code == DATA) {
+ if (bhead->len && !BHEADN_FROM_BHEAD(bhead)->is_memchunk_identical) {
+ return false;
+ }
+
+ bhead = blo_bhead_next(fd, bhead);
+ }
+
+ return true;
+}
+
+/* For undo, restore matching library datablock from the old main. */
+static bool read_libblock_undo_restore_library(FileData *fd, Main *main, const ID *id)
+{
+ /* In undo case, most libs and linked data should be kept as is from previous state
+ * (see BLO_read_from_memfile).
+ * However, some needed by the snapshot being read may have been removed in previous one,
+ * and would go missing.
+ * This leads e.g. to disappearing objects in some undo/redo case, see T34446.
+ * That means we have to carefully check whether current lib or
+ * libdata already exits in old main, if it does we merely copy it over into new main area,
+ * otherwise we have to do a full read of that bhead... */
+ DEBUG_PRINTF("UNDO: restore library %s\n", id->name);
+
+ Main *libmain = fd->old_mainlist->first;
+ /* Skip oldmain itself... */
+ for (libmain = libmain->next; libmain; libmain = libmain->next) {
+ DEBUG_PRINTF(" compare with %s -> ", libmain->curlib ? libmain->curlib->id.name : "<NULL>");
+ if (libmain->curlib && STREQ(id->name, libmain->curlib->id.name)) {
+ Main *oldmain = fd->old_mainlist->first;
+ DEBUG_PRINTF("match!\n");
+ /* In case of a library, we need to re-add its main to fd->mainlist,
+ * because if we have later a missing ID_LINK_PLACEHOLDER,
+ * we need to get the correct lib it is linked to!
+ * Order is crucial, we cannot bulk-add it in BLO_read_from_memfile()
+ * like it used to be. */
+ BLI_remlink(fd->old_mainlist, libmain);
+ BLI_remlink_safe(&oldmain->libraries, libmain->curlib);
+ BLI_addtail(fd->mainlist, libmain);
+ BLI_addtail(&main->libraries, libmain->curlib);
+ return true;
+ }
+ DEBUG_PRINTF("no match\n");
+ }
+
+ return false;
+}
+
+/* For undo, restore existing linked datablock from the old main. */
+static bool read_libblock_undo_restore_linked(FileData *fd, Main *main, const ID *id, BHead *bhead)
+{
+ DEBUG_PRINTF("UNDO: restore linked datablock %s\n", id->name);
+ DEBUG_PRINTF(" from %s (%s): ",
+ main->curlib ? main->curlib->id.name : "<NULL>",
+ main->curlib ? main->curlib->name : "<NULL>");
+
+ ID *id_old = BKE_libblock_find_name(main, GS(id->name), id->name + 2);
+ if (id_old != NULL) {
+ DEBUG_PRINTF(" found!\n");
+ /* Even though we found our linked ID, there is no guarantee its address
+ * is still the same. */
+ if (id_old != bhead->old) {
+ oldnewmap_insert(fd->libmap, bhead->old, id_old, GS(id_old->name));
+ }
+
+ /* No need to do anything else for ID_LINK_PLACEHOLDER, it's assumed
+ * already present in its lib's main. */
+ return true;
+ }
+
+ DEBUG_PRINTF(" not found\n");
+ return false;
+}
+
+/* For undo, restore unchanged datablock from old main. */
+static void read_libblock_undo_restore_identical(
+ FileData *fd, Main *main, const ID *UNUSED(id), ID *id_old, const int tag)
+{
+ BLI_assert((fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0);
+ BLI_assert(id_old != NULL);
+
+ id_old->tag = tag;
+ id_old->lib = main->curlib;
+ id_old->us = ID_FAKE_USERS(id_old);
+ /* Do not reset id->icon_id here, memory allocated for it remains valid. */
+ /* Needed because .blend may have been saved with crap value here... */
+ id_old->newid = NULL;
+ id_old->orig_id = NULL;
+
+ const short idcode = GS(id_old->name);
+ Main *old_bmain = fd->old_mainlist->first;
+ ListBase *old_lb = which_libbase(old_bmain, idcode);
+ ListBase *new_lb = which_libbase(main, idcode);
+ BLI_remlink(old_lb, id_old);
+ BLI_addtail(new_lb, id_old);
+
+ /* Recalc flags, mostly these just remain as they are. */
+ id_old->recalc |= direct_link_id_restore_recalc_exceptions(id_old);
+ id_old->recalc_undo_accumulated = 0;
+
+ /* As usual, proxies require some special love...
+ * In `blo_clear_proxy_pointers_from_lib()` we clear all `proxy_from` pointers to local IDs, for
+ * undo. This is required since we do not re-read linked data in that case, so we also do not
+ * re-'lib_link' their pointers.
+ * Those `proxy_from` pointers are then re-defined properly when lib_linking the newly read local
+ * object. However, in case of re-used data 'as-is', we never lib_link it again, so we have to
+ * fix those backward pointers here. */
+ if (GS(id_old->name) == ID_OB) {
+ Object *ob = (Object *)id_old;
+ if (ob->proxy != NULL) {
+ ob->proxy->proxy_from = ob;
+ }
+ }
+}
+
+/* For undo, store changed datablock at old address. */
+static void read_libblock_undo_restore_at_old_address(FileData *fd, Main *main, ID *id, ID *id_old)
+{
+ /* During memfile undo, if an ID changed and we cannot directly re-use existing one from old
+ * bmain, we do a full read of the new id from the memfile, and then fully swap its content
+ * with the old id. This allows us to keep the same pointer even for modified data, which
+ * helps reducing further detected changes by the depsgraph (since unchanged IDs remain fully
+ * unchanged, even if they are using/pointing to a changed one). */
+ BLI_assert((fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0);
+ BLI_assert(id_old != NULL);
+
+ const short idcode = GS(id->name);
+
+ Main *old_bmain = fd->old_mainlist->first;
+ ListBase *old_lb = which_libbase(old_bmain, idcode);
+ ListBase *new_lb = which_libbase(main, idcode);
+ BLI_remlink(old_lb, id_old);
+ BLI_remlink(new_lb, id);
+
+ /* We do not need any remapping from this call here, since no ID pointer is valid in the data
+ * currently (they are all pointing to old addresses, and need to go through `lib_link`
+ * process). So we can pass NULL for the Main pointer parameter. */
+ BKE_lib_id_swap_full(NULL, id, id_old);
+
+ BLI_addtail(new_lb, id_old);
+ BLI_addtail(old_lb, id);
+}
+
+static bool read_libblock_undo_restore(
+ FileData *fd, Main *main, BHead *bhead, const int tag, ID **r_id_old)
+{
+ /* Get pointer to memory of new ID that we will be reading. */
+ const ID *id = peek_struct_undo(fd, bhead);
+ const short idcode = GS(id->name);
+
+ if (bhead->code == ID_LI) {
+ /* Restore library datablock. */
+ if (read_libblock_undo_restore_library(fd, main, id)) {
+ return true;
+ }
+ }
+ else if (bhead->code == ID_LINK_PLACEHOLDER) {
+ /* Restore linked datablock. */
+ if (read_libblock_undo_restore_linked(fd, main, id, bhead)) {
+ return true;
+ }
+ }
+ else if (ELEM(idcode, ID_WM, ID_SCR, ID_WS)) {
+ /* Skip reading any UI datablocks, existing ones are kept. We don't
+ * support pointers from other datablocks to UI datablocks so those
+ * we also don't put UI datablocks in fd->libmap. */
+ return true;
+ }
+
+ /* Restore local datablocks. */
+ DEBUG_PRINTF("UNDO: read %s (uuid %d) -> ", id->name, id->session_uuid);
+
+ ID *id_old = NULL;
+ const bool do_partial_undo = (fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0;
+ if (do_partial_undo && (bhead->code != ID_LINK_PLACEHOLDER)) {
+ /* This code should only ever be reached for local data-blocks. */
+ BLI_assert(main->curlib == NULL);
+
+ /* Find the 'current' existing ID we want to reuse instead of the one we
+ * would read from the undo memfile. */
+ BLI_assert(fd->old_idmap != NULL);
+ id_old = BKE_main_idmap_lookup_uuid(fd->old_idmap, id->session_uuid);
+ }
+
+ if (id_old != NULL && read_libblock_is_identical(fd, bhead)) {
+ /* Local datablock was unchanged, restore from the old main. */
+ DEBUG_PRINTF("keep identical datablock\n");
+
+ /* Do not add LIB_TAG_NEW here, this should not be needed/used in undo case anyway (as
+ * this is only for do_version-like code), but for sake of consistency, and also because
+ * it will tell us which ID is re-used from old Main, and which one is actually new. */
+ const int id_tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_UNDO_OLD_ID_REUSED;
+ read_libblock_undo_restore_identical(fd, main, id, id_old, id_tag);
+
+ /* Insert into library map for lookup by newly read datablocks (with pointer
+ * value bhead->old) or existing datablocks in memory (pointer value id_old). */
+ oldnewmap_insert(fd->libmap, bhead->old, id_old, bhead->code);
+ oldnewmap_insert(fd->libmap, id_old, id_old, bhead->code);
+
+ *r_id_old = id_old;
+ return true;
+ }
+ else if (id_old != NULL) {
+ /* Local datablock was changed. Restore at the address of the old datablock. */
+ DEBUG_PRINTF("read to old existing address\n");
+ *r_id_old = id_old;
+ return false;
+ }
+ else {
+ /* Local datablock does not exist in the undo step, so read from scratch. */
+ DEBUG_PRINTF("read at new address\n");
+ return false;
+ }
+}
+
+/* This routine reads a datablock and its direct data, and advances bhead to
+ * the next datablock. For library linked datablocks, only a placeholder will
+ * be generated, to be replaced in read_library_linked_ids.
+ *
+ * When reading for undo, libraries, linked datablocks and unchanged datablocks
+ * will be restored from the old database. Only new or changed datablocks will
+ * actually be read. */
+static BHead *read_libblock(FileData *fd,
+ Main *main,
+ BHead *bhead,
+ const int tag,
+ const bool placeholder_set_indirect_extern,
+ ID **r_id)
+{
+ /* First attempt to restore existing datablocks for undo.
+ * When datablocks are changed but still exist, we restore them at the old
+ * address and inherit recalc flags for the dependency graph. */
+ ID *id_old = NULL;
+ if (fd->memfile != NULL) {
+ if (read_libblock_undo_restore(fd, main, bhead, tag, &id_old)) {
+ if (r_id) {
+ *r_id = id_old;
+ }
+ return blo_bhead_next(fd, bhead);
+ }
+ }
+
+ /* Read libblock struct. */
+ ID *id = read_struct(fd, bhead, "lib block");
+ if (id == NULL) {
+ if (r_id) {
+ *r_id = NULL;
+ }
+ return blo_bhead_next(fd, bhead);
+ }
+
+ /* Determine ID type and add to main database list. */
+ const short idcode = GS(id->name);
+ ListBase *lb = which_libbase(main, idcode);
+ if (lb == NULL) {
+ /* Unknown ID type. */
+ printf("%s: unknown id code '%c%c'\n", __func__, (idcode & 0xff), (idcode >> 8));
+ MEM_freeN(id);
+ if (r_id) {
+ *r_id = NULL;
+ }
+ return blo_bhead_next(fd, bhead);
+ }
+
+ /* NOTE: id must be added to the list before direct_link_id(), since
+ * direct_link_library() may remove it from there in case of duplicates. */
+ BLI_addtail(lb, id);
+
+ /* Insert into library map for lookup by newly read datablocks (with pointer
+ * value bhead->old) or existing datablocks in memory (pointer value id_old). */
+ ID *id_target = id_old ? id_old : id;
+ oldnewmap_insert(fd->libmap, bhead->old, id_target, bhead->code);
+ oldnewmap_insert(fd->libmap, id_old, id_target, bhead->code);
+
+ if (r_id) {
+ *r_id = id_target;
+ }
+
+ /* Set tag for new datablock to indicate lib linking and versioning needs
+ * to be done still. */
+ int id_tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_NEW;
+
+ if (bhead->code == ID_LINK_PLACEHOLDER) {
+ /* Read placeholder for linked datablock. */
+ id_tag |= LIB_TAG_ID_LINK_PLACEHOLDER;
+
+ if (placeholder_set_indirect_extern) {
+ if (id->flag & LIB_INDIRECT_WEAK_LINK) {
+ id_tag |= LIB_TAG_INDIRECT;
+ }
+ else {
+ id_tag |= LIB_TAG_EXTERN;
+ }
+ }
+
+ direct_link_id(fd, main, id_tag, id, id_old);
+ return blo_bhead_next(fd, bhead);
+ }
+
+ /* Read datablock contents.
+ * Use convenient malloc name for debugging and better memory link prints. */
+ const char *allocname = dataname(idcode);
+ bhead = read_data_into_datamap(fd, bhead, allocname);
+ const bool success = direct_link_id(fd, main, id_tag, id, id_old);
oldnewmap_clear(fd->datamap);
- if (wrong_id) {
+ if (!success) {
/* XXX This is probably working OK currently given the very limited scope of that flag.
* However, it is absolutely **not** handled correctly: it is freeing an ID pointer that has
* been added to the fd->libmap mapping, which in theory could lead to nice crashes...
@@ -9712,39 +9811,12 @@ static BHead *read_libblock(FileData *fd,
*r_id = NULL;
}
}
- else if (do_id_swap) {
- /* During memfile undo, if an ID changed and we cannot directly re-use existing one from old
- * bmain, we do a full read of the new id from the memfile, and then fully swap its content
- * with the old id. This allows us to keep the same pointer even for modified data, which helps
- * reducing further detected changes by the depsgraph (since unchanged IDs remain fully
- * unchanged, even if they are using/pointing to a changed one). */
-
- BLI_assert((fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0);
-
- Main *old_bmain = fd->old_mainlist->first;
- BLI_assert(id_old != NULL);
-
- ListBase *old_lb = which_libbase(old_bmain, idcode);
- ListBase *new_lb = which_libbase(main, idcode);
- BLI_remlink(old_lb, id_old);
- BLI_remlink(new_lb, id);
-
- /* We do not need any remapping from this call here, since no ID pointer is valid in the data
- * currently (they are all pointing to old addresses, and need to go through `lib_link`
- * process). So we can pass NULL for the Main pointer parameter. */
- BKE_lib_id_swap_full(NULL, id, id_old);
-
- BLI_addtail(new_lb, id_old);
- BLI_addtail(old_lb, id);
- }
- else if (fd->memfile != NULL) {
- DEBUG_PRINTF("We had to fully re-recreate ID %s (old addr: %p, new addr: %p)...\n",
- id->name,
- id_old,
- id);
+ else if (id_old) {
+ /* For undo, store contents read into id at id_old. */
+ read_libblock_undo_restore_at_old_address(fd, main, id, id_old);
}
- return (bhead);
+ return bhead;
}
/** \} */
@@ -10121,7 +10193,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
user->subversionfile = bfd->main->subversionfile;
/* read all data into fd->datamap */
- bhead = read_data_into_oldnewmap(fd, bhead, "user def");
+ bhead = read_data_into_datamap(fd, bhead, "user def");
link_list(fd, &user->themes);
link_list(fd, &user->user_keymaps);
@@ -10189,7 +10261,6 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
user->edit_studio_light = 0;
/* free fd->datamap again */
- oldnewmap_free_unused(fd->datamap);
oldnewmap_clear(fd->datamap);
return bhead;
@@ -10207,6 +10278,10 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
BlendFileData *bfd;
ListBase mainlist = {NULL, NULL};
+ if (fd->memfile != NULL) {
+ DEBUG_PRINTF("\nUNDO: read step\n");
+ }
+
bfd = MEM_callocN(sizeof(BlendFileData), "blendfiledata");
bfd->main = BKE_main_new();
diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h
index 5be7e703d6b..80f8bfc3f07 100644
--- a/source/blender/blenloader/intern/readfile.h
+++ b/source/blender/blenloader/intern/readfile.h
@@ -85,11 +85,6 @@ typedef struct FileData {
const char *buffer;
/** Variables needed for reading from memfile (undo). */
struct MemFile *memfile;
- /** Whether all data read from memfile so far was identical
- * (i.e. shared with some previous undo step).
- * Updated by `fd_read_from_memfile()`, user is responsible to reset it to true when needed.
- * Used to detect unchanged IDs. */
- bool are_memchunks_identical;
/** Whether we are undoing (< 0) or redoing (> 0), used to choose which 'unchanged' flag to use
* to detect unchanged data from memfile. */
short undo_direction;
diff --git a/source/blender/blenloader/intern/undofile.c b/source/blender/blenloader/intern/undofile.c
index d8780409e93..c7057883f88 100644
--- a/source/blender/blenloader/intern/undofile.c
+++ b/source/blender/blenloader/intern/undofile.c
@@ -106,7 +106,10 @@ void memfile_chunk_add(MemFile *memfile, const char *buf, uint size, MemFileChun
curchunk->size = size;
curchunk->buf = NULL;
curchunk->is_identical = false;
- curchunk->is_identical_future = false;
+ /* This is unsafe in the sense that an app handler or other code that does not
+ * perform an undo push may make changes after the last undo push that
+ * will then not be undo. Though it's not entirely clear that is wrong behavior. */
+ curchunk->is_identical_future = true;
BLI_addtail(&memfile->chunks, curchunk);
/* we compare compchunk with buf */
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index a95cb74af51..ff3aef4588f 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -665,17 +665,6 @@ static void do_versions_area_ensure_tool_region(Main *bmain,
}
}
}
- /* Activate fcurves drawing in the vse. */
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
- for (ScrArea *area = screen->areabase.first; area; area = area->next) {
- for (SpaceLink *sl = area->spacedata.first; sl; sl = sl->next) {
- if (sl->spacetype == SPACE_SEQ) {
- SpaceSeq *sseq = (SpaceSeq *)sl;
- sseq->flag |= SEQ_SHOW_FCURVES;
- }
- }
- }
- }
}
}
@@ -4136,7 +4125,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
v3d->shading.flag |= V3D_SHADING_SCENE_LIGHTS_RENDER | V3D_SHADING_SCENE_WORLD_RENDER;
/* files by default don't have studio lights selected unless interacted
- * with the shading popover. When no studiolight could be read, we will
+ * with the shading popover. When no studio-light could be read, we will
* select the default world one. */
StudioLight *studio_light = BKE_studiolight_find(v3d->shading.lookdev_light,
STUDIOLIGHT_TYPE_WORLD);
@@ -4205,7 +4194,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- /* Added studiolight intensity */
+ /* Added studio-light intensity. */
if (!DNA_struct_elem_find(fd->filesdna, "View3DShading", "float", "studiolight_intensity")) {
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
@@ -4889,18 +4878,19 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- /**
- * Versioning code until next subversion bump goes here.
- *
- * \note Be sure to check when bumping the version:
- * - #do_versions_after_linking_280 in this file.
- * - "versioning_userdef.c", #BLO_version_defaults_userpref_blend
- * - "versioning_userdef.c", #do_versions_theme
- *
- * \note Keep this message at the bottom of the function.
- */
- {
- /* Keep this block, even when empty. */
+ if (!MAIN_VERSION_ATLEAST(bmain, 283, 12)) {
+
+ /* Activate f-curve drawing in the sequencer. */
+ for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ for (ScrArea *area = screen->areabase.first; area; area = area->next) {
+ for (SpaceLink *sl = area->spacedata.first; sl; sl = sl->next) {
+ if (sl->spacetype == SPACE_SEQ) {
+ SpaceSeq *sseq = (SpaceSeq *)sl;
+ sseq->flag |= SEQ_SHOW_FCURVES;
+ }
+ }
+ }
+ }
/* Remesh Modifier Voxel Mode. */
if (!DNA_struct_elem_find(fd->filesdna, "RemeshModifierData", "float", "voxel_size")) {
@@ -4915,4 +4905,18 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - #do_versions_after_linking_280 in this file.
+ * - "versioning_userdef.c", #BLO_version_defaults_userpref_blend
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
+ }
}
diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c
index 28118d6e87a..a426e49dd77 100644
--- a/source/blender/blenloader/intern/versioning_userdef.c
+++ b/source/blender/blenloader/intern/versioning_userdef.c
@@ -26,6 +26,10 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#ifdef WITH_INTERNATIONAL
+# include "BLT_translation.h"
+#endif
+
#include "DNA_anim_types.h"
#include "DNA_curve_types.h"
#include "DNA_scene_types.h"
@@ -740,6 +744,15 @@ void BLO_version_defaults_userpref_blend(Main *bmain, UserDef *userdef)
userdef->gpu_flag |= USER_GPU_FLAG_OVERLAY_SMOOTH_WIRE;
}
+ if (!USER_VERSION_ATLEAST(283, 13)) {
+ /* If Translations is off then language should default to English. */
+ if ((userdef->transopts & USER_DOTRANSLATE_DEPRECATED) == 0) {
+ userdef->language = ULANGUAGE_ENGLISH;
+ }
+ /* Clear this deprecated flag. */
+ userdef->transopts &= ~USER_DOTRANSLATE_DEPRECATED;
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 1ba2fe117c8..8a6896c6690 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -4390,8 +4390,8 @@ bool BLO_write_file(Main *mainvar,
BLI_split_dir_part(filepath, dir_dst, sizeof(dir_dst));
/* Just in case there is some subtle difference. */
- BLI_cleanup_path(mainvar->name, dir_dst);
- BLI_cleanup_path(mainvar->name, dir_src);
+ BLI_path_normalize(mainvar->name, dir_dst);
+ BLI_path_normalize(mainvar->name, dir_src);
if (G.relbase_valid && (BLI_path_cmp(dir_dst, dir_src) == 0)) {
/* Saved to same path. Nothing to do. */
diff --git a/source/blender/blentranslation/intern/blt_lang.c b/source/blender/blentranslation/intern/blt_lang.c
index 112c615fac2..a533d29b188 100644
--- a/source/blender/blentranslation/intern/blt_lang.c
+++ b/source/blender/blentranslation/intern/blt_lang.c
@@ -253,7 +253,8 @@ void BLT_lang_free(void)
}
#ifdef WITH_INTERNATIONAL
-# define ULANGUAGE ((U.language >= 0 && U.language < num_locales) ? U.language : 0)
+# define ULANGUAGE \
+ ((U.language >= ULANGUAGE_AUTO && U.language < num_locales) ? U.language : ULANGUAGE_ENGLISH)
# define LOCALE(_id) (locales ? locales[(_id)] : "")
#endif
@@ -264,7 +265,7 @@ void BLT_lang_set(const char *str)
const char *short_locale = str ? str : LOCALE(ulang);
const char *short_locale_utf8 = NULL;
- if ((U.transopts & USER_DOTRANSLATE) == 0) {
+ if (U.language == ULANGUAGE_ENGLISH) {
return;
}
@@ -388,13 +389,7 @@ static void blt_lang_check_ime_supported(void)
{
#ifdef WITH_INPUT_IME
const char *uilng = BLT_lang_get();
- if (U.transopts & USER_DOTRANSLATE) {
- ime_is_lang_supported = STREQ(uilng, "zh_CN") || STREQ(uilng, "zh_TW") ||
- STREQ(uilng, "ja_JP");
- }
- else {
- ime_is_lang_supported = false;
- }
+ ime_is_lang_supported = STREQ(uilng, "zh_CN") || STREQ(uilng, "zh_TW") || STREQ(uilng, "ja_JP");
#else
ime_is_lang_supported = false;
#endif
diff --git a/source/blender/blentranslation/intern/blt_translation.c b/source/blender/blentranslation/intern/blt_translation.c
index 64e14522aca..0ca068d4263 100644
--- a/source/blender/blentranslation/intern/blt_translation.c
+++ b/source/blender/blentranslation/intern/blt_translation.c
@@ -81,7 +81,7 @@ const char *BLT_pgettext(const char *msgctxt, const char *msgid)
bool BLT_translate(void)
{
#ifdef WITH_INTERNATIONAL
- return BLI_thread_is_main() && (U.transopts & USER_DOTRANSLATE);
+ return BLI_thread_is_main() && (U.language != ULANGUAGE_ENGLISH);
#else
return false;
#endif
diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c
index 592d8124b23..0d79d60e5c1 100644
--- a/source/blender/bmesh/intern/bmesh_construct.c
+++ b/source/blender/bmesh/intern/bmesh_construct.c
@@ -717,6 +717,10 @@ BMesh *BM_mesh_copy(BMesh *bm_old)
MEM_freeN(vtable);
MEM_freeN(ftable);
+ /* Copy various settings. */
+ bm_new->shapenr = bm_old->shapenr;
+ bm_new->selectmode = bm_old->selectmode;
+
return bm_new;
}
diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c
index b65d563d441..854421d32d4 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.c
+++ b/source/blender/bmesh/intern/bmesh_mesh.c
@@ -1628,20 +1628,6 @@ void BM_loop_normal_editdata_array_free(BMLoopNorEditDataArray *lnors_ed_arr)
MEM_freeN(lnors_ed_arr);
}
-int BM_total_loop_select(BMesh *bm)
-{
- int r_sel = 0;
- BMVert *v;
- BMIter viter;
-
- BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
- if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
- r_sel += BM_vert_face_count(v);
- }
- }
- return r_sel;
-}
-
/**
* \brief BMesh Begin Edit
*
diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h
index ea0fea1f603..fa542ba5f12 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.h
+++ b/source/blender/bmesh/intern/bmesh_mesh.h
@@ -72,7 +72,6 @@ void BM_lnorspace_err(BMesh *bm);
struct BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm,
const bool do_all_loops_of_vert);
void BM_loop_normal_editdata_array_free(struct BMLoopNorEditDataArray *lnors_ed_arr);
-int BM_total_loop_select(BMesh *bm);
void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle);
diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c
index d3c8499477b..6454079a5dc 100644
--- a/source/blender/bmesh/intern/bmesh_operators.c
+++ b/source/blender/bmesh/intern/bmesh_operators.c
@@ -138,6 +138,8 @@ static void bmo_op_slots_init(const BMOSlotType *slot_types, BMOpSlot *slot_args
BMO_OP_SLOT_SUBTYPE_INT_ENUM,
BMO_OP_SLOT_SUBTYPE_INT_FLAG)) {
slot->data.enum_data.flags = slot_types[i].enum_flags;
+ /* Set the first value of the enum as the default value. */
+ slot->data.i = slot->data.enum_data.flags[0].value;
}
default:
break;
diff --git a/source/blender/bmesh/intern/bmesh_query.c b/source/blender/bmesh/intern/bmesh_query.c
index e3b6e243906..64950411fed 100644
--- a/source/blender/bmesh/intern/bmesh_query.c
+++ b/source/blender/bmesh/intern/bmesh_query.c
@@ -2431,6 +2431,22 @@ bool BM_edge_is_all_face_flag_test(const BMEdge *e, const char hflag, const bool
return true;
}
+bool BM_edge_is_any_face_flag_test(const BMEdge *e, const char hflag)
+{
+ if (e->l) {
+ BMLoop *l_iter, *l_first;
+
+ l_iter = l_first = e->l;
+ do {
+ if (BM_elem_flag_test(l_iter->f, hflag)) {
+ return true;
+ }
+ } while ((l_iter = l_iter->radial_next) != l_first);
+ }
+
+ return false;
+}
+
/* convenience functions for checking flags */
bool BM_edge_is_any_vert_flag_test(const BMEdge *e, const char hflag)
{
diff --git a/source/blender/bmesh/intern/bmesh_query.h b/source/blender/bmesh/intern/bmesh_query.h
index fb646b11076..aaf8191c5db 100644
--- a/source/blender/bmesh/intern/bmesh_query.h
+++ b/source/blender/bmesh/intern/bmesh_query.h
@@ -226,6 +226,8 @@ bool BM_edge_is_all_face_flag_test(const BMEdge *e,
bool BM_edge_is_any_vert_flag_test(const BMEdge *e, const char hflag) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL();
+bool BM_edge_is_any_face_flag_test(const BMEdge *e, const char hflag) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL();
bool BM_face_is_any_vert_flag_test(const BMFace *f, const char hflag) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL();
bool BM_face_is_any_edge_flag_test(const BMFace *f, const char hflag) ATTR_WARN_UNUSED_RESULT
diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp
index 22949a8e6f1..ae6f49bffcd 100644
--- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp
+++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cpp
@@ -1136,7 +1136,7 @@ static void do_fillGradientBuffer(unsigned int rw,
* purpose of GO for the proportion calculation.
*
* For the purposes of the minimum distance comparisons, we only check
- * the sums-of-squares against eachother, since they are in the same
+ * the sums-of-squares against each other, since they are in the same
* mathematical sort-order as if we did go ahead and take square roots
*
* Loop through all gradient pixels.
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index f1180c16a87..627a93b5869 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -622,6 +622,10 @@ void id_tag_update(Main *bmain, ID *id, int flag, eUpdateSource update_source)
for (DEG::Depsgraph *depsgraph : DEG::get_all_registered_graphs(bmain)) {
graph_id_tag_update(bmain, depsgraph, id, flag, update_source);
}
+
+ /* Accumulate all tags for an ID between two undo steps, so they can be
+ * replayed for undo. */
+ id->recalc_undo_accumulated |= deg_recalc_flags_effective(NULL, flag);
}
void graph_id_tag_update(
@@ -820,13 +824,10 @@ void DEG_ids_check_recalc(
static void deg_graph_clear_id_recalc_flags(ID *id)
{
- /* Keep incremental track of used recalc flags, to get proper update when undoing. */
- id->recalc_undo_accumulated |= id->recalc;
id->recalc &= ~ID_RECALC_ALL;
bNodeTree *ntree = ntreeFromID(id);
/* Clear embedded node trees too. */
if (ntree) {
- ntree->id.recalc_undo_accumulated |= ntree->id.recalc;
ntree->id.recalc &= ~ID_RECALC_ALL;
}
/* XXX And what about scene's master collection here? */
diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h
index 0e02b07e95b..b2fea957227 100644
--- a/source/blender/draw/intern/draw_cache_extract.h
+++ b/source/blender/draw/intern/draw_cache_extract.h
@@ -253,6 +253,7 @@ void mesh_buffer_cache_create_requested(MeshBatchCache *cache,
MeshBufferCache mbc,
Mesh *me,
const bool is_editmode,
+ const bool is_paint_mode,
const float obmat[4][4],
const bool do_final,
const bool do_uvedit,
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c
index 70fe6a55461..0449601adf7 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.c
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.c
@@ -129,6 +129,7 @@ typedef struct MeshRenderData {
static MeshRenderData *mesh_render_data_create(Mesh *me,
const bool is_editmode,
+ const bool is_paint_mode,
const float obmat[4][4],
const bool do_final,
const bool do_uvedit,
@@ -192,7 +193,7 @@ static MeshRenderData *mesh_render_data_create(Mesh *me,
mr->me = me;
mr->edit_bmesh = NULL;
- bool use_mapped = mr->me && !mr->me->runtime.is_original;
+ bool use_mapped = is_paint_mode && mr->me && !mr->me->runtime.is_original;
if (use_mapped) {
mr->v_origindex = CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX);
mr->e_origindex = CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX);
@@ -4596,6 +4597,7 @@ void mesh_buffer_cache_create_requested(MeshBatchCache *cache,
MeshBufferCache mbc,
Mesh *me,
const bool is_editmode,
+ const bool is_paint_mode,
const float obmat[4][4],
const bool do_final,
const bool do_uvedit,
@@ -4657,8 +4659,16 @@ void mesh_buffer_cache_create_requested(MeshBatchCache *cache,
double rdata_start = PIL_check_seconds_timer();
#endif
- MeshRenderData *mr = mesh_render_data_create(
- me, is_editmode, obmat, do_final, do_uvedit, iter_flag, data_flag, cd_layer_used, ts);
+ MeshRenderData *mr = mesh_render_data_create(me,
+ is_editmode,
+ is_paint_mode,
+ obmat,
+ do_final,
+ do_uvedit,
+ iter_flag,
+ data_flag,
+ cd_layer_used,
+ ts);
mr->cache = cache; /* HACK */
mr->use_hide = use_hide;
mr->use_subsurf_fdots = use_subsurf_fdots;
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c
index 308d87f1385..1aaead27ee7 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.c
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.c
@@ -1369,6 +1369,7 @@ void DRW_mesh_batch_cache_create_requested(
cache->uv_cage,
me,
is_editmode,
+ is_paint_mode,
ob->obmat,
false,
true,
@@ -1384,6 +1385,7 @@ void DRW_mesh_batch_cache_create_requested(
cache->cage,
me,
is_editmode,
+ is_paint_mode,
ob->obmat,
false,
false,
@@ -1398,6 +1400,7 @@ void DRW_mesh_batch_cache_create_requested(
cache->final,
me,
is_editmode,
+ is_paint_mode,
ob->obmat,
true,
false,
diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c
index dca4f8e01e6..331a1f80bec 100644
--- a/source/blender/draw/intern/draw_cache_impl_particles.c
+++ b/source/blender/draw/intern/draw_cache_impl_particles.c
@@ -339,7 +339,8 @@ static void particle_calculate_parent_mcol(ParticleSystem *psys,
if (num != DMCACHE_NOTFOUND && num != DMCACHE_ISCHILD) {
MFace *mface = &psmd->mesh_final->mface[num];
for (int j = 0; j < num_col_layers; j++) {
- psys_interpolate_mcol(mcols[j] + num, mface->v4, particle->fuv, &r_mcol[j]);
+ /* CustomDataLayer CD_MCOL has 4 structs per face. */
+ psys_interpolate_mcol(mcols[j] + num * 4, mface->v4, particle->fuv, &r_mcol[j]);
}
}
}
@@ -388,6 +389,7 @@ static void particle_interpolate_children_mcol(ParticleSystem *psys,
if (num != DMCACHE_NOTFOUND) {
MFace *mface = &psmd->mesh_final->mface[num];
for (int j = 0; j < num_col_layers; j++) {
+ /* CustomDataLayer CD_MCOL has 4 structs per face. */
psys_interpolate_mcol(mcols[j] + num * 4, mface->v4, particle->fuv, &r_mcol[j]);
}
}
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index 38a19e85661..c054691ca38 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -1844,11 +1844,15 @@ static size_t animdata_filter_gpencil(bAnimContext *ac,
ViewLayer *view_layer = (ViewLayer *)ac->view_layer;
Base *base;
- /* Active scene's GPencil block first - No parent item needed... */
- if (scene->gpd) {
- items += animdata_filter_gpencil_data(anim_data, ads, scene->gpd, filter_mode);
+ /* Include all annotation datablocks. */
+ if (((ads->filterflag & ADS_FILTER_ONLYSEL) == 0) ||
+ (ads->filterflag & ADS_FILTER_INCL_HIDDEN)) {
+ LISTBASE_FOREACH (bGPdata *, gpd, &ac->bmain->gpencils) {
+ if (gpd->flag & GP_DATA_ANNOTATIONS) {
+ items += animdata_filter_gpencil_data(anim_data, ads, gpd, filter_mode);
+ }
+ }
}
-
/* Objects in the scene */
for (base = view_layer->object_bases.first; base; base = base->next) {
/* Only consider this object if it has got some GP data (saving on all the other tests) */
diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c
index f6875a6e158..dfb274fdefe 100644
--- a/source/blender/editors/armature/armature_add.c
+++ b/source/blender/editors/armature/armature_add.c
@@ -22,6 +22,7 @@
* \ingroup edarmature
*/
+#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_constraint_types.h"
#include "DNA_object_types.h"
@@ -38,8 +39,12 @@
#include "BKE_constraint.h"
#include "BKE_context.h"
#include "BKE_deform.h"
+#include "BKE_fcurve.h"
#include "BKE_idprop.h"
#include "BKE_layer.h"
+#include "BKE_lib_id.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
#include "RNA_access.h"
#include "RNA_define.h"
@@ -375,25 +380,20 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob)
BLI_ghash_free(name_map, NULL, NULL);
}
-/*
- * Note: When duplicating cross objects, editbones here is the list of bones
- * from the SOURCE object but ob is the DESTINATION object
- * */
-void updateDuplicateSubtargetObjects(EditBone *dupBone,
+static void updateDuplicateSubtarget(EditBone *dup_bone,
ListBase *editbones,
- Object *src_ob,
- Object *dst_ob)
+ Object *ob,
+ bool lookup_mirror_subtarget)
{
- /* If an edit bone has been duplicated, lets
- * update it's constraints if the subtarget
- * they point to has also been duplicated
+ /* If an edit bone has been duplicated, lets update it's constraints if the
+ * subtarget they point to has also been duplicated.
*/
EditBone *oldtarget, *newtarget;
bPoseChannel *pchan;
bConstraint *curcon;
ListBase *conlist;
- if ((pchan = BKE_pose_channel_verify(dst_ob->pose, dupBone->name))) {
+ if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name))) {
if ((conlist = &pchan->constraints)) {
for (curcon = conlist->first; curcon; curcon = curcon->next) {
/* does this constraint have a subtarget in
@@ -407,8 +407,7 @@ void updateDuplicateSubtargetObjects(EditBone *dupBone,
cti->get_constraint_targets(curcon, &targets);
for (ct = targets.first; ct; ct = ct->next) {
- if ((ct->tar == src_ob) && (ct->subtarget[0])) {
- ct->tar = dst_ob; /* update target */
+ if ((ct->tar == ob) && (ct->subtarget[0])) {
oldtarget = get_named_editbone(editbones, ct->subtarget);
if (oldtarget) {
/* was the subtarget bone duplicated too? If
@@ -419,6 +418,17 @@ void updateDuplicateSubtargetObjects(EditBone *dupBone,
newtarget = oldtarget->temp.ebone;
BLI_strncpy(ct->subtarget, newtarget->name, sizeof(ct->subtarget));
}
+ else if (lookup_mirror_subtarget) {
+ /* The subtarget was not selected for duplication, try to see if a mirror bone of
+ * the current target exists */
+ char name_flip[MAXBONENAME];
+
+ BLI_string_flip_side_name(name_flip, oldtarget->name, false, sizeof(name_flip));
+ newtarget = get_named_editbone(editbones, name_flip);
+ if (newtarget) {
+ BLI_strncpy(ct->subtarget, newtarget->name, sizeof(ct->subtarget));
+ }
+ }
}
}
}
@@ -432,32 +442,434 @@ void updateDuplicateSubtargetObjects(EditBone *dupBone,
}
}
-void updateDuplicateSubtarget(EditBone *dupBone, ListBase *editbones, Object *ob)
+static void updateDuplicateActionConstraintSettings(EditBone *dup_bone,
+ EditBone *orig_bone,
+ Object *ob,
+ bConstraint *curcon)
{
- updateDuplicateSubtargetObjects(dupBone, editbones, ob, ob);
+ bActionConstraint *act_con = (bActionConstraint *)curcon->data;
+ bAction *act = (bAction *)act_con->act;
+
+ float mat[4][4];
+
+ unit_m4(mat);
+ bPoseChannel *target_pchan = BKE_pose_channel_find_name(ob->pose, act_con->subtarget);
+ BKE_constraint_mat_convertspace(
+ ob, target_pchan, mat, curcon->tarspace, CONSTRAINT_SPACE_LOCAL, false);
+
+ float max_axis_val = 0;
+ int max_axis = 0;
+ /* Which axis represents X now. IE, which axis defines the mirror plane. */
+ for (int i = 0; i < 3; i++) {
+ float cur_val = fabsf(mat[0][i]);
+ if (cur_val > max_axis_val) {
+ max_axis = i;
+ max_axis_val = cur_val;
+ }
+ }
+
+ /* data->type is mapped as follows for backwards compatibility:
+ * 00,01,02 - rotation (it used to be like this)
+ * 10,11,12 - scaling
+ * 20,21,22 - location
+ */
+ /* Mirror the target range */
+ if (act_con->type < 10 && act_con->type != max_axis) {
+ /* Y or Z rotation */
+ act_con->min = -act_con->min;
+ act_con->max = -act_con->max;
+ }
+ else if (act_con->type == max_axis + 10) {
+ /* X scaling */
+ }
+ else if (act_con->type == max_axis + 20) {
+ /* X location */
+ float imat[4][4];
+
+ invert_m4_m4(imat, mat);
+
+ float min_vec[3], max_vec[3];
+
+ zero_v3(min_vec);
+ zero_v3(max_vec);
+
+ min_vec[0] = act_con->min;
+ max_vec[0] = act_con->max;
+
+ /* convert values into local object space */
+ mul_m4_v3(mat, min_vec);
+ mul_m4_v3(mat, max_vec);
+
+ min_vec[0] *= -1;
+ max_vec[0] *= -1;
+
+ /* convert back to the settings space */
+ mul_m4_v3(imat, min_vec);
+ mul_m4_v3(imat, max_vec);
+
+ act_con->min = min_vec[0];
+ act_con->max = max_vec[0];
+ }
+
+ /* See if there is any channels that uses this bone */
+ ListBase ani_curves;
+ BLI_listbase_clear(&ani_curves);
+ if (list_find_data_fcurves(&ani_curves, &act->curves, "pose.bones[", orig_bone->name)) {
+ /* Create a copy and mirror the animation */
+ for (LinkData *ld = ani_curves.first; ld; ld = ld->next) {
+ FCurve *old_curve = ld->data;
+ FCurve *new_curve = copy_fcurve(old_curve);
+ bActionGroup *agrp;
+
+ char *old_path = new_curve->rna_path;
+
+ new_curve->rna_path = BLI_str_replaceN(old_path, orig_bone->name, dup_bone->name);
+ MEM_freeN(old_path);
+
+ /* Flip the animation */
+ int i;
+ BezTriple *bezt;
+ for (i = 0, bezt = new_curve->bezt; i < new_curve->totvert; i++, bezt++) {
+ const size_t slength = strlen(new_curve->rna_path);
+ bool flip = false;
+ if (BLI_strn_endswith(new_curve->rna_path, "location", slength) &&
+ new_curve->array_index == 0) {
+ flip = true;
+ }
+ else if (BLI_strn_endswith(new_curve->rna_path, "rotation_quaternion", slength) &&
+ ELEM(new_curve->array_index, 2, 3)) {
+ flip = true;
+ }
+ else if (BLI_strn_endswith(new_curve->rna_path, "rotation_euler", slength) &&
+ ELEM(new_curve->array_index, 1, 2)) {
+ flip = true;
+ }
+ else if (BLI_strn_endswith(new_curve->rna_path, "rotation_axis_angle", slength) &&
+ ELEM(new_curve->array_index, 2, 3)) {
+ flip = true;
+ }
+
+ if (flip) {
+ bezt->vec[0][1] *= -1;
+ bezt->vec[1][1] *= -1;
+ bezt->vec[2][1] *= -1;
+ }
+ }
+
+ /* Make sure that a action group name for the new bone exists */
+ agrp = BKE_action_group_find_name(act, dup_bone->name);
+
+ if (agrp == NULL) {
+ agrp = action_groups_add_new(act, dup_bone->name);
+ }
+ BLI_assert(agrp != NULL);
+ action_groups_add_channel(act, agrp, new_curve);
+ }
+ }
+ BLI_freelistN(&ani_curves);
+
+ /* Make deps graph aware of our changes */
+ DEG_id_tag_update(&act->id, ID_RECALC_ANIMATION_NO_FLUSH);
}
-EditBone *duplicateEditBoneObjects(
- EditBone *curBone, const char *name, ListBase *editbones, Object *src_ob, Object *dst_ob)
+static void updateDuplicateKinematicConstraintSettings(bConstraint *curcon)
{
- EditBone *eBone = MEM_mallocN(sizeof(EditBone), "addup_editbone");
+ /* IK constraint */
+ bKinematicConstraint *ik = (bKinematicConstraint *)curcon->data;
+ ik->poleangle = -M_PI - ik->poleangle;
+ /* Wrap the angle to the +/-180.0f range (default soft limit of the input boxes). */
+ ik->poleangle = angle_wrap_rad(ik->poleangle);
+}
- /* Copy data from old bone to new bone */
- memcpy(eBone, curBone, sizeof(EditBone));
+static void updateDuplicateLocRotConstraintSettings(Object *ob,
+ bPoseChannel *pchan,
+ bConstraint *curcon)
+{
+ /* This code assumes that bRotLimitConstraint and bLocLimitConstraint have the same fields in
+ * the same memory locations. */
+ BLI_assert(sizeof(bLocLimitConstraint) == sizeof(bRotLimitConstraint));
- curBone->temp.ebone = eBone;
- eBone->temp.ebone = curBone;
+ bRotLimitConstraint *limit = (bRotLimitConstraint *)curcon->data;
+ float local_mat[4][4], imat[4][4];
- if (name != NULL) {
- BLI_strncpy(eBone->name, name, sizeof(eBone->name));
+ float min_vec[3], max_vec[3];
+
+ min_vec[0] = limit->xmin;
+ min_vec[1] = limit->ymin;
+ min_vec[2] = limit->zmin;
+
+ max_vec[0] = limit->xmax;
+ max_vec[1] = limit->ymax;
+ max_vec[2] = limit->zmax;
+
+ unit_m4(local_mat);
+
+ BKE_constraint_mat_convertspace(
+ ob, pchan, local_mat, curcon->ownspace, CONSTRAINT_SPACE_LOCAL, false);
+
+ if (curcon->type == CONSTRAINT_TYPE_ROTLIMIT) {
+ /* Zero out any location translation */
+ local_mat[3][0] = local_mat[3][1] = local_mat[3][2] = 0;
+ }
+
+ invert_m4_m4(imat, local_mat);
+ /* convert values into local object space */
+ mul_m4_v3(local_mat, min_vec);
+ mul_m4_v3(local_mat, max_vec);
+
+ if (curcon->type == CONSTRAINT_TYPE_ROTLIMIT) {
+ float min_copy[3];
+
+ copy_v3_v3(min_copy, min_vec);
+
+ min_vec[1] = max_vec[1] * -1;
+ min_vec[2] = max_vec[2] * -1;
+
+ max_vec[1] = min_copy[1] * -1;
+ max_vec[2] = min_copy[2] * -1;
+ }
+ else {
+ float min_x_copy = min_vec[0];
+
+ min_vec[0] = max_vec[0] * -1;
+ max_vec[0] = min_x_copy * -1;
+ }
+
+ /* convert back to the settings space */
+ mul_m4_v3(imat, min_vec);
+ mul_m4_v3(imat, max_vec);
+
+ limit->xmin = min_vec[0];
+ limit->ymin = min_vec[1];
+ limit->zmin = min_vec[2];
+
+ limit->xmax = max_vec[0];
+ limit->ymax = max_vec[1];
+ limit->zmax = max_vec[2];
+}
+
+static void updateDuplicateTransformConstraintSettings(Object *ob,
+ bPoseChannel *pchan,
+ bConstraint *curcon)
+{
+ bTransformConstraint *trans = (bTransformConstraint *)curcon->data;
+
+ float target_mat[4][4], own_mat[4][4], imat[4][4];
+
+ unit_m4(own_mat);
+ BKE_constraint_mat_convertspace(
+ ob, pchan, own_mat, curcon->ownspace, CONSTRAINT_SPACE_LOCAL, false);
+
+ /* ###Source map mirroring### */
+ float old_min, old_max;
+
+ /* Source location */
+ invert_m4_m4(imat, own_mat);
+
+ /* convert values into local object space */
+ mul_m4_v3(own_mat, trans->from_min);
+ mul_m4_v3(own_mat, trans->from_max);
+
+ old_min = trans->from_min[0];
+ old_max = trans->from_max[0];
+
+ trans->from_min[0] = -old_max;
+ trans->from_max[0] = -old_min;
+
+ /* convert back to the settings space */
+ mul_m4_v3(imat, trans->from_min);
+ mul_m4_v3(imat, trans->from_max);
+
+ /* Source rotation */
+
+ /* Zero out any location translation */
+ own_mat[3][0] = own_mat[3][1] = own_mat[3][2] = 0;
+
+ invert_m4_m4(imat, own_mat);
+
+ /* convert values into local object space */
+ mul_m4_v3(own_mat, trans->from_min_rot);
+ mul_m4_v3(own_mat, trans->from_max_rot);
+
+ old_min = trans->from_min_rot[1];
+ old_max = trans->from_max_rot[1];
+
+ trans->from_min_rot[1] = old_max * -1;
+ trans->from_max_rot[1] = old_min * -1;
+
+ old_min = trans->from_min_rot[2];
+ old_max = trans->from_max_rot[2];
+
+ trans->from_min_rot[2] = old_max * -1;
+ trans->from_max_rot[2] = old_min * -1;
+
+ /* convert back to the settings space */
+ mul_m4_v3(imat, trans->from_min_rot);
+ mul_m4_v3(imat, trans->from_max_rot);
+
+ /* Source scale does not require any mirroring */
+
+ /* ###Destination map mirroring### */
+ float temp_vec[3];
+ float imat_rot[4][4];
+
+ bPoseChannel *target_pchan = BKE_pose_channel_find_name(ob->pose, trans->subtarget);
+ unit_m4(target_mat);
+ BKE_constraint_mat_convertspace(
+ ob, target_pchan, target_mat, curcon->tarspace, CONSTRAINT_SPACE_LOCAL, false);
+
+ invert_m4_m4(imat, target_mat);
+ /* convert values into local object space */
+ mul_m4_v3(target_mat, trans->to_min);
+ mul_m4_v3(target_mat, trans->to_max);
+ mul_m4_v3(target_mat, trans->to_min_scale);
+ mul_m4_v3(target_mat, trans->to_max_scale);
+
+ /* Zero out any location translation */
+ target_mat[3][0] = target_mat[3][1] = target_mat[3][2] = 0;
+ invert_m4_m4(imat_rot, target_mat);
+
+ mul_m4_v3(target_mat, trans->to_min_rot);
+ mul_m4_v3(target_mat, trans->to_max_rot);
+
+ /* TODO(sebpa): This does not support euler order, but doing so will make this way more complex.
+ * For now we have decided to not support all corner cases and advanced setups. */
+
+ /* Helper variables to denote the axis in trans->map */
+ const char X = 0;
+ const char Y = 1;
+ const char Z = 2;
+
+ switch (trans->to) {
+ case TRANS_SCALE:
+ copy_v3_v3(temp_vec, trans->to_max_scale);
+
+ for (int i = 0; i < 3; i++) {
+ if ((trans->from == TRANS_LOCATION && trans->map[i] == X) ||
+ (trans->from == TRANS_ROTATION && trans->map[i] != X)) {
+ /* X Loc to X/Y/Z Scale: Min/Max Flipped */
+ /* Y Rot to X/Y/Z Scale: Min/Max Flipped */
+ /* Z Rot to X/Y/Z Scale: Min/Max Flipped */
+ trans->to_max_scale[i] = trans->to_min_scale[i];
+ trans->to_min_scale[i] = temp_vec[i];
+ }
+ }
+ break;
+ case TRANS_LOCATION:
+ /* Invert the X location */
+ trans->to_min[0] *= -1;
+ trans->to_max[0] *= -1;
+
+ copy_v3_v3(temp_vec, trans->to_max);
+
+ for (int i = 0; i < 3; i++) {
+ if ((trans->from == TRANS_LOCATION && trans->map[i] == X) ||
+ (trans->from == TRANS_ROTATION && trans->map[i] != X)) {
+ /* X Loc to X/Y/Z Loc: Min/Max Flipped (and Inverted)
+ * Y Rot to X/Y/Z Loc: Min/Max Flipped
+ * Z Rot to X/Y/Z Loc: Min/Max Flipped */
+ trans->to_max[i] = trans->to_min[i];
+ trans->to_min[i] = temp_vec[i];
+ }
+ }
+ break;
+ case TRANS_ROTATION:
+ /* Invert the Z rotation */
+ trans->to_min_rot[2] *= -1;
+ trans->to_max_rot[2] *= -1;
+
+ if ((trans->from == TRANS_LOCATION && trans->map[1] != X) ||
+ (trans->from == TRANS_ROTATION && trans->map[1] != Y) || trans->from == TRANS_SCALE) {
+ /* Invert the Y rotation */
+ trans->to_min_rot[1] *= -1;
+ trans->to_max_rot[1] *= -1;
+ }
+
+ copy_v3_v3(temp_vec, trans->to_max_rot);
+
+ for (int i = 0; i < 3; i++) {
+ if ((trans->from == TRANS_LOCATION && trans->map[i] == X && i != 1) ||
+ (trans->from == TRANS_ROTATION && trans->map[i] == Y && i != 1) ||
+ (trans->from == TRANS_ROTATION && trans->map[i] == Z)) {
+ /* X Loc to X/Z Rot: Flipped
+ * Y Rot to X/Z Rot: Flipped
+ * Z Rot to X/Y/Z rot: Flipped */
+ trans->to_max_rot[i] = trans->to_min_rot[i];
+ trans->to_min_rot[i] = temp_vec[i];
+ }
+ }
+ break;
+ }
+ /* convert back to the settings space */
+ mul_m4_v3(imat, trans->to_min);
+ mul_m4_v3(imat, trans->to_max);
+ mul_m4_v3(imat_rot, trans->to_min_rot);
+ mul_m4_v3(imat_rot, trans->to_max_rot);
+ mul_m4_v3(imat, trans->to_min_scale);
+ mul_m4_v3(imat, trans->to_max_scale);
+}
+
+static void updateDuplicateConstraintSettings(EditBone *dup_bone, EditBone *orig_bone, Object *ob)
+{
+ /* If an edit bone has been duplicated, lets update it's constraints if the
+ * subtarget they point to has also been duplicated.
+ */
+ bPoseChannel *pchan;
+ bConstraint *curcon;
+ ListBase *conlist;
+
+ if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name)) == NULL ||
+ (conlist = &pchan->constraints) == NULL) {
+ return;
+ }
+
+ for (curcon = conlist->first; curcon; curcon = curcon->next) {
+ switch (curcon->type) {
+ case CONSTRAINT_TYPE_ACTION:
+ updateDuplicateActionConstraintSettings(dup_bone, orig_bone, ob, curcon);
+ break;
+ case CONSTRAINT_TYPE_KINEMATIC:
+ updateDuplicateKinematicConstraintSettings(curcon);
+ break;
+ case CONSTRAINT_TYPE_LOCLIMIT:
+ case CONSTRAINT_TYPE_ROTLIMIT:
+ updateDuplicateLocRotConstraintSettings(ob, pchan, curcon);
+ break;
+ case CONSTRAINT_TYPE_TRANSFORM:
+ updateDuplicateTransformConstraintSettings(ob, pchan, curcon);
+ break;
+ }
+ }
+}
+
+static void updateDuplicateCustomBoneShapes(bContext *C, EditBone *dup_bone, Object *ob)
+{
+ if (ob->pose == NULL) {
+ return;
}
+ bPoseChannel *pchan;
+ pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name);
- ED_armature_ebone_unique_name(editbones, eBone->name, NULL);
- BLI_addtail(editbones, eBone);
+ if (pchan->custom != NULL) {
+ Main *bmain = CTX_data_main(C);
+ char name_flip[MAX_ID_NAME - 2];
+ /* Skip the first two chars in the object name as those are used to store object type */
+ BLI_string_flip_side_name(name_flip, pchan->custom->id.name + 2, false, sizeof(name_flip));
+ Object *shape_ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name_flip);
+
+ if (shape_ob != NULL) {
+ /* A flipped shape object exists, use it! */
+ pchan->custom = shape_ob;
+ }
+ }
+}
+
+static void copy_pchan(EditBone *src_bone, EditBone *dst_bone, Object *src_ob, Object *dst_ob)
+{
/* copy the ID property */
- if (curBone->prop) {
- eBone->prop = IDP_CopyProperty(curBone->prop);
+ if (src_bone->prop) {
+ dst_bone->prop = IDP_CopyProperty(src_bone->prop);
}
/* Lets duplicate the list of constraints that the
@@ -466,25 +878,46 @@ EditBone *duplicateEditBoneObjects(
if (src_ob->pose) {
bPoseChannel *chanold, *channew;
- chanold = BKE_pose_channel_verify(src_ob->pose, curBone->name);
+ chanold = BKE_pose_channel_verify(src_ob->pose, src_bone->name);
if (chanold) {
/* WARNING: this creates a new posechannel, but there will not be an attached bone
* yet as the new bones created here are still 'EditBones' not 'Bones'.
*/
- channew = BKE_pose_channel_verify(dst_ob->pose, eBone->name);
+ channew = BKE_pose_channel_verify(dst_ob->pose, dst_bone->name);
if (channew) {
BKE_pose_channel_copy_data(channew, chanold);
}
}
}
+}
- return eBone;
+EditBone *duplicateEditBoneObjects(
+ EditBone *cur_bone, const char *name, ListBase *editbones, Object *src_ob, Object *dst_ob)
+{
+ EditBone *e_bone = MEM_mallocN(sizeof(EditBone), "addup_editbone");
+
+ /* Copy data from old bone to new bone */
+ memcpy(e_bone, cur_bone, sizeof(EditBone));
+
+ cur_bone->temp.ebone = e_bone;
+ e_bone->temp.ebone = cur_bone;
+
+ if (name != NULL) {
+ BLI_strncpy(e_bone->name, name, sizeof(e_bone->name));
+ }
+
+ ED_armature_ebone_unique_name(editbones, e_bone->name, NULL);
+ BLI_addtail(editbones, e_bone);
+
+ copy_pchan(cur_bone, e_bone, src_ob, dst_ob);
+
+ return e_bone;
}
-EditBone *duplicateEditBone(EditBone *curBone, const char *name, ListBase *editbones, Object *ob)
+EditBone *duplicateEditBone(EditBone *cur_bone, const char *name, ListBase *editbones, Object *ob)
{
- return duplicateEditBoneObjects(curBone, name, editbones, ob, ob);
+ return duplicateEditBoneObjects(cur_bone, name, editbones, ob, ob);
}
static int armature_duplicate_selected_exec(bContext *C, wmOperator *op)
@@ -538,8 +971,8 @@ static int armature_duplicate_selected_exec(bContext *C, wmOperator *op)
BLI_string_flip_side_name(
new_bone_name_buff, ebone_iter->name, false, sizeof(new_bone_name_buff));
- /* Only use flipped name if not yet in use. Otherwise we'd get again inconsistent namings
- * (different numbers), better keep default behavior in this case. */
+ /* Only use flipped name if not yet in use. Otherwise we'd get again inconsistent
+ * namings (different numbers), better keep default behavior in this case. */
if (ED_armature_ebone_find_name(arm->edbo, new_bone_name_buff) == NULL) {
new_bone_name = new_bone_name_buff;
}
@@ -567,13 +1000,13 @@ static int armature_duplicate_selected_exec(bContext *C, wmOperator *op)
}
else if (ebone_iter->parent->temp.ebone) {
/* If this bone has a parent that was duplicated,
- * Set the duplicate->parent to the curBone->parent->temp
+ * Set the duplicate->parent to the cur_bone->parent->temp
*/
ebone->parent = ebone_iter->parent->temp.ebone;
}
else {
/* If this bone has a parent that IS not selected,
- * Set the duplicate->parent to the curBone->parent
+ * Set the duplicate->parent to the cur_bone->parent
*/
ebone->parent = (EditBone *)ebone_iter->parent;
ebone->flag &= ~BONE_CONNECTED;
@@ -590,7 +1023,7 @@ static int armature_duplicate_selected_exec(bContext *C, wmOperator *op)
/* Lets try to fix any constraint subtargets that might
* have been duplicated
*/
- updateDuplicateSubtarget(ebone, arm->edbo, ob);
+ updateDuplicateSubtarget(ebone, arm->edbo, ob, false);
}
}
@@ -745,9 +1178,21 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
/* Find the selected bones and duplicate them as needed, with mirrored name. */
for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != ebone_first_dupe;
ebone_iter = ebone_iter->next) {
- if (EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED) &&
- /* will be set if the mirror bone already exists (no need to make a new one) */
- (ebone_iter->temp.ebone == NULL)) {
+ if (EBONE_VISIBLE(arm, ebone_iter) && (ebone_iter->flag & BONE_SELECTED)) {
+ if (ebone_iter->temp.ebone != NULL) {
+ /* This will be set if the mirror bone already exists (no need to make a new one)
+ * but we do need to make sure that the 'pchan' settings (constraints etc)
+ * is synchronized. */
+ bPoseChannel *pchan;
+ /* Make sure we clean up the old data before overwriting it */
+ pchan = BKE_pose_channel_verify(obedit->pose, ebone_iter->temp.ebone->name);
+ BKE_pose_channel_free(pchan);
+ /* Sync pchan data */
+ copy_pchan(ebone_iter, ebone_iter->temp.ebone, obedit, obedit);
+ /* Sync scale mode */
+ ebone_iter->temp.ebone->inherit_scale_mode = ebone_iter->inherit_scale_mode;
+ continue;
+ }
char name_flip[MAXBONENAME];
BLI_string_flip_side_name(name_flip, ebone_iter->name, false, sizeof(name_flip));
@@ -793,7 +1238,12 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
* then we can assume the parent has no L/R but is a center bone.
* So just use the same parent for both.
*/
- ebone->flag &= ~BONE_CONNECTED;
+
+ if (ebone->head[axis] != 0.0f) {
+ /* The mirrored bone doesn't start on the mirror axis, so assume that this one should
+ * not be connected to the old parent */
+ ebone->flag &= ~BONE_CONNECTED;
+ }
}
ebone->parent = ebone_parent;
@@ -803,10 +1253,19 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
ebone->bbone_prev = get_symmetrized_bone(arm, ebone_iter->bbone_prev);
ebone->bbone_next = get_symmetrized_bone(arm, ebone_iter->bbone_next);
+ /* Sync bbone handle types */
+ ebone->bbone_prev_type = ebone_iter->bbone_prev_type;
+ ebone->bbone_next_type = ebone_iter->bbone_next_type;
+
/* Lets try to fix any constraint subtargets that might
* have been duplicated
*/
- updateDuplicateSubtarget(ebone, arm->edbo, obedit);
+ updateDuplicateSubtarget(ebone, arm->edbo, obedit, true);
+ /* Try to update constraint options so that they are mirrored as well
+ * (need to supply bone_iter as well in case we are working with existing bones) */
+ updateDuplicateConstraintSettings(ebone, ebone_iter, obedit);
+ /* Mirror bone shapes if possible */
+ updateDuplicateCustomBoneShapes(C, ebone, obedit);
}
}
@@ -1232,7 +1691,7 @@ void ARMATURE_OT_subdivide(wmOperatorType *ot)
PropertyRNA *prop;
/* identifiers */
- ot->name = "Subdivide Multi";
+ ot->name = "Subdivide";
ot->idname = "ARMATURE_OT_subdivide";
ot->description = "Break selected bones into chains of smaller bones";
diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h
index a454461b144..3d41fd5f0c6 100644
--- a/source/blender/editors/armature/armature_intern.h
+++ b/source/blender/editors/armature/armature_intern.h
@@ -232,9 +232,6 @@ struct EditBone *duplicateEditBone(struct EditBone *curBone,
const char *name,
struct ListBase *editbones,
struct Object *ob);
-void updateDuplicateSubtarget(struct EditBone *dupBone,
- struct ListBase *editbones,
- struct Object *ob);
/* duplicate method (cross objects) */
/* editbones is the target list */
@@ -244,12 +241,6 @@ struct EditBone *duplicateEditBoneObjects(struct EditBone *curBone,
struct Object *src_ob,
struct Object *dst_ob);
-/* editbones is the source list */
-void updateDuplicateSubtargetObjects(struct EditBone *dupBone,
- struct ListBase *editbones,
- struct Object *src_ob,
- struct Object *dst_ob);
-
EditBone *add_points_bone(struct Object *obedit, float head[3], float tail[3]);
void bone_free(struct bArmature *arm, struct EditBone *bone);
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c
index 4b938fb0072..98f067af148 100644
--- a/source/blender/editors/armature/armature_select.c
+++ b/source/blender/editors/armature/armature_select.c
@@ -657,45 +657,28 @@ static EditBone *get_nearest_editbonepoint(
uint hitresult;
Base *base;
EditBone *ebone;
- } best = {
- .hitresult = BONESEL_NOSEL,
- .base = NULL,
- .ebone = NULL,
- };
+ } *result = NULL,
+
+ result_cycle = {.hitresult = BONESEL_NOSEL, .base = NULL, .ebone = NULL},
+ result_bias = {.hitresult = BONESEL_NOSEL, .base = NULL, .ebone = NULL};
/* find the bone after the current active bone, so as to bump up its chances in selection.
* this way overlapping bones will cycle selection state as with objects. */
- EditBone *ebone_next_act = ((bArmature *)vc->obedit->data)->act_edbone;
- {
- bArmature *arm = (bArmature *)vc->obedit->data;
- if (ebone_next_act && EBONE_VISIBLE(arm, ebone_next_act) &&
- ebone_next_act->flag & (BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) {
- ebone_next_act = ebone_next_act->next ? ebone_next_act->next : arm->edbo->first;
- }
- else {
- ebone_next_act = NULL;
- }
+ Object *obedit_orig = vc->obedit;
+ EditBone *ebone_active_orig = ((bArmature *)obedit_orig->data)->act_edbone;
+ if (ebone_active_orig == NULL) {
+ use_cycle = false;
}
- bool do_nearest = false;
-
- /* define if we use solid nearest select or not */
if (use_cycle) {
static int last_mval[2] = {-100, -100};
-
- if (!XRAY_ACTIVE(vc->v3d)) {
- do_nearest = true;
- if (len_manhattan_v2v2_int(vc->mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) {
- do_nearest = false;
- }
+ if ((len_manhattan_v2v2_int(vc->mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) == 0) {
+ use_cycle = false;
}
copy_v2_v2_int(last_mval, vc->mval);
}
- else {
- if (!XRAY_ACTIVE(vc->v3d)) {
- do_nearest = true;
- }
- }
+
+ const bool do_nearest = !(XRAY_ACTIVE(vc->v3d) || use_cycle);
/* matching logic from 'mixed_bones_object_selectbuffer' */
int hits = 0;
@@ -750,13 +733,39 @@ cache_end:
if (hits > 0) {
if (hits == 1) {
if (!(buffer[3] & BONESEL_NOSEL)) {
- best.hitresult = buffer[3];
- best.base = ED_armature_base_and_ebone_from_select_buffer(
- bases, bases_len, best.hitresult, &best.ebone);
+ result_bias.hitresult = buffer[3];
+ result_bias.base = ED_armature_base_and_ebone_from_select_buffer(
+ bases, bases_len, result_bias.hitresult, &result_bias.ebone);
}
}
else {
- int dep_min = 5;
+ int bias_max = INT_MIN;
+
+ /* Track cycle variables. */
+ struct {
+ union {
+ uint32_t cmp;
+ struct {
+#ifdef __BIG_ENDIAN__
+ uint16_t ob;
+ uint16_t bone;
+#else
+ uint16_t bone;
+ uint16_t ob;
+#endif
+ } index;
+ } active, test, best;
+ } cycle_order;
+
+ if (use_cycle) {
+ bArmature *arm = obedit_orig->data;
+ int ob_index = obedit_orig->runtime.select_id & 0xFFFF;
+ int bone_index = BLI_findindex(arm->edbo, ebone_active_orig);
+ cycle_order.active.index.ob = ob_index;
+ cycle_order.active.index.bone = bone_index;
+ cycle_order.best.cmp = 0xffffffff;
+ }
+
for (int i = 0; i < hits; i++) {
const uint hitresult = buffer[3 + (i * 4)];
if (!(hitresult & BONESEL_NOSEL)) {
@@ -767,69 +776,98 @@ cache_end:
/* If this fails, selection code is setting the selection ID's incorrectly. */
BLI_assert(base && ebone);
- int dep;
- /* clicks on bone points get advantage */
- if (hitresult & (BONESEL_ROOT | BONESEL_TIP)) {
- /* but also the unselected one */
- if (findunsel) {
- if ((hitresult & BONESEL_ROOT) && (ebone->flag & BONE_ROOTSEL) == 0) {
- dep = 1;
- }
- else if ((hitresult & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL) == 0) {
- dep = 1;
+ /* Prioritized selection. */
+ {
+ int bias;
+ /* clicks on bone points get advantage */
+ if (hitresult & (BONESEL_ROOT | BONESEL_TIP)) {
+ /* but also the unselected one */
+ if (findunsel) {
+ if ((hitresult & BONESEL_ROOT) && (ebone->flag & BONE_ROOTSEL) == 0) {
+ bias = 4;
+ }
+ else if ((hitresult & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL) == 0) {
+ bias = 4;
+ }
+ else {
+ bias = 3;
+ }
}
else {
- dep = 2;
+ bias = 4;
}
}
else {
- dep = 1;
- }
- }
- else {
- /* bone found */
- if (findunsel) {
- if ((ebone->flag & BONE_SELECTED) == 0) {
- dep = 3;
+ /* bone found */
+ if (findunsel) {
+ if ((ebone->flag & BONE_SELECTED) == 0) {
+ bias = 2;
+ }
+ else {
+ bias = 1;
+ }
}
else {
- dep = 4;
+ bias = 2;
}
}
- else {
- dep = 3;
+
+ if (bias > bias_max) {
+ bias_max = bias;
+
+ result_bias.hitresult = hitresult;
+ result_bias.base = base;
+ result_bias.ebone = ebone;
}
}
- if (ebone == ebone_next_act) {
- dep -= 1;
- }
+ /* Cycle selected items (objects & bones). */
+ if (use_cycle) {
+ bool found = false;
+ cycle_order.test.index.ob = hitresult & 0xFFFF;
+ cycle_order.test.index.bone = (hitresult & ~BONESEL_ANY) >> 16;
+ if (ebone == ebone_active_orig) {
+ BLI_assert(cycle_order.test.index.ob == cycle_order.active.index.ob);
+ BLI_assert(cycle_order.test.index.bone == cycle_order.active.index.bone);
+ }
+ cycle_order.test.cmp -= cycle_order.active.cmp;
- if (dep < dep_min) {
- dep_min = dep;
- best.hitresult = hitresult;
- best.base = base;
- best.ebone = ebone;
+ if (cycle_order.test.cmp < cycle_order.best.cmp && ebone != ebone_active_orig) {
+ cycle_order.best.cmp = cycle_order.test.cmp;
+ found = true;
+ }
+ else if (ELEM(result_cycle.ebone, NULL, ebone_active_orig)) {
+ /* Let the active bone become selected, but don't set the cycle order. */
+ found = true;
+ }
+
+ if (found) {
+ result_cycle.hitresult = hitresult;
+ result_cycle.base = base;
+ result_cycle.ebone = ebone;
+ }
}
}
}
}
- if (!(best.hitresult & BONESEL_NOSEL)) {
- *r_base = best.base;
+ result = (use_cycle && result_cycle.ebone) ? &result_cycle : &result_bias;
+
+ if (!(result->hitresult & BONESEL_NOSEL)) {
+ *r_base = result->base;
*r_selmask = 0;
- if (best.hitresult & BONESEL_ROOT) {
+ if (result->hitresult & BONESEL_ROOT) {
*r_selmask |= BONE_ROOTSEL;
}
- if (best.hitresult & BONESEL_TIP) {
+ if (result->hitresult & BONESEL_TIP) {
*r_selmask |= BONE_TIPSEL;
}
- if (best.hitresult & BONESEL_BONE) {
+ if (result->hitresult & BONESEL_BONE) {
*r_selmask |= BONE_SELECTED;
}
MEM_freeN(bases);
- return best.ebone;
+ return result->ebone;
}
}
*r_selmask = 0;
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index 28e632d2b82..e89903adf5f 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -1822,7 +1822,7 @@ void GPENCIL_OT_image_to_grease_pencil(wmOperatorType *ot)
0.0001f,
10.0f,
"Point Size",
- "Size used for graese pencil points",
+ "Size used for grease pencil points",
0.001f,
1.0f);
RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 40068b0fb85..9de15c411cc 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -3127,6 +3127,34 @@ static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op)
return op->customdata;
}
+/* Apply pressure change depending of the angle of the stroke for a segment. */
+static void gp_brush_angle_segment(tGPsdata *p, tGPspoint *pt_prev, tGPspoint *pt)
+{
+ Brush *brush = p->brush;
+ /* Sensitivity. */
+ const float sen = brush->gpencil_settings->draw_angle_factor;
+ /* Default angle of brush in radians */
+ const float angle = brush->gpencil_settings->draw_angle;
+
+ float mvec[2];
+ float fac;
+ float mpressure;
+
+ /* angle vector of the brush with full thickness */
+ float v0[2] = {cos(angle), sin(angle)};
+
+ mvec[0] = pt->x - pt_prev->x;
+ mvec[1] = pt->y - pt_prev->y;
+ normalize_v2(mvec);
+
+ fac = 1.0f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */
+ /* interpolate with previous point for smoother transitions */
+ mpressure = interpf(pt->pressure - (sen * fac), pt_prev->pressure, 0.3f);
+ pt->pressure = mpressure;
+
+ CLAMP(pt->pressure, pt_prev->pressure * 0.5f, 1.0f);
+}
+
/* Add arc points between two mouse events using the previous segment to determine the vertice of
* the arc.
* /+ CTL
@@ -3209,6 +3237,11 @@ static void gpencil_add_arc_points(tGPsdata *p, float mval[2], int segments)
pt->pressure = pt_prev->pressure;
pt->strength = pt_prev->strength;
+ /* Apply angle of stroke to brush size. */
+ if (brush_settings->draw_angle_factor != 0.0f) {
+ gp_brush_angle_segment(p, pt_prev, pt);
+ }
+
/* Apply randomness to pressure. */
if (brush_settings->draw_random_press > 0.0f) {
float rand = BLI_rng_get_float(p->rng) * 2.0f - 1.0f;
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index 7f69bcb25cd..20e54df1ccb 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -139,6 +139,12 @@ bool BMBVH_EdgeVisible(struct BMBVHTree *tree,
struct View3D *v3d,
struct Object *obedit);
+void EDBM_project_snap_verts(struct bContext *C,
+ struct Depsgraph *depsgraph,
+ struct ARegion *region,
+ struct Object *obedit,
+ struct BMEditMesh *em);
+
/* editmesh_automerge.c */
void EDBM_automerge(struct Object *ob, bool update, const char hflag, const float dist);
void EDBM_automerge_and_split(struct Object *ob,
@@ -293,13 +299,6 @@ void ED_operatortypes_mesh(void);
void ED_operatormacros_mesh(void);
void ED_keymap_mesh(struct wmKeyConfig *keyconf);
-/* editmesh_tools.c (could be moved) */
-void EDBM_project_snap_verts(struct bContext *C,
- struct Depsgraph *depsgraph,
- struct ARegion *region,
- struct Object *obedit,
- struct BMEditMesh *em);
-
/* editface.c */
void paintface_flush_flags(struct bContext *C, struct Object *ob, short flag);
bool paintface_mouse_select(struct bContext *C,
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 339124db355..18666daa8b8 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -790,6 +790,9 @@ static bool ui_but_update_from_old_block(const bContext *C,
SWAP(ListBase, but->extra_op_icons, oldbut->extra_op_icons);
+ SWAP(uiButSearchArgFreeFunc, oldbut->search_arg_free_func, but->search_arg_free_func);
+ SWAP(void *, oldbut->search_arg, but->search_arg);
+
/* copy hardmin for list rows to prevent 'sticking' highlight to mouse position
* when scrolling without moving mouse (see [#28432]) */
if (ELEM(oldbut->type, UI_BTYPE_ROW, UI_BTYPE_LISTROW)) {
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 942f19eb4e9..5573d9b2edb 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -1014,7 +1014,11 @@ typedef struct uiRNACollectionSearch {
PointerRNA search_ptr;
PropertyRNA *search_prop;
- bool *but_changed; /* pointer to uiBut.changed */
+ uiBut *search_but;
+ /* Let UI_butstore_ API update search_but pointer above over redraws. */
+ uiButStore *butstore;
+ /* Block has to be stored for freeing butstore (uiBut.block doesn't work with undo). */
+ uiBlock *butstore_block;
} uiRNACollectionSearch;
void ui_rna_collection_search_cb(const struct bContext *C,
void *arg,
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 44def6a2c09..92779c83d9a 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -2577,6 +2577,13 @@ static void search_id_collection(StructRNA *ptype, PointerRNA *r_ptr, PropertyRN
RNA_STRUCT_END;
}
+static void ui_rna_collection_search_free_cb(void *ptr)
+{
+ uiRNACollectionSearch *coll_search = ptr;
+ UI_butstore_free(coll_search->butstore_block, coll_search->butstore);
+ MEM_freeN(ptr);
+}
+
void ui_but_add_search(
uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *searchptr, PropertyRNA *searchprop)
{
@@ -2609,7 +2616,10 @@ void ui_but_add_search(
coll_search->target_prop = prop;
coll_search->search_ptr = *searchptr;
coll_search->search_prop = searchprop;
- coll_search->but_changed = &but->changed;
+ coll_search->search_but = but;
+ coll_search->butstore_block = but->block;
+ coll_search->butstore = UI_butstore_create(coll_search->butstore_block);
+ UI_butstore_register(coll_search->butstore, &coll_search->search_but);
if (RNA_property_type(prop) == PROP_ENUM) {
/* XXX, this will have a menu string,
@@ -2621,7 +2631,7 @@ void ui_but_add_search(
ui_searchbox_create_generic,
ui_rna_collection_search_cb,
coll_search,
- MEM_freeN,
+ ui_rna_collection_search_free_cb,
NULL,
NULL);
}
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c
index b3854cfc4ae..d1c61925d40 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.c
@@ -56,7 +56,9 @@
#include "UI_resources.h"
#include "UI_view2d.h"
+#include "GPU_batch_presets.h"
#include "GPU_immediate.h"
+#include "GPU_matrix.h"
#include "GPU_state.h"
#include "interface_intern.h"
@@ -544,59 +546,6 @@ static void ui_draw_panel_scalewidget(uint pos, const rcti *rect)
GPU_blend(false);
}
-static void immRectf_tris_color_ex(
- uint pos, float x1, float y1, float x2, float y2, uint col, const float color[3])
-{
- immAttr4fv(col, color);
- immVertex2f(pos, x1, y1);
- immAttr4fv(col, color);
- immVertex2f(pos, x2, y1);
- immAttr4fv(col, color);
- immVertex2f(pos, x2, y2);
-
- immAttr4fv(col, color);
- immVertex2f(pos, x1, y1);
- immAttr4fv(col, color);
- immVertex2f(pos, x2, y2);
- immAttr4fv(col, color);
- immVertex2f(pos, x1, y2);
-}
-
-static void ui_draw_panel_dragwidget(uint pos, uint col, const rctf *rect)
-{
- float col_high[4], col_dark[4];
- const int col_tint = 84;
-
- const int px = (int)U.pixelsize;
- const int px_zoom = max_ii(round_fl_to_int(BLI_rctf_size_y(rect) / 22.0f), 1);
-
- const int box_margin = max_ii(round_fl_to_int((float)(px_zoom * 2.0f)), px);
- const int box_size = max_ii(round_fl_to_int((BLI_rctf_size_y(rect) / 8.0f) - px), px);
-
- const int x_min = rect->xmin;
- const int y_min = rect->ymin;
- const int y_ofs = max_ii(round_fl_to_int(BLI_rctf_size_y(rect) / 2.5f), px);
- const int x_ofs = y_ofs;
- int i_x, i_y;
-
- UI_GetThemeColorShade4fv(TH_PANEL_HEADER, col_tint, col_high);
- UI_GetThemeColorShade4fv(TH_PANEL_BACK, -col_tint, col_dark);
-
- /* draw multiple boxes */
- immBegin(GPU_PRIM_TRIS, 4 * 2 * (6 * 2));
- for (i_x = 0; i_x < 4; i_x++) {
- for (i_y = 0; i_y < 2; i_y++) {
- const int x_co = (x_min + x_ofs) + (i_x * (box_size + box_margin));
- const int y_co = (y_min + y_ofs) + (i_y * (box_size + box_margin));
-
- immRectf_tris_color_ex(
- pos, x_co - box_size, y_co - px_zoom, x_co, (y_co + box_size) - px_zoom, col, col_dark);
- immRectf_tris_color_ex(pos, x_co - box_size, y_co, x_co, y_co + box_size, col, col_high);
- }
- }
- immEnd();
-}
-
/* For button layout next to label. */
void UI_panel_label_offset(uiBlock *block, int *r_x, int *r_y)
{
@@ -755,24 +704,27 @@ void ui_draw_aligned_panel(uiStyle *style,
ui_draw_aligned_panel_header(style, block, &titlerect, 'h', show_background);
if (show_drag) {
- uint col;
- GPUVertFormat *format = immVertexFormat();
- pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
-
/* itemrect smaller */
+ const float scale = 0.7;
itemrect.xmax = headrect.xmax - (0.2f * UI_UNIT_X);
itemrect.xmin = itemrect.xmax - BLI_rcti_size_y(&headrect);
itemrect.ymin = headrect.ymin;
itemrect.ymax = headrect.ymax;
+ BLI_rctf_scale(&itemrect, scale);
- BLI_rctf_scale(&itemrect, 0.7f);
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
- ui_draw_panel_dragwidget(pos, col, &itemrect);
- immUnbindProgram();
+ GPU_matrix_push();
+ GPU_matrix_translate_2f(itemrect.xmin, itemrect.ymin);
+
+ const int col_tint = 84;
+ float col_high[4], col_dark[4];
+ UI_GetThemeColorShade4fv(TH_PANEL_HEADER, col_tint, col_high);
+ UI_GetThemeColorShade4fv(TH_PANEL_BACK, -col_tint, col_dark);
- /* Restore format for the following draws. */
- pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ GPUBatch *batch = GPU_batch_preset_panel_drag_widget(
+ U.pixelsize, col_high, col_dark, BLI_rcti_size_y(&headrect) * scale);
+ GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_FLAT_COLOR);
+ GPU_batch_draw(batch);
+ GPU_matrix_pop();
}
}
@@ -1622,10 +1574,13 @@ static void ui_handle_panel_header(
}
else { /* collapse */
if (ctrl) {
- panels_collapse_all(area, region, block->panel);
+ /* Only collapse all for parent panels. */
+ if (block->panel->type != NULL && block->panel->type->parent == NULL) {
+ panels_collapse_all(area, region, block->panel);
- /* reset the view - we don't want to display a view without content */
- UI_view2d_offset(&region->v2d, 0.0f, 1.0f);
+ /* reset the view - we don't want to display a view without content */
+ UI_view2d_offset(&region->v2d, 0.0f, 1.0f);
+ }
}
if (block->panel->flag & PNL_CLOSED) {
diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c
index a69837e9b51..2d687781b61 100644
--- a/source/blender/editors/interface/interface_utils.c
+++ b/source/blender/editors/interface/interface_utils.c
@@ -399,7 +399,7 @@ void ui_rna_collection_search_cb(const struct bContext *C,
int i = 0, iconid = 0, flag = RNA_property_flag(data->target_prop);
ListBase *items_list = MEM_callocN(sizeof(ListBase), "items_list");
CollItemSearch *cis;
- const bool skip_filter = (data->but_changed && !(*data->but_changed));
+ const bool skip_filter = data->search_but && !data->search_but->changed;
/* build a temporary list of relevant items first */
RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) {
@@ -647,6 +647,7 @@ void UI_butstore_free(uiBlock *block, uiButStore *bs_handle)
}
BLI_freelistN(&bs_handle->items);
+ BLI_assert(BLI_findindex(&block->butstore, bs_handle) != -1);
BLI_remlink(&block->butstore, bs_handle);
MEM_freeN(bs_handle);
@@ -747,8 +748,7 @@ void UI_butstore_update(uiBlock *block)
/* move this list to the new block */
if (block->oldblock) {
if (block->oldblock->butstore.first) {
- block->butstore = block->oldblock->butstore;
- BLI_listbase_clear(&block->oldblock->butstore);
+ BLI_movelisttolist(&block->butstore, &block->oldblock->butstore);
}
}
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index f01511fa513..3847f32b19a 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -3201,8 +3201,12 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op)
BMEdge *e;
BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
if (!BMO_edge_flag_test(bm, e, BMO_ELE_TAG)) {
- BM_elem_flag_disable(e->v1, BM_ELEM_TAG);
- BM_elem_flag_disable(e->v2, BM_ELEM_TAG);
+ /* Check the edge for selected faces,
+ * this supports stepping off isolated vertices which would otherwise be ignored. */
+ if (BM_edge_is_any_face_flag_test(e, BM_ELEM_SELECT)) {
+ BM_elem_flag_disable(e->v1, BM_ELEM_TAG);
+ BM_elem_flag_disable(e->v2, BM_ELEM_TAG);
+ }
}
}
}
@@ -3258,10 +3262,13 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op)
if (delimit) {
BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
- BM_elem_flag_set(
- e,
- BM_ELEM_TAG,
- (BM_elem_flag_test(e, BM_ELEM_SELECT) && BMO_edge_flag_test(bm, e, BMO_ELE_TAG)));
+ /* Check the edge for selected faces,
+ * this supports stepping off isolated edges which would otherwise be ignored. */
+ BM_elem_flag_set(e,
+ BM_ELEM_TAG,
+ (BM_elem_flag_test(e, BM_ELEM_SELECT) &&
+ (BMO_edge_flag_test(bm, e, BMO_ELE_TAG) ||
+ !BM_edge_is_any_face_flag_test(e, BM_ELEM_SELECT))));
}
}
else {
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index b9ac24e1d03..ba3520f2217 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -71,7 +71,6 @@
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_transform.h"
-#include "ED_transform_snap_object_context.h"
#include "ED_uvedit.h"
#include "ED_view3d.h"
@@ -410,45 +409,6 @@ void MESH_OT_unsubdivide(wmOperatorType *ot)
ot->srna, "iterations", 2, 1, 1000, "Iterations", "Number of times to unsubdivide", 1, 100);
}
-void EDBM_project_snap_verts(
- bContext *C, Depsgraph *depsgraph, ARegion *region, Object *obedit, BMEditMesh *em)
-{
- Main *bmain = CTX_data_main(C);
- BMIter iter;
- BMVert *eve;
-
- ED_view3d_init_mats_rv3d(obedit, region->regiondata);
-
- struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d(
- bmain, CTX_data_scene(C), 0, region, CTX_wm_view3d(C));
-
- BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
- if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
- float mval[2], co_proj[3];
- if (ED_view3d_project_float_object(region, eve->co, mval, V3D_PROJ_TEST_NOP) ==
- V3D_PROJ_RET_OK) {
- if (ED_transform_snap_object_project_view3d(snap_context,
- depsgraph,
- SCE_SNAP_MODE_FACE,
- &(const struct SnapObjectParams){
- .snap_select = SNAP_NOT_ACTIVE,
- .use_object_edit_cage = false,
- .use_occlusion_test = true,
- },
- mval,
- NULL,
- NULL,
- co_proj,
- NULL)) {
- mul_v3_m4v3(eve->co, obedit->imat, co_proj);
- }
- }
- }
- }
-
- ED_transform_snap_object_context_destroy(snap_context);
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -1186,6 +1146,12 @@ void MESH_OT_mark_sharp(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Connect Vertex Path Operator
+ * \{ */
+
static bool edbm_connect_vert_pair(BMEditMesh *em, struct Mesh *me, wmOperator *op)
{
BMesh *bm = em->bm;
@@ -1311,7 +1277,7 @@ void MESH_OT_vert_connect(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Split Concave Faces Operator
+/** \name Connect Vertex Path Operator
* \{ */
/**
@@ -1600,6 +1566,12 @@ void MESH_OT_vert_connect_path(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Connect Concave Operator
+ * \{ */
+
static int edbm_vert_connect_concave_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -3210,7 +3182,7 @@ void MESH_OT_merge(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Remove Doubles Operator
+/** \name Merge By Distance Operator
* \{ */
static int edbm_remove_doubles_exec(bContext *C, wmOperator *op)
@@ -7812,7 +7784,11 @@ wmKeyMap *point_normals_modal_keymap(wmKeyConfig *keyconf)
#define CLNORS_VALID_VEC_LEN (1e-4f)
-/********************** 'Point to' Loop Normals **********************/
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Loop Normals 'Point To' Operator
+ * \{ */
enum {
EDBM_CLNOR_POINTTO_MODE_COORDINATES = 1,
@@ -7830,7 +7806,7 @@ static EnumPropertyItem clnors_pointto_mode_items[] = {
};
/* Initialize loop normal data */
-static int point_normals_init(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static bool point_normals_init(bContext *C, wmOperator *op)
{
Object *obedit = CTX_data_edit_object(C);
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -7842,14 +7818,29 @@ static int point_normals_init(bContext *C, wmOperator *op, const wmEvent *UNUSED
op->customdata = lnors_ed_arr;
- return lnors_ed_arr->totloop;
+ return (lnors_ed_arr->totloop != 0);
}
-static void point_normals_free(bContext *C, wmOperator *op)
+static bool point_normals_ensure(bContext *C, wmOperator *op)
{
- BMLoopNorEditDataArray *lnors_ed_arr = op->customdata;
- BM_loop_normal_editdata_array_free(lnors_ed_arr);
- op->customdata = NULL;
+ if (op->customdata != NULL) {
+ return true;
+ }
+ return point_normals_init(C, op);
+}
+
+static void point_normals_free(wmOperator *op)
+{
+ if (op->customdata != NULL) {
+ BMLoopNorEditDataArray *lnors_ed_arr = op->customdata;
+ BM_loop_normal_editdata_array_free(lnors_ed_arr);
+ op->customdata = NULL;
+ }
+}
+
+static void point_normals_cancel(bContext *C, wmOperator *op)
+{
+ point_normals_free(op);
ED_area_status_text(CTX_wm_area(C), NULL);
}
@@ -7966,6 +7957,13 @@ static void point_normals_apply(bContext *C, wmOperator *op, float target[3], co
static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
+ /* As this operator passes events through, we can't be sure the user didn't exit edit-mode.
+ * or performed some other operation. */
+ if (!WM_operator_poll(C, op->type)) {
+ point_normals_cancel(C, op);
+ return OPERATOR_CANCELLED;
+ }
+
View3D *v3d = CTX_wm_view3d(C);
Scene *scene = CTX_data_scene(C);
Object *obedit = CTX_data_edit_object(C);
@@ -8136,23 +8134,37 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *
if (!ELEM(ret, OPERATOR_CANCELLED, OPERATOR_FINISHED)) {
RNA_property_float_set_array(op->ptr, prop_target, target);
}
- point_normals_apply(C, op, target, do_reset);
- EDBM_update_generic(obedit->data, true, false); /* Recheck bools. */
- point_normals_update_header(C, op);
+ if (point_normals_ensure(C, op)) {
+ point_normals_apply(C, op, target, do_reset);
+ EDBM_update_generic(obedit->data, true, false); /* Recheck bools. */
+ point_normals_update_header(C, op);
+ }
+ else {
+ ret = OPERATOR_CANCELLED;
+ }
}
if (ELEM(ret, OPERATOR_CANCELLED, OPERATOR_FINISHED)) {
- point_normals_free(C, op);
+ point_normals_cancel(C, op);
}
+ /* If we allow other tools to run, we can't be sure if they will re-allocate
+ * the data this operator uses, see: T68159.
+ * Free the data here, then use #point_normals_ensure to add it back on demand. */
+ if (ret == OPERATOR_PASS_THROUGH) {
+ /* Don't free on mouse-move, causes creation/freeing of the loop data in an inefficient way. */
+ if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ point_normals_free(op);
+ }
+ }
return ret;
}
-static int edbm_point_normals_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static int edbm_point_normals_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (!point_normals_init(C, op, event)) {
- point_normals_free(C, op);
+ if (!point_normals_init(C, op)) {
+ point_normals_cancel(C, op);
return OPERATOR_CANCELLED;
}
@@ -8169,8 +8181,8 @@ static int edbm_point_normals_exec(bContext *C, wmOperator *op)
{
Object *obedit = CTX_data_edit_object(C);
- if (!point_normals_init(C, op, NULL)) {
- point_normals_free(C, op);
+ if (!point_normals_init(C, op)) {
+ point_normals_cancel(C, op);
return OPERATOR_CANCELLED;
}
@@ -8183,7 +8195,7 @@ static int edbm_point_normals_exec(bContext *C, wmOperator *op)
point_normals_apply(C, op, target, false);
EDBM_update_generic(obedit->data, true, false);
- point_normals_free(C, op);
+ point_normals_cancel(C, op);
return OPERATOR_FINISHED;
}
@@ -8228,7 +8240,7 @@ void MESH_OT_point_normals(struct wmOperatorType *ot)
ot->modal = edbm_point_normals_modal;
ot->poll = ED_operator_editmesh;
ot->ui = edbm_point_normals_ui;
- ot->cancel = point_normals_free;
+ ot->cancel = point_normals_cancel;
/* flags */
ot->flag = OPTYPE_BLOCKING | OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -8273,7 +8285,7 @@ void MESH_OT_point_normals(struct wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Split/Merge Loop Normals
+/** \name Split/Merge Loop Normals Operator
* \{ */
static void normals_merge(BMesh *bm, BMLoopNorEditDataArray *lnors_ed_arr)
@@ -8484,7 +8496,7 @@ void MESH_OT_split_normals(struct wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Average Loop Normals
+/** \name Average Loop Normals Operator
* \{ */
enum {
@@ -8716,7 +8728,7 @@ void MESH_OT_average_normals(struct wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Custom Normal Interface Tools
+/** \name Custom Normal Interface Tools Operator
* \{ */
enum {
@@ -8952,6 +8964,12 @@ void MESH_OT_normals_tools(struct wmOperatorType *ot)
"Copy Absolute coordinates or Normal vector");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Set Normals from Faces Operator
+ * \{ */
+
static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -9059,7 +9077,13 @@ void MESH_OT_set_normals_from_faces(struct wmOperatorType *ot)
RNA_def_boolean(ot->srna, "keep_sharp", 0, "Keep Sharp Edges", "Do not set sharp edges to face");
}
-static int edbm_smoothen_normals_exec(bContext *C, wmOperator *op)
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Smooth Normal Vectors Operator
+ * \{ */
+
+static int edbm_smooth_normals_exec(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
@@ -9112,7 +9136,7 @@ static int edbm_smoothen_normals_exec(bContext *C, wmOperator *op)
float current_normal[3];
if (normalize_v3(smooth_normal[i]) < CLNORS_VALID_VEC_LEN) {
- /* Skip in case smoothen normal is invalid... */
+ /* Skip in case the smooth normal is invalid. */
continue;
}
@@ -9126,7 +9150,7 @@ static int edbm_smoothen_normals_exec(bContext *C, wmOperator *op)
add_v3_v3(current_normal, smooth_normal[i]);
if (normalize_v3(current_normal) < CLNORS_VALID_VEC_LEN) {
- /* Skip in case smoothen normal is invalid... */
+ /* Skip in case the smoothed normal is invalid. */
continue;
}
@@ -9144,15 +9168,15 @@ static int edbm_smoothen_normals_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-void MESH_OT_smoothen_normals(struct wmOperatorType *ot)
+void MESH_OT_smooth_normals(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "Smooth Normals Vectors";
- ot->description = "Smoothen custom normals based on adjacent vertex normals";
- ot->idname = "MESH_OT_smoothen_normals";
+ ot->description = "Smooth custom normals based on adjacent vertex normals";
+ ot->idname = "MESH_OT_smooth_normals";
/* api callbacks */
- ot->exec = edbm_smoothen_normals_exec;
+ ot->exec = edbm_smooth_normals_exec;
ot->poll = ED_operator_editmesh;
/* flags */
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 998e0d736e6..a26003d78ed 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -52,6 +52,7 @@
#include "ED_mesh.h"
#include "ED_screen.h"
+#include "ED_transform_snap_object_context.h"
#include "ED_uvedit.h"
#include "ED_view3d.h"
@@ -1638,3 +1639,48 @@ bool BMBVH_EdgeVisible(struct BMBVHTree *tree,
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name BMesh Vertex Projection API
+ * \{ */
+
+void EDBM_project_snap_verts(
+ bContext *C, Depsgraph *depsgraph, ARegion *region, Object *obedit, BMEditMesh *em)
+{
+ Main *bmain = CTX_data_main(C);
+ BMIter iter;
+ BMVert *eve;
+
+ ED_view3d_init_mats_rv3d(obedit, region->regiondata);
+
+ struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d(
+ bmain, CTX_data_scene(C), 0, region, CTX_wm_view3d(C));
+
+ BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ float mval[2], co_proj[3];
+ if (ED_view3d_project_float_object(region, eve->co, mval, V3D_PROJ_TEST_NOP) ==
+ V3D_PROJ_RET_OK) {
+ if (ED_transform_snap_object_project_view3d(snap_context,
+ depsgraph,
+ SCE_SNAP_MODE_FACE,
+ &(const struct SnapObjectParams){
+ .snap_select = SNAP_NOT_ACTIVE,
+ .use_object_edit_cage = false,
+ .use_occlusion_test = true,
+ },
+ mval,
+ NULL,
+ NULL,
+ co_proj,
+ NULL)) {
+ mul_v3_m4v3(eve->co, obedit->imat, co_proj);
+ }
+ }
+ }
+ }
+
+ ED_transform_snap_object_context_destroy(snap_context);
+}
+
+/** \} */
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index 594429d4925..5e70069456b 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -248,7 +248,7 @@ void MESH_OT_split_normals(struct wmOperatorType *ot);
void MESH_OT_normals_tools(struct wmOperatorType *ot);
void MESH_OT_set_normals_from_faces(struct wmOperatorType *ot);
void MESH_OT_average_normals(struct wmOperatorType *ot);
-void MESH_OT_smoothen_normals(struct wmOperatorType *ot);
+void MESH_OT_smooth_normals(struct wmOperatorType *ot);
void MESH_OT_mod_weighted_strength(struct wmOperatorType *ot);
/* *** editmesh_mask_extract.c *** */
diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c
index c61fe39b5fd..66b2c66f2aa 100644
--- a/source/blender/editors/mesh/mesh_ops.c
+++ b/source/blender/editors/mesh/mesh_ops.c
@@ -203,7 +203,7 @@ void ED_operatortypes_mesh(void)
WM_operatortype_append(MESH_OT_normals_tools);
WM_operatortype_append(MESH_OT_set_normals_from_faces);
WM_operatortype_append(MESH_OT_average_normals);
- WM_operatortype_append(MESH_OT_smoothen_normals);
+ WM_operatortype_append(MESH_OT_smooth_normals);
WM_operatortype_append(MESH_OT_mod_weighted_strength);
}
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index d625b770164..debad321583 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -446,7 +446,8 @@ static bool bake_object_check(ViewLayer *view_layer, Object *ob, ReportList *rep
for (i = 0; i < ob->totcol; i++) {
bNodeTree *ntree = NULL;
bNode *node = NULL;
- ED_object_get_active_image(ob, i + 1, &image, NULL, &node, &ntree);
+ const int mat_nr = i + 1;
+ ED_object_get_active_image(ob, mat_nr, &image, NULL, &node, &ntree);
if (image) {
ImBuf *ibuf;
@@ -481,7 +482,7 @@ static bool bake_object_check(ViewLayer *view_layer, Object *ob, ReportList *rep
}
}
else {
- Material *mat = BKE_object_material_get(ob, i);
+ Material *mat = BKE_object_material_get(ob, mat_nr);
if (mat != NULL) {
BKE_reportf(reports,
RPT_INFO,
diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
index 5f75c1d6813..0ab3b8cd14e 100644
--- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
@@ -42,8 +42,8 @@
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_paint.h"
-#include "BKE_pbvh.h"
#include "BKE_particle.h"
+#include "BKE_pbvh.h"
#include "BKE_pointcache.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
@@ -141,10 +141,7 @@ void SCULPT_dyntopo_node_layers_add(SculptSession *ss)
ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY;
}
-void SCULPT_dynamic_topology_enable_ex(Main *bmain,
- Depsgraph *depsgraph,
- Scene *scene,
- Object *ob)
+void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
{
SculptSession *ss = ob->sculpt;
Mesh *me = ob->data;
@@ -281,9 +278,9 @@ void SCULPT_dynamic_topology_disable(bContext *C, SculptUndoNode *unode)
}
void sculpt_dynamic_topology_disable_with_undo(Main *bmain,
- Depsgraph *depsgraph,
- Scene *scene,
- Object *ob)
+ Depsgraph *depsgraph,
+ Scene *scene,
+ Object *ob)
{
SculptSession *ss = ob->sculpt;
if (ss->bm) {
@@ -331,7 +328,6 @@ static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator *UNUSED(o
return OPERATOR_FINISHED;
}
-
static int dyntopo_warning_popup(bContext *C, wmOperatorType *ot, enum eDynTopoWarnFlag flag)
{
uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Warning!"), ICON_ERROR);
diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c
index e2affa7bc36..9af623d8065 100644
--- a/source/blender/editors/space_buttons/buttons_ops.c
+++ b/source/blender/editors/space_buttons/buttons_ops.c
@@ -110,7 +110,7 @@ static int file_browse_exec(bContext *C, wmOperator *op)
if (BLI_is_dir(path)) {
/* do this first so '//' isnt converted to '//\' on windows */
- BLI_add_slash(path);
+ BLI_path_slash_ensure(path);
if (is_relative) {
BLI_strncpy(path, str, FILE_MAX);
BLI_path_rel(path, BKE_main_blendfile_path(bmain));
@@ -122,7 +122,7 @@ static int file_browse_exec(bContext *C, wmOperator *op)
}
}
else {
- char *const lslash = (char *)BLI_last_slash(str);
+ char *const lslash = (char *)BLI_path_slash_rfind(str);
if (lslash) {
lslash[1] = '\0';
}
@@ -187,7 +187,7 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event)
PointerRNA props_ptr;
if (event->alt) {
- char *lslash = (char *)BLI_last_slash(str);
+ char *lslash = (char *)BLI_path_slash_rfind(str);
if (lslash) {
*lslash = '\0';
}
diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c
index dc85b3959e1..3b1cc6fcab0 100644
--- a/source/blender/editors/space_clip/clip_buttons.c
+++ b/source/blender/editors/space_clip/clip_buttons.c
@@ -872,7 +872,7 @@ void uiTemplateMovieclipInformation(uiLayout *layout,
if (framenr <= clip->len) {
BKE_movieclip_filename_for_frame(clip, user, filepath);
- file = BLI_last_slash(filepath);
+ file = BLI_path_slash_rfind(filepath);
}
else {
file = "-";
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
index 48bc885405d..266212fdfd7 100644
--- a/source/blender/editors/space_clip/clip_ops.c
+++ b/source/blender/editors/space_clip/clip_ops.c
@@ -286,7 +286,7 @@ static int open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)
BLI_strncpy(path, clip->name, sizeof(path));
BLI_path_abs(path, CTX_data_main(C)->name);
- BLI_parent_dir(path);
+ BLI_path_parent_dir(path);
}
else {
BLI_strncpy(path, U.textudir, sizeof(path));
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 7cce243696d..a5263378850 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -211,7 +211,7 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
else {
if (is_parent_dir) {
/* avoids /../../ */
- BLI_parent_dir(params->dir);
+ BLI_path_parent_dir(params->dir);
if (params->recursion_level > 1) {
/* Disable 'dirtree' recursion when going up in tree. */
@@ -220,9 +220,9 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
}
}
else {
- BLI_cleanup_dir(BKE_main_blendfile_path(bmain), params->dir);
+ BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir);
strcat(params->dir, file->relpath);
- BLI_add_slash(params->dir);
+ BLI_path_slash_ensure(params->dir);
}
ED_file_change_dir(C);
@@ -940,7 +940,7 @@ static int bookmark_select_exec(bContext *C, wmOperator *op)
RNA_property_string_get(op->ptr, prop, entry);
BLI_strncpy(params->dir, entry, sizeof(params->dir));
- BLI_cleanup_dir(BKE_main_blendfile_path(bmain), params->dir);
+ BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir);
ED_file_change_dir(C);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
@@ -1627,12 +1627,12 @@ static int file_exec(bContext *C, wmOperator *exec_op)
}
if (FILENAME_IS_PARENT(file->relpath)) {
- BLI_parent_dir(sfile->params->dir);
+ BLI_path_parent_dir(sfile->params->dir);
}
else {
- BLI_cleanup_path(BKE_main_blendfile_path(bmain), sfile->params->dir);
+ BLI_path_normalize(BKE_main_blendfile_path(bmain), sfile->params->dir);
BLI_path_append(sfile->params->dir, sizeof(sfile->params->dir) - 1, file->relpath);
- BLI_add_slash(sfile->params->dir);
+ BLI_path_slash_ensure(sfile->params->dir);
}
if (file->redirection_path) {
STRNCPY(sfile->params->dir, file->redirection_path);
@@ -1754,8 +1754,8 @@ static int file_parent_exec(bContext *C, wmOperator *UNUSED(unused))
SpaceFile *sfile = CTX_wm_space_file(C);
if (sfile->params) {
- if (BLI_parent_dir(sfile->params->dir)) {
- BLI_cleanup_dir(BKE_main_blendfile_path(bmain), sfile->params->dir);
+ if (BLI_path_parent_dir(sfile->params->dir)) {
+ BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), sfile->params->dir);
ED_file_change_dir(C);
if (sfile->params->recursion_level > 1) {
/* Disable 'dirtree' recursion when going up in tree. */
@@ -2274,7 +2274,7 @@ static void file_expand_directory(bContext *C)
sfile->params->dir[3] = '\0';
}
else if (BLI_path_is_unc(sfile->params->dir)) {
- BLI_cleanup_unc(sfile->params->dir, FILE_MAX_LIBEXTRA);
+ BLI_path_normalize_unc(sfile->params->dir, FILE_MAX_LIBEXTRA);
}
#endif
}
@@ -2291,7 +2291,7 @@ static bool can_create_dir(const char *dir)
if (BLI_path_is_unc(dir)) {
char parent[PATH_MAX];
BLI_strncpy(parent, dir, PATH_MAX);
- BLI_parent_dir(parent);
+ BLI_path_parent_dir(parent);
return BLI_is_dir(parent);
}
return true;
@@ -2338,7 +2338,7 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN
}
}
- BLI_cleanup_dir(BKE_main_blendfile_path(bmain), sfile->params->dir);
+ BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), sfile->params->dir);
if (filelist_is_dir(sfile->files, sfile->params->dir)) {
if (!STREQ(sfile->params->dir, old_dir)) { /* Avoids flickering when nothing's changed. */
@@ -2423,7 +2423,7 @@ void file_filename_enter_handle(bContext *C, void *UNUSED(arg_unused), void *arg
/* if directory, open it and empty filename field */
if (filelist_is_dir(sfile->files, filepath)) {
- BLI_cleanup_dir(BKE_main_blendfile_path(bmain), filepath);
+ BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), filepath);
BLI_strncpy(sfile->params->dir, filepath, sizeof(sfile->params->dir));
sfile->params->file[0] = '\0';
ED_file_change_dir(C);
@@ -2487,7 +2487,7 @@ static bool file_filenum_poll(bContext *C)
}
/**
- * Looks for a string of digits within name (using BLI_stringdec) and adjusts it by add.
+ * Looks for a string of digits within name (using BLI_path_sequence_decode) and adjusts it by add.
*/
static void filenum_newname(char *name, size_t name_size, int add)
{
@@ -2496,7 +2496,7 @@ static void filenum_newname(char *name, size_t name_size, int add)
int pic;
ushort digits;
- pic = BLI_stringdec(name, head, tail, &digits);
+ pic = BLI_path_sequence_decode(name, head, tail, &digits);
/* are we going from 100 -> 99 or from 10 -> 9 */
if (add < 0 && digits > 0) {
@@ -2514,7 +2514,7 @@ static void filenum_newname(char *name, size_t name_size, int add)
if (pic < 0) {
pic = 0;
}
- BLI_stringenc(name_temp, head, tail, digits, pic);
+ BLI_path_sequence_encode(name_temp, head, tail, digits, pic);
BLI_strncpy(name, name_temp, name_size);
}
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 126430986a5..778c27e1a17 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -635,7 +635,7 @@ static bool is_hidden_dot_filename(const char *filename, FileListInternEntry *fi
/* filename might actually be a piece of path, in which case we have to check all its parts. */
bool hidden = false;
- char *sep = (char *)BLI_last_slash(filename);
+ char *sep = (char *)BLI_path_slash_rfind(filename);
if (!hidden && sep) {
char tmp_filename[FILE_MAX_LIBEXTRA];
@@ -654,7 +654,7 @@ static bool is_hidden_dot_filename(const char *filename, FileListInternEntry *fi
break;
}
*sep = '\0';
- sep = (char *)BLI_last_slash(tmp_filename);
+ sep = (char *)BLI_path_slash_rfind(tmp_filename);
}
}
return hidden;
@@ -1023,7 +1023,7 @@ static int filelist_geticon_ex(FileDirEntry *file,
FSMenuEntry *tfsm = ED_fsmenu_get_category(fsmenu, categories[i]);
char fullpath[FILE_MAX_LIBEXTRA];
BLI_join_dirfile(fullpath, sizeof(fullpath), root, file->relpath);
- BLI_add_slash(fullpath);
+ BLI_path_slash_ensure(fullpath);
for (; tfsm; tfsm = tfsm->next) {
if (STREQ(tfsm->path, fullpath)) {
/* Never want a little folder inside a large one. */
@@ -1112,7 +1112,7 @@ int filelist_geticon(struct FileList *filelist, const int index, const bool is_m
static void parent_dir_until_exists_or_default_root(char *dir)
{
- if (!BLI_parent_dir_until_exists(dir)) {
+ if (!BLI_path_parent_dir_until_exists(dir)) {
#ifdef WIN32
get_default_root(dir);
#else
@@ -1624,7 +1624,7 @@ void filelist_setdir(struct FileList *filelist, char *r_dir)
{
BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA);
- BLI_cleanup_dir(BKE_main_blendfile_path_from_global(), r_dir);
+ BLI_path_normalize_dir(BKE_main_blendfile_path_from_global(), r_dir);
const bool is_valid_path = filelist->checkdirf(filelist, r_dir, true);
BLI_assert(is_valid_path);
UNUSED_VARS_NDEBUG(is_valid_path);
@@ -2448,7 +2448,7 @@ static int groupname_to_code(const char *group)
BLI_assert(group);
BLI_strncpy(buf, group, sizeof(buf));
- lslash = (char *)BLI_last_slash(buf);
+ lslash = (char *)BLI_path_slash_rfind(buf);
if (lslash) {
lslash[0] = '\0';
}
@@ -2816,7 +2816,7 @@ static void filelist_readjob_do(const bool do_lib,
BLI_strncpy(dir, filelist->filelist.root, sizeof(dir));
BLI_strncpy(filter_glob, filelist->filter_data.filter_glob, sizeof(filter_glob));
- BLI_cleanup_dir(main_name, dir);
+ BLI_path_normalize_dir(main_name, dir);
td_dir->dir = BLI_strdup(dir);
while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) {
@@ -2842,7 +2842,7 @@ static void filelist_readjob_do(const bool do_lib,
* Note that in the end, this means we 'cache' valid relative subdir once here,
* this is actually better. */
BLI_strncpy(rel_subdir, subdir, sizeof(rel_subdir));
- BLI_cleanup_dir(root, rel_subdir);
+ BLI_path_normalize_dir(root, rel_subdir);
BLI_path_rel(rel_subdir, root);
if (do_lib) {
@@ -2884,7 +2884,7 @@ static void filelist_readjob_do(const bool do_lib,
else {
/* We have a directory we want to list, add it to todo list! */
BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath);
- BLI_cleanup_dir(main_name, dir);
+ BLI_path_normalize_dir(main_name, dir);
td_dir = BLI_stack_push_r(todo_dirs);
td_dir->level = recursion_level + 1;
td_dir->dir = BLI_strdup(dir);
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 67c0fae3565..6b594c02c15 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -156,7 +156,7 @@ short ED_fileselect_set_params(SpaceFile *sfile)
}
if (params->dir[0]) {
- BLI_cleanup_dir(blendfile_path, params->dir);
+ BLI_path_normalize_dir(blendfile_path, params->dir);
BLI_path_abs(params->dir, blendfile_path);
}
@@ -924,7 +924,7 @@ int autocomplete_directory(struct bContext *C, char *str, void *UNUSED(arg_v))
match = UI_autocomplete_end(autocpl, str);
if (match == AUTOCOMPLETE_FULL_MATCH) {
- BLI_add_slash(str);
+ BLI_path_slash_ensure(str);
}
}
}
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index 873091bd68d..3658ebae3a2 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -1276,7 +1276,7 @@ void uiTemplateImageInfo(uiLayout *layout, bContext *C, Image *ima, ImageUser *i
}
else if (ima->source == IMA_SRC_SEQUENCE && ibuf) {
/* Image sequence frame number + filename */
- const char *filename = BLI_last_slash(ibuf->name);
+ const char *filename = BLI_path_slash_rfind(ibuf->name);
filename = (filename == NULL) ? ibuf->name : filename + 1;
BLI_snprintf(str, MAX_IMAGE_INFO_LEN, TIP_("Frame %d: %s"), framenr, filename);
}
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 67e64c52051..14245327bdd 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -1794,7 +1794,8 @@ static int image_save_options_init(Main *bmain,
}
/* append UDIM numbering if not present */
- if (ima->source == IMA_SRC_TILED && (BLI_stringdec(ima->name, NULL, NULL, NULL) != 1001)) {
+ if (ima->source == IMA_SRC_TILED &&
+ (BLI_path_sequence_decode(ima->name, NULL, NULL, NULL) != 1001)) {
int len = strlen(opts->filepath);
STR_CONCAT(opts->filepath, len, ".1001");
}
diff --git a/source/blender/editors/space_image/image_sequence.c b/source/blender/editors/space_image/image_sequence.c
index 8b298045597..e65924d0eb9 100644
--- a/source/blender/editors/space_image/image_sequence.c
+++ b/source/blender/editors/space_image/image_sequence.c
@@ -74,7 +74,7 @@ static void image_sequence_get_frame_ranges(wmOperator *op, ListBase *ranges)
ImageFrame *frame = MEM_callocN(sizeof(ImageFrame), "image_frame");
/* use the first file in the list as base filename */
- frame->framenr = BLI_stringdec(filename, head, tail, &digits);
+ frame->framenr = BLI_path_sequence_decode(filename, head, tail, &digits);
/* still in the same sequence */
if (do_frame_range && (range != NULL) && (STREQLEN(base_head, head, FILE_MAX)) &&
@@ -127,7 +127,7 @@ static int image_get_udim(char *filepath, ListBase *udim_tiles)
ushort digits;
char base_head[FILE_MAX], base_tail[FILE_MAX];
- int id = BLI_stringdec(filename, base_head, base_tail, &digits);
+ int id = BLI_path_sequence_decode(filename, base_head, base_tail, &digits);
if (id < 1001 || id >= IMA_UDIM_MAX) {
return 0;
@@ -144,7 +144,7 @@ static int image_get_udim(char *filepath, ListBase *udim_tiles)
continue;
}
char head[FILE_MAX], tail[FILE_MAX];
- id = BLI_stringdec(dir[i].relname, head, tail, &digits);
+ id = BLI_path_sequence_decode(dir[i].relname, head, tail, &digits);
if (digits > 4 || !(STREQLEN(base_head, head, FILE_MAX)) ||
!(STREQLEN(base_tail, tail, FILE_MAX))) {
@@ -166,7 +166,7 @@ static int image_get_udim(char *filepath, ListBase *udim_tiles)
if (is_udim && has_primary) {
char primary_filename[FILE_MAX];
- BLI_stringenc(primary_filename, base_head, base_tail, digits, 1001);
+ BLI_path_sequence_encode(primary_filename, base_head, base_tail, digits, 1001);
BLI_join_dirfile(filepath, FILE_MAX, dirname, primary_filename);
return max_udim - 1000;
}
diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.c
index 0f296fd293a..2617384d046 100644
--- a/source/blender/editors/space_node/node_group.c
+++ b/source/blender/editors/space_node/node_group.c
@@ -198,11 +198,11 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
}
/* wgroup is a temporary copy of the NodeTree we're merging in
- * - all of wgroup's nodes are transferred across to their new home
+ * - all of wgroup's nodes are copied across to their new home
* - ngroup (i.e. the source NodeTree) is left unscathed
- * - temp copy. don't change ID usercount
+ * - temp copy. do change ID usercount for the copies
*/
- wgroup = ntreeCopyTree_ex_new_pointers(ngroup, bmain, false);
+ wgroup = ntreeCopyTree_ex_new_pointers(ngroup, bmain, true);
/* Add the nodes into the ntree */
for (node = wgroup->nodes.first; node; node = nextnode) {
@@ -356,8 +356,8 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
nodeRemoveNode(bmain, ntree, node, false);
}
- /* delete the group instance */
- nodeRemoveNode(bmain, ntree, gnode, false);
+ /* delete the group instance and dereference group tree */
+ nodeRemoveNode(bmain, ntree, gnode, true);
ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS;
diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c
index ec1595eb930..64d86293fb7 100644
--- a/source/blender/editors/space_outliner/outliner_select.c
+++ b/source/blender/editors/space_outliner/outliner_select.c
@@ -312,6 +312,7 @@ static eOLDrawState tree_element_set_active_object(bContext *C,
Scene *sce;
Base *base;
Object *ob = NULL;
+ TreeElement *te_ob = NULL;
/* if id is not object, we search back */
if (te->idcode == ID_OB) {
@@ -355,8 +356,12 @@ static eOLDrawState tree_element_set_active_object(bContext *C,
}
}
- parent_tselem = TREESTORE(outliner_find_id(soops, &soops->tree, (ID *)ob));
- if (base) {
+ te_ob = outliner_find_id(soops, &soops->tree, (ID *)ob);
+ if (te_ob != NULL) {
+ parent_tselem = TREESTORE(te_ob);
+ }
+
+ if (!ELEM(NULL, parent_tselem, base)) {
if (set == OL_SETSEL_EXTEND) {
/* swap select */
if (base->flag & BASE_SELECTED) {
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 93ff5263148..5d8851d5e3d 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -220,6 +220,10 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3])
}
}
+/**
+ * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2.
+ * \param stepsize: The width of a pixel.
+ */
static void draw_seq_waveform(View2D *v2d,
const bContext *C,
SpaceSeq *sseq,
@@ -231,9 +235,6 @@ static void draw_seq_waveform(View2D *v2d,
float y2,
float stepsize)
{
- /* X1, x2 is the starting and end X value to draw the wave, same for y1 and y2.
- * Stepsize is width of a pixel. */
-
/* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */
int x1_offset = max_ff(v2d->cur.xmin, x1);
int x2_offset = min_ff(v2d->cur.xmax + 1.0f, x2);
@@ -353,10 +354,10 @@ static void draw_seq_waveform(View2D *v2d,
}
}
-/* Don't use SEQ_BEGIN/SEQ_END here, because it changes seq->depth,
- * which is needed for tranform. */
static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, float x2, float y2)
{
+ /* Don't use SEQ_BEGIN/SEQ_END here,
+ * because it changes seq->depth, which is needed for transform. */
Sequence *seq;
uchar col[4];
@@ -988,9 +989,11 @@ static void fcurve_batch_add_verts(GPUVertBuf *vbo,
*vert_count += 2;
}
-/* Draw f-curves as darkened regions of the strip.
- * - Volume for sound strips.
- * - Opacity for the other types. */
+/**
+ * Draw f-curves as darkened regions of the strip:
+ * - Volume for sound strips.
+ * - Opacity for the other types.
+ */
static void draw_seq_fcurve(
Scene *scene, View2D *v2d, Sequence *seq, float x1, float y1, float x2, float y2, float pixelx)
{
diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.c b/source/blender/editors/space_sequencer/sequencer_scopes.c
index b08c35623af..b06bc7ad8b7 100644
--- a/source/blender/editors/space_sequencer/sequencer_scopes.c
+++ b/source/blender/editors/space_sequencer/sequencer_scopes.c
@@ -451,8 +451,8 @@ typedef struct MakeHistogramViewData {
} MakeHistogramViewData;
static void make_histogram_view_from_ibuf_byte_fn(void *__restrict userdata,
- const int y,
- const TaskParallelTLS *__restrict tls)
+ const int y,
+ const TaskParallelTLS *__restrict tls)
{
MakeHistogramViewData *data = userdata;
const ImBuf *ibuf = data->ibuf;
@@ -552,8 +552,8 @@ BLI_INLINE int get_bin_float(float f)
}
static void make_histogram_view_from_ibuf_float_fn(void *__restrict userdata,
- const int y,
- const TaskParallelTLS *__restrict tls)
+ const int y,
+ const TaskParallelTLS *__restrict tls)
{
const MakeHistogramViewData *data = userdata;
const ImBuf *ibuf = data->ibuf;
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index c48c86bd423..c1e890ed5f1 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -1767,6 +1767,7 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
}
else {
orientation = V3D_ORIENT_GLOBAL;
+ unit_m3(t->spacemtx);
}
if ((prop = RNA_struct_find_property(op->ptr, "orient_axis"))) {
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index 1e751f67c9d..0beacf1244a 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -377,23 +377,25 @@ static short apply_targetless_ik(Object *ob)
}
for (; segcount; segcount--) {
Bone *bone;
- float rmat[4][4] /*, tmat[4][4], imat[4][4]*/;
+ float mat[4][4];
/* pose_mat(b) = pose_mat(b-1) * offs_bone * channel * constraint * IK */
- /* we put in channel the entire result of rmat = (channel * constraint * IK) */
- /* pose_mat(b) = pose_mat(b-1) * offs_bone * rmat */
- /* rmat = pose_mat(b) * inv(pose_mat(b-1) * offs_bone ) */
+ /* we put in channel the entire result of mat = (channel * constraint * IK) */
+ /* pose_mat(b) = pose_mat(b-1) * offs_bone * mat */
+ /* mat = pose_mat(b) * inv(pose_mat(b-1) * offs_bone ) */
parchan = chanlist[segcount - 1];
bone = parchan->bone;
bone->flag |= BONE_TRANSFORM; /* ensures it gets an auto key inserted */
- BKE_armature_mat_pose_to_bone(parchan, parchan->pose_mat, rmat);
-
+ BKE_armature_mat_pose_to_bone(parchan, parchan->pose_mat, mat);
/* apply and decompose, doesn't work for constraints or non-uniform scale well */
{
float rmat3[3][3], qrmat[3][3], imat3[3][3], smat[3][3];
- copy_m3_m4(rmat3, rmat);
+
+ copy_m3_m4(rmat3, mat);
+ /* Make sure that our rotation matrix only contains rotation and not scale. */
+ normalize_m3(rmat3);
/* rotation */
/* [#22409] is partially caused by this, as slight numeric error introduced during
@@ -413,7 +415,7 @@ static short apply_targetless_ik(Object *ob)
/* causes problems with some constraints (e.g. childof), so disable this */
/* as it is IK shouldn't affect location directly */
- /* copy_v3_v3(parchan->loc, rmat[3]); */
+ /* copy_v3_v3(parchan->loc, mat[3]); */
}
}
diff --git a/source/blender/editors/undo/memfile_undo.c b/source/blender/editors/undo/memfile_undo.c
index 086aa6c4c9d..ebd5b2272b1 100644
--- a/source/blender/editors/undo/memfile_undo.c
+++ b/source/blender/editors/undo/memfile_undo.c
@@ -25,14 +25,17 @@
#include "BLI_ghash.h"
+#include "DNA_node_types.h"
#include "DNA_object_enums.h"
#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
#include "BKE_blender_undo.h"
#include "BKE_context.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
+#include "BKE_node.h"
#include "BKE_scene.h"
#include "BKE_undo_system.h"
@@ -106,7 +109,6 @@ static int memfile_undosys_step_id_reused_cb(LibraryIDLinkCallbackData *cb_data)
ID *id_self = cb_data->id_self;
ID **id_pointer = cb_data->id_pointer;
BLI_assert((id_self->tag & LIB_TAG_UNDO_OLD_ID_REUSED) != 0);
- Main *bmain = cb_data->user_data;
ID *id = *id_pointer;
if (id != NULL && id->lib == NULL && (id->tag & LIB_TAG_UNDO_OLD_ID_REUSED) == 0) {
@@ -129,9 +131,6 @@ static int memfile_undosys_step_id_reused_cb(LibraryIDLinkCallbackData *cb_data)
}
}
- /* In case an old, re-used ID is using a newly read data-block (i.e. one of its ID pointers got
- * updated), we have to tell the depsgraph about it. */
- DEG_id_tag_update_ex(bmain, id_self, ID_RECALC_COPY_ON_WRITE);
return do_stop_iter ? IDWALK_RET_STOP_ITER : IDWALK_RET_NOP;
}
@@ -219,10 +218,35 @@ static void memfile_undosys_step_decode(struct bContext *C,
BKE_library_foreach_ID_link(
bmain, id, memfile_undosys_step_id_reused_cb, bmain, IDWALK_READONLY);
}
+
+ /* Tag depsgraph to update data-block for changes that happened between the
+ * current and the target state, see direct_link_id_restore_recalc(). */
+ if (id->recalc) {
+ DEG_id_tag_update_ex(bmain, id, id->recalc);
+ }
}
FOREACH_MAIN_ID_END;
- BKE_main_id_tag_all(bmain, LIB_TAG_UNDO_OLD_ID_REUSED, false);
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ /* Clear temporary tag. */
+ id->tag &= ~LIB_TAG_UNDO_OLD_ID_REUSED;
+
+ /* We only start accumulating from this point, any tags set up to here
+ * are already part of the current undo state. This is done in a second
+ * loop because DEG_id_tag_update may set tags on other datablocks. */
+ id->recalc_undo_accumulated = 0;
+ bNodeTree *nodetree = ntreeFromID(id);
+ if (nodetree != NULL) {
+ nodetree->id.recalc_undo_accumulated = 0;
+ }
+ if (GS(id->name) == ID_SCE) {
+ Scene *scene = (Scene *)id;
+ if (scene->master_collection != NULL) {
+ scene->master_collection->id.recalc_undo_accumulated = 0;
+ }
+ }
+ }
+ FOREACH_MAIN_ID_END;
}
WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, CTX_data_scene(C));
diff --git a/source/blender/freestyle/intern/application/Controller.cpp b/source/blender/freestyle/intern/application/Controller.cpp
index f7da64624e0..c2f6796bf31 100644
--- a/source/blender/freestyle/intern/application/Controller.cpp
+++ b/source/blender/freestyle/intern/application/Controller.cpp
@@ -349,7 +349,7 @@ int Controller::LoadMesh(Render *re, ViewLayer *view_layer, Depsgraph *depsgraph
soc string basename((const char *)qfi.fileName().toAscii().data());
char cleaned[FILE_MAX];
BLI_strncpy(cleaned, iFileName, FILE_MAX);
- BLI_cleanup_path(NULL, cleaned);
+ BLI_path_normalize(NULL, cleaned);
string basename = string(cleaned);
#endif
diff --git a/source/blender/freestyle/intern/system/StringUtils.cpp b/source/blender/freestyle/intern/system/StringUtils.cpp
index 58d84010574..cd85aaaa5ff 100644
--- a/source/blender/freestyle/intern/system/StringUtils.cpp
+++ b/source/blender/freestyle/intern/system/StringUtils.cpp
@@ -48,7 +48,7 @@ void getPathName(const string &path, const string &base, vector<string> &pathnam
dir = path.substr(pos, sep - pos);
BLI_strncpy(cleaned, dir.c_str(), FILE_MAX);
- BLI_cleanup_path(NULL, cleaned);
+ BLI_path_normalize(NULL, cleaned);
res = string(cleaned);
if (!base.empty()) {
diff --git a/source/blender/gpu/GPU_batch_presets.h b/source/blender/gpu/GPU_batch_presets.h
index ba8ad3c4990..eb803333d98 100644
--- a/source/blender/gpu/GPU_batch_presets.h
+++ b/source/blender/gpu/GPU_batch_presets.h
@@ -39,6 +39,10 @@ extern "C" {
/* Replacement for gluSphere */
struct GPUBatch *GPU_batch_preset_sphere(int lod) ATTR_WARN_UNUSED_RESULT;
struct GPUBatch *GPU_batch_preset_sphere_wire(int lod) ATTR_WARN_UNUSED_RESULT;
+struct GPUBatch *GPU_batch_preset_panel_drag_widget(const float pixelsize,
+ const float col_high[4],
+ const float col_dark[4],
+ const float width) ATTR_WARN_UNUSED_RESULT;
void gpu_batch_presets_init(void);
void gpu_batch_presets_register(struct GPUBatch *preset_batch);
diff --git a/source/blender/gpu/intern/gpu_batch_presets.c b/source/blender/gpu/intern/gpu_batch_presets.c
index e322b9fb9b8..d16edab5ac9 100644
--- a/source/blender/gpu/intern/gpu_batch_presets.c
+++ b/source/blender/gpu/intern/gpu_batch_presets.c
@@ -27,13 +27,20 @@
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
+#include "DNA_userdef_types.h"
+
#include "UI_interface.h"
+#include "UI_resources.h"
#include "GPU_batch.h"
#include "GPU_batch_presets.h" /* own include */
#include "GPU_batch_utils.h"
#include "gpu_shader_private.h"
+/* -------------------------------------------------------------------- */
+/** \name Local Structures
+ * \{ */
+
/* Struct to store 3D Batches and their format */
static struct {
struct {
@@ -53,8 +60,27 @@ static struct {
ThreadMutex mutex;
} g_presets_3d = {{0}};
+static struct {
+ struct {
+ GPUBatch *panel_drag_widget;
+ } batch;
+
+ float panel_drag_widget_pixelsize;
+ float panel_drag_widget_width;
+ float panel_drag_widget_col_high[4];
+ float panel_drag_widget_col_dark[4];
+
+ GPUVertFormat format;
+
+ struct {
+ uint pos, col;
+ } attr_id;
+} g_presets_2d = {{0}};
+
static ListBase presets_list = {NULL, NULL};
+/** \} */
+
/* -------------------------------------------------------------------- */
/** \name 3D Primitives
* \{ */
@@ -71,6 +97,18 @@ static GPUVertFormat *preset_3d_format(void)
return &g_presets_3d.format;
}
+static GPUVertFormat *preset_2d_format(void)
+{
+ if (g_presets_2d.format.attr_len == 0) {
+ GPUVertFormat *format = &g_presets_2d.format;
+ g_presets_2d.attr_id.pos = GPU_vertformat_attr_add(
+ format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ g_presets_2d.attr_id.col = GPU_vertformat_attr_add(
+ format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ }
+ return &g_presets_2d.format;
+}
+
static void batch_sphere_lat_lon_vert(GPUVertBufRaw *pos_step,
GPUVertBufRaw *nor_step,
float lat,
@@ -193,6 +231,114 @@ static GPUBatch *batch_sphere_wire(int lat_res, int lon_res)
/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Panel Drag Widget
+ * \{ */
+
+static void gpu_batch_preset_rectf_tris_color_ex(GPUVertBufRaw *pos_step,
+ float x1,
+ float y1,
+ float x2,
+ float y2,
+ GPUVertBufRaw *col_step,
+ const float color[4])
+{
+ copy_v2_v2(GPU_vertbuf_raw_step(pos_step), (const float[2]){x1, y1});
+ copy_v4_v4(GPU_vertbuf_raw_step(col_step), color);
+
+ copy_v2_v2(GPU_vertbuf_raw_step(pos_step), (const float[2]){x2, y1});
+ copy_v4_v4(GPU_vertbuf_raw_step(col_step), color);
+
+ copy_v2_v2(GPU_vertbuf_raw_step(pos_step), (const float[2]){x2, y2});
+ copy_v4_v4(GPU_vertbuf_raw_step(col_step), color);
+
+ copy_v2_v2(GPU_vertbuf_raw_step(pos_step), (const float[2]){x1, y1});
+ copy_v4_v4(GPU_vertbuf_raw_step(col_step), color);
+
+ copy_v2_v2(GPU_vertbuf_raw_step(pos_step), (const float[2]){x2, y2});
+ copy_v4_v4(GPU_vertbuf_raw_step(col_step), color);
+
+ copy_v2_v2(GPU_vertbuf_raw_step(pos_step), (const float[2]){x1, y2});
+ copy_v4_v4(GPU_vertbuf_raw_step(col_step), color);
+}
+
+static GPUBatch *gpu_batch_preset_panel_drag_widget(float pixelsize,
+ const float col_high[4],
+ const float col_dark[4],
+ const float width)
+{
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(preset_2d_format());
+ const uint vbo_len = 4 * 2 * (6 * 2);
+ GPU_vertbuf_data_alloc(vbo, vbo_len);
+
+ GPUVertBufRaw pos_step, col_step;
+ GPU_vertbuf_attr_get_raw_data(vbo, g_presets_2d.attr_id.pos, &pos_step);
+ GPU_vertbuf_attr_get_raw_data(vbo, g_presets_2d.attr_id.col, &col_step);
+
+ const int px = (int)pixelsize;
+ const int px_zoom = max_ii(round_fl_to_int(width / 22.0f), 1);
+
+ const int box_margin = max_ii(round_fl_to_int((float)(px_zoom * 2.0f)), px);
+ const int box_size = max_ii(round_fl_to_int((width / 8.0f) - px), px);
+
+ const int y_ofs = max_ii(round_fl_to_int(width / 2.5f), px);
+ const int x_ofs = y_ofs;
+ int i_x, i_y;
+
+ for (i_x = 0; i_x < 4; i_x++) {
+ for (i_y = 0; i_y < 2; i_y++) {
+ const int x_co = (x_ofs) + (i_x * (box_size + box_margin));
+ const int y_co = (y_ofs) + (i_y * (box_size + box_margin));
+
+ gpu_batch_preset_rectf_tris_color_ex(&pos_step,
+ x_co - box_size,
+ y_co - px_zoom,
+ x_co,
+ (y_co + box_size) - px_zoom,
+ &col_step,
+ col_dark);
+ gpu_batch_preset_rectf_tris_color_ex(
+ &pos_step, x_co - box_size, y_co, x_co, y_co + box_size, &col_step, col_high);
+ }
+ }
+ return GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, NULL, GPU_BATCH_OWNS_VBO);
+}
+
+GPUBatch *GPU_batch_preset_panel_drag_widget(const float pixelsize,
+ const float col_high[4],
+ const float col_dark[4],
+ const float width)
+{
+ const bool parameters_changed = (g_presets_2d.panel_drag_widget_pixelsize != pixelsize) ||
+ (g_presets_2d.panel_drag_widget_width != width) ||
+ !equals_v4v4(g_presets_2d.panel_drag_widget_col_high,
+ col_high) ||
+ !equals_v4v4(g_presets_2d.panel_drag_widget_col_dark, col_dark);
+
+ if (g_presets_2d.batch.panel_drag_widget && parameters_changed) {
+ gpu_batch_presets_unregister(g_presets_2d.batch.panel_drag_widget);
+ GPU_batch_discard(g_presets_2d.batch.panel_drag_widget);
+ g_presets_2d.batch.panel_drag_widget = NULL;
+ }
+
+ if (!g_presets_2d.batch.panel_drag_widget) {
+ g_presets_2d.batch.panel_drag_widget = gpu_batch_preset_panel_drag_widget(
+ pixelsize, col_high, col_dark, width);
+ gpu_batch_presets_register(g_presets_2d.batch.panel_drag_widget);
+ g_presets_2d.panel_drag_widget_pixelsize = pixelsize;
+ g_presets_2d.panel_drag_widget_width = width;
+ copy_v4_v4(g_presets_2d.panel_drag_widget_col_high, col_high);
+ copy_v4_v4(g_presets_2d.panel_drag_widget_col_dark, col_dark);
+ }
+ return g_presets_2d.batch.panel_drag_widget;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Preset Registration Management
+ * \{ */
+
void gpu_batch_presets_init(void)
{
BLI_mutex_init(&g_presets_3d.mutex);
@@ -259,3 +405,5 @@ void gpu_batch_presets_exit(void)
BLI_mutex_end(&g_presets_3d.mutex);
}
+
+/** \} */
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index e9ac40de442..11b30a24cde 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -154,7 +154,7 @@ static int an_stringdec(const char *string, char *head, char *tail, unsigned sho
static void an_stringenc(
char *string, const char *head, const char *tail, unsigned short numlen, int pic)
{
- BLI_stringenc(string, head, tail, numlen, pic);
+ BLI_path_sequence_encode(string, head, tail, numlen, pic);
}
#ifdef WITH_AVI
diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c
index 195a2e9fe9c..17b619d451c 100644
--- a/source/blender/imbuf/intern/thumbs.c
+++ b/source/blender/imbuf/intern/thumbs.c
@@ -151,111 +151,24 @@ typedef enum {
UNSAFE_SLASHES = 0x20, /* Allows all characters except for '/' and '%' */
} UnsafeCharacterSet;
+/* Don't loose comment alignment. */
+/* clang-format off */
static const unsigned char acceptable[96] = {
/* A table of the ASCII chars from space (32) to DEL (127) */
/* ! " # $ % & ' ( ) * + , - . / */
- 0x00,
- 0x3F,
- 0x20,
- 0x20,
- 0x28,
- 0x00,
- 0x2C,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x2A,
- 0x28,
- 0x3F,
- 0x3F,
- 0x1C,
+ 0x00,0x3F,0x20,0x20,0x28,0x00,0x2C,0x3F,0x3F,0x3F,0x3F,0x2A,0x28,0x3F,0x3F,0x1C,
/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x38,
- 0x20,
- 0x20,
- 0x2C,
- 0x20,
- 0x20,
+ 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x38,0x20,0x20,0x2C,0x20,0x20,
/* @ A B C D E F G H I J K L M N O */
- 0x38,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
+ 0x38,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
/* P Q R S T U V W X Y Z [ \ ] ^ _ */
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x20,
- 0x20,
- 0x20,
- 0x20,
- 0x3F,
+ 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x20,0x3F,
/* ` a b c d e f g h i j k l m n o */
- 0x20,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
+ 0x20,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
/* p q r s t u v w x y z { | } ~ DEL */
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x3F,
- 0x20,
- 0x20,
- 0x20,
- 0x3F,
- 0x20,
+ 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x20,0x20,0x20,0x3F,0x20,
};
+/* clang-format on */
static const char hex[17] = "0123456789abcdef";
diff --git a/source/blender/io/collada/ImageExporter.cpp b/source/blender/io/collada/ImageExporter.cpp
index 9d2b873f862..1cd3301b8a8 100644
--- a/source/blender/io/collada/ImageExporter.cpp
+++ b/source/blender/io/collada/ImageExporter.cpp
@@ -108,7 +108,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies)
/* make absolute source path */
BLI_strncpy(source_path, image->name, sizeof(source_path));
BLI_path_abs(source_path, ID_BLEND_PATH_FROM_GLOBAL(&image->id));
- BLI_cleanup_path(NULL, source_path);
+ BLI_path_normalize(NULL, source_path);
if (use_copies) {
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 0d9336ac62b..5c620a9a346 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -1122,12 +1122,12 @@ typedef enum eAutokey_Flag {
typedef enum eUserpref_Translation_Flags {
USER_TR_TOOLTIPS = (1 << 0),
USER_TR_IFACE = (1 << 1),
- USER_TR_UNUSED_2 = (1 << 2), /* cleared */
- USER_TR_UNUSED_3 = (1 << 3), /* cleared */
- USER_TR_UNUSED_4 = (1 << 4), /* cleared */
- USER_DOTRANSLATE = (1 << 5),
- USER_TR_UNUSED_6 = (1 << 6), /* cleared */
- USER_TR_UNUSED_7 = (1 << 7), /* cleared */
+ USER_TR_UNUSED_2 = (1 << 2), /* cleared */
+ USER_TR_UNUSED_3 = (1 << 3), /* cleared */
+ USER_TR_UNUSED_4 = (1 << 4), /* cleared */
+ USER_DOTRANSLATE_DEPRECATED = (1 << 5), /* Deprecated in 2.83. */
+ USER_TR_UNUSED_6 = (1 << 6), /* cleared */
+ USER_TR_UNUSED_7 = (1 << 7), /* cleared */
USER_TR_NEWDATANAME = (1 << 8),
} eUserpref_Translation_Flags;
@@ -1306,6 +1306,13 @@ typedef enum eUserpref_DiskCacheCompression {
USER_SEQ_DISK_CACHE_COMPRESSION_HIGH = 2,
} eUserpref_DiskCacheCompression;
+/* Locale Ids. Auto will try to get local from OS. Our default is English though. */
+/** #UserDef.language */
+enum {
+ ULANGUAGE_AUTO = 0,
+ ULANGUAGE_ENGLISH = 1,
+};
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index b625b3f0a84..b5ed49b3476 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -1806,7 +1806,7 @@ static void rna_def_brush(BlenderRNA *brna)
"SURFACE",
0,
"Surface",
- "Smooths the surface of the mesh, preserving the volue"},
+ "Smooths the surface of the mesh, preserving the volume"},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 5354cf463eb..ee3434905b0 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -307,14 +307,27 @@ static void rna_userdef_screen_update_header_default(Main *bmain, Scene *scene,
USERDEF_TAG_DIRTY;
}
+static void rna_userdef_font_update(Main *UNUSED(bmain),
+ Scene *UNUSED(scene),
+ PointerRNA *UNUSED(ptr))
+{
+ BLF_cache_clear();
+ UI_reinit_font();
+}
+
static void rna_userdef_language_update(Main *UNUSED(bmain),
Scene *UNUSED(scene),
PointerRNA *UNUSED(ptr))
{
- BLF_cache_clear();
BLT_lang_set(NULL);
- UI_reinit_font();
- USERDEF_TAG_DIRTY;
+
+ const char *uilng = BLT_lang_get();
+ if (STREQ(uilng, "en_US")) {
+ U.transopts &= ~(USER_TR_IFACE | USER_TR_TOOLTIPS | USER_TR_NEWDATANAME);
+ }
+ else {
+ U.transopts |= (USER_TR_IFACE | USER_TR_TOOLTIPS | USER_TR_NEWDATANAME);
+ }
}
static void rna_userdef_script_autoexec_update(Main *UNUSED(bmain),
@@ -544,7 +557,7 @@ static void rna_Userdef_disk_cache_dir_update(Main *UNUSED(bmain),
{
if (U.sequencer_disk_cache_dir[0] != '\0') {
BLI_path_abs(U.sequencer_disk_cache_dir, BKE_main_blendfile_path_from_global());
- BLI_add_slash(U.sequencer_disk_cache_dir);
+ BLI_path_slash_ensure(U.sequencer_disk_cache_dir);
BLI_path_make_safe(U.sequencer_disk_cache_dir);
}
@@ -4716,21 +4729,15 @@ static void rna_def_userdef_view(BlenderRNA *brna)
prop = RNA_def_property(srna, "font_path_ui", PROP_STRING, PROP_FILEPATH);
RNA_def_property_string_sdna(prop, NULL, "font_path_ui");
RNA_def_property_ui_text(prop, "Interface Font", "Path to interface font");
- RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_language_update");
+ RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_font_update");
prop = RNA_def_property(srna, "font_path_ui_mono", PROP_STRING, PROP_FILEPATH);
RNA_def_property_string_sdna(prop, NULL, "font_path_ui_mono");
RNA_def_property_ui_text(prop, "Mono-space Font", "Path to interface mono-space Font");
- RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_language_update");
+ RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_font_update");
/* Language. */
- prop = RNA_def_property(srna, "use_international_fonts", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "transopts", USER_DOTRANSLATE);
- RNA_def_property_ui_text(
- prop, "Translate UI", "Enable UI translation and use international fonts");
- RNA_def_property_update(prop, NC_WINDOW, "rna_userdef_language_update");
-
prop = RNA_def_property(srna, "language", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_language_default_items);
# ifdef WITH_INTERNATIONAL
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index fdf7e127af9..b09d3187f3c 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -940,7 +940,7 @@ static void bpy_module_delay_init(PyObject *bpy_proxy)
char filename_abs[1024];
BLI_strncpy(filename_abs, filename_rel, sizeof(filename_abs));
- BLI_path_cwd(filename_abs, sizeof(filename_abs));
+ BLI_path_abs_from_cwd(filename_abs, sizeof(filename_abs));
Py_DECREF(filename_obj);
argv[0] = filename_abs;
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 95c7afcbf15..f2050befac1 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -375,6 +375,9 @@ static void wm_init_userdef(Main *bmain)
/* update tempdir from user preferences */
BKE_tempdir_init(U.tempdir);
+
+ /* Update tablet API preference. */
+ WM_init_tablet_api();
}
/* return codes */
@@ -1433,6 +1436,9 @@ static bool wm_file_write(bContext *C, const char *filepath, int fileflags, Repo
ibuf_thumb = IMB_thumb_create(filepath, THB_LARGE, THB_SOURCE_BLEND, ibuf_thumb);
}
+ /* Without this there is no feedback the file was saved. */
+ BKE_reportf(reports, RPT_INFO, "Saved \"%s\"", BLI_path_basename(filepath));
+
/* Success. */
ok = true;
}
@@ -2298,7 +2304,7 @@ static bool wm_open_mainfile_check(bContext *UNUSED(C), wmOperator *op)
RNA_string_get(op->ptr, "filepath", path);
/* get the dir */
- lslash = (char *)BLI_last_slash(path);
+ lslash = (char *)BLI_path_slash_rfind(path);
if (lslash) {
*(lslash + 1) = '\0';
}
@@ -2536,7 +2542,7 @@ void WM_OT_recover_auto_save(wmOperatorType *ot)
static void wm_filepath_default(char *filepath)
{
if (G.save_over == false) {
- BLI_ensure_filename(filepath, FILE_MAX, "untitled.blend");
+ BLI_path_filename_ensure(filepath, FILE_MAX, "untitled.blend");
}
}
@@ -2715,8 +2721,6 @@ static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *U
RNA_string_get(op->ptr, "filepath", path);
ret = wm_save_as_mainfile_exec(C, op);
- /* Without this there is no feedback the file was saved. */
- BKE_reportf(op->reports, RPT_INFO, "Saved \"%s\"", BLI_path_basename(path));
}
else {
WM_event_add_fileselect(C, op);
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index 5acc8a5fbc6..da4e4160724 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -104,7 +104,7 @@ static int wm_link_append_invoke(bContext *C, wmOperator *op, const wmEvent *UNU
else if (G.relbase_valid) {
char path[FILE_MAX];
BLI_strncpy(path, BKE_main_blendfile_path_from_global(), sizeof(path));
- BLI_parent_dir(path);
+ BLI_path_parent_dir(path);
RNA_string_set(op->ptr, "filepath", path);
}
}
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 6d25874da45..a7578291a21 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -103,6 +103,7 @@
#include "wm.h"
#include "wm_draw.h"
+#include "wm_event_system.h"
#include "wm_event_types.h"
#include "wm_files.h"
#include "wm_window.h"
@@ -3244,8 +3245,12 @@ static void redraw_timer_step(bContext *C,
}
}
else { /* eRTUndo */
+ /* Undo and redo, including depsgraph update since that can be a
+ * significant part of the cost. */
ED_undo_pop(C);
+ wm_event_do_refresh_wm_and_depsgraph(C);
ED_undo_redo(C);
+ wm_event_do_refresh_wm_and_depsgraph(C);
}
}
diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c
index 8a872010ecc..84f099b0dbc 100644
--- a/source/blender/windowmanager/intern/wm_playanim.c
+++ b/source/blender/windowmanager/intern/wm_playanim.c
@@ -429,7 +429,8 @@ static void build_pict_list_ex(
} fp_decoded;
BLI_strncpy(filepath, first, sizeof(filepath));
- fp_framenr = BLI_stringdec(filepath, fp_decoded.head, fp_decoded.tail, &fp_decoded.digits);
+ fp_framenr = BLI_path_sequence_decode(
+ filepath, fp_decoded.head, fp_decoded.tail, &fp_decoded.digits);
pupdate_time();
ptottime = 1.0;
@@ -522,7 +523,8 @@ static void build_pict_list_ex(
/* create a new filepath each time */
fp_framenr += fstep;
- BLI_stringenc(filepath, fp_decoded.head, fp_decoded.tail, fp_decoded.digits, fp_framenr);
+ BLI_path_sequence_encode(
+ filepath, fp_decoded.head, fp_decoded.tail, fp_decoded.digits, fp_framenr);
while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0))) {
GHOST_DispatchEvents(g_WS.ghost_system);
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index a83432e0248..1e25d73a86d 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -1679,8 +1679,6 @@ void wm_ghost_init(bContext *C)
}
GHOST_UseWindowFocus(wm_init_state.window_focus);
-
- WM_init_tablet_api();
}
}
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index a98e2d5c897..859a9852b64 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -1070,6 +1070,12 @@ unset(LIB)
setup_liblinks(blender)
+if(APPLE)
+ set_target_properties(blender PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/osx_locals.map)
+elseif(UNIX)
+ set_target_properties(blender PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/blender.map)
+endif()
+
# -----------------------------------------------------------------------------
# USD registry.
# USD requires a set of JSON files that define the standard schemas. These
diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c
index 6ce3066ece1..381a43ff521 100644
--- a/source/creator/creator_args.c
+++ b/source/creator/creator_args.c
@@ -1780,7 +1780,7 @@ static int arg_handle_python_file_run(int argc, const char **argv, void *data)
/* Make the path absolute because its needed for relative linked blends to be found */
char filename[FILE_MAX];
BLI_strncpy(filename, argv[1], sizeof(filename));
- BLI_path_cwd(filename, sizeof(filename));
+ BLI_path_abs_from_cwd(filename, sizeof(filename));
bool ok;
BPY_CTX_SETUP(ok = BPY_execute_filepath(C, filename, NULL));
@@ -1982,7 +1982,7 @@ static int arg_handle_load_file(int UNUSED(argc), const char **argv, void *data)
}
BLI_strncpy(filename, argv[0], sizeof(filename));
- BLI_path_cwd(filename, sizeof(filename));
+ BLI_path_abs_from_cwd(filename, sizeof(filename));
/* load the file */
BKE_reports_init(&reports, RPT_PRINT);
diff --git a/tests/gtests/blenlib/BLI_path_util_test.cc b/tests/gtests/blenlib/BLI_path_util_test.cc
index 18d7a9c4fea..480d48d6080 100644
--- a/tests/gtests/blenlib/BLI_path_util_test.cc
+++ b/tests/gtests/blenlib/BLI_path_util_test.cc
@@ -58,59 +58,59 @@ char *zLhm65070058860608_br_find_exe(const char *default_exe)
/* -------------------------------------------------------------------- */
/* tests */
-/* BLI_cleanup_path */
+/* BLI_path_normalize */
#ifndef _WIN32
TEST(path_util, Clean)
{
/* "/./" -> "/" */
{
char path[FILE_MAX] = "/a/./b/./c/./";
- BLI_cleanup_path(NULL, path);
+ BLI_path_normalize(NULL, path);
EXPECT_STREQ("/a/b/c/", path);
}
{
char path[FILE_MAX] = "/./././";
- BLI_cleanup_path(NULL, path);
+ BLI_path_normalize(NULL, path);
EXPECT_STREQ("/", path);
}
{
char path[FILE_MAX] = "/a/./././b/";
- BLI_cleanup_path(NULL, path);
+ BLI_path_normalize(NULL, path);
EXPECT_STREQ("/a/b/", path);
}
/* "//" -> "/" */
{
char path[FILE_MAX] = "a////";
- BLI_cleanup_path(NULL, path);
+ BLI_path_normalize(NULL, path);
EXPECT_STREQ("a/", path);
}
if (0) /* FIXME */
{
char path[FILE_MAX] = "./a////";
- BLI_cleanup_path(NULL, path);
+ BLI_path_normalize(NULL, path);
EXPECT_STREQ("./a/", path);
}
/* "foo/bar/../" -> "foo/" */
{
char path[FILE_MAX] = "/a/b/c/../../../";
- BLI_cleanup_path(NULL, path);
+ BLI_path_normalize(NULL, path);
EXPECT_STREQ("/", path);
}
{
char path[FILE_MAX] = "/a/../a/b/../b/c/../c/";
- BLI_cleanup_path(NULL, path);
+ BLI_path_normalize(NULL, path);
EXPECT_STREQ("/a/b/c/", path);
}
{
char path[FILE_MAX] = "//../";
- BLI_cleanup_path("/a/b/c/", path);
+ BLI_path_normalize("/a/b/c/", path);
EXPECT_STREQ("/a/b/", path);
}
}