Fluid: Updated Mantaflow source files
Among code cleanups, this update includes a new flood-fill helper function for levelsets.
This commit is contained in:
parent
2b72860ff4
commit
a0ebfab4f3
|
@ -377,19 +377,6 @@ class RandomStream {
|
|||
}
|
||||
|
||||
/*! get a random number from the stream */
|
||||
inline double getDouble(void)
|
||||
{
|
||||
return mtr.rand();
|
||||
};
|
||||
inline float getFloat(void)
|
||||
{
|
||||
return (float)mtr.rand();
|
||||
};
|
||||
|
||||
inline float getFloat(float min, float max)
|
||||
{
|
||||
return mtr.rand(max - min) + min;
|
||||
};
|
||||
inline float getRandNorm(float mean, float var)
|
||||
{
|
||||
return mtr.randNorm(mean, var);
|
||||
|
@ -400,12 +387,20 @@ class RandomStream {
|
|||
{
|
||||
return getFloat();
|
||||
}
|
||||
inline Real getReal(float min, float max)
|
||||
{
|
||||
return getFloat(min, max);
|
||||
}
|
||||
|
||||
#else
|
||||
inline Real getReal()
|
||||
{
|
||||
return getDouble();
|
||||
}
|
||||
inline Real getReal(double min, double max)
|
||||
{
|
||||
return getDouble(min, max);
|
||||
}
|
||||
#endif
|
||||
|
||||
inline Vec3 getVec3()
|
||||
|
@ -422,6 +417,24 @@ class RandomStream {
|
|||
|
||||
private:
|
||||
MTRand mtr;
|
||||
|
||||
inline double getDouble(void)
|
||||
{
|
||||
return mtr.rand();
|
||||
};
|
||||
inline float getFloat(void)
|
||||
{
|
||||
return (float)mtr.rand();
|
||||
};
|
||||
|
||||
inline double getDouble(double min, double max)
|
||||
{
|
||||
return mtr.rand(max - min) + min;
|
||||
};
|
||||
inline float getFloat(float min, float max)
|
||||
{
|
||||
return (float)(mtr.rand(max - min) + min);
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace Manta
|
||||
|
|
|
@ -405,6 +405,7 @@ int writeObjectsVDB(const string &filename,
|
|||
vdb_flags = openvdb::io::COMPRESS_NONE;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case COMPRESSION_ZIP: {
|
||||
vdb_flags |= openvdb::io::COMPRESS_ZIP;
|
||||
break;
|
||||
|
@ -445,6 +446,7 @@ int readObjectsVDB(const string &filename, std::vector<PbClass *> *objects, floa
|
|||
(void)metadata; // Unused for now
|
||||
}
|
||||
catch (const openvdb::IoError &e) {
|
||||
(void)e; // Unused for now
|
||||
debMsg("readObjectsVDB: Could not open vdb file " << filename, 1);
|
||||
file.close();
|
||||
return 0;
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
|
||||
|
||||
#define MANTA_GIT_VERSION "commit e2f6e59e3679f88e5100ae2145410cca4971b9df"
|
||||
#define MANTA_GIT_VERSION "commit b8e557707805720ff00a8eb946db2ee5b9361b5a"
|
||||
|
|
|
@ -355,7 +355,6 @@ class GridBase : public PbClass {
|
|||
return isInBounds(Vec3i(i, j, k), bnd);
|
||||
}
|
||||
|
||||
#ifdef BLENDER
|
||||
//! expose name field to Python for Blender
|
||||
void setName(const std::string &name)
|
||||
{
|
||||
|
@ -386,7 +385,6 @@ class GridBase : public PbClass {
|
|||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
protected:
|
||||
GridType mType;
|
||||
Vec3i mSize;
|
||||
|
|
|
@ -639,8 +639,207 @@ void LevelsetGrid::initFromFlags(const FlagGrid &flags, bool ignoreWalls)
|
|||
}
|
||||
}
|
||||
|
||||
/* Helper variables that are used in flood-fill functions. */
|
||||
static const int ID_UNKNOWN = 0;
|
||||
static const int ID_VISITED = 1;
|
||||
|
||||
/* Fills all cells in the target grid that have not been marked during a flood-fill. */
|
||||
|
||||
struct KnFillApply : public KernelBase {
|
||||
KnFillApply(Grid<Real> &target,
|
||||
Grid<int> &visited,
|
||||
const Real value,
|
||||
const int boundaryWidth,
|
||||
const bool outside)
|
||||
: KernelBase(&target, boundaryWidth),
|
||||
target(target),
|
||||
visited(visited),
|
||||
value(value),
|
||||
boundaryWidth(boundaryWidth),
|
||||
outside(outside)
|
||||
{
|
||||
runMessage();
|
||||
run();
|
||||
}
|
||||
inline void op(int i,
|
||||
int j,
|
||||
int k,
|
||||
Grid<Real> &target,
|
||||
Grid<int> &visited,
|
||||
const Real value,
|
||||
const int boundaryWidth,
|
||||
const bool outside) const
|
||||
{
|
||||
|
||||
if (visited(i, j, k) == ID_VISITED)
|
||||
return;
|
||||
if (outside && target(i, j, k) < 0)
|
||||
return;
|
||||
if (!outside && target(i, j, k) >= 0)
|
||||
return;
|
||||
|
||||
/* Actual flood-fill override. */
|
||||
target(i, j, k) = value;
|
||||
}
|
||||
inline Grid<Real> &getArg0()
|
||||
{
|
||||
return target;
|
||||
}
|
||||
typedef Grid<Real> type0;
|
||||
inline Grid<int> &getArg1()
|
||||
{
|
||||
return visited;
|
||||
}
|
||||
typedef Grid<int> type1;
|
||||
inline const Real &getArg2()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
typedef Real type2;
|
||||
inline const int &getArg3()
|
||||
{
|
||||
return boundaryWidth;
|
||||
}
|
||||
typedef int type3;
|
||||
inline const bool &getArg4()
|
||||
{
|
||||
return outside;
|
||||
}
|
||||
typedef bool type4;
|
||||
void runMessage()
|
||||
{
|
||||
debMsg("Executing kernel KnFillApply ", 3);
|
||||
debMsg("Kernel range"
|
||||
<< " x " << maxX << " y " << maxY << " z " << minZ << " - " << maxZ << " ",
|
||||
4);
|
||||
};
|
||||
void operator()(const tbb::blocked_range<IndexInt> &__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 = boundaryWidth; j < _maxY; j++)
|
||||
for (int i = boundaryWidth; i < _maxX; i++)
|
||||
op(i, j, k, target, visited, value, boundaryWidth, outside);
|
||||
}
|
||||
else {
|
||||
const int k = 0;
|
||||
for (int j = __r.begin(); j != (int)__r.end(); j++)
|
||||
for (int i = boundaryWidth; i < _maxX; i++)
|
||||
op(i, j, k, target, visited, value, boundaryWidth, outside);
|
||||
}
|
||||
}
|
||||
void run()
|
||||
{
|
||||
if (maxZ > 1)
|
||||
tbb::parallel_for(tbb::blocked_range<IndexInt>(minZ, maxZ), *this);
|
||||
else
|
||||
tbb::parallel_for(tbb::blocked_range<IndexInt>(boundaryWidth, maxY), *this);
|
||||
}
|
||||
Grid<Real> ⌖
|
||||
Grid<int> &visited;
|
||||
const Real value;
|
||||
const int boundaryWidth;
|
||||
const bool outside;
|
||||
};
|
||||
|
||||
/* Basic flood fill implementation used to fill inside / outside areas of levelset.
|
||||
* Calling this function will ensure that there are no fluid cells inside obstacles.
|
||||
* I.e. starting from walls, cells will be tagged in flood-fill fashion, stopping at 0 borders.
|
||||
* All remaining cells will be filled with the fill value. Outside mode inverts search behavior. */
|
||||
void LevelsetGrid::floodFill(const Real value, const bool outside, const int boundaryWidth)
|
||||
{
|
||||
|
||||
/* Sanity check: Filling mode and filling value need to "match". */
|
||||
if (outside) {
|
||||
assertMsg(value < 0, "Cannot fill outside with (positive) value " << value);
|
||||
}
|
||||
else {
|
||||
assertMsg(value >= 0, "Cannot fill inside with (negative) value " << value);
|
||||
}
|
||||
|
||||
Grid<Real> levelsetCopy(this->getParent());
|
||||
Grid<int> visited(this->getParent());
|
||||
std::stack<Vec3i> todoPos;
|
||||
|
||||
const int maxNeighbors = this->is3D() ? 6 : 4;
|
||||
const Vec3i maxSize(this->getSize() - 1);
|
||||
|
||||
Vec3i bnd(2 * boundaryWidth);
|
||||
if (!this->is3D())
|
||||
bnd.z = 0;
|
||||
const int cellCntNoBnd = (this->getSizeX() - bnd.x) * (this->getSizeY() - bnd.y) *
|
||||
(this->getSizeZ() - bnd.z);
|
||||
|
||||
/* Initialize temporary helper grids. */
|
||||
levelsetCopy.copyFrom(*this);
|
||||
visited.setConst(ID_UNKNOWN);
|
||||
|
||||
FOR_IJK_BND(visited, boundaryWidth)
|
||||
{
|
||||
|
||||
/* Skip inside / outside cells depending on search mode. */
|
||||
if (outside && levelsetCopy(i, j, k) < 0)
|
||||
continue;
|
||||
if (!outside && levelsetCopy(i, j, k) >= 0)
|
||||
continue;
|
||||
/* Skip cell if it already has been visited. */
|
||||
if (visited(i, j, k) == ID_VISITED)
|
||||
continue;
|
||||
|
||||
Vec3i c(i, j, k);
|
||||
|
||||
bool isWallCell = (c.x - boundaryWidth == 0 || c.x == maxSize.x - boundaryWidth);
|
||||
isWallCell |= (c.y - boundaryWidth == 0 || c.y == maxSize.y - boundaryWidth);
|
||||
if (this->is3D())
|
||||
isWallCell |= (c.z - boundaryWidth == 0 || c.z == maxSize.z - boundaryWidth);
|
||||
|
||||
/* Only start searching from borders. */
|
||||
if (!isWallCell)
|
||||
continue;
|
||||
|
||||
/* Start flood-fill loop by initializing todo stack with current cell. */
|
||||
todoPos.push(c);
|
||||
visited(c) = ID_VISITED;
|
||||
|
||||
while (!todoPos.empty()) {
|
||||
c = todoPos.top();
|
||||
todoPos.pop();
|
||||
|
||||
/* Add all neighbor cells to search stack. */
|
||||
for (int nb = 0; nb < maxNeighbors; nb++) {
|
||||
const Vec3i neigh(c + neighbors[nb]);
|
||||
|
||||
if (!visited.isInBounds(neigh, boundaryWidth))
|
||||
continue;
|
||||
/* Skip inside / outside area depening on what we search for. */
|
||||
if (outside && levelsetCopy(neigh) < 0)
|
||||
continue;
|
||||
if (!outside && levelsetCopy(neigh) >= 0)
|
||||
continue;
|
||||
/* Skip neighbor if it already has been visited. */
|
||||
if (visited(neigh) == ID_VISITED)
|
||||
continue;
|
||||
|
||||
assertMsg(visited(neigh) == ID_UNKNOWN,
|
||||
"Cell must be of type 'unknown' at this point in the loop");
|
||||
todoPos.push(neigh);
|
||||
visited(neigh) = ID_VISITED;
|
||||
}
|
||||
assertMsg(todoPos.size() <= cellCntNoBnd,
|
||||
"Flood-fill todo stack cannot be greater than domain cell count - "
|
||||
<< todoPos.size() << " vs " << cellCntNoBnd);
|
||||
}
|
||||
}
|
||||
KnFillApply(*this, visited, value, boundaryWidth, outside);
|
||||
}
|
||||
|
||||
/* Deprecated: Use floodFill() function instead. */
|
||||
void LevelsetGrid::fillHoles(int maxDepth, int boundaryWidth)
|
||||
{
|
||||
debMsg("Deprecated - do not use fillHoles() ... use floodFill() instead", 1);
|
||||
|
||||
Real curVal, i1, i2, j1, j2, k1, k2;
|
||||
Vec3i c, cTmp;
|
||||
std::stack<Vec3i> undoPos;
|
||||
|
|
|
@ -234,6 +234,35 @@ class LevelsetGrid : public Grid<Real> {
|
|||
}
|
||||
}
|
||||
|
||||
//! flood-fill the levelset to ensure that closed obstacles are filled inside
|
||||
void floodFill(const Real value = -0.5, const bool outside = true, const int boundaryWidth = 1);
|
||||
static PyObject *_W_7(PyObject *_self, PyObject *_linargs, PyObject *_kwds)
|
||||
{
|
||||
try {
|
||||
PbArgs _args(_linargs, _kwds);
|
||||
LevelsetGrid *pbo = dynamic_cast<LevelsetGrid *>(Pb::objFromPy(_self));
|
||||
bool noTiming = _args.getOpt<bool>("notiming", -1, 0);
|
||||
pbPreparePlugin(pbo->getParent(), "LevelsetGrid::floodFill", !noTiming);
|
||||
PyObject *_retval = 0;
|
||||
{
|
||||
ArgLocker _lock;
|
||||
const Real value = _args.getOpt<Real>("value", 0, -0.5, &_lock);
|
||||
const bool outside = _args.getOpt<bool>("outside", 1, true, &_lock);
|
||||
const int boundaryWidth = _args.getOpt<int>("boundaryWidth", 2, 1, &_lock);
|
||||
pbo->_args.copy(_args);
|
||||
_retval = getPyNone();
|
||||
pbo->floodFill(value, outside, boundaryWidth);
|
||||
pbo->_args.check();
|
||||
}
|
||||
pbFinalizePlugin(pbo->getParent(), "LevelsetGrid::floodFill", !noTiming);
|
||||
return _retval;
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
pbSetError("LevelsetGrid::floodFill", e.what());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static Real invalidTimeValue();
|
||||
public:
|
||||
PbArgs _args;
|
||||
|
|
|
@ -15,6 +15,7 @@ static const Pb::Register _R_15("LevelsetGrid", "join", LevelsetGrid::_W_3);
|
|||
static const Pb::Register _R_16("LevelsetGrid", "subtract", LevelsetGrid::_W_4);
|
||||
static const Pb::Register _R_17("LevelsetGrid", "initFromFlags", LevelsetGrid::_W_5);
|
||||
static const Pb::Register _R_18("LevelsetGrid", "fillHoles", LevelsetGrid::_W_6);
|
||||
static const Pb::Register _R_19("LevelsetGrid", "floodFill", LevelsetGrid::_W_7);
|
||||
#endif
|
||||
extern "C" {
|
||||
void PbRegister_file_11()
|
||||
|
@ -27,6 +28,7 @@ void PbRegister_file_11()
|
|||
KEEP_UNUSED(_R_16);
|
||||
KEEP_UNUSED(_R_17);
|
||||
KEEP_UNUSED(_R_18);
|
||||
KEEP_UNUSED(_R_19);
|
||||
}
|
||||
}
|
||||
} // namespace Manta
|
|
@ -695,7 +695,12 @@ struct KnApplyEmission : public KernelBase {
|
|||
// (important for emit from particles)
|
||||
bool isInflow = (type & FlagGrid::TypeInflow && flags.isInflow(i, j, k));
|
||||
bool isOutflow = (type & FlagGrid::TypeOutflow && flags.isOutflow(i, j, k));
|
||||
if ((type && !isInflow && !isOutflow) && (emissionTexture && !(*emissionTexture)(i, j, k)))
|
||||
|
||||
if (type && !isInflow)
|
||||
return;
|
||||
if (type && isOutflow)
|
||||
return;
|
||||
if (emissionTexture && !(*emissionTexture)(i, j, k))
|
||||
return;
|
||||
|
||||
if (isAbsolute)
|
||||
|
|
Loading…
Reference in New Issue