注:OSG 版本为3.6.3
1. osgDB::Archive 是否支持既读既写功能
参看函数声明及注释,粗略看接口,是读写要分开的,但本人还是抱有点幻想,希望其实现了既读既写的功能。
cpp
enum ArchiveStatus
{
READ,
WRITE,
CREATE
};
/**
Open an archive for reading or writing.*/
OSGDB_EXPORT Archive* openArchive(const std::string& filename, ReaderWriter::ArchiveStatus status, unsigned int indexBlockSizeHint=4096);
/** Open an archive for reading or writing.*/
OSGDB_EXPORT Archive* openArchive(const std::string& filename, ReaderWriter::ArchiveStatus status, unsigned int indexBlockSizeHint,Options* options);
进一步扒开源码看(OpenSceneGraph-3.6.3\src\osgPlugins\osga\OSGA_Archive.cpp),主要看_status这个成员变量,以及OSGA_Archive::write、OSGA_Archive::read函数可知,不支持既读既写的功能
cpp
bool OSGA_Archive::open(const std::string& filename, ArchiveStatus status, unsigned int indexBlockSize)
{
SERIALIZER();
_archiveFileName = filename;
if (status==READ)
{
_status = status;
_input.open(filename.c_str(), std::ios_base::binary | std::ios_base::in);
return _open(_input);
}
else
{
if (status==WRITE && open(filename,READ))
{
pos_type file_size( 0 );
_input.seekg( 0, std::ios_base::end );
file_size = ARCHIVE_POS( _input.tellg() );
if( _input.is_open() && file_size <= 0 )
{ // compute end of file postition manually ...
// seekp( 0, ios::end ), tellp( ) fails in 32 bit windows with files > 4 GiB
size_t BlockHeaderSize =
sizeof( unsigned int /*_blockSize*/ ) +
sizeof( pos_type /*_filePositionNextIndexBlock*/ ) +
sizeof( unsigned int /*_offsetOfNextAvailableSpace*/ );
for(IndexBlockList::iterator itr=_indexBlockList.begin();
itr!=_indexBlockList.end();
++itr)
{
pos_type end = (*itr)->getPosition() + BlockHeaderSize + (*itr)->getBlockSize();
if( file_size < end ) file_size = end;
}
for(FileNamePositionMap::iterator mitr=_indexMap.begin();
mitr!=_indexMap.end();
++mitr)
{
pos_type end = mitr->second.first + mitr->second.second;
if( file_size < end ) file_size = end;
}
}
_input.close();
_status = WRITE;
osgDB::open(_output, filename.c_str(), std::ios_base::binary | std::ios_base::in | std::ios_base::out);
OSG_INFO<<"File position after open = "<<ARCHIVE_POS( _output.tellp() )<<" is_open "<<_output.is_open()<<std::endl;
// place write position at end of file.
_output.seekp( STREAM_POS( file_size ) );
OSG_INFO<<"File position after seekp = "<<ARCHIVE_POS( _output.tellp() )<<std::endl;
OSG_INFO<<"OSGA_Archive::open("<<filename<<") open for writing"<<std::endl;
return true;
}
else // no file opened or using create so resort to creating the archive.
{
OSG_INFO<<"OSGA_Archive::open("<<filename<<"), archive being created."<<std::endl;
_status = WRITE;
osgDB::open(_output, filename.c_str(), std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
_output<<"osga";
_output.write(reinterpret_cast<const char*>(&ENDIAN_TEST_NUMBER),4);
_output.write(reinterpret_cast<char*>(&s_currentSupportedVersion),sizeof(float));
IndexBlock *indexBlock = new IndexBlock(indexBlockSize);
if (indexBlock)
{
indexBlock->write(_output);
_indexBlockList.push_back(indexBlock);
}
OSG_INFO<<"File position after write = "<<ARCHIVE_POS( _output.tellp() )<<std::endl;
return true;
}
}
}
ReaderWriter::ReadResult OSGA_Archive::read(const ReadFunctor& readFunctor)
{
SERIALIZER();
if (_status!=READ)
{
OSG_INFO<<"OSGA_Archive::readObject(obj, "<<readFunctor._filename<<") failed, archive opened as write only."<<std::endl;
return ReadResult(ReadResult::FILE_NOT_HANDLED);
}
FileNamePositionMap::const_iterator itr = _indexMap.find(readFunctor._filename);
if (itr==_indexMap.end())
{
OSG_INFO<<"OSGA_Archive::readObject(obj, "<<readFunctor._filename<<") failed, file not found in archive"<<std::endl;
return ReadResult(ReadResult::FILE_NOT_FOUND);
}
ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension(getLowerCaseFileExtension(readFunctor._filename));
if (!rw)
{
OSG_INFO<<"OSGA_Archive::readObject(obj, "<<readFunctor._filename<<") failed to find appropriate plugin to read file."<<std::endl;
return ReadResult(ReadResult::FILE_NOT_HANDLED);
}
OSG_INFO<<"OSGA_Archive::readObject(obj, "<<readFunctor._filename<<")"<<std::endl;
_input.seekg( STREAM_POS( itr->second.first ) );
// set up proxy stream buffer to provide the faked ending.
std::istream& ins = _input;
proxy_streambuf mystreambuf(ins.rdbuf(),itr->second.second);
ins.rdbuf(&mystreambuf);
ReaderWriter::ReadResult result = readFunctor.doRead(*rw, _input);
ins.rdbuf(mystreambuf._streambuf);
return result;
}
ReaderWriter::WriteResult OSGA_Archive::write(const WriteFunctor& writeFunctor)
{
SERIALIZER();
if (_status!=WRITE)
{
OSG_INFO<<"OSGA_Archive::write(obj, "<<writeFunctor._filename<<") failed, archive opened as read only."<<std::endl;
return WriteResult(WriteResult::FILE_NOT_HANDLED);
}
ReaderWriter* rw = osgDB::Registry::instance()->getReaderWriterForExtension(getLowerCaseFileExtension(writeFunctor._filename));
if (!rw)
{
OSG_INFO<<"OSGA_Archive::write(obj, "<<writeFunctor._filename<<") failed to find appropriate plugin to write file."<<std::endl;
return WriteResult(WriteResult::FILE_NOT_HANDLED);
}
OSG_INFO<<"OSGA_Archive::write(obj, "<<writeFunctor._filename<<")"<<std::endl;
pos_type position = ARCHIVE_POS( _output.tellp() );
WriteResult result = writeFunctor.doWrite(*rw,_output);
pos_type final_position = ARCHIVE_POS( _output.tellp() );
size_type size = size_type( final_position-position );
if (result.success())
{
OSG_INFO<<"Adding file "<<writeFunctor._filename<<" reference to archive."<<std::endl;
addFileReference(position, size, writeFunctor._filename);
}
else
{
OSG_INFO<<"writeFunctor unsuccessful."<<std::endl;
}
return result;
}
2. 先打开osgDB::Archive 读取,再打开同一个osgDB::Archive ,无法写入
cpp
//读取缓存
std::map<std::string, osg::ref_ptr<osg::Node>> mNodeCacheMap;
osg::ref_ptr<osgDB::Archive> pReadArchive = osgDB::openArchive( strPathFile + ".osga",osgDB::Archive::READ);
if (pReadArchive)
{
osgDB::Archive::FileNameList mAllFileNameList;
pReadArchive->getFileNames(mAllFileNameList);
for (const auto& mFileName : mAllFileNameList)
{
osgDB::ReaderWriter::ReadResult mResult = pReadArchive->readNode(mFileName);
if (mResult.success())
{
mNodeCacheMap[mFileName] = mResult.getNode();
}
}
pReadArchive->close();
}
//....
//osg::ref_ptr<osgDB::Archive> pWriteArchive = osgDB::openArchive(strPathFile + ".osga", osgDB::Archive::CREATE);
osg::ref_ptr<osgDB::Archive> pWriteArchive = osgDB::openArchive(strPathFile + ".osga", osgDB::Archive::WRITE);
//...
//使用缓存
std::string strElemFileName = strElemFileName + ".osgb";
auto itCache = mNodeCacheMap.find(strElemFileName);
if (itCache != mNodeCacheMap.end())
{
//直接显示
//...
}
else
{
//...
osg::ref_ptr<osgDB::ReaderWriter::Options> pWriteOptions = new osgDB::ReaderWriter::Options("Compressor=zlib");
osgDB::ReaderWriter::WriteResult mWriteRes = pWriteArchive->writeNode(*pShapeNode, strElemFileName, pWriteOptions);
}
然后不管是osgDB::Archive::WRITE还是osgDB::Archive::CREATE,均无法写入了,提示 ReadResult::FILE_NOT_HANDLED。
还是从源码(OpenSceneGraph-3.6.3\src\osgDB\Registry.cpp)找原因,看函数getRefFromArchiveCache,具体看了就应该知道,先前打开读取的Archive对象被缓存起来,用文件路径名当做key值的。所以第二次openArchive出来的是READ状态的Archive,自然就写不进了。
cpp
ReaderWriter::ReadResult Registry::openArchiveImplementation(const std::string& fileName, ReaderWriter::ArchiveStatus status, unsigned int indexBlockSizeHint, const Options* options)
{
osg::ref_ptr<osgDB::Archive> archive = getRefFromArchiveCache(fileName);
if (archive.valid()) return archive.get();
ReaderWriter::ReadResult result = readImplementation(ReadArchiveFunctor(fileName, status, indexBlockSizeHint, options),Options::CACHE_ARCHIVES);
// default to using caching archive if no options structure provided, but if options are provided use archives
// only if supplied.
if (result.validArchive() &&
(!options || (options->getObjectCacheHint() & Options::CACHE_ARCHIVES)) )
{
addToArchiveCache(fileName,result.getArchive());
}
return result;
}
osg::ref_ptr<osgDB::Archive> Registry::getRefFromArchiveCache(const std::string& fileName)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_archiveCacheMutex);
ArchiveCache::iterator itr = _archiveCache.find(fileName);
if (itr!=_archiveCache.end()) return itr->second;
else return 0;
}
解决方案是读取时不缓存ArChive对象
cpp
osg::ref_ptr<osgDB::ReaderWriter::Options> pReadOptions = new osgDB::ReaderWriter::Options();
pReadOptions->setObjectCacheHint(osgDB::ReaderWriter::Options::CacheHintOptions(
pReadOptions->getObjectCacheHint() & (~osgDB::ReaderWriter::Options::CacheHintOptions::CACHE_ARCHIVES)));
osg::ref_ptr<osgDB::Archive> pReadArchive = osgDB::openArchive(strPathFile + ".osga", osgDB::Archive::READ, 4096, pReadOptions);