1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3
4 /// @file io/File.cc
5
6 #include "File.h"
7
8 #include "TempFile.h"
9 #include <openvdb/Exceptions.h>
10 #include <openvdb/util/logging.h>
11 #include <cstdint>
12 #include <boost/iostreams/copy.hpp>
13 #ifndef _MSC_VER
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 #endif
18 #include <cassert>
19 #include <cstdlib> // for getenv(), strtoul()
20 #include <cstring> // for strerror_r()
21 #include <fstream>
22 #include <iostream>
23 #include <limits>
24 #include <sstream>
25
26
27 namespace openvdb {
28 OPENVDB_USE_VERSION_NAMESPACE
29 namespace OPENVDB_VERSION_NAME {
30 namespace io {
31
32 // Implementation details of the File class
33 struct File::Impl
34 {
35 enum { DEFAULT_COPY_MAX_BYTES = 500000000 }; // 500 MB
36
37 struct NoBBox {};
38
39 // Common implementation of the various File::readGrid() overloads,
40 // with and without bounding box clipping
41 template<typename BoxType>
readGridopenvdb::OPENVDB_VERSION_NAME::io::File::Impl42 static GridBase::Ptr readGrid(const File& file, const GridDescriptor& gd, const BoxType& bbox)
43 {
44 // This method should not be called for files that don't contain grid offsets.
45 assert(file.inputHasGridOffsets());
46
47 GridBase::Ptr grid = file.createGrid(gd);
48 gd.seekToGrid(file.inputStream());
49 unarchive(file, grid, gd, bbox);
50 return grid;
51 }
52
unarchiveopenvdb::OPENVDB_VERSION_NAME::io::File::Impl53 static void unarchive(const File& file, GridBase::Ptr& grid,
54 const GridDescriptor& gd, NoBBox)
55 {
56 file.Archive::readGrid(grid, gd, file.inputStream());
57 }
58
unarchiveopenvdb::OPENVDB_VERSION_NAME::io::File::Impl59 static void unarchive(const File& file, GridBase::Ptr& grid,
60 const GridDescriptor& gd, const CoordBBox& indexBBox)
61 {
62 file.Archive::readGrid(grid, gd, file.inputStream(), indexBBox);
63 }
64
unarchiveopenvdb::OPENVDB_VERSION_NAME::io::File::Impl65 static void unarchive(const File& file, GridBase::Ptr& grid,
66 const GridDescriptor& gd, const BBoxd& worldBBox)
67 {
68 file.Archive::readGrid(grid, gd, file.inputStream(), worldBBox);
69 }
70
getDefaultCopyMaxBytesopenvdb::OPENVDB_VERSION_NAME::io::File::Impl71 static Index64 getDefaultCopyMaxBytes()
72 {
73 Index64 result = DEFAULT_COPY_MAX_BYTES;
74 if (const char* s = std::getenv("OPENVDB_DELAYED_LOAD_COPY_MAX_BYTES")) {
75 char* endptr = nullptr;
76 result = std::strtoul(s, &endptr, /*base=*/10);
77 }
78 return result;
79 }
80
81 std::string mFilename;
82 // The file-level metadata
83 MetaMap::Ptr mMeta;
84 // The memory-mapped file
85 MappedFile::Ptr mFileMapping;
86 // The buffer for the input stream, if it is a memory-mapped file
87 SharedPtr<std::streambuf> mStreamBuf;
88 // The file stream that is open for reading
89 std::unique_ptr<std::istream> mInStream;
90 // File-level stream metadata (file format, compression, etc.)
91 StreamMetadata::Ptr mStreamMetadata;
92 // Flag indicating if we have read in the global information (header,
93 // metadata, and grid descriptors) for this VDB file
94 bool mIsOpen;
95 // File size limit for copying during delayed loading
96 Index64 mCopyMaxBytes;
97 // Grid descriptors for all grids stored in the file, indexed by grid name
98 NameMap mGridDescriptors;
99 // All grids, indexed by unique name (used only when mHasGridOffsets is false)
100 Archive::NamedGridMap mNamedGrids;
101 // All grids stored in the file (used only when mHasGridOffsets is false)
102 GridPtrVecPtr mGrids;
103 }; // class File::Impl
104
105
106 ////////////////////////////////////////
107
108
File(const std::string & filename)109 File::File(const std::string& filename): mImpl(new Impl)
110 {
111 mImpl->mFilename = filename;
112 mImpl->mIsOpen = false;
113 mImpl->mCopyMaxBytes = Impl::getDefaultCopyMaxBytes();
114 setInputHasGridOffsets(true);
115 }
116
117
~File()118 File::~File()
119 {
120 }
121
122
File(const File & other)123 File::File(const File& other)
124 : Archive(other)
125 , mImpl(new Impl)
126 {
127 *this = other;
128 }
129
130
131 File&
operator =(const File & other)132 File::operator=(const File& other)
133 {
134 if (&other != this) {
135 Archive::operator=(other);
136 const Impl& otherImpl = *other.mImpl;
137 mImpl->mFilename = otherImpl.mFilename;
138 mImpl->mMeta = otherImpl.mMeta;
139 mImpl->mIsOpen = false; // don't want two file objects reading from the same stream
140 mImpl->mCopyMaxBytes = otherImpl.mCopyMaxBytes;
141 mImpl->mGridDescriptors = otherImpl.mGridDescriptors;
142 mImpl->mNamedGrids = otherImpl.mNamedGrids;
143 mImpl->mGrids = otherImpl.mGrids;
144 }
145 return *this;
146 }
147
148
149 SharedPtr<Archive>
copy() const150 File::copy() const
151 {
152 return SharedPtr<Archive>{new File{*this}};
153 }
154
155
156 ////////////////////////////////////////
157
158
159 const std::string&
filename() const160 File::filename() const
161 {
162 return mImpl->mFilename;
163 }
164
165
166 MetaMap::Ptr
fileMetadata()167 File::fileMetadata()
168 {
169 return mImpl->mMeta;
170 }
171
172 MetaMap::ConstPtr
fileMetadata() const173 File::fileMetadata() const
174 {
175 return mImpl->mMeta;
176 }
177
178
179 const File::NameMap&
gridDescriptors() const180 File::gridDescriptors() const
181 {
182 return mImpl->mGridDescriptors;
183 }
184
185 File::NameMap&
gridDescriptors()186 File::gridDescriptors()
187 {
188 return mImpl->mGridDescriptors;
189 }
190
191
192 std::istream&
inputStream() const193 File::inputStream() const
194 {
195 if (!mImpl->mInStream) {
196 OPENVDB_THROW(IoError, filename() << " is not open for reading");
197 }
198 return *mImpl->mInStream;
199 }
200
201
202 ////////////////////////////////////////
203
204
205 Index64
getSize() const206 File::getSize() const
207 {
208 /// @internal boost::filesystem::file_size() would be a more portable alternative,
209 /// but as of 9/2014, Houdini ships without the Boost.Filesystem library,
210 /// which makes it much less convenient to use that library.
211
212 Index64 result = std::numeric_limits<Index64>::max();
213
214 std::string mesg = "could not get size of file " + filename();
215
216 #ifdef _MSC_VER
217 // Get the file size by seeking to the end of the file.
218 std::ifstream fstrm(filename());
219 if (fstrm) {
220 fstrm.seekg(0, fstrm.end);
221 result = static_cast<Index64>(fstrm.tellg());
222 } else {
223 OPENVDB_THROW(IoError, mesg);
224 }
225 #else
226 // Get the file size using the stat() system call.
227 struct stat info;
228 if (0 != ::stat(filename().c_str(), &info)) {
229 std::string s = getErrorString();
230 if (!s.empty()) mesg += " (" + s + ")";
231 OPENVDB_THROW(IoError, mesg);
232 }
233 if (!S_ISREG(info.st_mode)) {
234 mesg += " (not a regular file)";
235 OPENVDB_THROW(IoError, mesg);
236 }
237 result = static_cast<Index64>(info.st_size);
238 #endif
239
240 return result;
241 }
242
243
244 Index64
copyMaxBytes() const245 File::copyMaxBytes() const
246 {
247 return mImpl->mCopyMaxBytes;
248 }
249
250
251 void
setCopyMaxBytes(Index64 bytes)252 File::setCopyMaxBytes(Index64 bytes)
253 {
254 mImpl->mCopyMaxBytes = bytes;
255 }
256
257
258 ////////////////////////////////////////
259
260
261 bool
isOpen() const262 File::isOpen() const
263 {
264 return mImpl->mIsOpen;
265 }
266
267
268 bool
open(bool delayLoad,const MappedFile::Notifier & notifier)269 File::open(bool delayLoad, const MappedFile::Notifier& notifier)
270 {
271 if (isOpen()) {
272 OPENVDB_THROW(IoError, filename() << " is already open");
273 }
274 mImpl->mInStream.reset();
275
276 // Open the file.
277 std::unique_ptr<std::istream> newStream;
278 SharedPtr<std::streambuf> newStreamBuf;
279 MappedFile::Ptr newFileMapping;
280 if (!delayLoad || !Archive::isDelayedLoadingEnabled()) {
281 newStream.reset(new std::ifstream(
282 filename().c_str(), std::ios_base::in | std::ios_base::binary));
283 } else {
284 bool isTempFile = false;
285 std::string fname = filename();
286 if (getSize() < copyMaxBytes()) {
287 // If the file is not too large, make a temporary private copy of it
288 // and open the copy instead. The original file can then be modified
289 // or removed without affecting delayed load.
290 try {
291 TempFile tempFile;
292 std::ifstream fstrm(filename().c_str(),
293 std::ios_base::in | std::ios_base::binary);
294 boost::iostreams::copy(fstrm, tempFile);
295 fname = tempFile.filename();
296 isTempFile = true;
297 } catch (std::exception& e) {
298 std::string mesg;
299 if (e.what()) mesg = std::string(" (") + e.what() + ")";
300 OPENVDB_LOG_WARN("failed to create a temporary copy of " << filename()
301 << " for delayed loading" << mesg
302 << "; will read directly from " << filename() << " instead");
303 }
304 }
305
306 // While the file is open, its mapping, stream buffer and stream
307 // must all be maintained. Once the file is closed, the buffer and
308 // the stream can be discarded, but the mapping needs to persist
309 // if any grids were lazily loaded.
310 try {
311 newFileMapping.reset(new MappedFile(fname, /*autoDelete=*/isTempFile));
312 newStreamBuf = newFileMapping->createBuffer();
313 newStream.reset(new std::istream(newStreamBuf.get()));
314 } catch (std::exception& e) {
315 std::ostringstream ostr;
316 ostr << "could not open file " << filename();
317 if (e.what() != nullptr) ostr << " (" << e.what() << ")";
318 OPENVDB_THROW(IoError, ostr.str());
319 }
320 }
321
322 if (newStream->fail()) {
323 OPENVDB_THROW(IoError, "could not open file " << filename());
324 }
325
326 // Read in the file header.
327 bool newFile = false;
328 try {
329 newFile = Archive::readHeader(*newStream);
330 } catch (IoError& e) {
331 if (e.what() && std::string("not a VDB file") == e.what()) {
332 // Rethrow, adding the filename.
333 OPENVDB_THROW(IoError, filename() << " is not a VDB file");
334 }
335 throw;
336 }
337
338 mImpl->mFileMapping = newFileMapping;
339 if (mImpl->mFileMapping) mImpl->mFileMapping->setNotifier(notifier);
340 mImpl->mStreamBuf = newStreamBuf;
341 mImpl->mInStream.swap(newStream);
342
343 // Tag the input stream with the file format and library version numbers
344 // and other metadata.
345 mImpl->mStreamMetadata.reset(new StreamMetadata);
346 mImpl->mStreamMetadata->setSeekable(true);
347 io::setStreamMetadataPtr(inputStream(), mImpl->mStreamMetadata, /*transfer=*/false);
348 Archive::setFormatVersion(inputStream());
349 Archive::setLibraryVersion(inputStream());
350 Archive::setDataCompression(inputStream());
351 io::setMappedFilePtr(inputStream(), mImpl->mFileMapping);
352
353 // Read in the VDB metadata.
354 mImpl->mMeta = MetaMap::Ptr(new MetaMap);
355 mImpl->mMeta->readMeta(inputStream());
356
357 if (!inputHasGridOffsets()) {
358 OPENVDB_LOG_DEBUG_RUNTIME("file " << filename() << " does not support partial reading");
359
360 mImpl->mGrids.reset(new GridPtrVec);
361 mImpl->mNamedGrids.clear();
362
363 // Stream in the entire contents of the file and append all grids to mGrids.
364 const int32_t gridCount = readGridCount(inputStream());
365 for (int32_t i = 0; i < gridCount; ++i) {
366 GridDescriptor gd;
367 gd.read(inputStream());
368
369 GridBase::Ptr grid = createGrid(gd);
370 Archive::readGrid(grid, gd, inputStream());
371
372 gridDescriptors().insert(std::make_pair(gd.gridName(), gd));
373 mImpl->mGrids->push_back(grid);
374 mImpl->mNamedGrids[gd.uniqueName()] = grid;
375 }
376 // Connect instances (grids that share trees with other grids).
377 for (NameMapCIter it = gridDescriptors().begin(); it != gridDescriptors().end(); ++it) {
378 Archive::connectInstance(it->second, mImpl->mNamedGrids);
379 }
380 } else {
381 // Read in just the grid descriptors.
382 readGridDescriptors(inputStream());
383 }
384
385 mImpl->mIsOpen = true;
386 return newFile; // true if file is not identical to opened file
387 }
388
389
390 void
close()391 File::close()
392 {
393 // Reset all data.
394 mImpl->mMeta.reset();
395 mImpl->mGridDescriptors.clear();
396 mImpl->mGrids.reset();
397 mImpl->mNamedGrids.clear();
398 mImpl->mInStream.reset();
399 mImpl->mStreamBuf.reset();
400 mImpl->mStreamMetadata.reset();
401 mImpl->mFileMapping.reset();
402
403 mImpl->mIsOpen = false;
404 setInputHasGridOffsets(true);
405 }
406
407
408 ////////////////////////////////////////
409
410
411 bool
hasGrid(const Name & name) const412 File::hasGrid(const Name& name) const
413 {
414 if (!isOpen()) {
415 OPENVDB_THROW(IoError, filename() << " is not open for reading");
416 }
417 return (findDescriptor(name) != gridDescriptors().end());
418 }
419
420
421 MetaMap::Ptr
getMetadata() const422 File::getMetadata() const
423 {
424 if (!isOpen()) {
425 OPENVDB_THROW(IoError, filename() << " is not open for reading");
426 }
427 // Return a deep copy of the file-level metadata, which was read
428 // when the file was opened.
429 return MetaMap::Ptr(new MetaMap(*mImpl->mMeta));
430 }
431
432
433 GridPtrVecPtr
getGrids() const434 File::getGrids() const
435 {
436 if (!isOpen()) {
437 OPENVDB_THROW(IoError, filename() << " is not open for reading");
438 }
439
440 GridPtrVecPtr ret;
441 if (!inputHasGridOffsets()) {
442 // If the input file doesn't have grid offsets, then all of the grids
443 // have already been streamed in and stored in mGrids.
444 ret = mImpl->mGrids;
445 } else {
446 ret.reset(new GridPtrVec);
447
448 Archive::NamedGridMap namedGrids;
449
450 // Read all grids represented by the GridDescriptors.
451 for (NameMapCIter i = gridDescriptors().begin(), e = gridDescriptors().end(); i != e; ++i) {
452 const GridDescriptor& gd = i->second;
453 GridBase::Ptr grid = readGrid(gd);
454 ret->push_back(grid);
455 namedGrids[gd.uniqueName()] = grid;
456 }
457
458 // Connect instances (grids that share trees with other grids).
459 for (NameMapCIter i = gridDescriptors().begin(), e = gridDescriptors().end(); i != e; ++i) {
460 Archive::connectInstance(i->second, namedGrids);
461 }
462 }
463 return ret;
464 }
465
466
467 GridBase::Ptr
retrieveCachedGrid(const Name & name) const468 File::retrieveCachedGrid(const Name& name) const
469 {
470 // If the file has grid offsets, grids are read on demand
471 // and not cached in mNamedGrids.
472 if (inputHasGridOffsets()) return GridBase::Ptr();
473
474 // If the file does not have grid offsets, mNamedGrids should already
475 // contain the entire contents of the file.
476
477 // Search by unique name.
478 Archive::NamedGridMap::const_iterator it =
479 mImpl->mNamedGrids.find(GridDescriptor::stringAsUniqueName(name));
480 // If not found, search by grid name.
481 if (it == mImpl->mNamedGrids.end()) it = mImpl->mNamedGrids.find(name);
482 if (it == mImpl->mNamedGrids.end()) {
483 OPENVDB_THROW(KeyError, filename() << " has no grid named \"" << name << "\"");
484 }
485 return it->second;
486 }
487
488
489 ////////////////////////////////////////
490
491
492 GridPtrVecPtr
readAllGridMetadata()493 File::readAllGridMetadata()
494 {
495 if (!isOpen()) {
496 OPENVDB_THROW(IoError, filename() << " is not open for reading");
497 }
498
499 GridPtrVecPtr ret(new GridPtrVec);
500
501 if (!inputHasGridOffsets()) {
502 // If the input file doesn't have grid offsets, then all of the grids
503 // have already been streamed in and stored in mGrids.
504 for (size_t i = 0, N = mImpl->mGrids->size(); i < N; ++i) {
505 // Return copies of the grids, but with empty trees.
506 ret->push_back((*mImpl->mGrids)[i]->copyGridWithNewTree());
507 }
508 } else {
509 // Read just the metadata and transforms for all grids.
510 for (NameMapCIter i = gridDescriptors().begin(), e = gridDescriptors().end(); i != e; ++i) {
511 const GridDescriptor& gd = i->second;
512 GridBase::ConstPtr grid = readGridPartial(gd, /*readTopology=*/false);
513 // Return copies of the grids, but with empty trees.
514 // (As of 0.98.0, at least, it would suffice to just const cast
515 // the grid pointers returned by readGridPartial(), but shallow
516 // copying the grids helps to ensure future compatibility.)
517 ret->push_back(grid->copyGridWithNewTree());
518 }
519 }
520 return ret;
521 }
522
523
524 GridBase::Ptr
readGridMetadata(const Name & name)525 File::readGridMetadata(const Name& name)
526 {
527 if (!isOpen()) {
528 OPENVDB_THROW(IoError, filename() << " is not open for reading.");
529 }
530
531 GridBase::ConstPtr ret;
532 if (!inputHasGridOffsets()) {
533 // Retrieve the grid from mGrids, which should already contain
534 // the entire contents of the file.
535 ret = readGrid(name);
536 } else {
537 NameMapCIter it = findDescriptor(name);
538 if (it == gridDescriptors().end()) {
539 OPENVDB_THROW(KeyError, filename() << " has no grid named \"" << name << "\"");
540 }
541
542 // Seek to and read in the grid from the file.
543 const GridDescriptor& gd = it->second;
544 ret = readGridPartial(gd, /*readTopology=*/false);
545 }
546 return ret->copyGridWithNewTree();
547 }
548
549
550 ////////////////////////////////////////
551
552
553 GridBase::Ptr
readGrid(const Name & name)554 File::readGrid(const Name& name)
555 {
556 return readGridByName(name, BBoxd());
557 }
558
559
560 GridBase::Ptr
readGrid(const Name & name,const BBoxd & bbox)561 File::readGrid(const Name& name, const BBoxd& bbox)
562 {
563 return readGridByName(name, bbox);
564 }
565
566
567 GridBase::Ptr
readGridByName(const Name & name,const BBoxd & bbox)568 File::readGridByName(const Name& name, const BBoxd& bbox)
569 {
570 if (!isOpen()) {
571 OPENVDB_THROW(IoError, filename() << " is not open for reading.");
572 }
573
574 const bool clip = bbox.isSorted();
575
576 // If a grid with the given name was already read and cached
577 // (along with the entire contents of the file, because the file
578 // doesn't support random access), retrieve and return it.
579 GridBase::Ptr grid = retrieveCachedGrid(name);
580 if (grid) {
581 if (clip) {
582 grid = grid->deepCopyGrid();
583 grid->clipGrid(bbox);
584 }
585 return grid;
586 }
587
588 NameMapCIter it = findDescriptor(name);
589 if (it == gridDescriptors().end()) {
590 OPENVDB_THROW(KeyError, filename() << " has no grid named \"" << name << "\"");
591 }
592
593 // Seek to and read in the grid from the file.
594 const GridDescriptor& gd = it->second;
595 grid = (clip ? readGrid(gd, bbox) : readGrid(gd));
596
597 if (gd.isInstance()) {
598 /// @todo Refactor to share code with Archive::connectInstance()?
599 NameMapCIter parentIt =
600 findDescriptor(GridDescriptor::nameAsString(gd.instanceParentName()));
601 if (parentIt == gridDescriptors().end()) {
602 OPENVDB_THROW(KeyError, "missing instance parent \""
603 << GridDescriptor::nameAsString(gd.instanceParentName())
604 << "\" for grid " << GridDescriptor::nameAsString(gd.uniqueName())
605 << " in file " << filename());
606 }
607
608 GridBase::Ptr parent;
609 if (clip) {
610 const CoordBBox indexBBox = grid->constTransform().worldToIndexNodeCentered(bbox);
611 parent = readGrid(parentIt->second, indexBBox);
612 } else {
613 parent = readGrid(parentIt->second);
614 }
615 if (parent) grid->setTree(parent->baseTreePtr());
616 }
617 return grid;
618 }
619
620
621 ////////////////////////////////////////
622
623
624 void
writeGrids(const GridCPtrVec & grids,const MetaMap & meta) const625 File::writeGrids(const GridCPtrVec& grids, const MetaMap& meta) const
626 {
627 if (isOpen()) {
628 OPENVDB_THROW(IoError,
629 filename() << " cannot be written because it is open for reading");
630 }
631
632 // Create a file stream and write it out.
633 std::ofstream file;
634 file.open(filename().c_str(),
635 std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
636
637 if (file.fail()) {
638 OPENVDB_THROW(IoError, "could not open " << filename() << " for writing");
639 }
640
641 // Write out the vdb.
642 Archive::write(file, grids, /*seekable=*/true, meta);
643
644 file.close();
645 }
646
647
648 ////////////////////////////////////////
649
650
651 void
readGridDescriptors(std::istream & is)652 File::readGridDescriptors(std::istream& is)
653 {
654 // This method should not be called for files that don't contain grid offsets.
655 assert(inputHasGridOffsets());
656
657 gridDescriptors().clear();
658
659 for (int32_t i = 0, N = readGridCount(is); i < N; ++i) {
660 // Read the grid descriptor.
661 GridDescriptor gd;
662 gd.read(is);
663
664 // Add the descriptor to the dictionary.
665 gridDescriptors().insert(std::make_pair(gd.gridName(), gd));
666
667 // Skip forward to the next descriptor.
668 gd.seekToEnd(is);
669 }
670 }
671
672
673 ////////////////////////////////////////
674
675
676 File::NameMapCIter
findDescriptor(const Name & name) const677 File::findDescriptor(const Name& name) const
678 {
679 const Name uniqueName = GridDescriptor::stringAsUniqueName(name);
680
681 // Find all descriptors with the given grid name.
682 std::pair<NameMapCIter, NameMapCIter> range = gridDescriptors().equal_range(name);
683
684 if (range.first == range.second) {
685 // If no descriptors were found with the given grid name, the name might have
686 // a suffix ("name[N]"). In that case, remove the "[N]" suffix and search again.
687 range = gridDescriptors().equal_range(GridDescriptor::stripSuffix(uniqueName));
688 }
689
690 const size_t count = size_t(std::distance(range.first, range.second));
691 if (count > 1 && name == uniqueName) {
692 OPENVDB_LOG_WARN(filename() << " has more than one grid named \"" << name << "\"");
693 }
694
695 NameMapCIter ret = gridDescriptors().end();
696
697 if (count > 0) {
698 if (name == uniqueName) {
699 // If the given grid name is unique or if no "[N]" index was given,
700 // use the first matching descriptor.
701 ret = range.first;
702 } else {
703 // If the given grid name has a "[N]" index, find the descriptor
704 // with a matching unique name.
705 for (NameMapCIter it = range.first; it != range.second; ++it) {
706 const Name candidateName = it->second.uniqueName();
707 if (candidateName == uniqueName || candidateName == name) {
708 ret = it;
709 break;
710 }
711 }
712 }
713 }
714 return ret;
715 }
716
717
718 ////////////////////////////////////////
719
720
721 GridBase::Ptr
createGrid(const GridDescriptor & gd) const722 File::createGrid(const GridDescriptor& gd) const
723 {
724 // Create the grid.
725 if (!GridBase::isRegistered(gd.gridType())) {
726 OPENVDB_THROW(KeyError, "Cannot read grid "
727 << GridDescriptor::nameAsString(gd.uniqueName())
728 << " from " << filename() << ": grid type "
729 << gd.gridType() << " is not registered");
730 }
731
732 GridBase::Ptr grid = GridBase::createGrid(gd.gridType());
733 if (grid) grid->setSaveFloatAsHalf(gd.saveFloatAsHalf());
734
735 return grid;
736 }
737
738
739 GridBase::ConstPtr
readGridPartial(const GridDescriptor & gd,bool readTopology) const740 File::readGridPartial(const GridDescriptor& gd, bool readTopology) const
741 {
742 // This method should not be called for files that don't contain grid offsets.
743 assert(inputHasGridOffsets());
744
745 GridBase::Ptr grid = createGrid(gd);
746
747 // Seek to grid.
748 gd.seekToGrid(inputStream());
749
750 // Read the grid partially.
751 readGridPartial(grid, inputStream(), gd.isInstance(), readTopology);
752
753 // Promote to a const grid.
754 GridBase::ConstPtr constGrid = grid;
755
756 return constGrid;
757 }
758
759
760 GridBase::Ptr
readGrid(const GridDescriptor & gd) const761 File::readGrid(const GridDescriptor& gd) const
762 {
763 return Impl::readGrid(*this, gd, Impl::NoBBox());
764 }
765
766
767 GridBase::Ptr
readGrid(const GridDescriptor & gd,const BBoxd & bbox) const768 File::readGrid(const GridDescriptor& gd, const BBoxd& bbox) const
769 {
770 return Impl::readGrid(*this, gd, bbox);
771 }
772
773
774 GridBase::Ptr
readGrid(const GridDescriptor & gd,const CoordBBox & bbox) const775 File::readGrid(const GridDescriptor& gd, const CoordBBox& bbox) const
776 {
777 return Impl::readGrid(*this, gd, bbox);
778 }
779
780
781 void
readGridPartial(GridBase::Ptr grid,std::istream & is,bool isInstance,bool readTopology) const782 File::readGridPartial(GridBase::Ptr grid, std::istream& is,
783 bool isInstance, bool readTopology) const
784 {
785 // This method should not be called for files that don't contain grid offsets.
786 assert(inputHasGridOffsets());
787
788 // This code needs to stay in sync with io::Archive::readGrid(), in terms of
789 // the order of operations.
790 readGridCompression(is);
791 grid->readMeta(is);
792
793 // drop DelayedLoadMetadata from the grid as it is only useful for IO
794 if ((*grid)[GridBase::META_FILE_DELAYED_LOAD]) {
795 grid->removeMeta(GridBase::META_FILE_DELAYED_LOAD);
796 }
797
798 if (getFormatVersion(is) >= OPENVDB_FILE_VERSION_GRID_INSTANCING) {
799 grid->readTransform(is);
800 if (!isInstance && readTopology) {
801 grid->readTopology(is);
802 }
803 } else {
804 if (readTopology) {
805 grid->readTopology(is);
806 grid->readTransform(is);
807 }
808 }
809 }
810
811
812 ////////////////////////////////////////
813
814
815 File::NameIterator
beginName() const816 File::beginName() const
817 {
818 if (!isOpen()) {
819 OPENVDB_THROW(IoError, filename() << " is not open for reading");
820 }
821 return File::NameIterator(gridDescriptors().begin());
822 }
823
824
825 File::NameIterator
endName() const826 File::endName() const
827 {
828 return File::NameIterator(gridDescriptors().end());
829 }
830
831 } // namespace io
832 } // namespace OPENVDB_VERSION_NAME
833 } // namespace openvdb
834