OGRE示例Demo_BSP代码分析(1)

最近在自学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));
                    }
                }
            }
        }
    }

 

比较长。。。不过大体浏览过后发现内容上主要是在读取的文本上做文章,进行文本分析,这里也解释了关于默认section为BLANK的一些疑惑。先到此收笔,明天继续。。。。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值