OSG笔记:osgDB::Archive的疑惑点

注: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);
相关推荐
IT成长日记10 个月前
【自动化运维神器Ansible】Ansible常用模块之archive模块详解
运维·自动化·ansible·常用模块·archive