OGRE示例Demo_BSP代码分析

来源:互联网 发布:易语言图片压缩源码 编辑:程序博客网 时间:2024/05/12 12:41

最近在自学OGRE,自我感觉分析具体代码的时候就像在拨香蕉皮,一层嵌套另一层。

Resource life cycle感觉挺重要的,针对这个Demo分析下吧。

Class BspApplication中,有三个保护性成员变量

 protected:

 String mQuakePk3;                                   
 String mQuakeLevel;
 ExampleLoadingBar mLoadingBar;

 前面两个成员变量会在void setupResources(void)中被初始化

 ConfigFile cf;

 cf.load("quake3settings.cfg");

 mQuakePk3 = cf.getSetting("Pak0Location"); //从字面上看是读取资源路径
 mQuakeLevel = cf.getSetting("Map");           //字面上看跟地图有关

现在来看getSetting做了些什么,它的函数原型是,

String getSetting (const String& key, const String& section = StringUtil::BLANK, const String& defaultValue = StringUtil::BLANK) const

首先从直观上来判断,这个函数寻找脚本中的一个键值,然后返回一个字符串给接受着,例子中的键值分别是PakOLocation和Map.那么返回的字符串到底有何用呢,为了找到答案还是查阅下ogre的API手册,我的当前版本是1.4.0,OGRE世纪版本是1.6.1看来老了点,凑合用吧。。。。

手册上对该函数解释是 Gets the first setting from the file with the named key, 貌似是说返回键值指定的第一个设置,很模糊,接着看下面参数解释。。。

Parameters: 
  key  The name of the setting                                        
  section  The name of the section it must be in (if any)

看来还是没什么实际意义(对此手册很无语。。。),为了继续探寻,先看函数具体代码了

   

 String ConfigFile::getSetting(const String& key, const String& section, const String& defaultValue) const
    {
        
        SettingsBySection::const_iterator seci = mSettings.find(section);
        if (seci == mSettings.end())
        {
            return defaultValue;
        }
        else
        {
            SettingsMultiMap::const_iterator i = seci->second->find(key);
            if (i == seci->second->end())
            {
                return StringUtil::BLANK;
            }
            else
            {
                return i->second;
            }
        }
    }

此代码段给出的信息貌似不多,首先知道的是SettingsBySection是一个Map结构,定义为

typedef std :: map<String, SettingsMultiMap*> SettingsBySection,

其中第二项SettingsMultiMap是一个multimap结构,定义为

typedef std::multimap<String, String> SettingsMultiMap;

然后它定义了一个Iterator来找到脚本中符合section的一项,原程序中使用了默认值StringUtil::BLANK,于是接着if-else展开为三项,如果没有找到该值,就返回defaultValue,即StringUtil::BLANK,找到的话,执行

SettingsMultiMap::const_iterator i = seci->second->find(key);

等号右边表达比较诡异,只能更深层次挖掘,second的定义为

typedef struct tagSQL_DAY_SECOND
{
  SQLUINTEGER  day;
  SQLUINTEGER  hour;
  SQLUINTEGER  minute;
  SQLUINTEGER  second; //unsigned long型
  SQLUINTEGER  fraction;
} SQL_DAY_SECOND_STRUCT;

看来和秒有关系?实在想不出为什么。。。因为和秒相关太离谱,只能根据单词意思 “第二”做另外假设

因为结构声明上map里嵌套了一个multimap,second表示是在内层map中查找吧!

那么第二层else的意思就是如果在内层map中没有找到键值,就返回StringUtil::BLANK,如果找到就返回seci->second

为了确认这种推断,现在来用quake3settings.cfg做验证,这个配置文件相关内容是:

(令我很无语,这文件只有两行。。。)

Pak0Location: ../../../Media/packs/chiropteraDM.pk3 
Map: maps/chiropteradm.bsp

那么,在这个cfg文件被加载后,按照刚才对getSettings的分析,首先,它尝试再文件中寻找StringUtil::BLANK(就是字符串"BLANK")如果最后一个元素匹配,那就返回"BLANK",如果没有则进入内层map寻找key的值,这里键值是PakOLocation,很显然找到并返回 Pak0Location: ../../../Media/packs/chiropteraDM.pk3,找键值map时候同理。

 

