Ogre资源管理
http://blog.csdn.net/pizi0475/article/details/6264467
2.跟踪material的资源载入过程。
ResourceGroupManager::initialiseAllResourceGroups()是可以初始化ogre中资源列表中所有的资源。
主要通过两个函数来解析和声明一个组的资源。
parseResourceGroupScripts(grp);
createDeclaredResources(grp);
在 parseResourceGroupScripts函数中,
// Iterate over script users in loading order and get streams
ScriptLoaderOrderMap::iterator oi;
for (oi = mScriptLoaderOrderMap.begin();
oi != mScriptLoaderOrderMap.end(); ++oi)
{
。。。
}
根据加载器的顺序将他们一个个取出来做操作。这样保证了加载的优先级,因为有些资源的加载是有依赖关系的。
这个map里的东西从哪来呢?全解决方案搜索,就会发现在下面这个函数里。
void ResourceGroupManager::_registerScriptLoader(ScriptLoader* su) { OGRE_LOCK_AUTO_MUTEX
mScriptLoaderOrderMap.insert( ScriptLoaderOrderMap::value_type(su->getLoadingOrder(), su)); } |
而当每一种类型的ResourceManager被创建时,都会调用这个函数把自己注册进来,这样就能解析属于他们管理的资源类型的脚本了。可以到MaterialManager的构造函数中看到。
// Loading order mLoadOrder = 100.0f; // Scripting is supported by this manager #if OGRE_USE_NEW_COMPILERS == 0 mScriptPatterns.push_back("*.program"); mScriptPatterns.push_back("*.material"); ResourceGroupManager::getSingleton()._registerScriptLoader(this); #endif
|
// Get all the patterns and search them const StringVector& patterns = su->getScriptPatterns(); for (StringVector::const_iterator p = patterns.begin(); p != patterns.end(); ++p) { FileInfoListPtr fileList = findResourceFileInfo(grp->name, *p); scriptCount += fileList->size(); fileListList->push_back(fileList); } scriptLoaderFileList.push_back( LoaderFileListPair(su, fileListList)); |
得到一个加载器所能加载的资源类型,找出这个资源组中所有的这种资源类型的文件,把他们的文件信息存入列表中,并把这两者配对存储,放入了 scriptLoaderFileList 。
// Fire scripting event fireResourceGroupScriptingStarted(grp->name, scriptCount); |
发送一条信息,通知所有的 ResourceGroupListener,开始解析组里的脚本了。
// Iterate over scripts and parse // Note we respect original ordering for (ScriptLoaderFileList::iterator slfli = scriptLoaderFileList.begin(); slfli != scriptLoaderFileList.end(); ++slfli) { ScriptLoader* su = slfli->first; // Iterate over each list for (FileListList::iterator flli = slfli->second->begin(); flli != slfli->second->end(); ++flli) { // Iterate over each item in the list for (FileInfoList::iterator fii = (*flli)->begin(); fii != (*flli)->end(); ++fii) { bool skipScript = false; fireScriptStarted(fii->filename, skipScript); if(skipScript) { LogManager::getSingleton().logMessage( "Skipping script " + fii->filename); } else { LogManager::getSingleton().logMessage( "Parsing script " + fii->filename); DataStreamPtr stream = fii->archive->open(fii->filename); if (!stream.isNull()) { if (mLoadingListener) mLoadingListener->resourceStreamOpened(fii->filename, grp->name, 0, stream); su->parseScript(stream, grp->name); } } fireScriptEnded(fii->filename, skipScript); } } }
|
这段代码的作用,从加载器列表中取出每一个加载器,对加载器所要解析的脚本进行一一解析。
fireScriptStarted(fii->filename, skipScript); |
通知所有的 ResourceGroupListener,开始加载脚本了。
这边传入了一个bool型的skipScript的引用,这样应该就能在Listener里边对特定名称的文件进行过滤,让其不被加载。
DataStreamPtr stream = fii->archive->open(fii->filename); |
打开当前文件名的文件,存为数据流。
if (mLoadingListener) mLoadingListener->resourceStreamOpened(fii->filename, grp->name, 0, stream);
|
如果有 ResourceLoadingListener,就可以对资源的打开进行监听了,在这边可又得到打开的资源文件的名字。第三个参数传入的是0,所以在这还得不到资源的名字,因为还没有解析脚本。
su->parseScript(stream, grp->name); |
这里开始解析脚本了
跟踪跳转到
ScriptCompilerManager::parseScript(DataStreamPtr& stream, const String& groupName) |
不知道是不是ogre默认是用这个scriptLoader来解析脚本的。
具体的解析过程,由
mScriptCompiler->compile(stream->getAsString(), stream->getName(), groupName); |
即:
bool ScriptCompiler::compile(const String &str, const String &source, const String &group) { ScriptLexer lexer; ScriptParser parser; ConcreteNodeListPtr nodes = parser.parse(lexer.tokenize(str, source)); return compile(nodes, group); } |
上面的代码就将文件解析为一堆的ConcreteNode了,没具体细看,按照我的理解,在不知道文件具体是干嘛用的情况下,应该只是存最顶层的每一个名字,再存上大括号里的内容吧。
在OgreScriptCompiler.cpp中可以看到:
bool ScriptCompiler::compile(const ConcreteNodeListPtr &nodes, const String &group) { … // Translate the nodes for(AbstractNodeList::iterator i = ast->begin(); i != ast->end(); ++i) { //logAST(0, *i); if((*i)->type == ANT_OBJECT && reinterpret_cast<ObjectAbstractNode*>((*i).get())->abstract) continue; ScriptTranslator *translator = ScriptCompilerManager::getSingleton().getTranslator(*i); if(translator) translator->translate(this, *i); } … } |
这边将Node一个个取出来然后找出对应的ScriptTranslator来进行解析。
ScriptTranslator *ScriptCompilerManager::getTranslator(const AbstractNodePtr &node) { ScriptTranslator *translator = 0; { OGRE_LOCK_AUTO_MUTEX
// Start looking from the back for(std::vector<ScriptTranslatorManager*>::reverse_iterator i = mManagers.rbegin(); i != mManagers.rend(); ++i) { translator = (*i)->getTranslator(node); if(translator != 0) break; } } return translator; } |
具体的translator就要根据Node的ID来判断了:
ScriptTranslator *BuiltinScriptTranslatorManager::getTranslator(const AbstractNodePtr &node) { ScriptTranslator *translator = 0;
if(node->type == ANT_OBJECT) { ObjectAbstractNode *obj = reinterpret_cast<ObjectAbstractNode*>(node.get()); ObjectAbstractNode *parent = obj->parent ? reinterpret_cast<ObjectAbstractNode*>(obj->parent) : 0; if(obj->id == ID_MATERIAL) translator = &mMaterialTranslator; else if(obj->id == ID_TECHNIQUE && parent && parent->id == ID_MATERIAL) translator = &mTechniqueTranslator; else if(obj->id == ID_PASS && parent && parent->id == ID_TECHNIQUE) translator = &mPassTranslator; else if(obj->id == ID_TEXTURE_UNIT && parent && parent->id == ID_PASS) translator = &mTextureUnitTranslator; else if(obj->id == ID_TEXTURE_SOURCE && parent && parent->id == ID_TEXTURE_UNIT) translator = &mTextureSourceTranslator; else if(obj->id == ID_FRAGMENT_PROGRAM || obj->id == ID_VERTEX_PROGRAM || obj->id == ID_GEOMETRY_PROGRAM) translator = &mGpuProgramTranslator; else if(obj->id == ID_PARTICLE_SYSTEM) translator = &mParticleSystemTranslator; else if(obj->id == ID_EMITTER) translator = &mParticleEmitterTranslator; else if(obj->id == ID_AFFECTOR) translator = &mParticleAffectorTranslator; else if(obj->id == ID_COMPOSITOR) translator = &mCompositorTranslator; else if(obj->id == ID_TECHNIQUE && parent && parent->id == ID_COMPOSITOR) translator = &mCompositionTechniqueTranslator; else if((obj->id == ID_TARGET || obj->id == ID_TARGET_OUTPUT) && parent && parent->id == ID_TECHNIQUE) translator = &mCompositionTargetPassTranslator; else if(obj->id == ID_PASS && parent && (parent->id == ID_TARGET || parent->id == ID_TARGET_OUTPUT)) translator = &mCompositionPassTranslator; else if(obj->id == ID_CLEAR && parent && parent->id == ID_PASS) translator = &mCompositionPassClearTranslator; else if(obj->id == ID_STENCIL && parent && parent->id == ID_PASS) translator = &mCompositionPassStencilTranslator; }
return translator; } |
下面以mMaterialTranslator为例,依然是在OgreScriptTranslator.cpp:
void MaterialTranslator::translate(ScriptCompiler *compiler, const AbstractNodePtr &node) { ObjectAbstractNode *obj = reinterpret_cast<ObjectAbstractNode*>(node.get()); if(obj->name.empty()) compiler->addError(ScriptCompiler::CE_OBJECTNAMEEXPECTED, obj->file, obj->line);
// Create a material with the given name std::vector<Ogre::Any> args; args.push_back(Any(obj->file)); args.push_back(Any(obj->name)); args.push_back(Any(compiler->getResourceGroup())); Ogre::Any retval = compiler->_fireCreateObject("Material", args); if(retval.isEmpty()) { mMaterial = reinterpret_cast<Ogre::Material*>(MaterialManager::getSingleton().create(obj->name, compiler->getResourceGroup()).get()); } else { try{ mMaterial = Ogre::any_cast<Ogre::Material*>(retval); }catch(...){ compiler->addError(ScriptCompiler::CE_OBJECTALLOCATIONERROR, obj->file, obj->line, "failed to find or create material \"" + obj->name + "\""); return; } }
mMaterial->removeAllTechniques(); obj->context = Any(mMaterial); mMaterial->_notifyOrigin(obj->file);
//以下略 ……
} |
Ogre::Any retval = compiler->_fireCreateObject("Material", args); |
像compiler发送一个创建的消息,这其实是给compiler的监听器发送的消息,可以里边进行一些操作,如果没有创建东西,就让底下的MaterialManager来进行创建工作。
if(retval.isEmpty()) { mMaterial = reinterpret_cast<Ogre::Material*>(MaterialManager::getSingleton().create(obj->name, compiler->getResourceGroup()).get()); }
|
这里就到了create Material的地方了:
ResourcePtr ResourceManager::create(const String& name, const String& group, bool isManual, ManualResourceLoader* loader, const NameValuePairList* params) { // Call creation implementation ResourcePtr ret = ResourcePtr( createImpl(name, getNextHandle(), group, isManual, loader, params)); if (params) ret->setParameterList(*params);
addImpl(ret); // Tell resource group manager ResourceGroupManager::getSingleton()._notifyResourceCreated(ret); return ret;
} |
createImpl会根据不同的ResurceManager进行重载,会new出不同的资源。
addImpl则是将新的资源加入到资源的map里。在这里边可以进里资源名字的冲突问题。