Ogre资源管理

 

1.Ogre资源的载入、卸载和销毁的过程

http://ogre3d.cn/wiki/index.php?title=%E6%96%87%E6%A1%A3:%E6%95%99%E7%A8%8B:%E4%B8%AD%E7%BA%A7%E6%95%99%E7%A8%8B:%E4%B8%AD%E7%BA%A7%E6%95%99%E7%A8%8B%E4%B8%83

 

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里。在这里边可以进里资源名字的冲突问题。