osg之osgDB讀取文件詳解

osg、osgEarth所有文件都是通過osgDB庫來讀取,通過Registry來查找文件拓展名對應的osg庫(Registry是一個單例類,這個類特別重要,建議通讀代碼加深對此的理解),根據一定規則拼接成完成的osg庫名並加載,通過ReaderWriter對象來完成節點的讀取(ReaderWriter是讀寫節點的基類,可通過派生此類重寫讀寫方法實現自己的讀寫格式)。

一、osg讀取一個節點的方法

osg::ref_ptr<osg::Node> pNode = osgDB::readNodeFile("cow.osgt");

二、ReadFile這個文件是osgDB讀取所有文件類型的接口封裝,但是最終的讀取都是由註冊到Registry的代理來完成。
readNodeFile這個方法有兩個參數一個是filename,一個是options。Options是一個對讀取節點進行出來的對象,可對節點的三角面片進行進行優化,這個比較複雜,後面作爲重點詳細介紹,再次一筆帶過。

/** Read an osg::Node from file. 
 1. Return valid osg::Node on success,
 2. return NULL on failure.
 3. The osgDB::Registry is used to load the appropriate ReaderWriter plugin
 4. for the filename extension, and this plugin then handles the request
 5. to read the specified file.*/
inline osg::Node* readNodeFile(const std::string& filename)
{
    return readNodeFile(filename,Registry::instance()->getOptions());
}
Node* osgDB::readNodeFile(const std::string& filename,const Options* options)
{
    ReaderWriter::ReadResult rr = Registry::instance()->readNode(filename,options);
    if (rr.validNode()) return rr.takeNode();
    if (rr.error()) OSG_WARN << rr.message() << std::endl;
    if (rr.notEnoughMemory()) OSG_INFO << "Not enought memory to load file "<<filename << std::endl;
    return NULL;
}

三、Registry這個類通過RegisterReaderWriterProxy類完成文件的讀取,具體如下:
1、如果使用緩衝,我們會優先從緩衝文件讀取節點
2、通過文件拓展名查找對應的模塊名(例如:osgDB),根據規則拼接成完整的庫名(osgdb_osg.dll),同時加載該庫文件。
3、ReadFunctor對象的創建,以及通過doRead方法完成節點的讀取,此方法有一個ReaderWriter對象參數,通過此對象的readNode方法完成最終的節點讀取。我們也可以派生ReadWriter類重寫其中的方法來完成自定義格式的文件讀取。核心代碼如下:

//1.通過讀文件回調完成節點讀取
//2.通過readNodeImplementation方法讀取節點,最常用的方法
ReaderWriter::ReadResult readNode(const std::string& fileName, const Options* options, bool buildKdTreeIfRequired = true)
{
    ReaderWriter::ReadResult result;
    if (options && options->getReadFileCallback()) result = options->getReadFileCallback()->readNode(fileName, options);
    else if (_readFileCallback.valid()) result = _readFileCallback->readNode(fileName, options);
    else result = readNodeImplementation(fileName, options);

    if (buildKdTreeIfRequired) _buildKdTreeIfRequired(result, options);

    return result;
}
//ReadNodeFunctor對象至關重要,通過調用此對象的doRead方法完成讀節點讀取
ReaderWriter::ReadResult Registry::readNodeImplementation(const std::string& fileName,const Options* options)
{
#if 0

    osg::Timer_t startTick = osg::Timer::instance()->tick();
    ReaderWriter::ReadResult result = readImplementation(ReadNodeFunctor(fileName, options),Options::CACHE_NODES);
    osg::Timer_t endTick = osg::Timer::instance()->tick();
    OSG_NOTICE<<"time to load "<<fileName<<" "<<osg::Timer::instance()->delta_m(startTick, endTick)<<"ms"<<std::endl;
    return result;

#else

    return readImplementation(ReadNodeFunctor(fileName, options),Options::CACHE_NODES);
                              
#endif
}
//讀取節點的Functor結構,類似的有:ReadShaderFunctor、ReadImageFunctor等等
struct Registry::ReadNodeFunctor : public Registry::ReadFunctor
{
    ReadNodeFunctor(const std::string& filename, const Options* options):ReadFunctor(filename,options) {}

