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, ") )
{
return new XmlInputIterator(&fin);
}
fin.seekg( 0, std::ios::beg );
}
return NULL;
}