diff options
author | Jacques Lucke <jacques@blender.org> | 2020-04-09 12:24:11 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2020-04-09 12:24:11 +0300 |
commit | 40b269f50b98f074549182ccd741ec9d5db4cb7b (patch) | |
tree | 86d66c0b7a651421e751fad63e34da73be742a4b | |
parent | 69aecad5477dd3a01c3bf600d2f32d4b08c9e6a4 (diff) | |
parent | 80255e67e30c4d77be64a5fa8dc68c0a55f39ebc (diff) |
Merge branch 'master' into simulation-data-block
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 165761254d0..76e80fdb414 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 88b94a86f00..167091815b4 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -3316,13 +3316,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() @@ -3459,6 +3453,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" @@ -4172,7 +4179,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() @@ -7443,6 +7450,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 4dc44548642..ed39579429a 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -472,8 +472,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) { @@ -481,10 +482,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; @@ -666,7 +664,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); @@ -1682,7 +1680,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)) { @@ -2378,9 +2376,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)); @@ -2391,6 +2386,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 */ @@ -2770,7 +2774,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); @@ -2780,7 +2784,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); } @@ -2788,15 +2793,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) { @@ -2806,10 +2868,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. @@ -2825,29 +2885,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. */ @@ -8380,7 +8419,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; @@ -8390,10 +8429,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; } /** \} */ @@ -8438,7 +8477,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); @@ -9249,289 +9288,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); @@ -9539,36 +9304,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); @@ -9686,10 +9440,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... @@ -9699,39 +9798,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; } /** \} */ @@ -10111,7 +10183,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); @@ -10179,7 +10251,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; @@ -10197,6 +10268,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 7c20ba9a8df..c0dcc5921d0 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -4364,8 +4364,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 8821c6e84fa..7f28866ad0b 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1857,11 +1857,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(®ion->v2d, 0.0f, 1.0f); + /* reset the view - we don't want to display a view without content */ + UI_view2d_offset(®ion->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 2a4c6147d5d..3a0ef45a68d 100644 --- a/source/blender/editors/space_node/node_group.c +++ b/source/blender/editors/space_node/node_group.c @@ -193,11 +193,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) { @@ -351,8 +351,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 54470dc59fc..e8f4cae573a 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -1121,12 +1121,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; @@ -1305,6 +1305,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 b107c89388d..514d7428ff8 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 ced0532a8fa..4b51f9738b3 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -1061,6 +1061,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); } } |