以上分析中关注了从配置文件中读取信息,下面来分析一下cf.load("quake3settings.cfg")看OGRE怎样load文件。

经查找源代码,发现load函数根据传入参数不同有三个样本

void load(const String& filename, const String& separators = "/t:=", bool trimWhitespace = true);
        /// load from a filename (using resource group locations)
 

void load(const String& filename, const String& resourceGroup, const String& separators = "/t:=", bool trimWhitespace = true);
        /// load from a data stream
 

void load(const DataStreamPtr& stream, const String& separators = "/t:=", bool trimWhitespace = true);
        /// load from a filename (not using resource group locations)

并且前两个函数最终都调用了第三个load函数来进行加载,可以看出第二个函跟resourceGroup产生了互动,第三个使用了datastream. 先来分析第一个吧,第一个load的具体代码是

 

  void ConfigFile::load(const String& filename, const String& separators, bool trimWhitespace)
    {
        loadDirect(filename, separators, trimWhitespace);
    }

无奈接着找loadDirect,它的具体代码是

 void ConfigFile::loadDirect(const String& filename, const String& separators, 
  bool trimWhitespace)
 {
  /* Open the configuration file */
  std::ifstream fp;
        // Always open in binary mode
  fp.open(filename.c_str(), std::ios::in | std::ios::binary);
  if(!fp)
   OGRE_EXCEPT(
   Exception::ERR_FILE_NOT_FOUND, "'" + filename + "' file not found!", "ConfigFile::load" );

  // Wrap as a stream
  DataStreamPtr stream(OGRE_NEW FileStreamDataStream(filename, &fp, false));
  load(stream, separators, trimWhitespace);

 }

结构还算清晰,已经开始触及底层类了,主要分析下最后两行吧。。。

首先stream返回一个DataStreamPtr类型指针,DataStreamPtr是SharedPtr模板的一个实现,stream的参数是一个类的实例,此类是从DataStream派生而来,那么分析一下它具体的构造函数吧

 

 /** Construct stream from an STL stream
        @param s Pointer to source stream
        @param freeOnClose Whether to delete the underlying stream on 
            destruction of this class
        */
  FileStreamDataStream(std::ifstream* s, 
            bool freeOnClose = true);

描述上说明了这个stream类有STL支持,第二参数表示在释放该类实例时候要不要释放数据流。总而言之它的功能建立了一个与要加载文件的一个流供接下来的load函数使用。那么接下来看一看load(stream, separators, trimWhitespace);具体做了什么吧,首先是原函数代码。。。

 void ConfigFile::load(const DataStreamPtr& stream, const String& separators, 
        bool trimWhitespace)
    {
        /* Clear current settings map */
        clear();

        String currentSection = StringUtil::BLANK;
        SettingsMultiMap* currentSettings = OGRE_NEW_T(SettingsMultiMap, MEMCATEGORY_GENERAL)();
        mSettings[currentSection] = currentSettings;


        /* Process the file line for line */
        String line, optName, optVal;
        while (!stream->eof())
        {
            line = stream->getLine();
            /* Ignore comments & blanks */
            if (line.length() > 0 && line.at(0) != '#' && line.at(0) != '@')
            {
                if (line.at(0) == '[' && line.at(line.length()-1) == ']')
                {
                    // Section
                    currentSection = line.substr(1, line.length() - 2);
     SettingsBySection::const_iterator seci = mSettings.find(currentSection);
     if (seci == mSettings.end())
     {
      currentSettings = OGRE_NEW_T(SettingsMultiMap, MEMCATEGORY_GENERAL)();
      mSettings[currentSection] = currentSettings;
     }
     else
     {
      currentSettings = seci->second;
     } 
                }
                else
                {
                    /* Find the first seperator character and split the string there */
                    std::string::size_type separator_pos = line.find_first_of(separators, 0);
                    if (separator_pos != std::string::npos)
                    {
                        optName = line.substr(0, separator_pos);
                        /* Find the first non-seperator character following the name */
                        std::string::size_type nonseparator_pos = line.find_first_not_of(separators, separator_pos);
                        /* ... and extract the value */
                        /* Make sure we don't crash on an empty setting (it might be a valid value) */
                        optVal = (nonseparator_pos == std::string::npos) ? "" : line.substr(nonseparator_pos);
                        if (trimWhitespace)
                        {
                            StringUtil::trim(optVal);
                            StringUtil::trim(optName);
                        }
                        currentSettings->insert(std::multimap<String, String>::value_type(optName, optVal));
                    }
                }
            }
        }
    }

 

