1 
2 
3 // DO NOT EDIT !
4 // This file is generated using the MantaFlow preprocessor (prep generate).
5 
6 /******************************************************************************
7  *
8  * MantaFlow fluid solver framework
9  * Copyright 2020 Sebastian Barschkis, Nils Thuerey
10  *
11  * This program is free software, distributed under the terms of the
12  * Apache License, Version 2.0
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Loading and writing grids and particles from and to OpenVDB files.
16  *
17  ******************************************************************************/
18 
19 #include <iostream>
20 #include <fstream>
21 #include <cstdlib>
22 #include <cstring>
23 
24 #include "mantaio.h"
25 #include "grid.h"
26 #include "vector4d.h"
27 #include "grid4d.h"
28 #include "particle.h"
29 
30 #if OPENVDB == 1
31 #  include "openvdb/openvdb.h"
32 #  include <openvdb/points/PointConversion.h>
33 #  include <openvdb/points/PointCount.h>
34 #endif
35 
36 #define POSITION_NAME "P"
37 #define FLAG_NAME "U"
38 
39 using namespace std;
40 
41 namespace Manta {
42 
43 #if OPENVDB == 1
44 
importVDB(typename GridType::Ptr from,Grid<T> * to)45 template<class GridType, class T> void importVDB(typename GridType::Ptr from, Grid<T> *to)
46 {
47   using ValueT = typename GridType::ValueType;
48   typename GridType::Accessor accessor = from->getAccessor();
49 
50   FOR_IJK(*to)
51   {
52     openvdb::Coord xyz(i, j, k);
53     ValueT vdbValue = accessor.getValue(xyz);
54     T toMantaValue;
55     convertFrom(vdbValue, &toMantaValue);
56     to->set(i, j, k, toMantaValue);
57   }
58 }
59 
60 template<class VDBType, class T>
importVDB(VDBType vdbValue,ParticleDataImpl<T> * to,int index,float voxelSize)61 void importVDB(VDBType vdbValue, ParticleDataImpl<T> *to, int index, float voxelSize)
62 {
63   unusedParameter(voxelSize);  // Unused for now
64   T toMantaValue;
65   convertFrom(vdbValue, &toMantaValue);
66   to->set(index, toMantaValue);
67 }
68 
importVDB(openvdb::points::PointDataGrid::Ptr from,BasicParticleSystem * to,std::vector<ParticleDataBase * > & toPData,float voxelSize)69 void importVDB(openvdb::points::PointDataGrid::Ptr from,
70                BasicParticleSystem *to,
71                std::vector<ParticleDataBase *> &toPData,
72                float voxelSize)
73 {
74   openvdb::Index64 count = openvdb::points::pointCount(from->tree());
75   to->resizeAll(count);
76 
77   int cnt = 0;
78   for (auto leafIter = from->tree().cbeginLeaf(); leafIter; ++leafIter) {
79     const openvdb::points::AttributeArray &positionArray = leafIter->constAttributeArray(
80         POSITION_NAME);
81     const openvdb::points::AttributeArray &flagArray = leafIter->constAttributeArray(FLAG_NAME);
82 
83     openvdb::points::AttributeHandle<openvdb::Vec3s> positionHandle(positionArray);
84     openvdb::points::AttributeHandle<int> flagHandle(flagArray);
85 
86     // Get vdb handles to pdata objects in pdata list
87     std::vector<std::tuple<int, openvdb::points::AttributeHandle<int>>> pDataHandlesInt;
88     std::vector<std::tuple<int, openvdb::points::AttributeHandle<float>>> pDataHandlesReal;
89     std::vector<std::tuple<int, openvdb::points::AttributeHandle<openvdb::Vec3s>>>
90         pDataHandlesVec3;
91 
92     int pDataIndex = 0;
93     for (ParticleDataBase *pdb : toPData) {
94       std::string name = pdb->getName();
95       const openvdb::points::AttributeArray &pDataArray = leafIter->constAttributeArray(name);
96 
97       if (pdb->getType() == ParticleDataBase::TypeInt) {
98         openvdb::points::AttributeHandle<int> intHandle(pDataArray);
99         std::tuple<int, openvdb::points::AttributeHandle<int>> tuple = std::make_tuple(pDataIndex,
100                                                                                        intHandle);
101         pDataHandlesInt.push_back(tuple);
102       }
103       else if (pdb->getType() == ParticleDataBase::TypeReal) {
104         openvdb::points::AttributeHandle<float> floatHandle(pDataArray);
105         std::tuple<int, openvdb::points::AttributeHandle<float>> tuple = std::make_tuple(
106             pDataIndex, floatHandle);
107         pDataHandlesReal.push_back(tuple);
108       }
109       else if (pdb->getType() == ParticleDataBase::TypeVec3) {
110         openvdb::points::AttributeHandle<openvdb::Vec3s> vec3Handle(pDataArray);
111         std::tuple<int, openvdb::points::AttributeHandle<openvdb::Vec3s>> tuple = std::make_tuple(
112             pDataIndex, vec3Handle);
113         pDataHandlesVec3.push_back(tuple);
114       }
115       else {
116         errMsg("importVDB: unknown ParticleDataBase type");
117       }
118       ++pDataIndex;
119     }
120 
121     for (auto indexIter = leafIter->beginIndexOn(); indexIter; ++indexIter) {
122       // Extract the voxel-space position of the point (always between (-0.5, -0.5, -0.5) and (0.5,
123       // 0.5, 0.5)).
124       openvdb::Vec3s voxelPosition = positionHandle.get(*indexIter);
125       const openvdb::Vec3d xyz = indexIter.getCoord().asVec3d();
126       // Compute the world-space position of the point.
127       openvdb::Vec3f worldPosition = from->transform().indexToWorld(voxelPosition + xyz);
128       int flag = flagHandle.get(*indexIter);
129 
130       Vec3 toMantaValue;
131       convertFrom(worldPosition, &toMantaValue);
132       (*to)[cnt].pos = toMantaValue;
133       (*to)[cnt].pos /= voxelSize;  // convert from world space to grid space
134       (*to)[cnt].flag = flag;
135 
136       for (std::tuple<int, openvdb::points::AttributeHandle<int>> tuple : pDataHandlesInt) {
137         int pDataIndex = std::get<0>(tuple);
138         int vdbValue = std::get<1>(tuple).get(*indexIter);
139 
140         ParticleDataImpl<int> *pdi = dynamic_cast<ParticleDataImpl<int> *>(toPData[pDataIndex]);
141         importVDB<int, int>(vdbValue, pdi, cnt, voxelSize);
142       }
143       for (std::tuple<int, openvdb::points::AttributeHandle<float>> tuple : pDataHandlesReal) {
144         int pDataIndex = std::get<0>(tuple);
145         float vdbValue = std::get<1>(tuple).get(*indexIter);
146 
147         ParticleDataImpl<Real> *pdi = dynamic_cast<ParticleDataImpl<Real> *>(toPData[pDataIndex]);
148         importVDB<float, Real>(vdbValue, pdi, cnt, voxelSize);
149       }
150       for (std::tuple<int, openvdb::points::AttributeHandle<openvdb::Vec3s>> tuple :
151            pDataHandlesVec3) {
152         int pDataIndex = std::get<0>(tuple);
153         openvdb::Vec3f voxelPosition = std::get<1>(tuple).get(*indexIter);
154 
155         ParticleDataImpl<Vec3> *pdi = dynamic_cast<ParticleDataImpl<Vec3> *>(toPData[pDataIndex]);
156         importVDB<openvdb::Vec3s, Vec3>(voxelPosition, pdi, cnt, voxelSize);
157       }
158       ++cnt;
159     }
160   }
161 }
162 
163 template<class GridType>
setGridOptions(typename GridType::Ptr grid,string name,openvdb::GridClass cls,float voxelSize,int precision)164 static void setGridOptions(typename GridType::Ptr grid,
165                            string name,
166                            openvdb::GridClass cls,
167                            float voxelSize,
168                            int precision)
169 {
170   grid->setTransform(openvdb::math::Transform::createLinearTransform(voxelSize));
171   grid->setGridClass(cls);
172   grid->setName(name);
173   grid->setSaveFloatAsHalf(precision == PRECISION_MINI || precision == PRECISION_HALF);
174 }
175 
exportVDB(Grid<T> * from)176 template<class T, class GridType> typename GridType::Ptr exportVDB(Grid<T> *from)
177 {
178   using ValueT = typename GridType::ValueType;
179   typename GridType::Ptr to = GridType::create();
180   typename GridType::Accessor accessor = to->getAccessor();
181 
182   FOR_IJK(*from)
183   {
184     openvdb::Coord xyz(i, j, k);
185     T fromMantaValue = (*from)(i, j, k);
186     ValueT vdbValue;
187     convertTo(&vdbValue, fromMantaValue);
188     accessor.setValue(xyz, vdbValue);
189   }
190   return to;
191 }
192 
193 template<class MantaType, class VDBType>
exportVDB(ParticleDataImpl<MantaType> * from,openvdb::points::PointDataGrid::Ptr to,openvdb::tools::PointIndexGrid::Ptr pIndex,bool skipDeletedParts,int precision)194 void exportVDB(ParticleDataImpl<MantaType> *from,
195                openvdb::points::PointDataGrid::Ptr to,
196                openvdb::tools::PointIndexGrid::Ptr pIndex,
197                bool skipDeletedParts,
198                int precision)
199 {
200   std::vector<VDBType> vdbValues;
201   std::string name = from->getName();
202 
203   FOR_PARTS(*from)
204   {
205     // Optionally, skip exporting particles that have been marked as deleted
206     BasicParticleSystem *pp = dynamic_cast<BasicParticleSystem *>(from->getParticleSys());
207     if (skipDeletedParts && !pp->isActive(idx)) {
208       continue;
209     }
210     MantaType fromMantaValue = (*from)[idx];
211     VDBType vdbValue;
212     convertTo(&vdbValue, fromMantaValue);
213     vdbValues.push_back(vdbValue);
214   }
215 
216   // Use custom codec for precision of the attribute
217   openvdb::NamePair attribute;
218   if (precision == PRECISION_FULL) {
219     attribute =
220         openvdb::points::TypedAttributeArray<VDBType, openvdb::points::NullCodec>::attributeType();
221   }
222   else if (precision == PRECISION_HALF ||
223            precision == PRECISION_MINI) {  // Mini uses same precision as half for now
224     attribute =
225         openvdb::points::TypedAttributeArray<VDBType,
226                                              openvdb::points::TruncateCodec>::attributeType();
227   }
228   else {
229     errMsg("exportVDB: invalid precision level");
230   }
231   openvdb::points::appendAttribute(to->tree(), name, attribute);
232 
233   // Create a wrapper around the vdb values vector.
234   const openvdb::points::PointAttributeVector<VDBType> wrapper(vdbValues);
235 
236   // Populate the attribute on the points
237   openvdb::points::populateAttribute<openvdb::points::PointDataTree,
238                                      openvdb::tools::PointIndexTree,
239                                      openvdb::points::PointAttributeVector<VDBType>>(
240       to->tree(), pIndex->tree(), name, wrapper);
241 }
242 
exportVDB(BasicParticleSystem * from,std::vector<ParticleDataBase * > & fromPData,bool skipDeletedParts,float voxelSize,int precision)243 openvdb::points::PointDataGrid::Ptr exportVDB(BasicParticleSystem *from,
244                                               std::vector<ParticleDataBase *> &fromPData,
245                                               bool skipDeletedParts,
246                                               float voxelSize,
247                                               int precision)
248 {
249   std::vector<openvdb::Vec3s> positions;
250   std::vector<int> flags;
251 
252   FOR_PARTS(*from)
253   {
254     // Optionally, skip exporting particles that have been marked as deleted
255     if (skipDeletedParts && !from->isActive(idx)) {
256       continue;
257     }
258     Vector3D<float> pos = toVec3f((*from)[idx].pos);
259     pos *= voxelSize;  // convert from grid space to world space
260     openvdb::Vec3s posVDB(pos.x, pos.y, pos.z);
261     positions.push_back(posVDB);
262 
263     int flag = (*from)[idx].flag;
264     flags.push_back(flag);
265   }
266 
267   const openvdb::points::PointAttributeVector<openvdb::Vec3s> positionsWrapper(positions);
268   openvdb::math::Transform::Ptr transform = openvdb::math::Transform::createLinearTransform(
269       voxelSize);
270 
271   openvdb::tools::PointIndexGrid::Ptr pointIndexGrid =
272       openvdb::tools::createPointIndexGrid<openvdb::tools::PointIndexGrid>(positionsWrapper,
273                                                                            *transform);
274 
275   openvdb::points::PointDataGrid::Ptr to;
276   openvdb::NamePair flagAttribute;
277 
278   using CodecNull = openvdb::points::NullCodec;
279   using CodecTrunc = openvdb::points::TruncateCodec;
280   using CodecFixPoint = openvdb::points::FixedPointCodec<true, openvdb::points::PositionRange>;
281 
282   // Use custom codec for precision of the particle position and the flag attribute
283   if (precision == PRECISION_FULL) {
284     to = openvdb::points::createPointDataGrid<CodecNull, openvdb::points::PointDataGrid>(
285         *pointIndexGrid, positionsWrapper, *transform);
286     flagAttribute = openvdb::points::TypedAttributeArray<int, CodecNull>::attributeType();
287   }
288   else if (precision == PRECISION_HALF) {
289     to = openvdb::points::createPointDataGrid<CodecTrunc, openvdb::points::PointDataGrid>(
290         *pointIndexGrid, positionsWrapper, *transform);
291     flagAttribute = openvdb::points::TypedAttributeArray<int, CodecTrunc>::attributeType();
292   }
293   else if (precision == PRECISION_MINI) {
294     to = openvdb::points::createPointDataGrid<CodecFixPoint, openvdb::points::PointDataGrid>(
295         *pointIndexGrid, positionsWrapper, *transform);
296     flagAttribute = openvdb::points::TypedAttributeArray<int, CodecTrunc>::
297         attributeType();  // Use 16 bit trunc for flag for now
298   }
299   else {
300     errMsg("exportVDB: invalid precision level");
301   }
302 
303   openvdb::points::appendAttribute(to->tree(), FLAG_NAME, flagAttribute);
304   // Create a wrapper around the flag vector.
305   openvdb::points::PointAttributeVector<int> flagWrapper(flags);
306   // Populate the "flag" attribute on the points
307   openvdb::points::populateAttribute<openvdb::points::PointDataTree,
308                                      openvdb::tools::PointIndexTree,
309                                      openvdb::points::PointAttributeVector<int>>(
310       to->tree(), pointIndexGrid->tree(), FLAG_NAME, flagWrapper);
311 
312   // Add all already buffered pdata to this particle grid
313   for (ParticleDataBase *pdb : fromPData) {
314     if (pdb->getType() == ParticleDataBase::TypeInt) {
315       debMsg("Writing int particle data '" << pdb->getName() << "'", 1);
316       ParticleDataImpl<int> *pdi = dynamic_cast<ParticleDataImpl<int> *>(pdb);
317       exportVDB<int, int>(pdi, to, pointIndexGrid, skipDeletedParts, precision);
318     }
319     else if (pdb->getType() == ParticleDataBase::TypeReal) {
320       debMsg("Writing real particle data '" << pdb->getName() << "'", 1);
321       ParticleDataImpl<Real> *pdi = dynamic_cast<ParticleDataImpl<Real> *>(pdb);
322       exportVDB<Real, float>(pdi, to, pointIndexGrid, skipDeletedParts, precision);
323     }
324     else if (pdb->getType() == ParticleDataBase::TypeVec3) {
325       debMsg("Writing Vec3 particle data '" << pdb->getName() << "'", 1);
326       ParticleDataImpl<Vec3> *pdi = dynamic_cast<ParticleDataImpl<Vec3> *>(pdb);
327       exportVDB<Vec3, openvdb::Vec3s>(pdi, to, pointIndexGrid, skipDeletedParts, precision);
328     }
329     else {
330       errMsg("exportVDB: unknown ParticleDataBase type");
331     }
332   }
333   return to;
334 }
335 
registerCustomCodecs()336 static void registerCustomCodecs()
337 {
338   openvdb::points::TypedAttributeArray<int, openvdb::points::TruncateCodec>::registerType();
339   openvdb::points::TypedAttributeArray<float, openvdb::points::TruncateCodec>::registerType();
340   openvdb::points::TypedAttributeArray<openvdb::Vec3s,
341                                        openvdb::points::TruncateCodec>::registerType();
342 }
343 
writeObjectsVDB(const string & filename,std::vector<PbClass * > * objects,float worldSize,bool skipDeletedParts,int compression,int precision)344 int writeObjectsVDB(const string &filename,
345                     std::vector<PbClass *> *objects,
346                     float worldSize,
347                     bool skipDeletedParts,
348                     int compression,
349                     int precision)
350 {
351   openvdb::initialize();
352   openvdb::io::File file(filename);
353   openvdb::GridPtrVec gridsVDB;
354 
355   // Register custom codecs, this makes sure custom attributes can be read
356   registerCustomCodecs();
357 
358   std::vector<ParticleDataBase *> pdbBuffer;
359 
360   for (std::vector<PbClass *>::iterator iter = objects->begin(); iter != objects->end(); ++iter) {
361     openvdb::GridClass gClass = openvdb::GRID_UNKNOWN;
362     openvdb::GridBase::Ptr vdbGrid;
363 
364     PbClass *object = dynamic_cast<PbClass *>(*iter);
365     const Real dx = object->getParent()->getDx();
366     const Real voxelSize = worldSize * dx;
367     const string objectName = object->getName();
368 
369     if (GridBase *mantaGrid = dynamic_cast<GridBase *>(*iter)) {
370 
371       if (mantaGrid->getType() & GridBase::TypeInt) {
372         debMsg("Writing int grid '" << mantaGrid->getName() << "' to vdb file " << filename, 1);
373         Grid<int> *mantaIntGrid = (Grid<int> *)mantaGrid;
374         vdbGrid = exportVDB<int, openvdb::Int32Grid>(mantaIntGrid);
375         gridsVDB.push_back(vdbGrid);
376       }
377       else if (mantaGrid->getType() & GridBase::TypeReal) {
378         debMsg("Writing real grid '" << mantaGrid->getName() << "' to vdb file " << filename, 1);
379         gClass = (mantaGrid->getType() & GridBase::TypeLevelset) ? openvdb::GRID_LEVEL_SET :
380                                                                    openvdb::GRID_FOG_VOLUME;
381         Grid<Real> *mantaRealGrid = (Grid<Real> *)mantaGrid;
382         vdbGrid = exportVDB<Real, openvdb::FloatGrid>(mantaRealGrid);
383         gridsVDB.push_back(vdbGrid);
384       }
385       else if (mantaGrid->getType() & GridBase::TypeVec3) {
386         debMsg("Writing vec3 grid '" << mantaGrid->getName() << "' to vdb file " << filename, 1);
387         gClass = (mantaGrid->getType() & GridBase::TypeMAC) ? openvdb::GRID_STAGGERED :
388                                                               openvdb::GRID_UNKNOWN;
389         Grid<Vec3> *mantaVec3Grid = (Grid<Vec3> *)mantaGrid;
390         vdbGrid = exportVDB<Vec3, openvdb::Vec3SGrid>(mantaVec3Grid);
391         gridsVDB.push_back(vdbGrid);
392       }
393       else {
394         errMsg("writeObjectsVDB: unknown grid type");
395         return 0;
396       }
397     }
398     else if (BasicParticleSystem *mantaPP = dynamic_cast<BasicParticleSystem *>(*iter)) {
399       debMsg("Writing particle system '" << mantaPP->getName()
400                                          << "' (and buffered pData) to vdb file " << filename,
401              1);
402       vdbGrid = exportVDB(mantaPP, pdbBuffer, skipDeletedParts, voxelSize, precision);
403       gridsVDB.push_back(vdbGrid);
404       pdbBuffer.clear();
405     }
406     // Particle data will only be saved if there is a particle system too.
407     else if (ParticleDataBase *mantaPPImpl = dynamic_cast<ParticleDataBase *>(*iter)) {
408       debMsg("Buffering particle data '" << mantaPPImpl->getName() << "' to vdb file " << filename,
409              1);
410       pdbBuffer.push_back(mantaPPImpl);
411     }
412     else {
413       errMsg("writeObjectsVDB: Unsupported Python object. Cannot write to .vdb file " << filename);
414       return 0;
415     }
416 
417     // Set additional grid attributes, e.g. name, grid class, compression level, etc.
418     if (vdbGrid) {
419       setGridOptions<openvdb::GridBase>(vdbGrid, objectName, gClass, voxelSize, precision);
420     }
421   }
422 
423   // Give out a warning if pData items were present but could not be saved due to missing particle
424   // system.
425   if (!pdbBuffer.empty()) {
426     for (ParticleDataBase *pdb : pdbBuffer) {
427       debMsg("writeObjectsVDB Warning: Particle data '"
428                  << pdb->getName()
429                  << "' has not been saved. It's parent particle system was needs to be given too.",
430              1);
431     }
432   }
433 
434   // Write only if the is at least one grid, optionally write with compression.
435   if (gridsVDB.size()) {
436     int vdb_flags = openvdb::io::COMPRESS_ACTIVE_MASK;
437     switch (compression) {
438       case COMPRESSION_NONE: {
439         vdb_flags = openvdb::io::COMPRESS_NONE;
440         break;
441       }
442       default:
443       case COMPRESSION_ZIP: {
444         vdb_flags |= openvdb::io::COMPRESS_ZIP;
445         break;
446       }
447       case COMPRESSION_BLOSC: {
448 #  if OPENVDB_BLOSC == 1
449         vdb_flags |= openvdb::io::COMPRESS_BLOSC;
450 #  else
451         debMsg("OpenVDB was built without Blosc support, using Zip compression instead", 1);
452         vdb_flags |= openvdb::io::COMPRESS_ZIP;
453 #  endif  // OPENVDB_BLOSC==1
454         break;
455       }
456     }
457     file.setCompression(vdb_flags);
458     file.write(gridsVDB);
459   }
460   file.close();
461   return 1;
462 }
463 
readObjectsVDB(const string & filename,std::vector<PbClass * > * objects,float worldSize)464 int readObjectsVDB(const string &filename, std::vector<PbClass *> *objects, float worldSize)
465 {
466 
467   openvdb::initialize();
468   openvdb::io::File file(filename);
469   openvdb::GridPtrVec gridsVDB;
470 
471   // Register custom codecs, this makes sure custom attributes can be read
472   registerCustomCodecs();
473 
474   try {
475     file.setCopyMaxBytes(0);
476     file.open();
477     gridsVDB = *(file.getGrids());
478     openvdb::MetaMap::Ptr metadata = file.getMetadata();
479     unusedParameter(metadata);  // Unused for now
480   }
481   catch (const openvdb::IoError &e) {
482     unusedParameter(e);  // Unused for now
483     debMsg("readObjectsVDB: Could not open vdb file " << filename, 1);
484     file.close();
485     return 0;
486   }
487   file.close();
488 
489   // A buffer to store a handle to pData objects. These will be read alongside a particle system.
490   std::vector<ParticleDataBase *> pdbBuffer;
491 
492   for (std::vector<PbClass *>::iterator iter = objects->begin(); iter != objects->end(); ++iter) {
493 
494     if (gridsVDB.empty()) {
495       debMsg("readObjectsVDB: No vdb grids in file " << filename, 1);
496     }
497     // If there is just one grid in this file, load it regardless of name match (to vdb caches per
498     // grid).
499     bool onlyGrid = (gridsVDB.size() == 1);
500 
501     PbClass *object = dynamic_cast<PbClass *>(*iter);
502     const Real dx = object->getParent()->getDx();
503     const Real voxelSize = worldSize * dx;
504 
505     // Particle data objects are treated separately - buffered and inserted when reading the
506     // particle system
507     if (ParticleDataBase *mantaPPImpl = dynamic_cast<ParticleDataBase *>(*iter)) {
508       debMsg("Buffering particle data '" << mantaPPImpl->getName() << "' from vdb file "
509                                          << filename,
510              1);
511       pdbBuffer.push_back(mantaPPImpl);
512       continue;
513     }
514 
515     // For every manta object, we loop through the vdb grid list and check for a match
516     for (const openvdb::GridBase::Ptr &vdbGrid : gridsVDB) {
517       bool nameMatch = (vdbGrid->getName() == (*iter)->getName());
518 
519       // Sanity checks: Only load valid grids and make sure names match.
520       if (!vdbGrid) {
521         debMsg("Skipping invalid vdb grid '" << vdbGrid->getName() << "' in file " << filename, 1);
522         continue;
523       }
524       if (!nameMatch && !onlyGrid) {
525         continue;
526       }
527       if (GridBase *mantaGrid = dynamic_cast<GridBase *>(*iter)) {
528 
529         if (mantaGrid->getType() & GridBase::TypeInt) {
530           openvdb::Int32Grid::Ptr vdbIntGrid = openvdb::gridPtrCast<openvdb::Int32Grid>(vdbGrid);
531           if (!vdbIntGrid)
532             continue;  // Sanity check: Cast can fail if onlyGrid is true but object count > 1
533 
534           Grid<int> *mantaIntGrid = (Grid<int> *)mantaGrid;
535           debMsg("Reading into grid '" << mantaGrid->getName() << "' from int grid '"
536                                        << vdbGrid->getName() << "' in vdb file " << filename,
537                  1);
538           importVDB<openvdb::Int32Grid, int>(vdbIntGrid, mantaIntGrid);
539         }
540         else if (mantaGrid->getType() & GridBase::TypeReal) {
541           openvdb::FloatGrid::Ptr vdbFloatGrid = openvdb::gridPtrCast<openvdb::FloatGrid>(vdbGrid);
542           if (!vdbFloatGrid)
543             continue;  // Sanity check: Cast can fail if onlyGrid is true but object count > 1
544 
545           Grid<Real> *mantaRealGrid = (Grid<Real> *)mantaGrid;
546           debMsg("Reading into grid '" << mantaGrid->getName() << "' from real grid '"
547                                        << vdbGrid->getName() << "' in vdb file " << filename,
548                  1);
549           importVDB<openvdb::FloatGrid, Real>(vdbFloatGrid, mantaRealGrid);
550         }
551         else if (mantaGrid->getType() & GridBase::TypeVec3) {
552           openvdb::Vec3SGrid::Ptr vdbVec3Grid = openvdb::gridPtrCast<openvdb::Vec3SGrid>(vdbGrid);
553           if (!vdbVec3Grid)
554             continue;  // Sanity check: Cast can fail if onlyGrid is true but object count > 1
555 
556           Grid<Vec3> *mantaVec3Grid = (Grid<Vec3> *)mantaGrid;
557           debMsg("Reading into grid '" << mantaGrid->getName() << "' from vec3 grid '"
558                                        << vdbGrid->getName() << "' in vdb file " << filename,
559                  1);
560           importVDB<openvdb::Vec3SGrid, Vec3>(vdbVec3Grid, mantaVec3Grid);
561         }
562         else {
563           errMsg("readObjectsVDB: unknown grid type");
564           return 0;
565         }
566       }
567       else if (BasicParticleSystem *mantaPP = dynamic_cast<BasicParticleSystem *>(*iter)) {
568         openvdb::points::PointDataGrid::Ptr vdbPointGrid =
569             openvdb::gridPtrCast<openvdb::points::PointDataGrid>(vdbGrid);
570         if (!vdbPointGrid)
571           continue;  // Sanity check: Cast can fail if onlyGrid is true but objects > 1
572 
573         debMsg("Reading into particle system '" << mantaPP->getName() << "' from particle system '"
574                                                 << vdbGrid->getName() << "' in vdb file "
575                                                 << filename,
576                1);
577         importVDB(vdbPointGrid, mantaPP, pdbBuffer, voxelSize);
578         pdbBuffer.clear();
579       }
580       else {
581         errMsg("readObjectsVDB: Unsupported Python object. Cannot read from .vdb file "
582                << filename);
583         return 0;
584       }
585     }
586   }
587 
588   // Give out a warning if pData items were present but could not be read due to missing particle
589   // system.
590   if (!pdbBuffer.empty()) {
591     for (ParticleDataBase *pdb : pdbBuffer) {
592       debMsg("readObjectsVDB Warning: Particle data '"
593                  << pdb->getName()
594                  << "' has not been read. The parent particle system needs to be given too.",
595              1);
596     }
597   }
598 
599   return 1;
600 }
601 
602 template void importVDB<int, int>(int vdbValue,
603                                   ParticleDataImpl<int> *to,
604                                   int index,
605                                   float voxelSize = 1.0);
606 template void importVDB<float, Real>(float vdbValue,
607                                      ParticleDataImpl<Real> *to,
608                                      int index,
609                                      float voxelSize = 1.0);
610 template void importVDB<openvdb::Vec3f, Vec3>(openvdb::Vec3s vdbValue,
611                                               ParticleDataImpl<Vec3> *to,
612                                               int index,
613                                               float voxelSize = 1.0);
614 
615 void importVDB(openvdb::points::PointDataGrid::Ptr from,
616                BasicParticleSystem *to,
617                std::vector<ParticleDataBase *> &toPData,
618                float voxelSize = 1.0);
619 template void importVDB<openvdb::Int32Grid, int>(openvdb::Int32Grid::Ptr from, Grid<int> *to);
620 template void importVDB<openvdb::FloatGrid, Real>(openvdb::FloatGrid::Ptr from, Grid<Real> *to);
621 template void importVDB<openvdb::Vec3SGrid, Vec3>(openvdb::Vec3SGrid::Ptr from, Grid<Vec3> *to);
622 
623 template openvdb::Int32Grid::Ptr exportVDB<int, openvdb::Int32Grid>(Grid<int> *from);
624 template openvdb::FloatGrid::Ptr exportVDB<Real, openvdb::FloatGrid>(Grid<Real> *from);
625 template openvdb::Vec3SGrid::Ptr exportVDB<Vec3, openvdb::Vec3SGrid>(Grid<Vec3> *from);
626 
627 openvdb::points::PointDataGrid::Ptr exportVDB(BasicParticleSystem *from,
628                                               std::vector<ParticleDataBase *> &fromPData,
629                                               bool skipDeletedParts = false,
630                                               float voxelSize = 1.0,
631                                               int precision = PRECISION_HALF);
632 template void exportVDB<int, int>(ParticleDataImpl<int> *from,
633                                   openvdb::points::PointDataGrid::Ptr to,
634                                   openvdb::tools::PointIndexGrid::Ptr pIndex,
635                                   bool skipDeletedParts = false,
636                                   int precision = PRECISION_HALF);
637 template void exportVDB<Real, float>(ParticleDataImpl<Real> *from,
638                                      openvdb::points::PointDataGrid::Ptr to,
639                                      openvdb::tools::PointIndexGrid::Ptr pIndex,
640                                      bool skipDeletedParts = false,
641                                      int precision = PRECISION_HALF);
642 template void exportVDB<Vec3, openvdb::Vec3s>(ParticleDataImpl<Vec3> *from,
643                                               openvdb::points::PointDataGrid::Ptr to,
644                                               openvdb::tools::PointIndexGrid::Ptr pIndex,
645                                               bool skipDeletedParts = false,
646                                               int precision = PRECISION_HALF);
647 
648 #else
649 
650 int writeObjectsVDB(const string &filename,
651                     std::vector<PbClass *> *objects,
652                     float worldSize,
653                     bool skipDeletedParts,
654                     int compression,
655                     int precision)
656 {
657   errMsg("Cannot save to .vdb file. Mantaflow has not been built with OpenVDB support.");
658   return 0;
659 }
660 
661 int readObjectsVDB(const string &filename, std::vector<PbClass *> *objects, float worldSize)
662 {
663   errMsg("Cannot load from .vdb file. Mantaflow has not been built with OpenVDB support.");
664   return 0;
665 }
666 
667 #endif  // OPENVDB==1
668 
669 }  // namespace Manta
670