    virtual ReaderWriter::ReadResult doRead(ReaderWriter& rw) const { return rw.readNode(_filename, _options); }    
    virtual bool isValid(ReaderWriter::ReadResult& readResult) const { return readResult.validNode(); }
    virtual bool isValid(osg::Object* object) const { return dynamic_cast<osg::Node*>(object)!=0;  }

};
//如果啓用對象緩衝,我們可以優先從緩衝文件中讀取節點,否則通過read方法讀取節點
ReaderWriter::ReadResult Registry::readImplementation(const ReadFunctor& readFunctor,Options::CacheHintOptions cacheHint)
{
    std::string file(readFunctor._filename);

    bool useObjectCache=false;
    //Note CACHE_ARCHIVES has a different object that it caches to so it will never be used here
    if (cacheHint!=Options::CACHE_ARCHIVES)
    {
        const Options* options=readFunctor._options;
        useObjectCache=options ? (options->getObjectCacheHint()&cacheHint)!=0: false;
    }
    if (useObjectCache)
    {
        // search for entry in the object cache.
        {
            OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_objectCacheMutex);
            ObjectCache::iterator oitr=_objectCache.find(file);
            if (oitr!=_objectCache.end())
            {
                OSG_NOTIFY(INFO)<<"returning cached instanced of "<<file<<std::endl;
                if (readFunctor.isValid(oitr->second.first.get())) return ReaderWriter::ReadResult(oitr->second.first.get(), ReaderWriter::ReadResult::FILE_LOADED_FROM_CACHE);
                else return ReaderWriter::ReadResult("Error file does not contain an osg::Object");
            }
        }
        
        ReaderWriter::ReadResult rr = read(readFunctor);
        if (rr.validObject()) 
        {
            // update cache with new entry.
            OSG_NOTIFY(INFO)<<"Adding to object cache "<<file<<std::endl;
            addEntryToObjectCache(file,rr.getObject());
        }
        else
        {
            OSG_NOTIFY(INFO)<<"No valid object found for "<<file<<std::endl;
        }

        return rr;

    }
    else
    {
        ReaderWriter::ReadResult rr = read(readFunctor);
        return rr;
    }
}
//得到文件的拓展名,用於在_extAliasMap容器中查找對應的osg庫名
std::string osgDB::getFileExtension(const std::string& fileName)
{
    std::string::size_type dot = fileName.find_last_of('.');
    std::string::size_type slash = fileName.find_last_of(PATH_SEPARATORS);
    if (dot==std::string::npos || (slash!=std::string::npos && dot<slash)) return std::string("");
    return std::string(fileName.begin()+dot+1,fileName.end());
}
std::string Registry::createLibraryNameForFile(const std::string& fileName)
{
    return createLibraryNameForExtension(getFileExtension(fileName));
}
//根據規則拼接完成的庫名
std::string Registry::createLibraryNameForExtension(const std::string& ext)
{
    std::string lowercase_ext;
    for(std::string::const_iterator sitr=ext.begin();
        sitr!=ext.end();
        ++sitr)
    {
        lowercase_ext.push_back(tolower(*sitr));
    }

    ExtensionAliasMap::iterator itr=_extAliasMap.find(lowercase_ext);
    if (itr!=_extAliasMap.end() && ext != itr->second) return createLibraryNameForExtension(itr->second);

#if defined(OSG_JAVA_BUILD)
    static std::string prepend = std::string("osgPlugins-")+std::string(osgGetVersion())+std::string("/java");
#else
    static std::string prepend = std::string("osgPlugins-")+std::string(osgGetVersion())+std::string("/");
#endif

#if defined(__CYGWIN__)
    return prepend+"cygwin_"+"osgdb_"+lowercase_ext+OSG_LIBRARY_POSTFIX_WITH_QUOTES+".dll";
#elif defined(__MINGW32__)
    return prepend+"mingw_"+"osgdb_"+lowercase_ext+OSG_LIBRARY_POSTFIX_WITH_QUOTES+".dll";
#elif defined(WIN32)
    return prepend+"osgdb_"+lowercase_ext+OSG_LIBRARY_POSTFIX_WITH_QUOTES+".dll";
#elif macintosh
    return prepend+"osgdb_"+lowercase_ext+OSG_LIBRARY_POSTFIX_WITH_QUOTES;
#else
    return prepend+"osgdb_"+lowercase_ext+OSG_LIBRARY_POSTFIX_WITH_QUOTES+ADDQUOTES(OSG_PLUGIN_EXTENSION);
#endif

}
// 加載庫
Registry::LoadStatus Registry::loadLibrary(const std::string& fileName)
{
    OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(_pluginMutex);

    DynamicLibraryList::iterator ditr = getLibraryItr(fileName);
    if (ditr != _dlList.end()) return PREVIOUSLY_LOADED;

    _openingLibrary = true;

    DynamicLibrary* dl = DynamicLibrary::loadLibrary(fileName);
    _openingLibrary = false;

    if (dl)
    {
        _dlList.push_back(dl);
        return LOADED;
    }
    return NOT_LOADED;
}
//ReaderWriterOSG2
//將文件轉換成流格式,通過流來讀取節點
virtual ReadResult readNode(const std::string& file, const Options* options) const
{
    ReadResult result = ReadResult::FILE_LOADED;
    std::string fileName = file;
    std::ios::openmode mode = std::ios::in;
    Options* local_opt = prepareReading(result, fileName, mode, options);
    if (!result.success()) return result;

    osgDB::ifstream istream(fileName.c_str(), mode);
    return readNode(istream, local_opt);
}
//讀取對象,並轉換成node節點
virtual ReadResult readNode(std::istream& fin, const Options* options) const
{
    osg::ref_ptr<InputIterator> ii = readInputIterator(fin, options);
    if (!ii) return ReadResult::FILE_NOT_HANDLED;

    InputStream is(options);
    if (is.start(ii.get()) != InputStream::READ_SCENE)
    {
        CATCH_EXCEPTION(is);
        return ReadResult::FILE_NOT_HANDLED;
    }

    is.decompress(); CATCH_EXCEPTION(is);
    osg::Node* node = dynamic_cast<osg::Node*>(is.readObject()); CATCH_EXCEPTION(is);
    return node;
}
//根據options對象來創建對應的InputIterator對象,可讀取二進制流、ascii、xml格式流
InputIterator* readInputIterator( std::istream& fin, const Options* options )
{
    bool extensionIsAscii = false, extensionIsXML = false;
    if ( options )
    {
        const std::string& optionString = options->getOptionString();
        if ( optionString.find("Ascii")!=std::string::npos ) extensionIsAscii = true;
        else if ( optionString.find("XML")!=std::string::npos ) extensionIsXML = true;
    }
    
    if ( !extensionIsAscii && !extensionIsXML )
    {
        unsigned int headerLow = 0, headerHigh = 0;
        fin.read( (char*)&headerLow, INT_SIZE );
        fin.read( (char*)&headerHigh, INT_SIZE );
        if ( headerLow==OSG_HEADER_LOW && headerHigh==OSG_HEADER_HIGH )
        {
            return new BinaryInputIterator(&fin);
        }
        fin.seekg( 0, std::ios::beg );
    }
    
    if ( !extensionIsXML )
    {
        std::string header; fin >> header;
        if ( header=="#Ascii" )
        {
            return new AsciiInputIterator(&fin);
        }
        fin.seekg( 0, std::ios::beg );
    }
    
    if ( 1 )
    {
        std::string header; std::getline( fin, header );
        if ( !header.compare(0, 5, "<?xml") )
        {
            return new XmlInputIterator(&fin);
        }
        fin.seekg( 0, std::ios::beg );
    }
    return NULL;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章