现在来分析void loadResources(void)函数

首先是mLoadingBar.start(mWindow, 1, 1, 0.75),mLoadingBar被定义为ExampleLoadingBar的一个实例,该类的start函数具体代码如下

 

virtual void start(RenderWindow* window,

         unsigned short numGroupsInit = 1,

         unsigned short numGroupsLoad = 1,

         Real initProportion = 0.70f)

     {

         mWindow = window;

         mNumGroupsInit = numGroupsInit;

         mNumGroupsLoad = numGroupsLoad;

         mInitProportion = initProportion;

         // We need to pre-initialise the 'Bootstrap' group so we can use

         // the basic contents in the loading screen

         ResourceGroupManager::getSingleton().initialiseResourceGroup("Bootstrap");

 

         OverlayManager& omgr = OverlayManager::getSingleton();

         mLoadOverlay = (Overlay*)omgr.getByName("Core/LoadOverlay");

         if (!mLoadOverlay)

         {

              OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,

                   "Cannot find loading overlay", "ExampleLoadingBar::start");

         }

         mLoadOverlay->show();

 

         // Save links to the bar and to the loading text, for updates as we go

         mLoadingBarElement = omgr.getOverlayElement("Core/LoadPanel/Bar/Progress");

         mLoadingCommentElement = omgr.getOverlayElement("Core/LoadPanel/Comment");

         mLoadingDescriptionElement = omgr.getOverlayElement("Core/LoadPanel/Description");

 

         OverlayElement* barContainer = omgr.getOverlayElement("Core/LoadPanel/Bar");

         mProgressBarMaxSize = barContainer->getWidth();

         mLoadingBarElement->setWidth(0);

 

         // self is listener

         ResourceGroupManager::getSingleton().addResourceGroupListener(this);

     }

纵观整个代码段,首先涉及到的关键语句是ResourceGroupManager::getSingleton().initialiseResourceGroup("Bootstrap");

从表面上看这段代码初始化了Bootstrap资源,具体怎样实现还要继续深层次挖掘,那么void initialiseResourceGroup (const String& name)的具体代码是

 

void ResourceGroupManager::initialiseResourceGroup(const String& name)

     {

         OGRE_LOCK_AUTO_MUTEX

         LogManager::getSingleton().logMessage("Initialising resource group " + name);

         ResourceGroup* grp = getResourceGroup(name);

         if (!grp)

         {

              OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,

                   "Cannot find a group named " + name,

                   "ResourceGroupManager::initialiseResourceGroup");

         }

         OGRE_LOCK_MUTEX(grp->OGRE_AUTO_MUTEX_NAME) // lock group mutex

 

         if (grp->groupStatus == ResourceGroup::UNINITIALSED)

         {

              // in the process of initialising

              grp->groupStatus = ResourceGroup::INITIALISING;

              // Set current group

              parseResourceGroupScripts(grp);

              mCurrentGroup = grp;

              createDeclaredResources(grp);

              grp->groupStatus = ResourceGroup::INITIALISED;

 

              // Reset current group

              mCurrentGroup = 0;

         }

     }

从这段代码可以看出,首先它在log上保存加载状态信息,并加入了异常处理,接下来,ResourceGroup出现了UNINIIALSED, INITIALISING和INITIALISED三个状态,状态切换过程中,有两个函数需要关注,一个是parseResourceGroupScripts(grp),另一个是createDeclaredResources(grp),如果想知道细节,看来还要继续挖掘。不过这里首先放出一副ResourceGroup状态图,它显示了resource lifecycle

 

 


 

此图标注详细,不做过多注释了,接下来还是先看函数parseResourceGroupScripts具体代码

