diff --git a/extern/mantaflow/CMakeLists.txt b/extern/mantaflow/CMakeLists.txt index ccf272650e3..82bf95a9742 100644 --- a/extern/mantaflow/CMakeLists.txt +++ b/extern/mantaflow/CMakeLists.txt @@ -200,6 +200,7 @@ set(SRC ${MANTA_PP}/plugin/ptsplugins.cpp ${MANTA_PP}/plugin/secondaryparticles.cpp ${MANTA_PP}/plugin/surfaceturbulence.cpp + ${MANTA_PP}/plugin/viscosity.cpp ${MANTA_PP}/plugin/vortexplugins.cpp ${MANTA_PP}/plugin/waveletturbulence.cpp ${MANTA_PP}/plugin/waves.cpp diff --git a/extern/mantaflow/helper/util/rcmatrix.h b/extern/mantaflow/helper/util/rcmatrix.h index f1f0efe6416..330fd1f64f7 100644 --- a/extern/mantaflow/helper/util/rcmatrix.h +++ b/extern/mantaflow/helper/util/rcmatrix.h @@ -1035,7 +1035,7 @@ template struct RCFixedMatrix { typedef RCMatrix Matrix; typedef RCFixedMatrix FixedMatrix; -} // namespace Manta +} #undef parallel_for #undef parallel_end diff --git a/extern/mantaflow/preprocessed/conjugategrad.cpp b/extern/mantaflow/preprocessed/conjugategrad.cpp index da82a412a97..bdcceb29520 100644 --- a/extern/mantaflow/preprocessed/conjugategrad.cpp +++ b/extern/mantaflow/preprocessed/conjugategrad.cpp @@ -397,7 +397,7 @@ struct UpdateSearchVec : public KernelBase { }; //***************************************************************************** -// CG class +// CG class template GridCg::GridCg(Grid &dst, @@ -406,10 +406,8 @@ GridCg::GridCg(Grid &dst, Grid &search, const FlagGrid &flags, Grid &tmp, - Grid *pA0, - Grid *pAi, - Grid *pAj, - Grid *pAk) + std::vector *> matrixAVec, + std::vector *> rhsVec) : GridCgInterface(), mInited(false), mIterations(0), @@ -419,10 +417,8 @@ GridCg::GridCg(Grid &dst, mSearch(search), mFlags(flags), mTmp(tmp), - mpA0(pA0), - mpAi(pAi), - mpAj(pAj), - mpAk(pAk), + mMatrixA(matrixAVec), + mVecRhs(rhsVec), mPcMethod(PC_None), mpPCA0(nullptr), mpPCAi(nullptr), @@ -445,19 +441,37 @@ template void GridCg::doInit() if (mPcMethod == PC_ICP) { assertMsg(mDst.is3D(), "ICP only supports 3D grids so far"); - InitPreconditionIncompCholesky( - mFlags, *mpPCA0, *mpPCAi, *mpPCAj, *mpPCAk, *mpA0, *mpAi, *mpAj, *mpAk); - ApplyPreconditionIncompCholesky( - mTmp, mResidual, mFlags, *mpPCA0, *mpPCAi, *mpPCAj, *mpPCAk, *mpA0, *mpAi, *mpAj, *mpAk); + InitPreconditionIncompCholesky(mFlags, + *mpPCA0, + *mpPCAi, + *mpPCAj, + *mpPCAk, + *mMatrixA[0], + *mMatrixA[1], + *mMatrixA[2], + *mMatrixA[3]); + ApplyPreconditionIncompCholesky(mTmp, + mResidual, + mFlags, + *mpPCA0, + *mpPCAi, + *mpPCAj, + *mpPCAk, + *mMatrixA[0], + *mMatrixA[1], + *mMatrixA[2], + *mMatrixA[3]); } else if (mPcMethod == PC_mICP) { assertMsg(mDst.is3D(), "mICP only supports 3D grids so far"); - InitPreconditionModifiedIncompCholesky2(mFlags, *mpPCA0, *mpA0, *mpAi, *mpAj, *mpAk); + InitPreconditionModifiedIncompCholesky2( + mFlags, *mpPCA0, *mMatrixA[0], *mMatrixA[1], *mMatrixA[2], *mMatrixA[3]); ApplyPreconditionModifiedIncompCholesky2( - mTmp, mResidual, mFlags, *mpPCA0, *mpA0, *mpAi, *mpAj, *mpAk); + mTmp, mResidual, mFlags, *mpPCA0, *mMatrixA[0], *mMatrixA[1], *mMatrixA[2], *mMatrixA[3]); } else if (mPcMethod == PC_MGP) { - InitPreconditionMultigrid(mMG, *mpA0, *mpAi, *mpAj, *mpAk, mAccuracy); + InitPreconditionMultigrid( + mMG, *mMatrixA[0], *mMatrixA[1], *mMatrixA[2], *mMatrixA[3], mAccuracy); ApplyPreconditionMultigrid(mMG, mTmp, mResidual); } else { @@ -465,7 +479,6 @@ template void GridCg::doInit() } mSearch.copyFrom(mTmp); - mSigma = GridDotProduct(mTmp, mResidual); } @@ -480,7 +493,7 @@ template bool GridCg::iterate() // this could reinterpret the mpA pointers (not so clean right now) // tmp = applyMat(search) - APPLYMAT(mFlags, mTmp, mSearch, *mpA0, *mpAi, *mpAj, *mpAk); + APPLYMAT(mFlags, mTmp, mSearch, mMatrixA, mVecRhs); // alpha = sigma/dot(tmp, search) Real dp = GridDotProduct(mTmp, mSearch); @@ -492,11 +505,20 @@ template bool GridCg::iterate() gridScaledAdd(mResidual, mTmp, -alpha); // residual += tmp * -alpha if (mPcMethod == PC_ICP) - ApplyPreconditionIncompCholesky( - mTmp, mResidual, mFlags, *mpPCA0, *mpPCAi, *mpPCAj, *mpPCAk, *mpA0, *mpAi, *mpAj, *mpAk); + ApplyPreconditionIncompCholesky(mTmp, + mResidual, + mFlags, + *mpPCA0, + *mpPCAi, + *mpPCAj, + *mpPCAk, + *mMatrixA[0], + *mMatrixA[1], + *mMatrixA[2], + *mMatrixA[3]); else if (mPcMethod == PC_mICP) ApplyPreconditionModifiedIncompCholesky2( - mTmp, mResidual, mFlags, *mpPCA0, *mpA0, *mpAi, *mpAj, *mpAk); + mTmp, mResidual, mFlags, *mpPCA0, *mMatrixA[0], *mMatrixA[1], *mMatrixA[2], *mMatrixA[3]); else if (mPcMethod == PC_MGP) ApplyPreconditionMultigrid(mMG, mTmp, mResidual); else @@ -584,13 +606,15 @@ void GridCg::setMGPreconditioner(PreconditionType method, GridMg *MG) assertMsg(method == PC_MGP, "GridCg::setMGPreconditioner: Invalid method specified."); mPcMethod = method; - mMG = MG; } // explicit instantiation template class GridCg; template class GridCg; +template class GridCg; +template class GridCg; +template class GridCg; //***************************************************************************** // diffusion for real and vec grids, e.g. for viscosity @@ -638,10 +662,15 @@ void cgSolveDiffusion(const FlagGrid &flags, if (grid.getType() & GridBase::TypeReal) { Grid &u = ((Grid &)grid); rhs.copyFrom(u); - if (flags.is3D()) - gcg = new GridCg(u, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak); - else - gcg = new GridCg(u, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak); + vector *> matA{&A0, &Ai, &Aj}; + + if (flags.is3D()) { + matA.push_back(&Ak); + gcg = new GridCg(u, rhs, residual, search, flags, tmp, matA); + } + else { + gcg = new GridCg(u, rhs, residual, search, flags, tmp, matA); + } gcg->setAccuracy(cgAccuracy); gcg->solve(maxIter); @@ -653,12 +682,17 @@ void cgSolveDiffusion(const FlagGrid &flags, else if ((grid.getType() & GridBase::TypeVec3) || (grid.getType() & GridBase::TypeMAC)) { Grid &vec = ((Grid &)grid); Grid u(parent); + vector *> matA{&A0, &Ai, &Aj}; // core solve is same as for a regular real grid - if (flags.is3D()) - gcg = new GridCg(u, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak); - else - gcg = new GridCg(u, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak); + if (flags.is3D()) { + matA.push_back(&Ak); + gcg = new GridCg(u, rhs, residual, search, flags, tmp, matA); + } + else { + gcg = new GridCg(u, rhs, residual, search, flags, tmp, matA); + } + gcg->setAccuracy(cgAccuracy); // diffuse every component separately diff --git a/extern/mantaflow/preprocessed/conjugategrad.h b/extern/mantaflow/preprocessed/conjugategrad.h index 58ccff28179..4974aa6a4d6 100644 --- a/extern/mantaflow/preprocessed/conjugategrad.h +++ b/extern/mantaflow/preprocessed/conjugategrad.h @@ -78,13 +78,9 @@ template class GridCg : public GridCgInterface { Grid &search, const FlagGrid &flags, Grid &tmp, - Grid *A0, - Grid *pAi, - Grid *pAj, - Grid *pAk); - ~GridCg() - { - } + std::vector *> matrixAVec, + std::vector *> rhsVec = {}); + ~GridCg(){}; void doInit(); bool iterate(); @@ -133,7 +129,10 @@ template class GridCg : public GridCgInterface { const FlagGrid &mFlags; Grid &mTmp; - Grid *mpA0, *mpAi, *mpAj, *mpAk; + //! shape of A matrix defined here (e.g. diagonal, positive neighbor cells, etc) + std::vector *> mMatrixA; + //! shape of rhs vector defined here (e.g. 1 rhs for regular fluids solve, 3 rhs for viscosity) + std::vector *> mVecRhs; PreconditionType mPcMethod; //! preconditioning grids @@ -154,11 +153,9 @@ struct ApplyMatrix : public KernelBase { ApplyMatrix(const FlagGrid &flags, Grid &dst, const Grid &src, - Grid &A0, - Grid &Ai, - Grid &Aj, - Grid &Ak) - : KernelBase(&flags, 0), flags(flags), dst(dst), src(src), A0(A0), Ai(Ai), Aj(Aj), Ak(Ak) + const std::vector *> matrixA, + const std::vector *> vecRhs) + : KernelBase(&flags, 0), flags(flags), dst(dst), src(src), matrixA(matrixA), vecRhs(vecRhs) { runMessage(); run(); @@ -167,11 +164,18 @@ struct ApplyMatrix : public KernelBase { const FlagGrid &flags, Grid &dst, const Grid &src, - Grid &A0, - Grid &Ai, - Grid &Aj, - Grid &Ak) const + const std::vector *> matrixA, + const std::vector *> vecRhs) const { + unusedParameter(vecRhs); // Not needed in this matrix application + + if (matrixA.size() != 4) + errMsg("ConjugatedGrad: Invalid A matrix in apply matrix step"); + Grid &A0 = *matrixA[0]; + Grid &Ai = *matrixA[1]; + Grid &Aj = *matrixA[2]; + Grid &Ak = *matrixA[3]; + if (!flags.isFluid(idx)) { dst[idx] = src[idx]; return; @@ -196,26 +200,16 @@ struct ApplyMatrix : public KernelBase { return src; } typedef Grid type2; - inline Grid &getArg3() + inline const std::vector *> &getArg3() { - return A0; + return matrixA; } - typedef Grid type3; - inline Grid &getArg4() + typedef std::vector *> type3; + inline const std::vector *> &getArg4() { - return Ai; + return vecRhs; } - typedef Grid type4; - inline Grid &getArg5() - { - return Aj; - } - typedef Grid type5; - inline Grid &getArg6() - { - return Ak; - } - typedef Grid type6; + typedef std::vector *> type4; void runMessage() { debMsg("Executing kernel ApplyMatrix ", 3); @@ -226,7 +220,7 @@ struct ApplyMatrix : public KernelBase { void operator()(const tbb::blocked_range &__r) const { for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) - op(idx, flags, dst, src, A0, Ai, Aj, Ak); + op(idx, flags, dst, src, matrixA, vecRhs); } void run() { @@ -235,10 +229,8 @@ struct ApplyMatrix : public KernelBase { const FlagGrid &flags; Grid &dst; const Grid &src; - Grid &A0; - Grid &Ai; - Grid &Aj; - Grid &Ak; + const std::vector *> matrixA; + const std::vector *> vecRhs; }; //! Kernel: Apply symmetric stored Matrix. 2D version @@ -247,11 +239,9 @@ struct ApplyMatrix2D : public KernelBase { ApplyMatrix2D(const FlagGrid &flags, Grid &dst, const Grid &src, - Grid &A0, - Grid &Ai, - Grid &Aj, - Grid &Ak) - : KernelBase(&flags, 0), flags(flags), dst(dst), src(src), A0(A0), Ai(Ai), Aj(Aj), Ak(Ak) + const std::vector *> matrixA, + const std::vector *> vecRhs) + : KernelBase(&flags, 0), flags(flags), dst(dst), src(src), matrixA(matrixA), vecRhs(vecRhs) { runMessage(); run(); @@ -260,12 +250,16 @@ struct ApplyMatrix2D : public KernelBase { const FlagGrid &flags, Grid &dst, const Grid &src, - Grid &A0, - Grid &Ai, - Grid &Aj, - Grid &Ak) const + const std::vector *> matrixA, + const std::vector *> vecRhs) const { - unusedParameter(Ak); // only there for parameter compatibility with ApplyMatrix + unusedParameter(vecRhs); // Not needed in this matrix application + + if (matrixA.size() != 3) + errMsg("ConjugatedGrad: Invalid A matrix in apply matrix step"); + Grid &A0 = *matrixA[0]; + Grid &Ai = *matrixA[1]; + Grid &Aj = *matrixA[2]; if (!flags.isFluid(idx)) { dst[idx] = src[idx]; @@ -290,26 +284,16 @@ struct ApplyMatrix2D : public KernelBase { return src; } typedef Grid type2; - inline Grid &getArg3() + inline const std::vector *> &getArg3() { - return A0; + return matrixA; } - typedef Grid type3; - inline Grid &getArg4() + typedef std::vector *> type3; + inline const std::vector *> &getArg4() { - return Ai; + return vecRhs; } - typedef Grid type4; - inline Grid &getArg5() - { - return Aj; - } - typedef Grid type5; - inline Grid &getArg6() - { - return Ak; - } - typedef Grid type6; + typedef std::vector *> type4; void runMessage() { debMsg("Executing kernel ApplyMatrix2D ", 3); @@ -320,7 +304,7 @@ struct ApplyMatrix2D : public KernelBase { void operator()(const tbb::blocked_range &__r) const { for (IndexInt idx = __r.begin(); idx != (IndexInt)__r.end(); idx++) - op(idx, flags, dst, src, A0, Ai, Aj, Ak); + op(idx, flags, dst, src, matrixA, vecRhs); } void run() { @@ -329,12 +313,358 @@ struct ApplyMatrix2D : public KernelBase { const FlagGrid &flags; Grid &dst; const Grid &src; - Grid &A0; - Grid &Ai; - Grid &Aj; - Grid &Ak; + const std::vector *> matrixA; + const std::vector *> vecRhs; }; +struct ApplyMatrixViscosityU : public KernelBase { + ApplyMatrixViscosityU(const FlagGrid &flags, + Grid &dst, + const Grid &src, + const std::vector *> matrixA, + const std::vector *> vecRhs) + : KernelBase(&flags, 1), flags(flags), dst(dst), src(src), matrixA(matrixA), vecRhs(vecRhs) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + Grid &dst, + const Grid &src, + const std::vector *> matrixA, + const std::vector *> vecRhs) const + { + if (matrixA.size() != 15) + errMsg("ConjugatedGrad: Invalid A matrix in apply matrix step"); + Grid &A0 = *matrixA[0]; + Grid &Aplusi = *matrixA[1]; + Grid &Aplusj = *matrixA[2]; + Grid &Aplusk = *matrixA[3]; + Grid &Aminusi = *matrixA[4]; + Grid &Aminusj = *matrixA[5]; + Grid &Aminusk = *matrixA[6]; + + if (vecRhs.size() != 2) + errMsg("ConjugatedGrad: Invalid rhs vector in apply matrix step"); + Grid &srcV = *vecRhs[0]; + Grid &srcW = *vecRhs[1]; + + dst(i, j, k) = src(i, j, k) * A0(i, j, k) + src(i + 1, j, k) * Aplusi(i, j, k) + + src(i, j + 1, k) * Aplusj(i, j, k) + src(i, j, k + 1) * Aplusk(i, j, k) + + src(i - 1, j, k) * Aminusi(i, j, k) + src(i, j - 1, k) * Aminusj(i, j, k) + + src(i, j, k - 1) * Aminusk(i, j, k); + + dst(i, j, k) += srcV(i, j + 1, k) * (*matrixA[7])(i, j, k) + + srcV(i - 1, j + 1, k) * (*matrixA[8])(i, j, k) + + srcV(i, j, k) * (*matrixA[9])(i, j, k) + + srcV(i - 1, j, k) * (*matrixA[10])(i, j, k) + + srcW(i, j, k + 1) * (*matrixA[11])(i, j, k) + + srcW(i - 1, j, k + 1) * (*matrixA[12])(i, j, k) + + srcW(i, j, k) * (*matrixA[13])(i, j, k) + + srcW(i - 1, j, k) * (*matrixA[14])(i, j, k); + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid &getArg1() + { + return dst; + } + typedef Grid type1; + inline const Grid &getArg2() + { + return src; + } + typedef Grid type2; + inline const std::vector *> &getArg3() + { + return matrixA; + } + typedef std::vector *> type3; + inline const std::vector *> &getArg4() + { + return vecRhs; + } + typedef std::vector *> type4; + void runMessage() + { + debMsg("Executing kernel ApplyMatrixViscosityU ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, dst, src, matrixA, vecRhs); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, dst, src, matrixA, vecRhs); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range(1, maxY), *this); + } + const FlagGrid &flags; + Grid &dst; + const Grid &src; + const std::vector *> matrixA; + const std::vector *> vecRhs; +}; + +struct ApplyMatrixViscosityV : public KernelBase { + ApplyMatrixViscosityV(const FlagGrid &flags, + Grid &dst, + const Grid &src, + const std::vector *> matrixA, + const std::vector *> vecRhs) + : KernelBase(&flags, 1), flags(flags), dst(dst), src(src), matrixA(matrixA), vecRhs(vecRhs) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + Grid &dst, + const Grid &src, + const std::vector *> matrixA, + const std::vector *> vecRhs) const + { + if (matrixA.size() != 15) + errMsg("ConjugatedGrad: Invalid A matrix in apply matrix step"); + Grid &A0 = *matrixA[0]; + Grid &Aplusi = *matrixA[1]; + Grid &Aplusj = *matrixA[2]; + Grid &Aplusk = *matrixA[3]; + Grid &Aminusi = *matrixA[4]; + Grid &Aminusj = *matrixA[5]; + Grid &Aminusk = *matrixA[6]; + + if (vecRhs.size() != 2) + errMsg("ConjugatedGrad: Invalid rhs vector in apply matrix step"); + Grid &srcU = *vecRhs[0]; + Grid &srcW = *vecRhs[1]; + + dst(i, j, k) = src(i, j, k) * A0(i, j, k) + src(i + 1, j, k) * Aplusi(i, j, k) + + src(i, j + 1, k) * Aplusj(i, j, k) + src(i, j, k + 1) * Aplusk(i, j, k) + + src(i - 1, j, k) * Aminusi(i, j, k) + src(i, j - 1, k) * Aminusj(i, j, k) + + src(i, j, k - 1) * Aminusk(i, j, k); + + dst(i, j, k) += srcU(i + 1, j, k) * (*matrixA[7])(i, j, k) + + srcU(i + 1, j - 1, k) * (*matrixA[8])(i, j, k) + + srcU(i, j, k) * (*matrixA[9])(i, j, k) + + srcU(i, j - 1, k) * (*matrixA[10])(i, j, k) + + srcW(i, j, k + 1) * (*matrixA[11])(i, j, k) + + srcW(i, j - 1, k + 1) * (*matrixA[12])(i, j, k) + + srcW(i, j, k) * (*matrixA[13])(i, j, k) + + srcW(i, j - 1, k) * (*matrixA[14])(i, j, k); + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid &getArg1() + { + return dst; + } + typedef Grid type1; + inline const Grid &getArg2() + { + return src; + } + typedef Grid type2; + inline const std::vector *> &getArg3() + { + return matrixA; + } + typedef std::vector *> type3; + inline const std::vector *> &getArg4() + { + return vecRhs; + } + typedef std::vector *> type4; + void runMessage() + { + debMsg("Executing kernel ApplyMatrixViscosityV ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, dst, src, matrixA, vecRhs); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, dst, src, matrixA, vecRhs); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range(1, maxY), *this); + } + const FlagGrid &flags; + Grid &dst; + const Grid &src; + const std::vector *> matrixA; + const std::vector *> vecRhs; +}; + +struct ApplyMatrixViscosityW : public KernelBase { + ApplyMatrixViscosityW(const FlagGrid &flags, + Grid &dst, + const Grid &src, + const std::vector *> matrixA, + const std::vector *> vecRhs) + : KernelBase(&flags, 1), flags(flags), dst(dst), src(src), matrixA(matrixA), vecRhs(vecRhs) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + Grid &dst, + const Grid &src, + const std::vector *> matrixA, + const std::vector *> vecRhs) const + { + if (matrixA.size() != 15) + errMsg("ConjugatedGrad: Invalid A matrix in apply matrix step"); + Grid &A0 = *matrixA[0]; + Grid &Aplusi = *matrixA[1]; + Grid &Aplusj = *matrixA[2]; + Grid &Aplusk = *matrixA[3]; + Grid &Aminusi = *matrixA[4]; + Grid &Aminusj = *matrixA[5]; + Grid &Aminusk = *matrixA[6]; + + if (vecRhs.size() != 2) + errMsg("ConjugatedGrad: Invalid rhs vector in apply matrix step"); + Grid &srcU = *vecRhs[0]; + Grid &srcV = *vecRhs[1]; + + dst(i, j, k) = src(i, j, k) * A0(i, j, k) + src(i + 1, j, k) * Aplusi(i, j, k) + + src(i, j + 1, k) * Aplusj(i, j, k) + src(i, j, k + 1) * Aplusk(i, j, k) + + src(i - 1, j, k) * Aminusi(i, j, k) + src(i, j - 1, k) * Aminusj(i, j, k) + + src(i, j, k - 1) * Aminusk(i, j, k); + + dst(i, j, k) += srcU(i + 1, j, k) * (*matrixA[7])(i, j, k) + + srcU(i + 1, j, k - 1) * (*matrixA[8])(i, j, k) + + srcU(i, j, k) * (*matrixA[9])(i, j, k) + + srcU(i, j, k - 1) * (*matrixA[10])(i, j, k) + + srcV(i, j + 1, k) * (*matrixA[11])(i, j, k) + + srcV(i, j + 1, k - 1) * (*matrixA[12])(i, j, k) + + srcV(i, j, k) * (*matrixA[13])(i, j, k) + + srcV(i, j, k - 1) * (*matrixA[14])(i, j, k); + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid &getArg1() + { + return dst; + } + typedef Grid type1; + inline const Grid &getArg2() + { + return src; + } + typedef Grid type2; + inline const std::vector *> &getArg3() + { + return matrixA; + } + typedef std::vector *> type3; + inline const std::vector *> &getArg4() + { + return vecRhs; + } + typedef std::vector *> type4; + void runMessage() + { + debMsg("Executing kernel ApplyMatrixViscosityW ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 1; j < _maxY; j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, dst, src, matrixA, vecRhs); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 1; i < _maxX; i++) + op(i, j, k, flags, dst, src, matrixA, vecRhs); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range(1, maxY), *this); + } + const FlagGrid &flags; + Grid &dst; + const Grid &src; + const std::vector *> matrixA; + const std::vector *> vecRhs; +}; + +/* NOTE: Use this template for new matrix application kernels + +//! Template for matrix application kernels +KERNEL() +void ApplyMatrixTemplate (const FlagGrid& flags, Grid& dst, const Grid& src, + const std::vector *> matrixA, const std::vector *> vecRhs) +{ + // The kernel must define how to use the grids from the matrixA and vecRhs lists +} + +*/ + //! Kernel: Construct the matrix for the poisson equation struct MakeLaplaceMatrix : public KernelBase { diff --git a/extern/mantaflow/preprocessed/general.h b/extern/mantaflow/preprocessed/general.h index 7a840517cef..50eac71e87e 100644 --- a/extern/mantaflow/preprocessed/general.h +++ b/extern/mantaflow/preprocessed/general.h @@ -42,7 +42,7 @@ inline void updateQtGui(bool full, int frame, float time, const std::string &cur # ifdef _DEBUG # define DEBUG 1 # endif // _DEBUG -#endif // DEBUG +#endif // DEBUG // Standard exception class Error : public std::exception { @@ -242,6 +242,39 @@ inline bool c_isnan(float c) return d != d; } +//! Swap so that a inline void sort(T &a, T &b) +{ + if (a > b) + std::swap(a, b); +} + +//! Swap so that a inline void sort(T &a, T &b, T &c) +{ + if (a > b) + std::swap(a, b); + if (a > c) + std::swap(a, c); + if (b > c) + std::swap(b, c); +} + +//! Swap so that a inline void sort(T &a, T &b, T &c, T &d) +{ + if (a > b) + std::swap(a, b); + if (c > d) + std::swap(c, d); + if (a > c) + std::swap(a, c); + if (b > d) + std::swap(b, d); + if (b > c) + std::swap(b, c); +} + } // namespace Manta #endif diff --git a/extern/mantaflow/preprocessed/gitinfo.h b/extern/mantaflow/preprocessed/gitinfo.h index a0590e6a0b0..afc34a00d3d 100644 --- a/extern/mantaflow/preprocessed/gitinfo.h +++ b/extern/mantaflow/preprocessed/gitinfo.h @@ -1,3 +1,3 @@ -#define MANTA_GIT_VERSION "commit 327917cd59b03bef3a953b5f58fc1637b3a83e01" +#define MANTA_GIT_VERSION "commit e2285cb9bc492987f728123be6cfc1fe11fe73d6" diff --git a/extern/mantaflow/preprocessed/plugin/extforces.cpp b/extern/mantaflow/preprocessed/plugin/extforces.cpp index 558008afe3b..88935fa7ae9 100644 --- a/extern/mantaflow/preprocessed/plugin/extforces.cpp +++ b/extern/mantaflow/preprocessed/plugin/extforces.cpp @@ -1135,26 +1135,27 @@ struct KnAddForceIfLower : public KernelBase { if (!curFluid && !curEmpty) return; + Real minVal, maxVal, sum; if (flags.isFluid(i - 1, j, k) || (curFluid && flags.isEmpty(i - 1, j, k))) { Real forceMACX = 0.5 * (force(i - 1, j, k).x + force(i, j, k).x); - Real min = std::min(vel(i, j, k).x, forceMACX); - Real max = std::max(vel(i, j, k).x, forceMACX); - Real sum = vel(i, j, k).x + forceMACX; - vel(i, j, k).x = (forceMACX > 0) ? std::min(sum, max) : std::max(sum, min); + minVal = min(vel(i, j, k).x, forceMACX); + maxVal = max(vel(i, j, k).x, forceMACX); + sum = vel(i, j, k).x + forceMACX; + vel(i, j, k).x = (forceMACX > 0) ? min(sum, maxVal) : max(sum, minVal); } if (flags.isFluid(i, j - 1, k) || (curFluid && flags.isEmpty(i, j - 1, k))) { Real forceMACY = 0.5 * (force(i, j - 1, k).y + force(i, j, k).y); - Real min = std::min(vel(i, j, k).y, forceMACY); - Real max = std::max(vel(i, j, k).y, forceMACY); - Real sum = vel(i, j, k).y + forceMACY; - vel(i, j, k).y = (forceMACY > 0) ? std::min(sum, max) : std::max(sum, min); + minVal = min(vel(i, j, k).y, forceMACY); + maxVal = max(vel(i, j, k).y, forceMACY); + sum = vel(i, j, k).y + forceMACY; + vel(i, j, k).y = (forceMACY > 0) ? min(sum, maxVal) : max(sum, minVal); } if (vel.is3D() && (flags.isFluid(i, j, k - 1) || (curFluid && flags.isEmpty(i, j, k - 1)))) { Real forceMACZ = 0.5 * (force(i, j, k - 1).z + force(i, j, k).z); - Real min = std::min(vel(i, j, k).z, forceMACZ); - Real max = std::max(vel(i, j, k).z, forceMACZ); - Real sum = vel(i, j, k).z + forceMACZ; - vel(i, j, k).z = (forceMACZ > 0) ? std::min(sum, max) : std::max(sum, min); + minVal = min(vel(i, j, k).z, forceMACZ); + maxVal = max(vel(i, j, k).z, forceMACZ); + sum = vel(i, j, k).z + forceMACZ; + vel(i, j, k).z = (forceMACZ > 0) ? min(sum, maxVal) : max(sum, minVal); } } inline const FlagGrid &getArg0() diff --git a/extern/mantaflow/preprocessed/plugin/pressure.cpp b/extern/mantaflow/preprocessed/plugin/pressure.cpp index 1100a58db47..593aeb16859 100644 --- a/extern/mantaflow/preprocessed/plugin/pressure.cpp +++ b/extern/mantaflow/preprocessed/plugin/pressure.cpp @@ -1138,11 +1138,15 @@ void solvePressureSystem(Grid &rhs, // note: the last factor increases the max iterations for 2d, which right now can't use a // preconditioner GridCgInterface *gcg; - if (vel.is3D()) - gcg = new GridCg(pressure, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak); - else - gcg = new GridCg( - pressure, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak); + vector *> matA{&A0, &Ai, &Aj}; + + if (vel.is3D()) { + matA.push_back(&Ak); + gcg = new GridCg(pressure, rhs, residual, search, flags, tmp, matA); + } + else { + gcg = new GridCg(pressure, rhs, residual, search, flags, tmp, matA); + } gcg->setAccuracy(cgAccuracy); gcg->setUseL2Norm(useL2Norm); diff --git a/extern/mantaflow/preprocessed/plugin/viscosity.cpp b/extern/mantaflow/preprocessed/plugin/viscosity.cpp new file mode 100644 index 00000000000..5dc478b13dd --- /dev/null +++ b/extern/mantaflow/preprocessed/plugin/viscosity.cpp @@ -0,0 +1,1430 @@ + + +// DO NOT EDIT ! +// This file is generated using the MantaFlow preprocessor (prep generate). + +/****************************************************************************** + * + * MantaFlow fluid solver framework + * Copyright 2020 Sebastian Barschkis, Nils Thuerey + * + * This program is free software, distributed under the terms of the + * Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Accurate Viscous Free Surfaces for Buckling, Coiling, and Rotating Liquids + * Batty et al., SCA 2008 + * + ******************************************************************************/ + +#include "conjugategrad.h" +#include "general.h" +#include "grid.h" +#include "vectorbase.h" + +#include + +#if OPENMP == 1 || TBB == 1 +# define ENABLE_PARALLEL 0 +#endif + +#if ENABLE_PARALLEL == 1 +# include +# include + +static const int manta_num_threads = std::thread::hardware_concurrency(); + +# define parallel_block \ + { \ + std::vector threads; \ + { + +# define do_parallel threads.push_back( std::thread([&]() { +# define do_end \ + } ) ); + +# define block_end \ + } \ + for (auto &thread : threads) { \ + thread.join(); \ + } \ + } + +#endif + +#define FOR_INT_IJK(num) \ + for (int k_off = 0; k_off < num; ++k_off) \ + for (int j_off = 0; j_off < num; ++j_off) \ + for (int i_off = 0; i_off < num; ++i_off) + +using namespace std; + +namespace Manta { + +//! Assumes phi0<0 and phi1>=0, phi2>=0, and phi3>=0 or vice versa. +//! In particular, phi0 must not equal any of phi1, phi2 or phi3. +static Real sortedTetFraction(Real phi0, Real phi1, Real phi2, Real phi3) +{ + return phi0 * phi0 * phi0 / ((phi0 - phi1) * (phi0 - phi2) * (phi0 - phi3)); +} + +//! Assumes phi0<0, phi1<0, and phi2>=0, and phi3>=0 or vice versa. +//! In particular, phi0 and phi1 must not equal any of phi2 and phi3. +static Real sortedPrismFraction(Real phi0, Real phi1, Real phi2, Real phi3) +{ + Real a = phi0 / (phi0 - phi2); + Real b = phi0 / (phi0 - phi3); + Real c = phi1 / (phi1 - phi3); + Real d = phi1 / (phi1 - phi2); + return a * b * (1 - d) + b * (1 - c) * d + c * d; +} + +Real volumeFraction(Real phi0, Real phi1, Real phi2, Real phi3) +{ + sort(phi0, phi1, phi2, phi3); + if (phi3 <= 0) + return 1; + else if (phi2 <= 0) + return 1 - sortedTetFraction(phi3, phi2, phi1, phi0); + else if (phi1 <= 0) + return sortedPrismFraction(phi0, phi1, phi2, phi3); + else if (phi0 <= 0) + return sortedTetFraction(phi0, phi1, phi2, phi3); + else + return 0; +} + +//! The average of the two possible decompositions of the cube into five tetrahedra. +Real volumeFraction(Real phi000, + Real phi100, + Real phi010, + Real phi110, + Real phi001, + Real phi101, + Real phi011, + Real phi111) +{ + return (volumeFraction(phi000, phi001, phi101, phi011) + + volumeFraction(phi000, phi101, phi100, phi110) + + volumeFraction(phi000, phi010, phi011, phi110) + + volumeFraction(phi101, phi011, phi111, phi110) + + 2 * volumeFraction(phi000, phi011, phi101, phi110) + + volumeFraction(phi100, phi101, phi001, phi111) + + volumeFraction(phi100, phi001, phi000, phi010) + + volumeFraction(phi100, phi110, phi111, phi010) + + volumeFraction(phi001, phi111, phi011, phi010) + + 2 * volumeFraction(phi100, phi111, phi001, phi010)) / + 12; +} + +//! Kernel loop over grid with 2x base resolution! + +struct KnEstimateVolumeFraction : public KernelBase { + KnEstimateVolumeFraction(Grid &volumes, + const Grid &phi, + const Vec3 &startCentre, + const Real dx) + : KernelBase(&volumes, 0), volumes(volumes), phi(phi), startCentre(startCentre), dx(dx) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + Grid &volumes, + const Grid &phi, + const Vec3 &startCentre, + const Real dx) const + { + const Vec3 centre = startCentre + Vec3(i, j, k) * 0.5; + const Real offset = 0.5 * dx; + const int order = 2; + + Real phi000 = phi.getInterpolatedHi(centre + Vec3(-offset, -offset, -offset), order); + Real phi001 = phi.getInterpolatedHi(centre + Vec3(-offset, -offset, +offset), order); + Real phi010 = phi.getInterpolatedHi(centre + Vec3(-offset, +offset, -offset), order); + Real phi011 = phi.getInterpolatedHi(centre + Vec3(-offset, +offset, +offset), order); + Real phi100 = phi.getInterpolatedHi(centre + Vec3(+offset, -offset, -offset), order); + Real phi101 = phi.getInterpolatedHi(centre + Vec3(+offset, -offset, +offset), order); + Real phi110 = phi.getInterpolatedHi(centre + Vec3(+offset, +offset, -offset), order); + Real phi111 = phi.getInterpolatedHi(centre + Vec3(+offset, +offset, +offset), order); + + volumes(i, j, k) = volumeFraction( + phi000, phi100, phi010, phi110, phi001, phi101, phi011, phi111); + } + inline Grid &getArg0() + { + return volumes; + } + typedef Grid type0; + inline const Grid &getArg1() + { + return phi; + } + typedef Grid type1; + inline const Vec3 &getArg2() + { + return startCentre; + } + typedef Vec3 type2; + inline const Real &getArg3() + { + return dx; + } + typedef Real type3; + void runMessage() + { + debMsg("Executing kernel KnEstimateVolumeFraction ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, volumes, phi, startCentre, dx); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, volumes, phi, startCentre, dx); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range(0, maxY), *this); + } + Grid &volumes; + const Grid φ + const Vec3 &startCentre; + const Real dx; +}; + +struct KnUpdateVolumeGrid : public KernelBase { + KnUpdateVolumeGrid(Grid &cVolLiquid, + Grid &uVolLiquid, + Grid &vVolLiquid, + Grid &wVolLiquid, + Grid &exVolLiquid, + Grid &eyVolLiquid, + Grid &ezVolLiquid, + const Grid &src) + : KernelBase(&cVolLiquid, 0), + cVolLiquid(cVolLiquid), + uVolLiquid(uVolLiquid), + vVolLiquid(vVolLiquid), + wVolLiquid(wVolLiquid), + exVolLiquid(exVolLiquid), + eyVolLiquid(eyVolLiquid), + ezVolLiquid(ezVolLiquid), + src(src) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + Grid &cVolLiquid, + Grid &uVolLiquid, + Grid &vVolLiquid, + Grid &wVolLiquid, + Grid &exVolLiquid, + Grid &eyVolLiquid, + Grid &ezVolLiquid, + const Grid &src) const + { + // Work out c + cVolLiquid(i, j, k) = 0; + FOR_INT_IJK(2) + { + cVolLiquid(i, j, k) += src(2 * i + i_off, 2 * j + j_off, 2 * k + k_off); + } + cVolLiquid(i, j, k) /= 8; + + // Work out u + if (i >= 1) { + uVolLiquid(i, j, k) = 0; + int base_i = 2 * i - 1; + int base_j = 2 * j; + int base_k = 2 * k; + FOR_INT_IJK(2) + { + uVolLiquid(i, j, k) += src(base_i + i_off, base_j + j_off, base_k + k_off); + } + uVolLiquid(i, j, k) /= 8; + } + + // v + if (j >= 1) { + vVolLiquid(i, j, k) = 0; + int base_i = 2 * i; + int base_j = 2 * j - 1; + int base_k = 2 * k; + FOR_INT_IJK(2) + { + vVolLiquid(i, j, k) += src(base_i + i_off, base_j + j_off, base_k + k_off); + } + vVolLiquid(i, j, k) /= 8; + } + + // w + if (k >= 1) { + wVolLiquid(i, j, k) = 0; + int base_i = 2 * i; + int base_j = 2 * j; + int base_k = 2 * k - 1; + FOR_INT_IJK(2) + { + wVolLiquid(i, j, k) += src(base_i + i_off, base_j + j_off, base_k + k_off); + } + wVolLiquid(i, j, k) /= 8; + } + + // e-x + if (j >= 1 && k >= 1) { + exVolLiquid(i, j, k) = 0; + int base_i = 2 * i; + int base_j = 2 * j - 1; + int base_k = 2 * k - 1; + FOR_INT_IJK(2) + { + exVolLiquid(i, j, k) += src(base_i + i_off, base_j + j_off, base_k + k_off); + } + exVolLiquid(i, j, k) /= 8; + } + + // e-y + if (i >= 1 && k >= 1) { + eyVolLiquid(i, j, k) = 0; + int base_i = 2 * i - 1; + int base_j = 2 * j; + int base_k = 2 * k - 1; + FOR_INT_IJK(2) + { + eyVolLiquid(i, j, k) += src(base_i + i_off, base_j + j_off, base_k + k_off); + } + eyVolLiquid(i, j, k) /= 8; + } + + // e-z + if (i >= 1 && j >= 1) { + ezVolLiquid(i, j, k) = 0; + int base_i = 2 * i - 1; + int base_j = 2 * j - 1; + int base_k = 2 * k; + FOR_INT_IJK(2) + { + ezVolLiquid(i, j, k) += src(base_i + i_off, base_j + j_off, base_k + k_off); + } + ezVolLiquid(i, j, k) /= 8; + } + } + inline Grid &getArg0() + { + return cVolLiquid; + } + typedef Grid type0; + inline Grid &getArg1() + { + return uVolLiquid; + } + typedef Grid type1; + inline Grid &getArg2() + { + return vVolLiquid; + } + typedef Grid type2; + inline Grid &getArg3() + { + return wVolLiquid; + } + typedef Grid type3; + inline Grid &getArg4() + { + return exVolLiquid; + } + typedef Grid type4; + inline Grid &getArg5() + { + return eyVolLiquid; + } + typedef Grid type5; + inline Grid &getArg6() + { + return ezVolLiquid; + } + typedef Grid type6; + inline const Grid &getArg7() + { + return src; + } + typedef Grid type7; + void runMessage() + { + debMsg("Executing kernel KnUpdateVolumeGrid ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, + j, + k, + cVolLiquid, + uVolLiquid, + vVolLiquid, + wVolLiquid, + exVolLiquid, + eyVolLiquid, + ezVolLiquid, + src); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, + j, + k, + cVolLiquid, + uVolLiquid, + vVolLiquid, + wVolLiquid, + exVolLiquid, + eyVolLiquid, + ezVolLiquid, + src); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range(0, maxY), *this); + } + Grid &cVolLiquid; + Grid &uVolLiquid; + Grid &vVolLiquid; + Grid &wVolLiquid; + Grid &exVolLiquid; + Grid &eyVolLiquid; + Grid &ezVolLiquid; + const Grid &src; +}; + +void computeWeights(const Grid &phi, + Grid &doubleSized, + Grid &cVolLiquid, + Grid &uVolLiquid, + Grid &vVolLiquid, + Grid &wVolLiquid, + Grid &exVolLiquid, + Grid &eyVolLiquid, + Grid &ezVolLiquid, + Real dx) +{ + KnEstimateVolumeFraction(doubleSized, phi, Vec3(0.25 * dx, 0.25 * dx, 0.25 * dx), 0.5 * dx); + KnUpdateVolumeGrid(cVolLiquid, + uVolLiquid, + vVolLiquid, + wVolLiquid, + exVolLiquid, + eyVolLiquid, + ezVolLiquid, + doubleSized); +} + +struct KnUpdateFaceStates : public KernelBase { + KnUpdateFaceStates(const FlagGrid &flags, + Grid &uState, + Grid &vState, + Grid &wState) + : KernelBase(&flags, 0), flags(flags), uState(uState), vState(vState), wState(wState) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + const FlagGrid &flags, + Grid &uState, + Grid &vState, + Grid &wState) const + { + bool curObs = flags.isObstacle(i, j, k); + uState(i, j, k) = (i > 0 && !flags.isObstacle(i - 1, j, k) && !curObs) ? + FlagGrid::TypeFluid : + FlagGrid::TypeObstacle; + vState(i, j, k) = (j > 0 && !flags.isObstacle(i, j - 1, k) && !curObs) ? + FlagGrid::TypeFluid : + FlagGrid::TypeObstacle; + wState(i, j, k) = (k > 0 && !flags.isObstacle(i, j, k - 1) && !curObs) ? + FlagGrid::TypeFluid : + FlagGrid::TypeObstacle; + } + inline const FlagGrid &getArg0() + { + return flags; + } + typedef FlagGrid type0; + inline Grid &getArg1() + { + return uState; + } + typedef Grid type1; + inline Grid &getArg2() + { + return vState; + } + typedef Grid type2; + inline Grid &getArg3() + { + return wState; + } + typedef Grid type3; + void runMessage() + { + debMsg("Executing kernel KnUpdateFaceStates ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, uState, vState, wState); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, flags, uState, vState, wState); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range(0, maxY), *this); + } + const FlagGrid &flags; + Grid &uState; + Grid &vState; + Grid &wState; +}; + +struct KnApplyVelocities : public KernelBase { + KnApplyVelocities(MACGrid &dst, + const Grid &uState, + const Grid &vState, + const Grid &wState, + Grid &srcU, + Grid &srcV, + Grid &srcW) + : KernelBase(&dst, 0), + dst(dst), + uState(uState), + vState(vState), + wState(wState), + srcU(srcU), + srcV(srcV), + srcW(srcW) + { + runMessage(); + run(); + } + inline void op(int i, + int j, + int k, + MACGrid &dst, + const Grid &uState, + const Grid &vState, + const Grid &wState, + Grid &srcU, + Grid &srcV, + Grid &srcW) const + { + dst(i, j, k).x = (uState(i, j, k) == FlagGrid::TypeFluid) ? srcU(i, j, k) : 0; + dst(i, j, k).y = (vState(i, j, k) == FlagGrid::TypeFluid) ? srcV(i, j, k) : 0; + if (dst.is3D()) + dst(i, j, k).z = (wState(i, j, k) == FlagGrid::TypeFluid) ? srcW(i, j, k) : 0; + } + inline MACGrid &getArg0() + { + return dst; + } + typedef MACGrid type0; + inline const Grid &getArg1() + { + return uState; + } + typedef Grid type1; + inline const Grid &getArg2() + { + return vState; + } + typedef Grid type2; + inline const Grid &getArg3() + { + return wState; + } + typedef Grid type3; + inline Grid &getArg4() + { + return srcU; + } + typedef Grid type4; + inline Grid &getArg5() + { + return srcV; + } + typedef Grid type5; + inline Grid &getArg6() + { + return srcW; + } + typedef Grid type6; + void runMessage() + { + debMsg("Executing kernel KnApplyVelocities ", 3); + debMsg("Kernel range" + << " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ", + 4); + }; + void operator()(const tbb::blocked_range &__r) const + { + const int _maxX = maxX; + const int _maxY = maxY; + if (maxZ > 1) { + for (int k = __r.begin(); k != (int)__r.end(); k++) + for (int j = 0; j < _maxY; j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, dst, uState, vState, wState, srcU, srcV, srcW); + } + else { + const int k = 0; + for (int j = __r.begin(); j != (int)__r.end(); j++) + for (int i = 0; i < _maxX; i++) + op(i, j, k, dst, uState, vState, wState, srcU, srcV, srcW); + } + } + void run() + { + if (maxZ > 1) + tbb::parallel_for(tbb::blocked_range(minZ, maxZ), *this); + else + tbb::parallel_for(tbb::blocked_range(0, maxY), *this); + } + MACGrid &dst; + const Grid &uState; + const Grid &vState; + const Grid &wState; + Grid &srcU; + Grid &srcV; + Grid &srcW; +}; + +void solveViscosity(const FlagGrid &flags, + MACGrid &vel, + Grid &cVolLiquid, + Grid &uVolLiquid, + Grid &vVolLiquid, + Grid &wVolLiquid, + Grid &exVolLiquid, + Grid &eyVolLiquid, + Grid &ezVolLiquid, + Grid &viscosity, + const Real dt, + const Real dx, + const Real cgAccuracy, + const Real cgMaxIterFac) +{ + const Real factor = dt * square(1.0 / dx); + const int maxIter = (int)(cgMaxIterFac * flags.getSize().max()) * (flags.is3D() ? 1 : 4); + GridCg *uGcg; + GridCg *vGcg; + GridCg *wGcg; + + // Tmp grids for CG solve in U, V, W dimensions + FluidSolver *parent = flags.getParent(); + Grid uResidual(parent); + Grid vResidual(parent); + Grid wResidual(parent); + Grid uSearch(parent); + Grid vSearch(parent); + Grid wSearch(parent); + Grid uTmp(parent); + Grid vTmp(parent); + Grid wTmp(parent); + Grid uRhs(parent); + Grid vRhs(parent); + Grid wRhs(parent); + + // A matrix U grids + Grid uA0(parent); // diagonal elements in A matrix + Grid uAplusi(parent); // neighbor at i+1 + Grid uAplusj(parent); // neighbor at j+1 + Grid uAplusk(parent); // neighbor at k+1 + Grid uAminusi(parent); // neighbor at i-1 + Grid uAminusj(parent); // neighbor at j-1 + Grid uAminusk(parent); // neighbor at k-1 + Grid uAhelper1(parent); // additional helper grids for off diagonal elements + Grid uAhelper2(parent); + Grid uAhelper3(parent); + Grid uAhelper4(parent); + Grid uAhelper5(parent); + Grid uAhelper6(parent); + Grid uAhelper7(parent); + Grid uAhelper8(parent); + + // A matrix V grids + Grid vA0(parent); + Grid vAplusi(parent); + Grid vAplusj(parent); + Grid vAplusk(parent); + Grid vAminusi(parent); + Grid vAminusj(parent); + Grid vAminusk(parent); + Grid vAhelper1(parent); + Grid vAhelper2(parent); + Grid vAhelper3(parent); + Grid vAhelper4(parent); + Grid vAhelper5(parent); + Grid vAhelper6(parent); + Grid vAhelper7(parent); + Grid vAhelper8(parent); + + // A matrix W grids + Grid wA0(parent); + Grid wAplusi(parent); + Grid wAplusj(parent); + Grid wAplusk(parent); + Grid wAminusi(parent); + Grid wAminusj(parent); + Grid wAminusk(parent); + Grid wAhelper1(parent); + Grid wAhelper2(parent); + Grid wAhelper3(parent); + Grid wAhelper4(parent); + Grid wAhelper5(parent); + Grid wAhelper6(parent); + Grid wAhelper7(parent); + Grid wAhelper8(parent); + + // Solution grids for CG solvers + Grid uSolution(parent); + Grid vSolution(parent); + Grid wSolution(parent); + + // Save state of voxel face (fluid or obstacle) + Grid uState(parent); + Grid vState(parent); + Grid wState(parent); + + // Save state of voxel face (fluid or obstacle) + KnUpdateFaceStates(flags, uState, vState, wState); + + // Shorter names for flags, we will use them often + int isFluid = FlagGrid::TypeFluid; + int isObstacle = FlagGrid::TypeObstacle; + + // Main viscosity loop: construct A matrices and rhs's in all dimensions + FOR_IJK_BND(flags, 1) + { + + // U-terms: 2u_xx+ v_xy +uyy + u_zz + w_xz + if (uState(i, j, k) == isFluid) { + + uRhs(i, j, k) = uVolLiquid(i, j, k) * vel(i, j, k).x; + uA0(i, j, k) = uVolLiquid(i, j, k); + + Real viscRight = viscosity(i, j, k); + Real viscLeft = viscosity(i - 1, j, k); + Real volRight = cVolLiquid(i, j, k); + Real volLeft = cVolLiquid(i - 1, j, k); + + Real viscTop = 0.25 * (viscosity(i - 1, j + 1, k) + viscosity(i - 1, j, k) + + viscosity(i, j + 1, k) + viscosity(i, j, k)); + Real viscBottom = 0.25 * (viscosity(i - 1, j, k) + viscosity(i - 1, j - 1, k) + + viscosity(i, j, k) + viscosity(i, j - 1, k)); + Real volTop = ezVolLiquid(i, j + 1, k); + Real volBottom = ezVolLiquid(i, j, k); + + Real viscFront = 0.25 * (viscosity(i - 1, j, k + 1) + viscosity(i - 1, j, k) + + viscosity(i, j, k + 1) + viscosity(i, j, k)); + Real viscBack = 0.25 * (viscosity(i - 1, j, k) + viscosity(i - 1, j, k - 1) + + viscosity(i, j, k) + viscosity(i, j, k - 1)); + Real volFront = eyVolLiquid(i, j, k + 1); + Real volBack = eyVolLiquid(i, j, k); + + Real factorRight = 2 * factor * viscRight * volRight; + Real factorLeft = 2 * factor * viscLeft * volLeft; + Real factorTop = factor * viscTop * volTop; + Real factorBottom = factor * viscBottom * volBottom; + Real factorFront = factor * viscFront * volFront; + Real factorBack = factor * viscBack * volBack; + + // u_x_right + uA0(i, j, k) += factorRight; + if (uState(i + 1, j, k) == isFluid) { + uAplusi(i, j, k) += -factorRight; + } + else if (uState(i + 1, j, k) == isObstacle) { + uRhs(i, j, k) -= -vel(i + 1, j, k).x * factorRight; + } + + // u_x_left + uA0(i, j, k) += factorLeft; + if (uState(i - 1, j, k) == isFluid) { + uAminusi(i, j, k) += -factorLeft; + } + else if (uState(i - 1, j, k) == isObstacle) { + uRhs(i, j, k) -= -vel(i - 1, j, k).x * factorLeft; + } + + // u_y_top + uA0(i, j, k) += factorTop; + if (uState(i, j + 1, k) == isFluid) { + uAplusj(i, j, k) += -factorTop; + } + else if (uState(i, j + 1, k) == isObstacle) { + uRhs(i, j, k) -= -vel(i, j + 1, k).x * factorTop; + } + + // u_y_bottom + uA0(i, j, k) += factorBottom; + if (uState(i, j - 1, k) == isFluid) { + uAminusj(i, j, k) += -factorBottom; + } + else if (uState(i, j - 1, k) == isObstacle) { + uRhs(i, j, k) -= -vel(i, j - 1, k).x * factorBottom; + } + + // u_z_front + uA0(i, j, k) += factorFront; + if (uState(i, j, k + 1) == isFluid) { + uAplusk(i, j, k) += -factorFront; + } + else if (uState(i, j, k + 1) == isObstacle) { + uRhs(i, j, k) -= -vel(i, j, k + 1).x * factorFront; + } + + // u_z_back + uA0(i, j, k) += factorBack; + if (uState(i, j, k - 1) == isFluid) { + uAminusk(i, j, k) += -factorBack; + } + else if (uState(i, j, k - 1) == isObstacle) { + uRhs(i, j, k) -= -vel(i, j, k - 1).x * factorBack; + } + + // v_x_top + if (vState(i, j + 1, k) == isFluid) { + uAhelper1(i, j, k) += -factorTop; + } + else if (vState(i, j + 1, k) == isObstacle) { + uRhs(i, j, k) -= -vel(i, j + 1, k).y * factorTop; + } + + if (vState(i - 1, j + 1, k) == isFluid) { + uAhelper2(i, j, k) += factorTop; + } + else if (vState(i - 1, j + 1, k) == isObstacle) { + uRhs(i, j, k) -= vel(i - 1, j + 1, k).y * factorTop; + } + + // v_x_bottom + if (vState(i, j, k) == isFluid) { + uAhelper3(i, j, k) += factorBottom; + } + else if (vState(i, j, k) == isObstacle) { + uRhs(i, j, k) -= vel(i, j, k).y * factorBottom; + } + + if (vState(i - 1, j, k) == isFluid) { + uAhelper4(i, j, k) += -factorBottom; + } + else if (vState(i - 1, j, k) == isObstacle) { + uRhs(i, j, k) -= -vel(i - 1, j, k).y * factorBottom; + } + + // w_x_front + if (wState(i, j, k + 1) == isFluid) { + uAhelper5(i, j, k) += -factorFront; + } + else if (wState(i, j, k + 1) == isObstacle) { + uRhs(i, j, k) -= -vel(i, j, k + 1).z * factorFront; + } + + if (wState(i - 1, j, k + 1) == isFluid) { + uAhelper6(i, j, k) += factorFront; + } + else if (wState(i - 1, j, k + 1) == isObstacle) { + uRhs(i, j, k) -= vel(i - 1, j, k + 1).z * factorFront; + } + + // w_x_back + if (wState(i, j, k) == isFluid) { + uAhelper7(i, j, k) += factorBack; + } + else if (wState(i, j, k) == isObstacle) { + uRhs(i, j, k) -= vel(i, j, k).z * factorBack; + } + + if (wState(i - 1, j, k) == isFluid) { + uAhelper8(i, j, k) += -factorBack; + } + else if (wState(i - 1, j, k) == isObstacle) { + uRhs(i, j, k) -= -vel(i - 1, j, k).z * factorBack; + } + } + + // V-terms: vxx + 2vyy + vzz + u_yx + w_yz + if (vState(i, j, k) == isFluid) { + + vRhs(i, j, k) = vVolLiquid(i, j, k) * vel(i, j, k).y; + vA0(i, j, k) = vVolLiquid(i, j, k); + + Real viscRight = 0.25 * (viscosity(i, j - 1, k) + viscosity(i + 1, j - 1, k) + + viscosity(i, j, k) + viscosity(i + 1, j, k)); + Real viscLeft = 0.25 * (viscosity(i, j - 1, k) + viscosity(i - 1, j - 1, k) + + viscosity(i, j, k) + viscosity(i - 1, j, k)); + Real volRight = ezVolLiquid(i + 1, j, k); + Real volLeft = ezVolLiquid(i, j, k); + + Real viscTop = viscosity(i, j, k); + Real viscBottom = viscosity(i, j - 1, k); + Real volTop = cVolLiquid(i, j, k); + Real volBottom = cVolLiquid(i, j - 1, k); + + Real viscFront = 0.25 * (viscosity(i, j - 1, k) + viscosity(i, j - 1, k + 1) + + viscosity(i, j, k) + viscosity(i, j, k + 1)); + Real viscBack = 0.25 * (viscosity(i, j - 1, k) + viscosity(i, j - 1, k - 1) + + viscosity(i, j, k) + viscosity(i, j, k - 1)); + Real volFront = exVolLiquid(i, j, k + 1); + Real volBack = exVolLiquid(i, j, k); + + Real factorRight = factor * viscRight * volRight; + Real factorLeft = factor * viscLeft * volLeft; + Real factorTop = 2 * factor * viscTop * volTop; + Real factorBottom = 2 * factor * viscBottom * volBottom; + Real factorFront = factor * viscFront * volFront; + Real factorBack = factor * viscBack * volBack; + + // v_x_right + vA0(i, j, k) += factorRight; + if (vState(i + 1, j, k) == isFluid) { + vAplusi(i, j, k) += -factorRight; + } + else if (vState(i + 1, j, k) == isObstacle) { + vRhs(i, j, k) -= -vel(i + 1, j, k).y * factorRight; + } + + // v_x_left + vA0(i, j, k) += factorLeft; + if (vState(i - 1, j, k) == isFluid) { + vAminusi(i, j, k) += -factorLeft; + } + else if (vState(i - 1, j, k) == isObstacle) { + vRhs(i, j, k) -= -vel(i - 1, j, k).y * factorLeft; + } + + // vy_top + vA0(i, j, k) += factorTop; + if (vState(i, j + 1, k) == isFluid) { + vAplusj(i, j, k) += -factorTop; + } + else if (vState(i, j + 1, k) == isObstacle) { + vRhs(i, j, k) -= -vel(i, j + 1, k).y * factorTop; + } + + // vy_bottom + vA0(i, j, k) += factorBottom; + if (vState(i, j - 1, k) == isFluid) { + vAminusj(i, j, k) += -factorBottom; + } + else if (vState(i, j - 1, k) == isObstacle) { + vRhs(i, j, k) -= -vel(i, j - 1, k).y * factorBottom; + } + + // v_z_front + vA0(i, j, k) += factorFront; + if (vState(i, j, k + 1) == isFluid) { + vAplusk(i, j, k) += -factorFront; + } + else if (vState(i, j, k + 1) == isObstacle) { + vRhs(i, j, k) -= -vel(i, j, k + 1).y * factorFront; + } + + // v_z_back + vA0(i, j, k) += factorBack; + if (vState(i, j, k - 1) == isFluid) { + vAminusk(i, j, k) += -factorBack; + } + else if (vState(i, j, k - 1) == isObstacle) { + vRhs(i, j, k) -= -vel(i, j, k - 1).y * factorBack; + } + + // u_y_right + if (uState(i + 1, j, k) == isFluid) { + vAhelper1(i, j, k) += -factorRight; + } + else if (uState(i + 1, j, k) == isObstacle) { + vRhs(i, j, k) -= -vel(i + 1, j, k).x * factorRight; + } + + if (uState(i + 1, j - 1, k) == isFluid) { + vAhelper2(i, j, k) += factorRight; + } + else if (uState(i + 1, j - 1, k) == isObstacle) { + vRhs(i, j, k) -= vel(i + 1, j - 1, k).x * factorRight; + } + + // u_y_left + if (uState(i, j, k) == isFluid) { + vAhelper3(i, j, k) += factorLeft; + } + else if (uState(i, j, k) == isObstacle) { + vRhs(i, j, k) -= vel(i, j, k).x * factorLeft; + } + + if (uState(i, j - 1, k) == isFluid) { + vAhelper4(i, j, k) += -factorLeft; + } + else if (uState(i, j - 1, k) == isObstacle) { + vRhs(i, j, k) -= -vel(i, j - 1, k).x * factorLeft; + } + + // w_y_front + if (wState(i, j, k + 1) == isFluid) { + vAhelper5(i, j, k) += -factorFront; + } + else if (wState(i, j, k + 1) == isObstacle) { + vRhs(i, j, k) -= -vel(i, j, k + 1).z * factorFront; + } + + if (wState(i, j - 1, k + 1) == isFluid) { + vAhelper6(i, j, k) += factorFront; + } + else if (wState(i, j - 1, k + 1) == isObstacle) { + vRhs(i, j, k) -= vel(i, j - 1, k + 1).z * factorFront; + } + + // w_y_back + if (wState(i, j, k) == isFluid) { + vAhelper7(i, j, k) += factorBack; + } + else if (wState(i, j, k) == isObstacle) { + vRhs(i, j, k) -= vel(i, j, k).z * factorBack; + } + + if (wState(i, j - 1, k) == isFluid) { + vAhelper8(i, j, k) += -factorBack; + } + else if (wState(i, j - 1, k) == isObstacle) { + vRhs(i, j, k) -= -vel(i, j - 1, k).z * factorBack; + } + } + + // W-terms: wxx+ wyy+ 2wzz + u_zx + v_zy + if (wState(i, j, k) == isFluid) { + + wRhs(i, j, k) = wVolLiquid(i, j, k) * vel(i, j, k).z; + wA0(i, j, k) = wVolLiquid(i, j, k); + + Real viscRight = 0.25 * (viscosity(i, j, k) + viscosity(i, j, k - 1) + + viscosity(i + 1, j, k) + viscosity(i + 1, j, k - 1)); + Real viscLeft = 0.25 * (viscosity(i, j, k) + viscosity(i, j, k - 1) + + viscosity(i - 1, j, k) + viscosity(i - 1, j, k - 1)); + Real volRight = eyVolLiquid(i + 1, j, k); + Real volLeft = eyVolLiquid(i, j, k); + + Real viscTop = 0.25 * (viscosity(i, j, k) + viscosity(i, j, k - 1) + viscosity(i, j + 1, k) + + viscosity(i, j + 1, k - 1)); + ; + Real viscBottom = 0.25 * (viscosity(i, j, k) + viscosity(i, j, k - 1) + + viscosity(i, j - 1, k) + viscosity(i, j - 1, k - 1)); + ; + Real volTop = exVolLiquid(i, j + 1, k); + Real volBottom = exVolLiquid(i, j, k); + + Real viscFront = viscosity(i, j, k); + Real viscBack = viscosity(i, j, k - 1); + Real volFront = cVolLiquid(i, j, k); + Real volBack = cVolLiquid(i, j, k - 1); + + Real factorRight = factor * viscRight * volRight; + Real factorLeft = factor * viscLeft * volLeft; + Real factorTop = factor * viscTop * volTop; + Real factorBottom = factor * viscBottom * volBottom; + Real factorFront = 2 * factor * viscFront * volFront; + Real factorBack = 2 * factor * viscBack * volBack; + + // w_x_right + wA0(i, j, k) += factorRight; + if (wState(i + 1, j, k) == isFluid) { + wAplusi(i, j, k) += -factorRight; + } + else if (wState(i + 1, j, k) == isObstacle) { + wRhs(i, j, k) -= -vel(i + 1, j, k).z * factorRight; + } + + // w_x_left + wA0(i, j, k) += factorLeft; + if (wState(i - 1, j, k) == isFluid) { + wAminusi(i, j, k) += -factorLeft; + } + else if (wState(i - 1, j, k) == isObstacle) { + wRhs(i, j, k) -= -vel(i - 1, j, k).z * factorLeft; + } + + // w_y_top + wA0(i, j, k) += factorTop; + if (wState(i, j + 1, k) == isFluid) { + wAplusj(i, j, k) += -factorTop; + } + else if (wState(i, j + 1, k) == isObstacle) { + wRhs(i, j, k) -= -vel(i, j + 1, k).z * factorTop; + } + + // w_y_bottom + wA0(i, j, k) += factorBottom; + if (wState(i, j - 1, k) == isFluid) { + wAminusj(i, j, k) += -factorBottom; + } + else if (wState(i, j - 1, k) == isObstacle) { + wRhs(i, j, k) -= -vel(i, j - 1, k).z * factorBottom; + } + + // w_z_front + wA0(i, j, k) += factorFront; + if (wState(i, j, k + 1) == isFluid) { + wAplusk(i, j, k) += -factorFront; + } + else if (wState(i, j, k + 1) == isObstacle) { + wRhs(i, j, k) -= -vel(i, j, k + 1).z * factorFront; + } + + // w_z_back + wA0(i, j, k) += factorBack; + if (wState(i, j, k - 1) == isFluid) { + wAminusk(i, j, k) += -factorBack; + } + else if (wState(i, j, k - 1) == isObstacle) { + wRhs(i, j, k) -= -vel(i, j, k - 1).z * factorBack; + } + + // u_z_right + if (uState(i + 1, j, k) == isFluid) { + wAhelper1(i, j, k) += -factorRight; + } + else if (uState(i + 1, j, k) == isObstacle) { + wRhs(i, j, k) -= -vel(i + 1, j, k).x * factorRight; + } + + if (uState(i + 1, j, k - 1) == isFluid) { + wAhelper2(i, j, k) += factorRight; + } + else if (uState(i + 1, j, k - 1) == isObstacle) { + wRhs(i, j, k) -= vel(i + 1, j, k - 1).x * factorRight; + } + + // u_z_left + if (uState(i, j, k) == isFluid) { + wAhelper3(i, j, k) += factorLeft; + } + else if (uState(i, j, k) == isObstacle) { + wRhs(i, j, k) -= vel(i, j, k).x * factorLeft; + } + + if (uState(i, j, k - 1) == isFluid) { + wAhelper4(i, j, k) += -factorLeft; + } + else if (uState(i, j, k - 1) == isObstacle) { + wRhs(i, j, k) -= -vel(i, j, k - 1).x * factorLeft; + } + + // v_z_top + if (vState(i, j + 1, k) == isFluid) { + wAhelper5(i, j, k) += -factorTop; + } + else if (vState(i, j + 1, k) == isObstacle) { + wRhs(i, j, k) -= -vel(i, j + 1, k).y * factorTop; + } + + if (vState(i, j + 1, k - 1) == isFluid) { + wAhelper6(i, j, k) += factorTop; + } + else if (vState(i, j + 1, k - 1) == isObstacle) { + wRhs(i, j, k) -= vel(i, j + 1, k - 1).y * factorTop; + } + + // v_z_bottom + if (vState(i, j, k) == isFluid) { + wAhelper7(i, j, k) += factorBottom; + } + else if (vState(i, j, k) == isObstacle) { + wRhs(i, j, k) -= vel(i, j, k).y * factorBottom; + } + + if (vState(i, j, k - 1) == isFluid) { + wAhelper8(i, j, k) += -factorBottom; + } + else if (vState(i, j, k - 1) == isObstacle) { + wRhs(i, j, k) -= -vel(i, j, k - 1).y * factorBottom; + } + } + } + + // CG solver for U + if (flags.is3D()) { + vector *> uMatA{&uA0, + &uAplusi, + &uAplusj, + &uAplusk, + &uAminusi, + &uAminusj, + &uAminusk, + &uAhelper1, + &uAhelper2, + &uAhelper3, + &uAhelper4, + &uAhelper5, + &uAhelper6, + &uAhelper7, + &uAhelper8}; + vector *> uVecRhs{&vRhs, &wRhs}; + uGcg = new GridCg( + uSolution, uRhs, uResidual, uSearch, flags, uTmp, uMatA, uVecRhs); + } + else { + errMsg("2D Matrix application not yet supported in viscosity solver"); + } + + // CG solver for V + if (flags.is3D()) { + vector *> vMatA{&vA0, + &vAplusi, + &vAplusj, + &vAplusk, + &vAminusi, + &vAminusj, + &vAminusk, + &vAhelper1, + &vAhelper2, + &vAhelper3, + &vAhelper4, + &vAhelper5, + &vAhelper6, + &vAhelper7, + &vAhelper8}; + vector *> vVecRhs{&uRhs, &wRhs}; + vGcg = new GridCg( + vSolution, vRhs, vResidual, vSearch, flags, vTmp, vMatA, vVecRhs); + } + else { + errMsg("2D Matrix application not yet supported in viscosity solver"); + } + + // CG solver for W + if (flags.is3D()) { + vector *> wMatA{&wA0, + &wAplusi, + &wAplusj, + &wAplusk, + &wAminusi, + &wAminusj, + &wAminusk, + &wAhelper1, + &wAhelper2, + &wAhelper3, + &wAhelper4, + &wAhelper5, + &wAhelper6, + &wAhelper7, + &wAhelper8}; + vector *> wVecRhs{&uRhs, &vRhs}; + wGcg = new GridCg( + wSolution, wRhs, wResidual, wSearch, flags, wTmp, wMatA, wVecRhs); + } + else { + errMsg("2D Matrix application not yet supported in viscosity solver"); + } + + // Same accuracy for all dimensions + uGcg->setAccuracy(cgAccuracy); + vGcg->setAccuracy(cgAccuracy); + wGcg->setAccuracy(cgAccuracy); + + // CG solve. Preconditioning not supported yet. Instead, U, V, W can optionally be solved in + // parallel. + for (int uIter = 0, vIter = 0, wIter = 0; uIter < maxIter || vIter < maxIter || wIter < maxIter; + uIter++, vIter++, wIter++) { +#if ENABLE_PARALLEL == 1 + parallel_block do_parallel +#endif + if (uIter < maxIter && !uGcg->iterate()) uIter = maxIter; +#if ENABLE_PARALLEL == 1 + do_end do_parallel +#endif + if (vIter < maxIter && !vGcg->iterate()) vIter = maxIter; +#if ENABLE_PARALLEL == 1 + do_end do_parallel +#endif + if (wIter < maxIter && !wGcg->iterate()) wIter = maxIter; +#if ENABLE_PARALLEL == 1 + do_end block_end +#endif + + // Make sure that next CG iteration has updated rhs grids + uRhs.copyFrom(uSearch); + vRhs.copyFrom(vSearch); + wRhs.copyFrom(wSearch); + } + debMsg( + "Viscosity::solveViscosity done. " + "Iterations (u,v,w): (" + << uGcg->getIterations() << "," << vGcg->getIterations() << "," << wGcg->getIterations() + << "), " + "Residual (u,v,w): (" + << uGcg->getResNorm() << "," << vGcg->getResNorm() << "," << wGcg->getResNorm() << ")", + 2); + + delete uGcg; + delete vGcg; + delete wGcg; + + // Apply solutions to global velocity grid + KnApplyVelocities(vel, uState, vState, wState, uSolution, vSolution, wSolution); +} + +//! To use the viscosity plugin, scenes must call this function before solving pressure. +//! Note that the 'volumes' grid uses 2x the base resolution + +void applyViscosity(const FlagGrid &flags, + const Grid &phi, + MACGrid &vel, + Grid &volumes, + Grid &viscosity, + const Real cgAccuracy = 1e-9, + const Real cgMaxIterFac = 1.5) +{ + const Real dx = flags.getParent()->getDx(); + const Real dt = flags.getParent()->getDt(); + + // Reserve temp grids for volume weight calculation + FluidSolver *parent = flags.getParent(); + Grid cVolLiquid(parent); + Grid uVolLiquid(parent); + Grid vVolLiquid(parent); + Grid wVolLiquid(parent); + Grid exVolLiquid(parent); + Grid eyVolLiquid(parent); + Grid ezVolLiquid(parent); + + // Ensure final weight grid gets cleared at every step + volumes.clear(); + + // Save viscous fluid volume in double-sized volumes grid + computeWeights(phi, + volumes, + cVolLiquid, + uVolLiquid, + vVolLiquid, + wVolLiquid, + exVolLiquid, + eyVolLiquid, + ezVolLiquid, + dx); + + // Set up A matrix and rhs. Solve with CG. Update velocity grid. + solveViscosity(flags, + vel, + cVolLiquid, + uVolLiquid, + vVolLiquid, + wVolLiquid, + exVolLiquid, + eyVolLiquid, + ezVolLiquid, + viscosity, + dt, + dx, + cgAccuracy, + cgMaxIterFac); +} +static PyObject *_W_0(PyObject *_self, PyObject *_linargs, PyObject *_kwds) +{ + try { + PbArgs _args(_linargs, _kwds); + FluidSolver *parent = _args.obtainParent(); + bool noTiming = _args.getOpt("notiming", -1, 0); + pbPreparePlugin(parent, "applyViscosity", !noTiming); + PyObject *_retval = nullptr; + { + ArgLocker _lock; + const FlagGrid &flags = *_args.getPtr("flags", 0, &_lock); + const Grid &phi = *_args.getPtr>("phi", 1, &_lock); + MACGrid &vel = *_args.getPtr("vel", 2, &_lock); + Grid &volumes = *_args.getPtr>("volumes", 3, &_lock); + Grid &viscosity = *_args.getPtr>("viscosity", 4, &_lock); + const Real cgAccuracy = _args.getOpt("cgAccuracy", 5, 1e-9, &_lock); + const Real cgMaxIterFac = _args.getOpt("cgMaxIterFac", 6, 1.5, &_lock); + _retval = getPyNone(); + applyViscosity(flags, phi, vel, volumes, viscosity, cgAccuracy, cgMaxIterFac); + _args.check(); + } + pbFinalizePlugin(parent, "applyViscosity", !noTiming); + return _retval; + } + catch (std::exception &e) { + pbSetError("applyViscosity", e.what()); + return 0; + } +} +static const Pb::Register _RP_applyViscosity("", "applyViscosity", _W_0); +extern "C" { +void PbRegister_applyViscosity() +{ + KEEP_UNUSED(_RP_applyViscosity); +} +} + +} // namespace Manta + +#if ENABLE_PARALLEL == 1 + +# undef parallel_block +# undef do_parallel +# undef do_end +# undef block_end +# undef parallel_for +# undef parallel_end + +#endif diff --git a/extern/mantaflow/preprocessed/plugin/vortexplugins.cpp b/extern/mantaflow/preprocessed/plugin/vortexplugins.cpp index d5d7d597a7c..18222c4ccda 100644 --- a/extern/mantaflow/preprocessed/plugin/vortexplugins.cpp +++ b/extern/mantaflow/preprocessed/plugin/vortexplugins.cpp @@ -576,8 +576,10 @@ void VICintegration(VortexSheetMesh &mesh, // prepare CG solver const int maxIter = (int)(cgMaxIterFac * vel.getSize().max()); + vector *> matA{&A0, &Ai, &Aj, &Ak}; + GridCgInterface *gcg = new GridCg( - solution, rhs, residual, search, flags, temp1, &A0, &Ai, &Aj, &Ak); + solution, rhs, residual, search, flags, temp1, matA); gcg->setAccuracy(cgAccuracy); gcg->setUseL2Norm(true); gcg->setICPreconditioner( diff --git a/extern/mantaflow/preprocessed/plugin/waves.cpp b/extern/mantaflow/preprocessed/plugin/waves.cpp index 6fb3b16c742..53c56b8c506 100644 --- a/extern/mantaflow/preprocessed/plugin/waves.cpp +++ b/extern/mantaflow/preprocessed/plugin/waves.cpp @@ -423,10 +423,15 @@ void cgSolveWE(const FlagGrid &flags, const int maxIter = (int)(cgMaxIterFac * flags.getSize().max()) * (flags.is3D() ? 1 : 4); GridCgInterface *gcg; - if (flags.is3D()) - gcg = new GridCg(out, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak); - else - gcg = new GridCg(out, rhs, residual, search, flags, tmp, &A0, &Ai, &Aj, &Ak); + vector *> matA{&A0, &Ai, &Aj}; + + if (flags.is3D()) { + matA.push_back(&Ak); + gcg = new GridCg(out, rhs, residual, search, flags, tmp, matA); + } + else { + gcg = new GridCg(out, rhs, residual, search, flags, tmp, matA); + } gcg->setAccuracy(cgAccuracy); diff --git a/extern/mantaflow/preprocessed/registration.cpp b/extern/mantaflow/preprocessed/registration.cpp index d5dae479f0e..fd32463b95f 100644 --- a/extern/mantaflow/preprocessed/registration.cpp +++ b/extern/mantaflow/preprocessed/registration.cpp @@ -145,6 +145,7 @@ extern void PbRegister_flipComputeSurfaceNormals(); extern void PbRegister_flipUpdateNeighborRatio(); extern void PbRegister_particleSurfaceTurbulence(); extern void PbRegister_debugCheckParts(); +extern void PbRegister_applyViscosity(); extern void PbRegister_markAsFixed(); extern void PbRegister_texcoordInflow(); extern void PbRegister_meshSmokeInflow(); @@ -342,6 +343,7 @@ void MantaEnsureRegistration() PbRegister_flipUpdateNeighborRatio(); PbRegister_particleSurfaceTurbulence(); PbRegister_debugCheckParts(); + PbRegister_applyViscosity(); PbRegister_markAsFixed(); PbRegister_texcoordInflow(); PbRegister_meshSmokeInflow(); diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 4a70cbbf41f..5261fbcee07 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -351,6 +351,10 @@ static bool lookup_property(BL::ID b_id, const string &name, float4 *r_value) return false; } + if (prop == NULL) { + return false; + } + PropertyType type = RNA_property_type(prop); int arraylen = RNA_property_array_length(&ptr, prop); diff --git a/intern/mantaflow/intern/MANTA_main.cpp b/intern/mantaflow/intern/MANTA_main.cpp index d49915e2452..d5d41e71f22 100644 --- a/intern/mantaflow/intern/MANTA_main.cpp +++ b/intern/mantaflow/intern/MANTA_main.cpp @@ -72,6 +72,7 @@ MANTA::MANTA(int *res, FluidModifierData *fmd) mUsingFractions = (fds->flags & FLUID_DOMAIN_USE_FRACTIONS) && mUsingLiquid; mUsingMesh = (fds->flags & FLUID_DOMAIN_USE_MESH) && mUsingLiquid; mUsingDiffusion = (fds->flags & FLUID_DOMAIN_USE_DIFFUSION) && mUsingLiquid; + mUsingViscosity = (fds->flags & FLUID_DOMAIN_USE_VISCOSITY) && mUsingLiquid; mUsingMVel = (fds->flags & FLUID_DOMAIN_USE_SPEED_VECTORS) && mUsingLiquid; mUsingGuiding = (fds->flags & FLUID_DOMAIN_USE_GUIDE); mUsingDrops = (fds->particle_type & FLUID_DOMAIN_PARTICLE_SPRAY) && mUsingLiquid; @@ -221,6 +222,10 @@ MANTA::MANTA(int *res, FluidModifierData *fmd) initSuccess &= initLiquidMesh(); } + if (mUsingViscosity) { + initSuccess &= initLiquidViscosity(); + } + if (mUsingDiffusion) { initSuccess &= initCurvature(); } @@ -440,6 +445,17 @@ bool MANTA::initLiquidMesh(FluidModifierData *fmd) return runPythonString(pythonCommands); } +bool MANTA::initLiquidViscosity(FluidModifierData *fmd) +{ + vector pythonCommands; + string tmpString = fluid_variables_viscosity + fluid_solver_viscosity + liquid_alloc_viscosity; + string finalString = parseScript(tmpString, fmd); + pythonCommands.push_back(finalString); + + mUsingViscosity = true; + return runPythonString(pythonCommands); +} + bool MANTA::initCurvature(FluidModifierData *fmd) { std::vector pythonCommands; @@ -871,8 +887,10 @@ void MANTA::initializeRNAMap(FluidModifierData *fmd) mRNAMap["CACHE_DIR"] = cacheDirectory; mRNAMap["COMPRESSION_OPENVDB"] = vdbCompressionMethod; mRNAMap["PRECISION_OPENVDB"] = vdbPrecisionHalf; - mRNAMap["CLIP_OPENVDB"] = to_string(fds->clipping); + mRNAMap["CLIP_OPENVDB"] = to_string(fds->clipping); mRNAMap["PP_PARTICLE_MAXIMUM"] = to_string(fds->sys_particle_maximum); + mRNAMap["USING_VISCOSITY"] = getBooleanString(fds->flags & FLUID_DOMAIN_USE_VISCOSITY); + mRNAMap["VISCOSITY_VALUE"] = to_string(fds->viscosity_value); /* Fluid object names. */ mRNAMap["NAME_FLAGS"] = FLUID_NAME_FLAGS; @@ -1728,6 +1746,7 @@ bool MANTA::exportLiquidScript(FluidModifierData *fmd) bool guiding = fds->active_fields & FLUID_DOMAIN_ACTIVE_GUIDE; bool invel = fds->active_fields & FLUID_DOMAIN_ACTIVE_INVEL; bool outflow = fds->active_fields & FLUID_DOMAIN_ACTIVE_OUTFLOW; + bool viscosity = fds->flags & FLUID_DOMAIN_USE_VISCOSITY; string manta_script; @@ -1742,6 +1761,8 @@ bool MANTA::exportLiquidScript(FluidModifierData *fmd) manta_script += fluid_variables_particles + liquid_variables_particles; if (guiding) manta_script += fluid_variables_guiding; + if (viscosity) + manta_script += fluid_variables_viscosity; /* Solvers. */ manta_script += header_solvers + fluid_solver; @@ -1751,6 +1772,8 @@ bool MANTA::exportLiquidScript(FluidModifierData *fmd) manta_script += fluid_solver_particles; if (guiding) manta_script += fluid_solver_guiding; + if (viscosity) + manta_script += fluid_solver_viscosity; /* Grids. */ manta_script += header_grids + fluid_alloc + liquid_alloc; @@ -1768,6 +1791,8 @@ bool MANTA::exportLiquidScript(FluidModifierData *fmd) manta_script += fluid_alloc_invel; if (outflow) manta_script += fluid_alloc_outflow; + if (viscosity) + manta_script += liquid_alloc_viscosity; /* Domain init. */ manta_script += header_gridinit + liquid_init_phi; diff --git a/intern/mantaflow/intern/MANTA_main.h b/intern/mantaflow/intern/MANTA_main.h index 163b168e43d..7129a545243 100644 --- a/intern/mantaflow/intern/MANTA_main.h +++ b/intern/mantaflow/intern/MANTA_main.h @@ -67,6 +67,7 @@ struct MANTA { bool initColorsHigh(struct FluidModifierData *fmd = nullptr); bool initLiquid(FluidModifierData *fmd = nullptr); bool initLiquidMesh(FluidModifierData *fmd = nullptr); + bool initLiquidViscosity(FluidModifierData *fmd = nullptr); bool initObstacle(FluidModifierData *fmd = nullptr); bool initCurvature(FluidModifierData *fmd = nullptr); bool initGuiding(FluidModifierData *fmd = nullptr); @@ -757,6 +758,7 @@ struct MANTA { bool mUsingNoise; bool mUsingMesh; bool mUsingDiffusion; + bool mUsingViscosity; bool mUsingMVel; bool mUsingLiquid; bool mUsingSmoke; diff --git a/intern/mantaflow/intern/strings/fluid_script.h b/intern/mantaflow/intern/strings/fluid_script.h index 8c9025dd435..73e8552d260 100644 --- a/intern/mantaflow/intern/strings/fluid_script.h +++ b/intern/mantaflow/intern/strings/fluid_script.h @@ -79,6 +79,11 @@ const std::string fluid_solver_guiding = mantaMsg('Solver guiding')\n\ sg$ID$ = Solver(name='solver_guiding$ID$', gridSize=gs_sg$ID$)\n"; +const std::string fluid_solver_viscosity = + "\n\ +mantaMsg('Solver viscosity')\n\ +sv$ID$ = Solver(name='solver_viscosity$ID$', gridSize=gs_sv$ID$, dim=dim_s$ID$)\n"; + ////////////////////////////////////////////////////////////////////// // VARIABLES ////////////////////////////////////////////////////////////////////// @@ -133,7 +138,7 @@ end_frame_s$ID$ = $END_FRAME$\n\ \n\ # Fluid diffusion / viscosity\n\ domainSize_s$ID$ = $FLUID_DOMAIN_SIZE$ # longest domain side in meters\n\ -viscosity_s$ID$ = $FLUID_VISCOSITY$ / (domainSize_s$ID$*domainSize_s$ID$) # kinematic viscosity in m^2/s\n\ +kinViscosity_s$ID$ = $FLUID_VISCOSITY$ / (domainSize_s$ID$*domainSize_s$ID$) # kinematic viscosity in m^2/s\n\ \n\ # Factors to convert Blender units to Manta units\n\ ratioMetersToRes_s$ID$ = float(domainSize_s$ID$) / float(res_s$ID$) # [meters / cells]\n\ @@ -199,6 +204,10 @@ tau_sg$ID$ = 1.0\n\ sigma_sg$ID$ = 0.99/tau_sg$ID$\n\ theta_sg$ID$ = 1.0\n"; +const std::string fluid_variables_viscosity = + "\n\ +gs_sv$ID$ = vec3($RESX$*2, $RESY$*2, $RESZ$*2)\n"; + const std::string fluid_with_obstacle = "\n\ using_obstacle_s$ID$ = True\n"; diff --git a/intern/mantaflow/intern/strings/liquid_script.h b/intern/mantaflow/intern/strings/liquid_script.h index 8e0d113d680..c44727bd47e 100644 --- a/intern/mantaflow/intern/strings/liquid_script.h +++ b/intern/mantaflow/intern/strings/liquid_script.h @@ -40,6 +40,8 @@ radiusFactor_s$ID$ = $PARTICLE_RADIUS$\n\ using_mesh_s$ID$ = $USING_MESH$\n\ using_final_mesh_s$ID$ = $USING_IMPROVED_MESH$\n\ using_fractions_s$ID$ = $USING_FRACTIONS$\n\ +using_apic_s$ID$ = $USING_APIC$\n\ +using_viscosity_s$ID$ = $USING_VISCOSITY$\n\ fracThreshold_s$ID$ = $FRACTIONS_THRESHOLD$\n\ fracDistance_s$ID$ = $FRACTIONS_DISTANCE$\n\ flipRatio_s$ID$ = $FLIP_RATIO$\n\ @@ -51,7 +53,7 @@ smoothenNeg_s$ID$ = $MESH_SMOOTHEN_NEG$\n\ randomness_s$ID$ = $PARTICLE_RANDOMNESS$\n\ surfaceTension_s$ID$ = $LIQUID_SURFACE_TENSION$\n\ maxSysParticles_s$ID$ = $PP_PARTICLE_MAXIMUM$\n\ -using_apic_s$ID$ = $USING_APIC$\n"; +viscosityValue_s$ID$ = $VISCOSITY_VALUE$\n"; const std::string liquid_variables_particles = "\n\ @@ -135,6 +137,13 @@ liquid_mesh_dict_s$ID$ = { 'lMesh' : mesh_sm$ID$ }\n\ if using_speedvectors_s$ID$:\n\ liquid_meshvel_dict_s$ID$ = { 'lVelMesh' : mVel_mesh$ID$ }\n"; +const std::string liquid_alloc_viscosity = + "\n\ +# Viscosity grids\n\ +volumes_s$ID$ = sv$ID$.create(RealGrid)\n\ +viscosity_s$ID$ = s$ID$.create(RealGrid)\n\ +viscosity_s$ID$.setConst(viscosityValue_s$ID$)\n"; + const std::string liquid_alloc_curvature = "\n\ mantaMsg('Liquid alloc curvature')\n\ @@ -306,7 +315,7 @@ def liquid_step_$ID$():\n\ if using_diffusion_s$ID$:\n\ mantaMsg('Viscosity')\n\ # diffusion param for solve = const * dt / dx^2\n\ - alphaV = viscosity_s$ID$ * s$ID$.timestep * float(res_s$ID$*res_s$ID$)\n\ + alphaV = kinViscosity_s$ID$ * s$ID$.timestep * float(res_s$ID$*res_s$ID$)\n\ setWallBcs(flags=flags_s$ID$, vel=vel_s$ID$, obvel=None if using_fractions_s$ID$ else obvel_s$ID$, phiObs=phiObs_s$ID$, fractions=fractions_s$ID$)\n\ cgSolveDiffusion(flags_s$ID$, vel_s$ID$, alphaV)\n\ \n\ @@ -315,7 +324,11 @@ def liquid_step_$ID$():\n\ curvature_s$ID$.clamp(-1.0, 1.0)\n\ \n\ setWallBcs(flags=flags_s$ID$, vel=vel_s$ID$, obvel=None if using_fractions_s$ID$ else obvel_s$ID$, phiObs=phiObs_s$ID$, fractions=fractions_s$ID$)\n\ + if using_viscosity_s$ID$:\n\ + viscosity_s$ID$.setConst(viscosityValue_s$ID$)\n\ + applyViscosity(flags=flags_s$ID$, phi=phi_s$ID$, vel=vel_s$ID$, volumes=volumes_s$ID$, viscosity=viscosity_s$ID$)\n\ \n\ + setWallBcs(flags=flags_s$ID$, vel=vel_s$ID$, obvel=None if using_fractions_s$ID$ else obvel_s$ID$, phiObs=phiObs_s$ID$, fractions=fractions_s$ID$)\n\ if using_guiding_s$ID$:\n\ mantaMsg('Guiding and pressure')\n\ PD_fluid_guiding(vel=vel_s$ID$, velT=velT_s$ID$, flags=flags_s$ID$, phi=phi_s$ID$, curv=curvature_s$ID$, surfTens=surfaceTension_s$ID$, fractions=fractions_s$ID$, weight=weightGuide_s$ID$, blurRadius=beta_sg$ID$, pressure=pressure_s$ID$, tau=tau_sg$ID$, sigma=sigma_sg$ID$, theta=theta_sg$ID$, zeroPressureFixing=domainClosed_s$ID$)\n\ diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py index 3afe7a47028..5c3975ffb76 100644 --- a/release/scripts/startup/bl_ui/properties_physics_fluid.py +++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py @@ -1006,6 +1006,46 @@ class PHYSICS_PT_particles(PhysicButtonsPanel, Panel): split.operator("fluid.free_particles", text="Free Particles") +class PHYSICS_PT_viscosity(PhysicButtonsPanel, Panel): + bl_label = "Viscosity" + bl_parent_id = 'PHYSICS_PT_liquid' + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + + @classmethod + def poll(cls, context): + # Fluid viscosity only enabled for liquids + if not PhysicButtonsPanel.poll_liquid_domain(context): + return False + + return (context.engine in cls.COMPAT_ENGINES) + + def draw_header(self, context): + md = context.fluid.domain_settings + domain = context.fluid.domain_settings + is_baking_any = domain.is_cache_baking_any + has_baked_any = domain.has_cache_baked_any + self.layout.enabled = not is_baking_any and not has_baked_any + self.layout.prop(md, "use_viscosity", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + domain = context.fluid.domain_settings + layout.active = domain.use_viscosity + + is_baking_any = domain.is_cache_baking_any + has_baked_any = domain.has_cache_baked_any + has_baked_data = domain.has_cache_baked_data + + flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) + flow.enabled = not is_baking_any and not has_baked_any and not has_baked_data + + col = flow.column(align=True) + col.prop(domain, "viscosity_value", text="Strength") + + class PHYSICS_PT_diffusion(PhysicButtonsPanel, Panel): bl_label = "Diffusion" bl_parent_id = 'PHYSICS_PT_liquid' @@ -1470,6 +1510,7 @@ classes = ( PHYSICS_PT_noise, PHYSICS_PT_fire, PHYSICS_PT_liquid, + PHYSICS_PT_viscosity, PHYSICS_PT_diffusion, PHYSICS_PT_particles, PHYSICS_PT_mesh, diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 7ceb52a55e5..65af1bccd80 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -514,6 +514,7 @@ geometry_node_categories = [ NodeItem("GeometryNodePointDistribute"), NodeItem("GeometryNodePointInstance"), NodeItem("GeometryNodePointSeparate"), + NodeItem("GeometryNodeRotatePoints"), ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ NodeItem("ShaderNodeMapRange"), diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 1ed4d1183a1..06a11fc8d1d 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 8 +#define BLENDER_FILE_SUBVERSION 9 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index e18f46c9988..89a51afede9 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1356,6 +1356,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_ATTRIBUTE_COLOR_RAMP 1013 #define GEO_NODE_POINT_SEPARATE 1014 #define GEO_NODE_ATTRIBUTE_COMPARE 1015 +#define GEO_NODE_ROTATE_POINTS 1016 /** \} */ diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index aaed2649ad9..bd2ac3da21b 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -635,9 +635,6 @@ void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(struct Mesh *mesh); void BKE_sculpt_sync_face_sets_visibility_to_grids(struct Mesh *mesh, struct SubdivCCG *subdiv_ccg); -/* Ensures that a Face Set data-layers exists. If it does not, it creates one respecting the - * visibility stored in the vertices of the mesh. If it does, it copies the visibility from the - * mesh to the Face Sets. */ void BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(struct Mesh *mesh); bool BKE_sculptsession_use_pbvh_draw(const struct Object *ob, const struct View3D *v3d); diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 1bdc2a5dfd2..b93e6148b86 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -5011,6 +5011,9 @@ void BKE_fluid_modifier_copy(const struct FluidModifierData *fmd, tfds->sys_particle_maximum = fds->sys_particle_maximum; tfds->simulation_method = fds->simulation_method; + /* viscosity options */ + tfds->viscosity_value = fds->viscosity_value; + /* diffusion options*/ tfds->surface_tension = fds->surface_tension; tfds->viscosity_base = fds->viscosity_base; diff --git a/source/blender/blenkernel/intern/multires_reshape_ccg.c b/source/blender/blenkernel/intern/multires_reshape_ccg.c index 55f7766c878..aa003909bb0 100644 --- a/source/blender/blenkernel/intern/multires_reshape_ccg.c +++ b/source/blender/blenkernel/intern/multires_reshape_ccg.c @@ -61,8 +61,24 @@ bool multires_reshape_assign_final_coords_from_ccg(const MultiresReshapeContext sizeof(float[3])); if (reshape_level_key.has_mask) { - BLI_assert(grid_element.mask != NULL); - *grid_element.mask = *CCG_grid_elem_mask(&reshape_level_key, ccg_grid, x, y); + /* Assert about a non-NULL `grid_element.mask` may fail here, this code may be called + * from cleanup code during COW evaluation phase by depsgraph (e.g. + * `object_update_from_subsurf_ccg` call in `BKE_object_free_derived_caches`). + * + * `reshape_level_key.has_mask` is ultimately set from MultiRes modifier apply code + * (through `multires_as_ccg` -> `multires_ccg_settings_init`), when object is in sculpt + * mode only, and there is matching loop cdlayer. + * + * `grid_element.mask` is directly set from existing matching loop cdlayer during + * initialization of `MultiresReshapeContext` struct. + * + * Since ccg data is preserved during undos, we may end up with a state where there is no + * mask data in mesh loops' cdlayer, while ccg's `has_mask` is still set to true. + */ + // BLI_assert(grid_element.mask != NULL); + if (grid_element.mask != NULL) { + *grid_element.mask = *CCG_grid_elem_mask(&reshape_level_key, ccg_grid, x, y); + } } } } diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 676d0bf9385..5d3994068ec 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -4744,6 +4744,7 @@ static void registerGeometryNodes(void) register_node_type_geo_join_geometry(); register_node_type_geo_attribute_mix(); register_node_type_geo_attribute_color_ramp(); + register_node_type_geo_rotate_points(); } static void registerFunctionNodes(void) diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index d726a4b1e37..7c2fc40d537 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1596,7 +1596,7 @@ static void sculpt_update_object(Depsgraph *depsgraph, Scene *scene = DEG_get_input_scene(depsgraph); Sculpt *sd = scene->toolsettings->sculpt; SculptSession *ss = ob->sculpt; - Mesh *me = BKE_object_get_original_mesh(ob); + const Mesh *me = BKE_object_get_original_mesh(ob); MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); const bool use_face_sets = (ob->mode & OB_MODE_SCULPT) != 0; @@ -1612,20 +1612,13 @@ static void sculpt_update_object(Depsgraph *depsgraph, if (need_mask) { if (mmd == NULL) { - if (!CustomData_has_layer(&me->vdata, CD_PAINT_MASK)) { - BKE_sculpt_mask_layers_ensure(ob, NULL); - } + BLI_assert(CustomData_has_layer(&me->vdata, CD_PAINT_MASK)); } else { - if (!CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK)) { - BKE_sculpt_mask_layers_ensure(ob, mmd); - } + BLI_assert(CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK)); } } - /* tessfaces aren't used and will become invalid */ - BKE_mesh_tessface_clear(me); - ss->shapekey_active = (mmd == NULL) ? BKE_keyblock_from_object(ob) : NULL; /* NOTE: Weight pPaint require mesh info for loop lookup, but it never uses multires code path, @@ -1660,12 +1653,7 @@ static void sculpt_update_object(Depsgraph *depsgraph, /* Sculpt Face Sets. */ if (use_face_sets) { - if (!CustomData_has_layer(&me->pdata, CD_SCULPT_FACE_SETS)) { - /* By checking here if the data-layer already exist this avoids copying the visibility from - * the mesh and looping over all vertices on every sculpt editing operation, using this - * function only the first time the Face Sets data-layer needs to be created. */ - BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(me); - } + BLI_assert(CustomData_has_layer(&me->pdata, CD_SCULPT_FACE_SETS)); ss->face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS); } else { @@ -1928,6 +1916,10 @@ static bool check_sculpt_object_deformed(Object *object, const bool for_construc return deformed; } +/** + * Ensures that a Face Set data-layers exists. If it does not, it creates one respecting the + * visibility stored in the vertices of the mesh. If it does, it copies the visibility from the + * mesh to the Face Sets. */ void BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(Mesh *mesh) { const int face_sets_default_visible_id = 1; diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 8a85278a931..63ef436d8e2 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1430,18 +1430,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "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, 292, 9)) { FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { @@ -1474,5 +1463,32 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Ensure that new viscosity strength field is initialized correctly. */ + if (!DNA_struct_elem_find(fd->filesdna, "FluidModifierData", "float", "viscosity_value")) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Fluid) { + FluidModifierData *fmd = (FluidModifierData *)md; + if (fmd->domain != NULL) { + fmd->domain->viscosity_value = 0.05; + } + } + } + } + } + } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "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 f1572c563bf..b9f09e0326d 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -820,6 +820,12 @@ void blo_do_versions_userdef(UserDef *userdef) userdef->uiflag &= ~USER_UIFLAG_UNUSED_3; } + if (!USER_VERSION_ATLEAST(292, 9)) { + if (BLI_listbase_is_empty(&userdef->asset_libraries)) { + BKE_preferences_asset_library_default_add(userdef); + } + } + /** * Versioning code until next subversion bump goes here. * @@ -831,9 +837,6 @@ void blo_do_versions_userdef(UserDef *userdef) */ { /* Keep this block, even when empty. */ - if (BLI_listbase_is_empty(&userdef->asset_libraries)) { - BKE_preferences_asset_library_default_add(userdef); - } } LISTBASE_FOREACH (bTheme *, btheme, &userdef->themes) { diff --git a/source/blender/compositor/nodes/COM_ColorExposureNode.cpp b/source/blender/compositor/nodes/COM_ColorExposureNode.cpp index 10738fcfe47..cd0285ac373 100644 --- a/source/blender/compositor/nodes/COM_ColorExposureNode.cpp +++ b/source/blender/compositor/nodes/COM_ColorExposureNode.cpp @@ -26,7 +26,7 @@ ExposureNode::ExposureNode(bNode *editorNode) : Node(editorNode) } void ExposureNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const + const CompositorContext & /*context*/) const { ExposureOperation *operation = new ExposureOperation(); converter.addOperation(operation); diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c index ba03cee8149..449f2cd9606 100644 --- a/source/blender/draw/intern/draw_instance_data.c +++ b/source/blender/draw/intern/draw_instance_data.c @@ -633,6 +633,10 @@ static bool drw_uniform_property_lookup(ID *id, const char *name, float r_data[4 return false; } + if (prop == NULL) { + return false; + } + PropertyType type = RNA_property_type(prop); int arraylen = RNA_property_array_length(&ptr, prop); diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index 1e87a940a7d..54e2b3ba501 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -52,9 +52,6 @@ void ED_spacedata_id_remap(struct ScrArea *area, struct ID *old_id, struct ID *new_id); -void ED_OT_lib_id_load_custom_preview(struct wmOperatorType *ot); -void ED_OT_lib_id_generate_preview(struct wmOperatorType *ot); - void ED_operatortypes_edutils(void); /* ************** XXX OLD CRUFT WARNING ************* */ diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 7713efd1c78..d47d9c5e874 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1626,10 +1626,10 @@ static uiButExtraOpIcon *ui_but_extra_operator_icon_add_ptr(uiBut *but, extra_op_icon->icon = (BIFIconID)icon; extra_op_icon->optype_params = MEM_callocN(sizeof(*extra_op_icon->optype_params), - "uiButExtraOpIcon.optype_hook"); + "uiButExtraOpIcon.optype_params"); extra_op_icon->optype_params->optype = optype; extra_op_icon->optype_params->opptr = MEM_callocN(sizeof(*extra_op_icon->optype_params->opptr), - "uiButExtraOpIcon.optype_hook.opptr"); + "uiButExtraOpIcon.optype_params.opptr"); WM_operator_properties_create_ptr(extra_op_icon->optype_params->opptr, extra_op_icon->optype_params->optype); extra_op_icon->optype_params->opcontext = opcontext; diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index f134038918d..8349577ff04 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -1392,7 +1392,7 @@ static void template_id_name_button( UI_but_extra_operator_icon_add(but, template_ui->unlink_op, WM_OP_INVOKE_DEFAULT, ICON_X); } else if (!never_unlink) { - UI_but_extra_operator_icon_add(but, "ED_OT_lib_unlink", WM_OP_INVOKE_DEFAULT, ICON_X); + UI_but_extra_operator_icon_add(but, "ED_OT_lib_id_unlink", WM_OP_INVOKE_DEFAULT, ICON_X); } } @@ -1401,7 +1401,7 @@ static void template_id_name_button( if (add_extra_fake_user_icon && id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS))) { UI_but_extra_operator_icon_add(but, - "ED_OT_lib_fake_user_toggle", + "ED_OT_lib_id_fake_user_toggle", WM_OP_INVOKE_DEFAULT, ID_FAKE_USERS(id) ? ICON_FAKE_USER_ON : ICON_FAKE_USER_OFF); } diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 8768404d74f..ca0b9aa93b7 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -3138,6 +3138,8 @@ static int screen_maximize_area_exec(bContext *C, wmOperator *op) ScrArea *area = NULL; const bool hide_panels = RNA_boolean_get(op->ptr, "use_hide_panels"); + BLI_assert(!screen->temp); + /* search current screen for 'fullscreen' areas */ /* prevents restoring info header, when mouse is over it */ LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) { @@ -3169,11 +3171,14 @@ static int screen_maximize_area_exec(bContext *C, wmOperator *op) static bool screen_maximize_area_poll(bContext *C) { + const wmWindow *win = CTX_wm_window(C); const bScreen *screen = CTX_wm_screen(C); const ScrArea *area = CTX_wm_area(C); return ED_operator_areaactive(C) && /* Don't allow maximizing global areas but allow minimizing from them. */ - ((screen->state != SCREENNORMAL) || !ED_area_is_global(area)); + ((screen->state != SCREENNORMAL) || !ED_area_is_global(area)) && + /* Don't change temporary screens. */ + !WM_window_is_temp_screen(win); } static void SCREEN_OT_screen_full_area(wmOperatorType *ot) @@ -5501,16 +5506,6 @@ void ED_operatortypes_screen(void) /* new/delete */ WM_operatortype_append(SCREEN_OT_new); WM_operatortype_append(SCREEN_OT_delete); - - /* tools shared by more space types */ - WM_operatortype_append(ED_OT_undo); - WM_operatortype_append(ED_OT_undo_push); - WM_operatortype_append(ED_OT_redo); - WM_operatortype_append(ED_OT_undo_redo); - WM_operatortype_append(ED_OT_undo_history); - - WM_operatortype_append(ED_OT_lib_id_load_custom_preview); - WM_operatortype_append(ED_OT_lib_id_generate_preview); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 51cd1ad0b9f..96e130c0283 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1190,25 +1190,6 @@ void SCULPT_floodfill_free(SculptFloodFill *flood) * * \{ */ -/* Check if there are any active modifiers in stack. - * Used for flushing updates at enter/exit sculpt mode. */ -static bool sculpt_has_active_modifiers(Scene *scene, Object *ob) -{ - ModifierData *md; - VirtualModifierData virtualModifierData; - - md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); - - /* Exception for shape keys because we can edit those. */ - for (; md; md = md->next) { - if (BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { - return true; - } - } - - return false; -} - static bool sculpt_tool_needs_original(const char sculpt_tool) { return ELEM(sculpt_tool, @@ -8655,7 +8636,7 @@ static void sculpt_init_session(Depsgraph *depsgraph, Scene *scene, Object *ob) /* Here we can detect geometry that was just added to Sculpt Mode as it has the * SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */ /* In sculpt mode all geometry that is assigned to SCULPT_FACE_SET_NONE is considered as not - * initialized, which is used is some operators that modify the mesh topology to preform certain + * initialized, which is used is some operators that modify the mesh topology to perform certain * actions in the new polys. After these operations are finished, all polys should have a valid * face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their visibility * correctly. */ @@ -8669,23 +8650,6 @@ static void sculpt_init_session(Depsgraph *depsgraph, Scene *scene, Object *ob) ss->face_sets[i] = new_face_set; } } - - /* Update the Face Sets visibility with the vertex visibility changes that may have been done - * outside Sculpt Mode */ - Mesh *mesh = ob->data; - BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(mesh); -} - -static int ed_object_sculptmode_flush_recalc_flag(Scene *scene, - Object *ob, - MultiresModifierData *mmd) -{ - int flush_recalc = 0; - /* Multires in sculpt mode could have different from object mode subdivision level. */ - flush_recalc |= mmd && mmd->sculptlvl != mmd->lvl; - /* If object has got active modifiers, its dm could be different in sculpt mode. */ - flush_recalc |= sculpt_has_active_modifiers(scene, ob); - return flush_recalc; } void ED_object_sculptmode_enter_ex(Main *bmain, @@ -8703,13 +8667,6 @@ void ED_object_sculptmode_enter_ex(Main *bmain, MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); - const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd); - - if (flush_recalc) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - BKE_scene_graph_evaluated_ensure(depsgraph, bmain); - } - /* Create sculpt mode session data. */ if (ob->sculpt) { BKE_sculptsession_free(ob); @@ -8718,14 +8675,30 @@ void ED_object_sculptmode_enter_ex(Main *bmain, /* Copy the current mesh visibility to the Face Sets. */ BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(me); - sculpt_init_session(depsgraph, scene, ob); + /* Mask layer is required for Multires. */ + BKE_sculpt_mask_layers_ensure(ob, mmd); - /* Mask layer is required. */ - if (mmd) { - /* XXX, we could attempt to support adding mask data mid-sculpt mode (with multi-res) - * but this ends up being quite tricky (and slow). */ - BKE_sculpt_mask_layers_ensure(ob, mmd); - } + /* Tessfaces aren't used and will become invalid. */ + BKE_mesh_tessface_clear(me); + + /* We always need to flush updates from depsgraph here, since at the very least + * `BKE_sculpt_face_sets_ensure_from_base_mesh_visibility()` will have updated some data layer of + * the mesh. + * + * All known potential sources of updates: + * - Addition of, or changes to, the `CD_SCULPT_FACE_SETS` data layer + * (`BKE_sculpt_face_sets_ensure_from_base_mesh_visibility`). + * - Addition of a `CD_PAINT_MASK` data layer (`BKE_sculpt_mask_layers_ensure`). + * - Object has any active modifier (modifier stack can be different in Sculpt mode). + * - Multires: + * + Differences of subdiv levels between sculpt and object modes + * (`mmd->sculptlvl != mmd->lvl`). + * + Addition of a `CD_GRID_PAINT_MASK` data layer (`BKE_sculpt_mask_layers_ensure`). + */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + BKE_scene_graph_evaluated_ensure(depsgraph, bmain); + + sculpt_init_session(depsgraph, scene, ob); if (!(fabsf(ob->scale[0] - ob->scale[1]) < 1e-4f && fabsf(ob->scale[1] - ob->scale[2]) < 1e-4f)) { diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index af2aad14008..9677152cf7e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -921,7 +921,7 @@ SculptUndoNode *SCULPT_undo_get_first_node() return usculpt->nodes.first; } -static void sculpt_undo_alloc_and_store_hidden(PBVH *pbvh, SculptUndoNode *unode) +static size_t sculpt_undo_alloc_and_store_hidden(PBVH *pbvh, SculptUndoNode *unode) { PBVHNode *node = unode->node; BLI_bitmap **grid_hidden = BKE_pbvh_grid_hidden(pbvh); @@ -929,28 +929,34 @@ static void sculpt_undo_alloc_and_store_hidden(PBVH *pbvh, SculptUndoNode *unode int *grid_indices, totgrid; BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, NULL, NULL, NULL); - unode->grid_hidden = MEM_callocN(sizeof(*unode->grid_hidden) * totgrid, "unode->grid_hidden"); + size_t alloc_size = sizeof(*unode->grid_hidden) * (size_t)totgrid; + unode->grid_hidden = MEM_callocN(alloc_size, "unode->grid_hidden"); for (int i = 0; i < totgrid; i++) { if (grid_hidden[grid_indices[i]]) { unode->grid_hidden[i] = MEM_dupallocN(grid_hidden[grid_indices[i]]); + alloc_size += MEM_allocN_len(unode->grid_hidden[i]); } else { unode->grid_hidden[i] = NULL; } } + + return alloc_size; } /* Allocate node and initialize its default fields specific for the given undo type. * Will also add the node to the list in the undo step. */ static SculptUndoNode *sculpt_undo_alloc_node_type(Object *object, SculptUndoType type) { - SculptUndoNode *unode = MEM_callocN(sizeof(SculptUndoNode), "SculptUndoNode"); + const size_t alloc_size = sizeof(SculptUndoNode); + SculptUndoNode *unode = MEM_callocN(alloc_size, "SculptUndoNode"); BLI_strncpy(unode->idname, object->id.name, sizeof(unode->idname)); unode->type = type; UndoSculpt *usculpt = sculpt_undo_get_nodes(); BLI_addtail(&usculpt->nodes, unode); + usculpt->undo_size += alloc_size; return unode; } @@ -975,7 +981,12 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt { UndoSculpt *usculpt = sculpt_undo_get_nodes(); SculptSession *ss = ob->sculpt; - int totvert, allvert, totgrid, maxgrid, gridsize, *grids; + int totvert = 0; + int allvert = 0; + int totgrid = 0; + int maxgrid = 0; + int gridsize = 0; + int *grids = NULL; SculptUndoNode *unode = sculpt_undo_alloc_node_type(ob, type); unode->node = node; @@ -986,39 +997,43 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt unode->totvert = totvert; } - else { - maxgrid = 0; - } - /* General TODO, fix count_alloc. */ switch (type) { - case SCULPT_UNDO_COORDS: - unode->co = MEM_callocN(sizeof(float[3]) * allvert, "SculptUndoNode.co"); - unode->no = MEM_callocN(sizeof(short[3]) * allvert, "SculptUndoNode.no"); + case SCULPT_UNDO_COORDS: { + size_t alloc_size = sizeof(*unode->co) * (size_t)allvert; + unode->co = MEM_callocN(alloc_size, "SculptUndoNode.co"); + usculpt->undo_size += alloc_size; - usculpt->undo_size = (sizeof(float[3]) + sizeof(short[3]) + sizeof(int)) * allvert; + /* FIXME: Should explain why this is allocated here, to be freed in + * `SCULPT_undo_push_end_ex()`? */ + alloc_size = sizeof(*unode->no) * (size_t)allvert; + unode->no = MEM_callocN(alloc_size, "SculptUndoNode.no"); + usculpt->undo_size += alloc_size; break; - case SCULPT_UNDO_HIDDEN: + } + case SCULPT_UNDO_HIDDEN: { if (maxgrid) { - sculpt_undo_alloc_and_store_hidden(ss->pbvh, unode); + usculpt->undo_size += sculpt_undo_alloc_and_store_hidden(ss->pbvh, unode); } else { unode->vert_hidden = BLI_BITMAP_NEW(allvert, "SculptUndoNode.vert_hidden"); + usculpt->undo_size += BLI_BITMAP_SIZE(allvert); } break; - case SCULPT_UNDO_MASK: - unode->mask = MEM_callocN(sizeof(float) * allvert, "SculptUndoNode.mask"); - - usculpt->undo_size += (sizeof(float) * sizeof(int)) * allvert; - + } + case SCULPT_UNDO_MASK: { + const size_t alloc_size = sizeof(*unode->mask) * (size_t)allvert; + unode->mask = MEM_callocN(alloc_size, "SculptUndoNode.mask"); + usculpt->undo_size += alloc_size; break; - case SCULPT_UNDO_COLOR: - unode->col = MEM_callocN(sizeof(MPropCol) * allvert, "SculptUndoNode.col"); - - usculpt->undo_size += (sizeof(MPropCol) * sizeof(int)) * allvert; - + } + case SCULPT_UNDO_COLOR: { + const size_t alloc_size = sizeof(*unode->col) * (size_t)allvert; + unode->col = MEM_callocN(alloc_size, "SculptUndoNode.col"); + usculpt->undo_size += alloc_size; break; + } case SCULPT_UNDO_DYNTOPO_BEGIN: case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: @@ -1033,16 +1048,24 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt unode->maxgrid = maxgrid; unode->totgrid = totgrid; unode->gridsize = gridsize; - unode->grids = MEM_callocN(sizeof(int) * totgrid, "SculptUndoNode.grids"); + + const size_t alloc_size = sizeof(*unode->grids) * (size_t)totgrid; + unode->grids = MEM_callocN(alloc_size, "SculptUndoNode.grids"); + usculpt->undo_size += alloc_size; } else { /* Regular mesh. */ unode->maxvert = ss->totvert; - unode->index = MEM_callocN(sizeof(int) * allvert, "SculptUndoNode.index"); + + const size_t alloc_size = sizeof(*unode->index) * (size_t)allvert; + unode->index = MEM_callocN(alloc_size, "SculptUndoNode.index"); + usculpt->undo_size += alloc_size; } if (ss->deform_modifiers_active) { - unode->orig_co = MEM_callocN(allvert * sizeof(*unode->orig_co), "undoSculpt orig_cos"); + const size_t alloc_size = sizeof(*unode->orig_co) * (size_t)allvert; + unode->orig_co = MEM_callocN(alloc_size, "undoSculpt orig_cos"); + usculpt->undo_size += alloc_size; } return unode; @@ -1364,6 +1387,7 @@ void SCULPT_undo_push_end_ex(const bool use_nested_undo) /* We don't need normals in the undo stack. */ for (unode = usculpt->nodes.first; unode; unode = unode->next) { if (unode->no) { + usculpt->undo_size -= MEM_allocN_len(unode->no); MEM_freeN(unode->no); unode->no = NULL; } diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c index e0f603eb3d1..78dc6eabbd2 100644 --- a/source/blender/editors/space_clip/clip_buttons.c +++ b/source/blender/editors/space_clip/clip_buttons.c @@ -104,17 +104,11 @@ void ED_clip_buttons_register(ARegionType *art) void uiTemplateMovieClip( uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, bool compact) { - PropertyRNA *prop; - PointerRNA clipptr; - MovieClip *clip; - uiLayout *row, *split; - uiBlock *block; - if (!ptr->data) { return; } - prop = RNA_struct_find_property(ptr, propname); + PropertyRNA *prop = RNA_struct_find_property(ptr, propname); if (!prop) { printf( "%s: property not found: %s.%s\n", __func__, RNA_struct_identifier(ptr->type), propname); @@ -129,8 +123,8 @@ void uiTemplateMovieClip( return; } - clipptr = RNA_property_pointer_get(ptr, prop); - clip = clipptr.data; + PointerRNA clipptr = RNA_property_pointer_get(ptr, prop); + MovieClip *clip = clipptr.data; uiLayoutSetContextPointer(layout, "edit_movieclip", &clipptr); @@ -149,20 +143,18 @@ void uiTemplateMovieClip( } if (clip) { - uiLayout *col; - - row = uiLayoutRow(layout, false); - block = uiLayoutGetBlock(row); + uiLayout *row = uiLayoutRow(layout, false); + uiBlock *block = uiLayoutGetBlock(row); uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("File Path:"), 0, 19, 145, 19, NULL, 0, 0, 0, 0, ""); row = uiLayoutRow(layout, false); - split = uiLayoutSplit(row, 0.0f, false); + uiLayout *split = uiLayoutSplit(row, 0.0f, false); row = uiLayoutRow(split, true); uiItemR(row, &clipptr, "filepath", 0, "", ICON_NONE); uiItemO(row, "", ICON_FILE_REFRESH, "clip.reload"); - col = uiLayoutColumn(layout, false); + uiLayout *col = uiLayoutColumn(layout, false); uiTemplateColorspaceSettings(col, &clipptr, "colorspace_settings"); } } @@ -171,17 +163,11 @@ void uiTemplateMovieClip( void uiTemplateTrack(uiLayout *layout, PointerRNA *ptr, const char *propname) { - PropertyRNA *prop; - PointerRNA scopesptr; - uiBlock *block; - uiLayout *col; - MovieClipScopes *scopes; - if (!ptr->data) { return; } - prop = RNA_struct_find_property(ptr, propname); + PropertyRNA *prop = RNA_struct_find_property(ptr, propname); if (!prop) { printf( "%s: property not found: %s.%s\n", __func__, RNA_struct_identifier(ptr->type), propname); @@ -196,8 +182,8 @@ void uiTemplateTrack(uiLayout *layout, PointerRNA *ptr, const char *propname) return; } - scopesptr = RNA_property_pointer_get(ptr, prop); - scopes = (MovieClipScopes *)scopesptr.data; + PointerRNA scopesptr = RNA_property_pointer_get(ptr, prop); + MovieClipScopes *scopes = (MovieClipScopes *)scopesptr.data; if (scopes->track_preview_height < UI_UNIT_Y) { scopes->track_preview_height = UI_UNIT_Y; @@ -206,8 +192,8 @@ void uiTemplateTrack(uiLayout *layout, PointerRNA *ptr, const char *propname) scopes->track_preview_height = UI_UNIT_Y * 20; } - col = uiLayoutColumn(layout, true); - block = uiLayoutGetBlock(col); + uiLayout *col = uiLayoutColumn(layout, true); + uiBlock *block = uiLayoutGetBlock(col); uiDefBut(block, UI_BTYPE_TRACK_PREVIEW, @@ -319,8 +305,6 @@ static void marker_block_handler(bContext *C, void *arg_cb, int event) } else if (event == B_MARKER_PAT_DIM) { float dim[2], pat_dim[2], pat_min[2], pat_max[2]; - float scale_x, scale_y; - int a; BKE_tracking_marker_pattern_minmax(cb->marker, pat_min, pat_max); @@ -329,10 +313,10 @@ static void marker_block_handler(bContext *C, void *arg_cb, int event) dim[0] = cb->marker_pat[0] / width; dim[1] = cb->marker_pat[1] / height; - scale_x = dim[0] / pat_dim[0]; - scale_y = dim[1] / pat_dim[1]; + float scale_x = dim[0] / pat_dim[0]; + float scale_y = dim[1] / pat_dim[1]; - for (a = 0; a < 4; a++) { + for (int a = 0; a < 4; a++) { cb->marker->pattern_corners[a][0] *= scale_x; cb->marker->pattern_corners[a][1] *= scale_y; } @@ -415,23 +399,11 @@ void uiTemplateMarker(uiLayout *layout, PointerRNA *trackptr, bool compact) { - PropertyRNA *prop; - uiBlock *block; - uiBut *bt; - PointerRNA clipptr; - MovieClip *clip; - MovieClipUser *user; - MovieTrackingTrack *track; - MovieTrackingMarker *marker; - MarkerUpdateCb *cb; - const char *tip; - float pat_min[2], pat_max[2]; - if (!ptr->data) { return; } - prop = RNA_struct_find_property(ptr, propname); + PropertyRNA *prop = RNA_struct_find_property(ptr, propname); if (!prop) { printf( "%s: property not found: %s.%s\n", __func__, RNA_struct_identifier(ptr->type), propname); @@ -446,15 +418,15 @@ void uiTemplateMarker(uiLayout *layout, return; } - clipptr = RNA_property_pointer_get(ptr, prop); - clip = (MovieClip *)clipptr.data; - user = userptr->data; - track = trackptr->data; + PointerRNA clipptr = RNA_property_pointer_get(ptr, prop); + MovieClip *clip = (MovieClip *)clipptr.data; + MovieClipUser *user = userptr->data; + MovieTrackingTrack *track = trackptr->data; int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, user->framenr); - marker = BKE_tracking_marker_get(track, clip_framenr); + MovieTrackingMarker *marker = BKE_tracking_marker_get(track, clip_framenr); - cb = MEM_callocN(sizeof(MarkerUpdateCb), "uiTemplateMarker update_cb"); + MarkerUpdateCb *cb = MEM_callocN(sizeof(MarkerUpdateCb), "uiTemplateMarker update_cb"); cb->compact = compact; cb->clip = clip; cb->user = user; @@ -464,7 +436,8 @@ void uiTemplateMarker(uiLayout *layout, cb->framenr = user->framenr; if (compact) { - block = uiLayoutGetBlock(layout); + const char *tip; + uiBlock *block = uiLayoutGetBlock(layout); if (cb->marker_flag & MARKER_DISABLED) { tip = TIP_("Marker is disabled at current frame"); @@ -473,34 +446,32 @@ void uiTemplateMarker(uiLayout *layout, tip = TIP_("Marker is enabled at current frame"); } - bt = uiDefIconButBitI(block, - UI_BTYPE_TOGGLE_N, - MARKER_DISABLED, - 0, - ICON_HIDE_OFF, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y, - &cb->marker_flag, - 0, - 0, - 1, - 0, - tip); + uiBut *bt = uiDefIconButBitI(block, + UI_BTYPE_TOGGLE_N, + MARKER_DISABLED, + 0, + ICON_HIDE_OFF, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + &cb->marker_flag, + 0, + 0, + 1, + 0, + tip); UI_but_funcN_set(bt, marker_update_cb, cb, NULL); UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE); } else { - int width, height, step, digits; - float pat_dim[2], search_dim[2], search_pos[2]; - uiLayout *col; + int width, height; BKE_movieclip_get_size(clip, user, &width, &height); if (track->flag & TRACK_LOCKED) { uiLayoutSetActive(layout, false); - block = uiLayoutAbsoluteBlock(layout); + uiBlock *block = uiLayoutAbsoluteBlock(layout); uiDefBut(block, UI_BTYPE_LABEL, 0, @@ -519,8 +490,8 @@ void uiTemplateMarker(uiLayout *layout, return; } - step = 100; - digits = 2; + float pat_min[2], pat_max[2]; + float pat_dim[2], search_dim[2], search_pos[2]; BKE_tracking_marker_pattern_minmax(marker, pat_min, pat_max); @@ -538,10 +509,14 @@ void uiTemplateMarker(uiLayout *layout, cb->marker_flag = marker->flag; - block = uiLayoutAbsoluteBlock(layout); + uiBlock *block = uiLayoutAbsoluteBlock(layout); UI_block_func_handle_set(block, marker_block_handler, cb); UI_block_funcN_set(block, marker_update_cb, cb, NULL); + const char *tip; + int step = 100; + int digits = 2; + if (cb->marker_flag & MARKER_DISABLED) { tip = TIP_("Marker is disabled at current frame"); } @@ -565,7 +540,7 @@ void uiTemplateMarker(uiLayout *layout, 0, tip); - col = uiLayoutColumn(layout, true); + uiLayout *col = uiLayoutColumn(layout, true); uiLayoutSetActive(col, (cb->marker_flag & MARKER_DISABLED) == 0); block = uiLayoutAbsoluteBlock(col); @@ -585,20 +560,20 @@ void uiTemplateMarker(uiLayout *layout, 0, 0, ""); - bt = uiDefButF(block, - UI_BTYPE_NUM, - B_MARKER_POS, - IFACE_("X:"), - 0.5 * UI_UNIT_X, - 9 * UI_UNIT_Y, - 7.25 * UI_UNIT_X, - UI_UNIT_Y, - &cb->marker_pos[0], - -10 * width, - 10.0 * width, - 0, - 0, - TIP_("X-position of marker at frame in screen coordinates")); + uiBut *bt = uiDefButF(block, + UI_BTYPE_NUM, + B_MARKER_POS, + IFACE_("X:"), + 0.5 * UI_UNIT_X, + 9 * UI_UNIT_Y, + 7.25 * UI_UNIT_X, + UI_UNIT_Y, + &cb->marker_pos[0], + -10 * width, + 10.0 * width, + 0, + 0, + TIP_("X-position of marker at frame in screen coordinates")); UI_but_number_step_size_set(bt, step); UI_but_number_precision_set(bt, digits); bt = uiDefButF(block, @@ -802,21 +777,11 @@ void uiTemplateMovieclipInformation(uiLayout *layout, const char *propname, PointerRNA *userptr) { - PropertyRNA *prop; - PointerRNA clipptr; - MovieClip *clip; - MovieClipUser *user; - uiLayout *col; - char str[1024]; - int width, height, framenr; - ImBuf *ibuf; - size_t ofs = 0; - if (!ptr->data) { return; } - prop = RNA_struct_find_property(ptr, propname); + PropertyRNA *prop = RNA_struct_find_property(ptr, propname); if (!prop) { printf( "%s: property not found: %s.%s\n", __func__, RNA_struct_identifier(ptr->type), propname); @@ -831,17 +796,21 @@ void uiTemplateMovieclipInformation(uiLayout *layout, return; } - clipptr = RNA_property_pointer_get(ptr, prop); - clip = (MovieClip *)clipptr.data; - user = userptr->data; + PointerRNA clipptr = RNA_property_pointer_get(ptr, prop); + MovieClip *clip = (MovieClip *)clipptr.data; + MovieClipUser *user = userptr->data; - col = uiLayoutColumn(layout, false); + uiLayout *col = uiLayoutColumn(layout, false); uiLayoutSetAlignment(col, UI_LAYOUT_ALIGN_RIGHT); - ibuf = BKE_movieclip_get_ibuf_flag(clip, user, clip->flag, MOVIECLIP_CACHE_SKIP); + ImBuf *ibuf = BKE_movieclip_get_ibuf_flag(clip, user, clip->flag, MOVIECLIP_CACHE_SKIP); + int width, height; /* Display frame dimensions, channels number and byffer type. */ BKE_movieclip_get_size(clip, user, &width, &height); + + char str[1024]; + size_t ofs = 0; ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, TIP_("%d x %d"), width, height); if (ibuf) { @@ -882,7 +851,7 @@ void uiTemplateMovieclipInformation(uiLayout *layout, uiItemL(col, str, ICON_NONE); /* Display current frame number. */ - framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, user->framenr); + int framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, user->framenr); if (framenr <= clip->len) { BLI_snprintf(str, sizeof(str), TIP_("Frame: %d / %d"), framenr, clip->len); } diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index b919a30e6cd..6b74b344375 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -1142,7 +1142,9 @@ void ED_fileselect_exit(wmWindowManager *wm, Scene *owner_scene, SpaceFile *sfil return; } if (sfile->op) { - wmWindow *temp_win = WM_window_is_temp_screen(wm->winactive) ? wm->winactive : NULL; + wmWindow *temp_win = (wm->winactive && WM_window_is_temp_screen(wm->winactive)) ? + wm->winactive : + NULL; if (temp_win) { int win_size[2]; bool is_maximized; diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 0ce31707204..409327b0d1c 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -3260,6 +3260,25 @@ static void node_geometry_buts_attribute_color_ramp(uiLayout *layout, uiTemplateColorRamp(layout, ptr, "color_ramp", 0); } +static void node_geometry_buts_rotate_points(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + NodeGeometryRotatePoints *storage = (NodeGeometryRotatePoints *)((bNode *)ptr->data)->storage; + + uiItemR(layout, ptr, "type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, ptr, "space", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE); + + uiLayout *col = uiLayoutColumn(layout, false); + if (storage->type == GEO_NODE_ROTATE_POINTS_TYPE_AXIS_ANGLE) { + uiItemR(col, ptr, "input_type_axis", DEFAULT_FLAGS, IFACE_("Axis"), ICON_NONE); + uiItemR(col, ptr, "input_type_angle", DEFAULT_FLAGS, IFACE_("Angle"), ICON_NONE); + } + else { + uiItemR(col, ptr, "input_type_rotation", DEFAULT_FLAGS, IFACE_("Rotation"), ICON_NONE); + } +} + static void node_geometry_set_butfunc(bNodeType *ntype) { switch (ntype->type) { @@ -3296,6 +3315,9 @@ static void node_geometry_set_butfunc(bNodeType *ntype) case GEO_NODE_ATTRIBUTE_COLOR_RAMP: ntype->draw_buttons = node_geometry_buts_attribute_color_ramp; break; + case GEO_NODE_ROTATE_POINTS: + ntype->draw_buttons = node_geometry_buts_rotate_points; + break; } } diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 5d2584c566d..4b1335cf047 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -29,50 +29,34 @@ #include "DNA_armature_types.h" #include "DNA_mesh_types.h" -#include "DNA_object_types.h" -#include "DNA_packedFile_types.h" -#include "DNA_scene_types.h" -#include "DNA_screen_types.h" -#include "DNA_space_types.h" -#include "BLI_fileops.h" #include "BLI_listbase.h" #include "BLI_path_util.h" #include "BLI_string.h" -#include "BLI_utildefines.h" #include "BLT_translation.h" -#include "BKE_context.h" #include "BKE_global.h" -#include "BKE_icons.h" -#include "BKE_layer.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_multires.h" #include "BKE_object.h" #include "BKE_packedFile.h" #include "BKE_paint.h" -#include "BKE_report.h" #include "BKE_screen.h" #include "BKE_undo_system.h" -#include "BKE_workspace.h" #include "DEG_depsgraph.h" #include "ED_armature.h" #include "ED_image.h" #include "ED_mesh.h" -#include "ED_node.h" #include "ED_object.h" -#include "ED_outliner.h" #include "ED_paint.h" -#include "ED_render.h" #include "ED_space_api.h" #include "ED_util.h" #include "GPU_immediate.h" -#include "GPU_state.h" #include "UI_interface.h" #include "UI_resources.h" @@ -484,104 +468,3 @@ void ED_spacedata_id_remap(struct ScrArea *area, struct SpaceLink *sl, ID *old_i st->id_remap(area, sl, old_id, new_id); } } - -static bool lib_id_preview_editing_poll(bContext *C) -{ - const PointerRNA idptr = CTX_data_pointer_get(C, "id"); - BLI_assert(!idptr.data || RNA_struct_is_ID(idptr.type)); - - const ID *id = idptr.data; - if (!id) { - return false; - } - if (ID_IS_LINKED(id)) { - CTX_wm_operator_poll_msg_set(C, TIP_("Can't edit external library data")); - return false; - } - if (ID_IS_OVERRIDE_LIBRARY(id)) { - CTX_wm_operator_poll_msg_set(C, TIP_("Can't edit previews of overridden library data")); - return false; - } - if (!BKE_previewimg_id_get_p(id)) { - CTX_wm_operator_poll_msg_set(C, TIP_("Data-block does not support previews")); - return false; - } - - return true; -} - -static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op) -{ - char path[FILE_MAX]; - - RNA_string_get(op->ptr, "filepath", path); - - if (!BLI_is_file(path)) { - BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", path); - return OPERATOR_CANCELLED; - } - - PointerRNA idptr = CTX_data_pointer_get(C, "id"); - ID *id = idptr.data; - - BKE_previewimg_id_custom_set(id, path); - - WM_event_add_notifier(C, NC_ASSET, NULL); - - return OPERATOR_FINISHED; -} - -void ED_OT_lib_id_load_custom_preview(wmOperatorType *ot) -{ - ot->name = "Load Custom Preview"; - ot->description = "Choose an image to help identify the data-block visually"; - ot->idname = "ED_OT_lib_id_load_custom_preview"; - - /* api callbacks */ - ot->poll = lib_id_preview_editing_poll; - ot->exec = lib_id_load_custom_preview_exec; - ot->invoke = WM_operator_filesel; - - /* flags */ - ot->flag = OPTYPE_INTERNAL; - - WM_operator_properties_filesel(ot, - FILE_TYPE_FOLDER | FILE_TYPE_IMAGE, - FILE_SPECIAL, - FILE_OPENFILE, - WM_FILESEL_FILEPATH, - FILE_DEFAULTDISPLAY, - FILE_SORT_DEFAULT); -} - -static int lib_id_generate_preview_exec(bContext *C, wmOperator *UNUSED(op)) -{ - PointerRNA idptr = CTX_data_pointer_get(C, "id"); - ID *id = idptr.data; - - ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - - PreviewImage *preview = BKE_previewimg_id_get(id); - if (preview) { - BKE_previewimg_clear(preview); - } - UI_icon_render_id(C, NULL, id, true, true); - - WM_event_add_notifier(C, NC_ASSET, NULL); - - return OPERATOR_FINISHED; -} - -void ED_OT_lib_id_generate_preview(wmOperatorType *ot) -{ - ot->name = "Generate Preview"; - ot->description = "Create an automatic preview for the selected data-block"; - ot->idname = "ED_OT_lib_id_generate_preview"; - - /* api callbacks */ - ot->poll = lib_id_preview_editing_poll; - ot->exec = lib_id_generate_preview_exec; - - /* flags */ - ot->flag = OPTYPE_INTERNAL; -} diff --git a/source/blender/editors/util/ed_util_ops.c b/source/blender/editors/util/ed_util_ops.c index d8d1a64c1ee..88df20c5966 100644 --- a/source/blender/editors/util/ed_util_ops.c +++ b/source/blender/editors/util/ed_util_ops.c @@ -22,15 +22,22 @@ #include +#include "BLI_fileops.h" #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_icons.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_report.h" +#include "BLT_translation.h" + +#include "DNA_space_types.h" #include "DNA_windowmanager_types.h" +#include "ED_render.h" +#include "ED_undo.h" #include "ED_util.h" #include "RNA_access.h" @@ -40,7 +47,118 @@ #include "WM_api.h" #include "WM_types.h" -static int lib_fake_user_toggle_exec(bContext *C, wmOperator *op) +/* -------------------------------------------------------------------- */ +/** \name ID Previews + * \{ */ + +static bool lib_id_preview_editing_poll(bContext *C) +{ + const PointerRNA idptr = CTX_data_pointer_get(C, "id"); + BLI_assert(!idptr.data || RNA_struct_is_ID(idptr.type)); + + const ID *id = idptr.data; + if (!id) { + return false; + } + if (ID_IS_LINKED(id)) { + CTX_wm_operator_poll_msg_set(C, TIP_("Can't edit external library data")); + return false; + } + if (ID_IS_OVERRIDE_LIBRARY(id)) { + CTX_wm_operator_poll_msg_set(C, TIP_("Can't edit previews of overridden library data")); + return false; + } + if (!BKE_previewimg_id_get_p(id)) { + CTX_wm_operator_poll_msg_set(C, TIP_("Data-block does not support previews")); + return false; + } + + return true; +} + +static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op) +{ + char path[FILE_MAX]; + + RNA_string_get(op->ptr, "filepath", path); + + if (!BLI_is_file(path)) { + BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", path); + return OPERATOR_CANCELLED; + } + + PointerRNA idptr = CTX_data_pointer_get(C, "id"); + ID *id = idptr.data; + + BKE_previewimg_id_custom_set(id, path); + + WM_event_add_notifier(C, NC_ASSET, NULL); + + return OPERATOR_FINISHED; +} + +static void ED_OT_lib_id_load_custom_preview(wmOperatorType *ot) +{ + ot->name = "Load Custom Preview"; + ot->description = "Choose an image to help identify the data-block visually"; + ot->idname = "ED_OT_lib_id_load_custom_preview"; + + /* api callbacks */ + ot->poll = lib_id_preview_editing_poll; + ot->exec = lib_id_load_custom_preview_exec; + ot->invoke = WM_operator_filesel; + + /* flags */ + ot->flag = OPTYPE_INTERNAL; + + WM_operator_properties_filesel(ot, + FILE_TYPE_FOLDER | FILE_TYPE_IMAGE, + FILE_SPECIAL, + FILE_OPENFILE, + WM_FILESEL_FILEPATH, + FILE_DEFAULTDISPLAY, + FILE_SORT_DEFAULT); +} + +static int lib_id_generate_preview_exec(bContext *C, wmOperator *UNUSED(op)) +{ + PointerRNA idptr = CTX_data_pointer_get(C, "id"); + ID *id = idptr.data; + + ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); + + PreviewImage *preview = BKE_previewimg_id_get(id); + if (preview) { + BKE_previewimg_clear(preview); + } + UI_icon_render_id(C, NULL, id, true, true); + + WM_event_add_notifier(C, NC_ASSET, NULL); + + return OPERATOR_FINISHED; +} + +static void ED_OT_lib_id_generate_preview(wmOperatorType *ot) +{ + ot->name = "Generate Preview"; + ot->description = "Create an automatic preview for the selected data-block"; + ot->idname = "ED_OT_lib_id_generate_preview"; + + /* api callbacks */ + ot->poll = lib_id_preview_editing_poll; + ot->exec = lib_id_generate_preview_exec; + + /* flags */ + ot->flag = OPTYPE_INTERNAL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Generic ID Operators + * \{ */ + +static int lib_id_fake_user_toggle_exec(bContext *C, wmOperator *op) { PropertyPointerRNA pprop; PointerRNA idptr = PointerRNA_NULL; @@ -74,21 +192,21 @@ static int lib_fake_user_toggle_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static void ED_OT_lib_fake_user_toggle(wmOperatorType *ot) +static void ED_OT_lib_id_fake_user_toggle(wmOperatorType *ot) { /* identifiers */ ot->name = "Toggle Fake User"; ot->description = "Save this data-block even if it has no users"; - ot->idname = "ED_OT_lib_fake_user_toggle"; + ot->idname = "ED_OT_lib_id_fake_user_toggle"; /* api callbacks */ - ot->exec = lib_fake_user_toggle_exec; + ot->exec = lib_id_fake_user_toggle_exec; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; } -static int lib_unlink_exec(bContext *C, wmOperator *op) +static int lib_id_unlink_exec(bContext *C, wmOperator *op) { PropertyPointerRNA pprop; PointerRNA idptr; @@ -112,20 +230,26 @@ static int lib_unlink_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static void ED_OT_lib_unlink(wmOperatorType *ot) +static void ED_OT_lib_id_unlink(wmOperatorType *ot) { /* identifiers */ ot->name = "Unlink Data-Block"; ot->description = "Remove a usage of a data-block, clearing the assignment"; - ot->idname = "ED_OT_lib_unlink"; + ot->idname = "ED_OT_lib_id_unlink"; /* api callbacks */ - ot->exec = lib_unlink_exec; + ot->exec = lib_id_unlink_exec; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name General editor utils. + * \{ */ + static int ed_flush_edits_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); @@ -147,9 +271,21 @@ static void ED_OT_flush_edits(wmOperatorType *ot) ot->flag = OPTYPE_INTERNAL; } +/** \} */ + void ED_operatortypes_edutils(void) { - WM_operatortype_append(ED_OT_lib_fake_user_toggle); - WM_operatortype_append(ED_OT_lib_unlink); + WM_operatortype_append(ED_OT_lib_id_load_custom_preview); + WM_operatortype_append(ED_OT_lib_id_generate_preview); + + WM_operatortype_append(ED_OT_lib_id_fake_user_toggle); + WM_operatortype_append(ED_OT_lib_id_unlink); + WM_operatortype_append(ED_OT_flush_edits); + + WM_operatortype_append(ED_OT_undo); + WM_operatortype_append(ED_OT_undo_push); + WM_operatortype_append(ED_OT_redo); + WM_operatortype_append(ED_OT_undo_redo); + WM_operatortype_append(ED_OT_undo_history); } diff --git a/source/blender/makesdna/DNA_fluid_defaults.h b/source/blender/makesdna/DNA_fluid_defaults.h index 2ee83cf3387..9454342654c 100644 --- a/source/blender/makesdna/DNA_fluid_defaults.h +++ b/source/blender/makesdna/DNA_fluid_defaults.h @@ -113,6 +113,7 @@ .flip_ratio = 0.97f, \ .sys_particle_maximum = 0, \ .simulation_method = FLUID_DOMAIN_METHOD_FLIP, \ + .viscosity_value = 0.05f, \ .surface_tension = 0.0f, \ .viscosity_base = 1.0f, \ .viscosity_exponent = 6.0f, \ diff --git a/source/blender/makesdna/DNA_fluid_types.h b/source/blender/makesdna/DNA_fluid_types.h index 98007a2b0b1..e787b44e557 100644 --- a/source/blender/makesdna/DNA_fluid_types.h +++ b/source/blender/makesdna/DNA_fluid_types.h @@ -52,6 +52,7 @@ enum { FLUID_DOMAIN_DELETE_IN_OBSTACLE = (1 << 14), /* Delete fluid inside obstacles. */ FLUID_DOMAIN_USE_DIFFUSION = (1 << 15), /* Use diffusion (e.g. viscosity, surface tension). */ FLUID_DOMAIN_USE_RESUMABLE_CACHE = (1 << 16), /* Determine if cache should be resumable. */ + FLUID_DOMAIN_USE_VISCOSITY = (1 << 17), /* Use viscosity. */ }; /** @@ -596,6 +597,10 @@ typedef struct FluidDomainSettings { short simulation_method; char _pad4[6]; + /* Viscosity options. */ + float viscosity_value; + char _pad5[4]; + /* Diffusion options. */ float surface_tension; float viscosity_base; @@ -610,7 +615,7 @@ typedef struct FluidDomainSettings { int mesh_scale; int totvert; short mesh_generator; - char _pad5[6]; /* Unused. */ + char _pad6[6]; /* Unused. */ /* Secondary particle options. */ int particle_type; @@ -631,7 +636,7 @@ typedef struct FluidDomainSettings { int sndparticle_update_radius; char sndparticle_boundary; char sndparticle_combined_export; - char _pad6[6]; /* Unused. */ + char _pad7[6]; /* Unused. */ /* Fluid guiding options. */ float guide_alpha; /* Guiding weight scalar (determines strength). */ @@ -639,7 +644,7 @@ typedef struct FluidDomainSettings { float guide_vel_factor; /* Multiply guiding velocity by this factor. */ int guide_res[3]; /* Res for velocity guide grids - independent from base res. */ short guide_source; - char _pad7[2]; /* Unused. */ + char _pad8[2]; /* Unused. */ /* Cache options. */ int cache_frame_start; @@ -659,7 +664,7 @@ typedef struct FluidDomainSettings { char error[64]; /* Bake error description. */ short cache_type; char cache_id[4]; /* Run-time only */ - char _pad8[2]; + char _pad9[2]; /* Unused. */ /* Time options. */ float dt; @@ -694,19 +699,19 @@ typedef struct FluidDomainSettings { char interp_method; char gridlines_color_field; /* Simulation field used to color map onto gridlines. */ char gridlines_cell_filter; - char _pad9[7]; + char _pad10[7]; /* Unused. */ /* OpenVDB cache options. */ int openvdb_compression; float clipping; char openvdb_data_depth; - char _pad10[7]; /* Unused. */ + char _pad11[7]; /* Unused. */ /* -- Deprecated / unsed options (below). -- */ /* View options. */ int viewsettings; - char _pad11[4]; /* Unused. */ + char _pad12[4]; /* Unused. */ /* Pointcache options. */ /* Smoke uses only one cache from now on (index [0]), but keeping the array for now for reading @@ -716,7 +721,7 @@ typedef struct FluidDomainSettings { int cache_comp; int cache_high_comp; char cache_file_format; - char _pad12[7]; /* Unused. */ + char _pad13[7]; /* Unused. */ } FluidDomainSettings; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 64dd489b850..3b655790de9 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1111,6 +1111,19 @@ typedef struct NodeInputVector { float vector[3]; } NodeInputVector; +typedef struct NodeGeometryRotatePoints { + /* GeometryNodeRotatePointsType */ + uint8_t type; + /* GeometryNodeRotatePointsSpace */ + uint8_t space; + + /* GeometryNodeAttributeInputMode */ + uint8_t input_type_axis; + uint8_t input_type_angle; + uint8_t input_type_rotation; + char _pad[3]; +} NodeGeometryRotatePoints; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1529,6 +1542,16 @@ typedef enum GeometryNodePointDistributeMethod { GEO_NODE_POINT_DISTRIBUTE_POISSON = 1, } GeometryNodePointDistributeMethod; +typedef enum GeometryNodeRotatePointsType { + GEO_NODE_ROTATE_POINTS_TYPE_EULER = 0, + GEO_NODE_ROTATE_POINTS_TYPE_AXIS_ANGLE = 1, +} GeometryNodeRotatePointsType; + +typedef enum GeometryNodeRotatePointsSpace { + GEO_NODE_ROTATE_POINTS_SPACE_OBJECT = 0, + GEO_NODE_ROTATE_POINTS_SPACE_POINT = 1, +} GeometryNodeRotatePointsSpace; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c index bb8280ede91..afe564dff0a 100644 --- a/source/blender/makesrna/intern/rna_fluid.c +++ b/source/blender/makesrna/intern/rna_fluid.c @@ -1929,6 +1929,22 @@ static void rna_def_fluid_domain_settings(BlenderRNA *brna) "Maximum number of fluid particles that are allowed in this simulation"); RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_datacache_reset"); + /* viscosity options */ + + prop = RNA_def_property(srna, "use_viscosity", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", FLUID_DOMAIN_USE_VISCOSITY); + RNA_def_property_ui_text(prop, "Use Viscosity", "Enable fluid viscosity settings"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_datacache_reset"); + + prop = RNA_def_property(srna, "viscosity_value", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.0, 10.0); + RNA_def_property_ui_range(prop, 0.0, 5.0, 0.01, 3); + RNA_def_property_ui_text(prop, + "Strength", + "Viscosity of liquid (higher values result in more viscous fluids, a " + "value of 0 will still apply some viscosity)"); + RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER, "rna_Fluid_datacache_reset"); + /* diffusion options */ prop = RNA_def_property(srna, "use_diffusion", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index cbd677582a9..a625b671c98 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -452,6 +452,11 @@ static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_float ITEM_FLOAT, {0, NULL, 0, NULL, NULL}, }; +static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_vector[] = { + ITEM_ATTRIBUTE, + ITEM_VECTOR, + {0, NULL, 0, NULL, NULL}, +}; static const EnumPropertyItem rna_node_geometry_attribute_input_type_items_no_boolean[] = { ITEM_ATTRIBUTE, ITEM_FLOAT, @@ -8584,6 +8589,66 @@ static void def_geo_attribute_color_ramp(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_rotate_points(StructRNA *srna) +{ + static const EnumPropertyItem type_items[] = { + {GEO_NODE_ROTATE_POINTS_TYPE_AXIS_ANGLE, + "AXIS_ANGLE", + ICON_NONE, + "Axis Angle", + "Rotate around an axis by an angle"}, + {GEO_NODE_ROTATE_POINTS_TYPE_EULER, + "EULER", + ICON_NONE, + "Euler", + "Rotate around the x, y and z axis"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem space_items[] = { + {GEO_NODE_ROTATE_POINTS_SPACE_OBJECT, + "OBJECT", + ICON_NONE, + "Object", + "Rotate points in the local space of the object"}, + {GEO_NODE_ROTATE_POINTS_SPACE_POINT, + "POINT", + ICON_NONE, + "Point", + "Rotate every point in its local space (as defined by the 'rotation' attribute)"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryRotatePoints", "storage"); + + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, type_items); + RNA_def_property_ui_text(prop, "Type", "Method used to describe the rotation"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "space", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, space_items); + RNA_def_property_ui_text(prop, "Space", "Base orientation of the points"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "input_type_axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); + RNA_def_property_ui_text(prop, "Input Type Axis", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "input_type_angle", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); + RNA_def_property_ui_text(prop, "Input Type Angle", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "input_type_rotation", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_vector); + RNA_def_property_ui_text(prop, "Input Type Rotation", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index b93922e46f2..e37df0225cf 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -4241,8 +4241,9 @@ void rna_def_view_layer_common(StructRNA *srna, const bool scene) prop = RNA_def_property(srna, "use_ztransp", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "layflag", SCE_LAY_ZTRA); - RNA_def_property_ui_text( - prop, "Z-Transparent", "Render Z-transparent faces in this layer (on top of Solid and Halos)"); + RNA_def_property_ui_text(prop, + "Z-Transparent", + "Render Z-transparent faces in this layer (on top of Solid and Halos)"); if (scene) { RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); } diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 60d4ee8f7c4..00e5f8246f3 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -155,6 +155,7 @@ set(SRC geometry/nodes/node_geo_point_distribute_poisson_disk.cc geometry/nodes/node_geo_point_instance.cc geometry/nodes/node_geo_point_separate.cc + geometry/nodes/node_geo_rotate_points.cc geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_transform.cc geometry/nodes/node_geo_triangulate.cc diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 19a7acf2299..4653c9aae3f 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -42,6 +42,7 @@ void register_node_type_geo_point_separate(void); void register_node_type_geo_attribute_compare(void); void register_node_type_geo_attribute_mix(void); void register_node_type_geo_attribute_color_ramp(void); +void register_node_type_geo_rotate_points(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 5156bac0e73..d6ae98594a5 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -284,6 +284,7 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_MIX, def_geo_attribute_mix, "ATTRIBUTE_ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "ATTRIBUTE_COLOR_RAMP", AttributeColorRamp, "Attribute Color Ramp", "") DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparate, "Point Separate", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "ATTRIBUTE_COMPARE", AttributeCompare, "Attribute Compare", "") +DefNode(GeometryNode, GEO_NODE_ROTATE_POINTS, def_geo_rotate_points, "ROTATE_POINTS", RotatePoints, "Rotate Points", "") /* undefine macros */ #undef DefNode diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc index bcebaa6a37b..eace75b46b6 100644 --- a/source/blender/nodes/geometry/node_geometry_util.cc +++ b/source/blender/nodes/geometry/node_geometry_util.cc @@ -19,19 +19,28 @@ namespace blender::nodes { +/** + * Update the availability of a group of input sockets with the same name, + * used for switching between attribute inputs or single values. + * + * \param mode: Controls which socket of the group to make available. + * \param name_is_available: If false, make all sockets with this name unavailable. + */ void update_attribute_input_socket_availabilities(bNode &node, const StringRef name, - const GeometryNodeAttributeInputMode mode) + const GeometryNodeAttributeInputMode mode, + const bool name_is_available) { const GeometryNodeAttributeInputMode mode_ = (GeometryNodeAttributeInputMode)mode; LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) { if (name == socket->name) { - const bool is_available = + const bool socket_is_available = + name_is_available && ((socket->type == SOCK_STRING && mode_ == GEO_NODE_ATTRIBUTE_INPUT_ATTRIBUTE) || (socket->type == SOCK_FLOAT && mode_ == GEO_NODE_ATTRIBUTE_INPUT_FLOAT) || (socket->type == SOCK_VECTOR && mode_ == GEO_NODE_ATTRIBUTE_INPUT_VECTOR) || (socket->type == SOCK_RGBA && mode_ == GEO_NODE_ATTRIBUTE_INPUT_COLOR)); - nodeSetSocketAvailability(socket, is_available); + nodeSetSocketAvailability(socket, socket_is_available); } } } diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 7c12611a898..d9c066d576f 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -41,7 +41,8 @@ bool geo_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree); namespace blender::nodes { void update_attribute_input_socket_availabilities(bNode &node, const StringRef name, - const GeometryNodeAttributeInputMode mode); + const GeometryNodeAttributeInputMode mode, + const bool can_be_available = true); CustomDataType attribute_domain_highest_complexity(Span); diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_points.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_points.cc new file mode 100644 index 00000000000..1272e36a216 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_points.cc @@ -0,0 +1,207 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +#include "BLI_math_rotation.h" + +static bNodeSocketTemplate geo_node_rotate_points_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Axis")}, + {SOCK_VECTOR, N_("Axis"), 0.0, 0.0, 1.0, 0.0, -FLT_MAX, FLT_MAX, PROP_XYZ}, + {SOCK_STRING, N_("Angle")}, + {SOCK_FLOAT, N_("Angle"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_ANGLE}, + {SOCK_STRING, N_("Rotation")}, + {SOCK_VECTOR, N_("Rotation"), 0.0, 0.0, 0.0, 0.0, -FLT_MAX, FLT_MAX, PROP_EULER}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_rotate_points_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void rotate_points__axis_angle__object_space(const int domain_size, + const Float3ReadAttribute &axis, + const FloatReadAttribute &angles, + MutableSpan rotations) +{ + for (const int i : IndexRange(domain_size)) { + float old_rotation[3][3]; + eul_to_mat3(old_rotation, rotations[i]); + float rotation[3][3]; + axis_angle_to_mat3(rotation, axis[i], angles[i]); + float new_rotation[3][3]; + mul_m3_m3m3(new_rotation, rotation, old_rotation); + mat3_to_eul(rotations[i], new_rotation); + } +} + +static void rotate_points__axis_angle__point_space(const int domain_size, + const Float3ReadAttribute &axis, + const FloatReadAttribute &angles, + MutableSpan rotations) +{ + for (const int i : IndexRange(domain_size)) { + float old_rotation[3][3]; + eul_to_mat3(old_rotation, rotations[i]); + float rotation[3][3]; + axis_angle_to_mat3(rotation, axis[i], angles[i]); + float new_rotation[3][3]; + mul_m3_m3m3(new_rotation, old_rotation, rotation); + mat3_to_eul(rotations[i], new_rotation); + } +} + +static void rotate_points__euler__object_space(const int domain_size, + const Float3ReadAttribute &eulers, + MutableSpan rotations) +{ + for (const int i : IndexRange(domain_size)) { + float old_rotation[3][3]; + eul_to_mat3(old_rotation, rotations[i]); + float rotation[3][3]; + eul_to_mat3(rotation, eulers[i]); + float new_rotation[3][3]; + mul_m3_m3m3(new_rotation, rotation, old_rotation); + mat3_to_eul(rotations[i], new_rotation); + } +} + +static void rotate_points__euler__point_space(const int domain_size, + const Float3ReadAttribute &eulers, + MutableSpan rotations) +{ + for (const int i : IndexRange(domain_size)) { + float old_rotation[3][3]; + eul_to_mat3(old_rotation, rotations[i]); + float rotation[3][3]; + eul_to_mat3(rotation, eulers[i]); + float new_rotation[3][3]; + mul_m3_m3m3(new_rotation, old_rotation, rotation); + mat3_to_eul(rotations[i], new_rotation); + } +} + +static void rotate_points_on_component(GeometryComponent &component, + const GeoNodeExecParams ¶ms) +{ + const bNode &node = params.node(); + const NodeGeometryRotatePoints &storage = *(const NodeGeometryRotatePoints *)node.storage; + + WriteAttributePtr rotation_attribute = component.attribute_try_ensure_for_write( + "rotation", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + if (!rotation_attribute) { + return; + } + + MutableSpan rotations = rotation_attribute->get_span().typed(); + const int domain_size = rotations.size(); + + if (storage.type == GEO_NODE_ROTATE_POINTS_TYPE_AXIS_ANGLE) { + Float3ReadAttribute axis = params.get_input_attribute( + "Axis", component, ATTR_DOMAIN_POINT, {0, 0, 1}); + FloatReadAttribute angles = params.get_input_attribute( + "Angle", component, ATTR_DOMAIN_POINT, 0); + + if (storage.space == GEO_NODE_ROTATE_POINTS_SPACE_OBJECT) { + rotate_points__axis_angle__object_space(domain_size, axis, angles, rotations); + } + else { + rotate_points__axis_angle__point_space(domain_size, axis, angles, rotations); + } + } + else { + Float3ReadAttribute eulers = params.get_input_attribute( + "Rotation", component, ATTR_DOMAIN_POINT, {0, 0, 0}); + + if (storage.space == GEO_NODE_ROTATE_POINTS_SPACE_OBJECT) { + rotate_points__euler__object_space(domain_size, eulers, rotations); + } + else { + rotate_points__euler__point_space(domain_size, eulers, rotations); + } + } + + rotation_attribute->apply_span(); +} + +static void geo_node_rotate_points_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + + if (geometry_set.has()) { + rotate_points_on_component(geometry_set.get_component_for_write(), params); + } + if (geometry_set.has()) { + rotate_points_on_component(geometry_set.get_component_for_write(), + params); + } + + params.set_output("Geometry", geometry_set); +} + +static void geo_node_rotate_points_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)MEM_callocN( + sizeof(NodeGeometryRotatePoints), __func__); + + node_storage->type = GEO_NODE_ROTATE_POINTS_TYPE_EULER; + node_storage->space = GEO_NODE_ROTATE_POINTS_SPACE_OBJECT; + node_storage->input_type_axis = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; + node_storage->input_type_angle = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; + node_storage->input_type_rotation = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; + + node->storage = node_storage; +} + +static void geo_node_rotate_points_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryRotatePoints *node_storage = (NodeGeometryRotatePoints *)node->storage; + update_attribute_input_socket_availabilities( + *node, + "Axis", + (GeometryNodeAttributeInputMode)node_storage->input_type_axis, + node_storage->type == GEO_NODE_ROTATE_POINTS_TYPE_AXIS_ANGLE); + update_attribute_input_socket_availabilities( + *node, + "Angle", + (GeometryNodeAttributeInputMode)node_storage->input_type_angle, + node_storage->type == GEO_NODE_ROTATE_POINTS_TYPE_AXIS_ANGLE); + update_attribute_input_socket_availabilities( + *node, + "Rotation", + (GeometryNodeAttributeInputMode)node_storage->input_type_rotation, + node_storage->type == GEO_NODE_ROTATE_POINTS_TYPE_EULER); +} + +} // namespace blender::nodes + +void register_node_type_geo_rotate_points() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_ROTATE_POINTS, "Rotate Points", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_rotate_points_in, geo_node_rotate_points_out); + node_type_init(&ntype, blender::nodes::geo_node_rotate_points_init); + node_type_update(&ntype, blender::nodes::geo_node_rotate_points_update); + node_type_storage( + &ntype, "NodeGeometryRotatePoints", node_free_standard_storage, node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_rotate_points_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 20fa500f0b3..90aa6a92d3c 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -2259,38 +2259,47 @@ static int wm_handler_fileselect_do(bContext *C, bScreen *screen = WM_window_get_active_screen(win); ScrArea *file_area = screen->areabase.first; - if (screen->temp && (file_area->spacetype == SPACE_FILE)) { - int win_size[2]; - bool is_maximized; - ED_fileselect_window_params_get(win, win_size, &is_maximized); - ED_fileselect_params_to_userdef(file_area->spacedata.first, win_size, is_maximized); - - if (BLI_listbase_is_single(&file_area->spacedata)) { - BLI_assert(ctx_win != win); - - wm_window_close(C, wm, win); - - CTX_wm_window_set(C, ctx_win); /* #wm_window_close() NULLs. */ - /* Some operators expect a drawable context (for EVT_FILESELECT_EXEC). */ - wm_window_make_drawable(wm, ctx_win); - /* Ensure correct cursor position, otherwise, popups may close immediately after - * opening (UI_BLOCK_MOVEMOUSE_QUIT). */ - wm_get_cursor_position(ctx_win, &ctx_win->eventstate->x, &ctx_win->eventstate->y); - wm->winactive = ctx_win; /* Reports use this... */ - if (handler->context.win == win) { - handler->context.win = NULL; - } - } - else if (file_area->full) { - ED_screen_full_prevspace(C, file_area); - } - else { - ED_area_prevspace(C, file_area); - } - - temp_win = win; - break; + if ((file_area->spacetype != SPACE_FILE) || !WM_window_is_temp_screen(win)) { + continue; } + + if (ctx_area->full) { + /* Users should not be able to maximize/fullscreen an area in a temporary screen. So if + * there's a maximized file browser in a temporary screen, it was likely opened by + * #EVT_FILESELECT_FULL_OPEN. */ + continue; + } + + int win_size[2]; + bool is_maximized; + ED_fileselect_window_params_get(win, win_size, &is_maximized); + ED_fileselect_params_to_userdef(file_area->spacedata.first, win_size, is_maximized); + + if (BLI_listbase_is_single(&file_area->spacedata)) { + BLI_assert(ctx_win != win); + + wm_window_close(C, wm, win); + + CTX_wm_window_set(C, ctx_win); /* #wm_window_close() NULLs. */ + /* Some operators expect a drawable context (for EVT_FILESELECT_EXEC). */ + wm_window_make_drawable(wm, ctx_win); + /* Ensure correct cursor position, otherwise, popups may close immediately after + * opening (UI_BLOCK_MOVEMOUSE_QUIT). */ + wm_get_cursor_position(ctx_win, &ctx_win->eventstate->x, &ctx_win->eventstate->y); + wm->winactive = ctx_win; /* Reports use this... */ + if (handler->context.win == win) { + handler->context.win = NULL; + } + } + else if (file_area->full) { + ED_screen_full_prevspace(C, file_area); + } + else { + ED_area_prevspace(C, file_area); + } + + temp_win = win; + break; } if (!temp_win && ctx_area->full) {