void ResourceGroupManager::parseResourceGroupScripts(ResourceGroup* grp)

     {

 

         LogManager::getSingleton().logMessage(

              "Parsing scripts for resource group " + grp->name);

 

         // Count up the number of scripts we have to parse

        typedef std::list<FileInfoListPtr> FileListList;

        typedef SharedPtr<FileListList> FileListListPtr;

        typedef std::pair<ScriptLoader*, FileListListPtr> LoaderFileListPair;

        typedef std::list<LoaderFileListPair> ScriptLoaderFileList;

        ScriptLoaderFileList scriptLoaderFileList;

         size_t scriptCount = 0;

         // Iterate over script users in loading order and get streams

         ScriptLoaderOrderMap::iterator oi;

         for (oi = mScriptLoaderOrderMap.begin();

              oi != mScriptLoaderOrderMap.end(); ++oi)

         {

              ScriptLoader* su = oi->second;

              // MEMCATEGORY_GENERAL is the only category supported for SharedPtr

            FileListListPtr fileListList(OGRE_NEW_T(FileListList, MEMCATEGORY_GENERAL)(), SPFM_DELETE_T);

 

              // 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));

         }

         // Fire scripting event

         fireResourceGroupScriptingStarted(grp->name, scriptCount);

 

         // 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);

                  }

            }

         }

 

         fireResourceGroupScriptingEnded(grp->name);

         LogManager::getSingleton().logMessage(

              "Finished parsing scripts for resource group " + grp->name);

     }

真是好长啊,无语了,接着读吧。。。。宏观上看了一下,感觉这个代码段借助STL定义了一些指针LIST来保存要加载资源的地址,然后对这些指针做逻辑安排,并将状态信息登陆到log里。在接下来的resourceloading中,这些指针应该会有用处。

 

从此段开始,我感觉再进行深层次代码上的挖掘会触及底层,因此偏离大的方向,所以,只对两个相关类以及一些函数的功能和属性进行概述性说明吧,两个相关类是FileInfo,ScriptLoader。

经过查找API手册,发现FileInfo是一个结构体,手册上对它的说明是

“Information about a file/directory within the archive will be returned using a FileInfo struct.”

 
 
Public Attributes
 
Archive * 
 archive
 
 
 The archive in which the file has been found (for info when performing multi-Archive searches, note you should still open through ResourceGroupManager). 
 
String 
 filename
 
 
 The file's fully qualified name. 
 
String 
 path
 
 
 Path name; separated by '/' and ending with '/'. 
 
String 
 basename
 
 
 Base filename. 
 
size_t 
 compressedSize
 
 
 Compressed size. 
 
size_t 
 uncompressedSize
 
 
 Uncompressed size. 
 

主要意思就是由Archive找到得文件信息都会保存在这个结构体中。接下来看scriptLoader,从手册中发现此类已经属于比较低层的类,因为许多manager类都继承了它的方法,比如SkeletonManager,TextureManager等等。手册上对此类的描述是“Abstract class defining the interface used by classes which wish to perform script loading to define instances of whatever they manage” 即此类属于抽象类,定义了一个接口,供继承自它的那些manager来调用,当这些manager要做脚本加载来定义实例的时候。顺手再放上一副类层次图吧,方便以后查阅

 

 

需要概述的函数有

1.fireResourceGroupScriptingStarted(grp->name, scriptCount),2.fireResourceGroupScriptingEnded(grp->name),

3.fireScriptStarted(fii->filename, skipScript),

4.fireScriptEnded(fii->filename, skipScript)

5. mLoadingListener->resourceStreamOpened(fii->filename, grp->name, 0, stream);

6.su->parseScript(stream, grp->name),

 

第一个函数的具体代码是

     void ResourceGroupManager::fireResourceGroupScriptingStarted(const String& groupName, size_t scriptCount)

     {

         OGRE_LOCK_AUTO_MUTEX

         for (ResourceGroupListenerList::iterator l = mResourceGroupListenerList.begin();

              l != mResourceGroupListenerList.end(); ++l)

         {

              (*l)->resourceGroupScriptingStarted(groupName, scriptCount);

         }

     }

可以看出,此函数对ResourceGroupListenerList中的每一个元素调用resourceGroupScriptingStarted函数,那么resourceGroupScriptingStarted的具体代码是

     // ResourceGroupListener callbacks

     void resourceGroupScriptingStarted(const String& groupName, size_t scriptCount)

     {

         assert(mNumGroupsInit > 0 && "You stated you were not going to init "

              "any groups, but you did! Divide by zero would follow...");

         // Lets assume script loading is 70%

         mProgressBarInc = mProgressBarMaxSize * mInitProportion / (Real)scriptCount;

         mProgressBarInc /= mNumGroupsInit;

         mLoadingDescriptionElement->setCaption("Parsing scripts...");

         mWindow->update();

     }

这个函数是ResourceGroupListener调用的回调函数,在每一个windowupdate之后,都会增加进度条长度。

第二个函数具体代码是

void ResourceGroupManager::fireResourceGroupScriptingEnded(const String& groupName)

     {

         OGRE_LOCK_AUTO_MUTEX

         for (ResourceGroupListenerList::iterator l = mResourceGroupListenerList.begin();

              l != mResourceGroupListenerList.end(); ++l)

         {

              (*l)->resourceGroupScriptingEnded(groupName);

         }

     }

跟函数1是一个组合,最终对list中的每个元素调用resourceGroupScriptingEnded,其具体代码是

     void resourceGroupScriptingEnded(const String& groupName)

     {

     }

很惊奇这段代码形同虚设,父类中同名函数是纯虚函数,看来这个函数是留作以后扩充用的。

 

函数3的具体代码是

     void ResourceGroupManager::fireScriptStarted(const String& scriptName, bool &skipScript)

     {

         OGRE_LOCK_AUTO_MUTEX

         for (ResourceGroupListenerList::iterator l = mResourceGroupListenerList.begin();

              l != mResourceGroupListenerList.end(); ++l)

         {

              bool temp = false;

              (*l)->scriptParseStarted(scriptName, temp);

              if(temp)

                   skipScript = true;

         }

     }

调用了scriptParseStarted函数,其具体代码是

     void scriptParseStarted(const String& scriptName, bool &skipThisScript)

     {

         mLoadingCommentElement->setCaption(scriptName);

         mWindow->update();

     }

其中定义OverlayElement* mLoadingCommentElement,那么setCaption方法具体做的是

   void OverlayElement::setCaption( const DisplayString& caption )

    {

        mCaption = caption;

        _positionsOutOfDate();

}

设置了一个标题字符串,之后又调用_positionsOutOfDate(),那么它做的是

     void OverlayElement::_positionsOutOfDate(void)

     {

         mGeomPositionsOutOfDate = true;

}

 

 /// Flag indicating if the vertex positions need recalculating

        bool mGeomPositionsOutOfDate;

将OoverlayElement类中的此标记置为true,说明顶点位置需要重新计算。那么整体这个代码段取得了要读脚本的标题,标记顶点位置要更新,之后更新置窗口。

 

函数4的具体代码是

  void ResourceGroupManager::fireScriptEnded(const String& scriptName, bool skipped)

    {

        OGRE_LOCK_AUTO_MUTEX

            for (ResourceGroupListenerList::iterator l = mResourceGroupListenerList.begin();

                l != mResourceGroupListenerList.end(); ++l)

            {

                (*l)->scriptParseEnded(scriptName, skipped);

            }

    }

那么scriptParseEnded的具体代码是

     void scriptParseEnded(const String& scriptName, bool skipped)

     {

         mLoadingBarElement->setWidth(

              mLoadingBarElement->getWidth() + mProgressBarInc);

         mWindow->update();

}

这段代码增加进度条长度,并更新到窗口。

 

函数5的具体代码

/** This event is called when a resource stream has been opened, but not processed yet.

         @remarks

              You may alter the stream if you wish or alter the incoming pointer to point at

              another stream if you wish.

         */

    virtual void resourceStreamOpened(const String &name, const String &group, Resource *resource, DataStreamPtr& dataStream) = 0;

这个方法在ResourceLoadingListener类中被定义为纯虚函数,解释为资源流被打开但是还没有处理的时候来调用它,那么在实例中调用它的类对象是mLoadingListener,是ResourceLoadingListener对象

(怎么会调用了一个纯虚函数。。。汗。。。怀疑是否符合规则),这段先放着了,暂时猜测这样的做法是留出接口,等待将来扩充用

 

下面来看su->parseScript(stream, grp->name)的具体实现,发现su调用的parseScript也是本类ScriptLoader中的纯虚函数,猜测同上.

 

那么到此initialiseResourceGroup函数分析完毕,它主要实现的功能是定位资源。下一篇继续分析createDeclaredResources(grp)。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/sgao2/archive/2009/07/22/4371344.aspx

原创粉丝点击