Ogre的许多外部资源数据都有着相应的脚本格式,现例举如下:

  • Material(材质):Ogre使用的是“大材质”的概念。狭义的“材质”概念往往是与“贴图”等概念区分开的,比如在Lambert光照模型中,它一般用来指物体表面对模拟光的环境分量、漫反射分量和镜面反射分量的作用的响应属性。而在Ogre中,“材质”既包括了上述狭义的材质含义,又包括对要使用的贴图的描述,还可以包括要使用的shader的相关信息。这些都是用Ogre的材质脚本来描述的。其实仔细思考一下就会发现,Ogre对材质概念的定义是恰当的,因为贴图实际上是图形学为了模拟光照效果的一种方法;而后期引入的shader其原意也是为了让程序员能更自由、更精确地表达各种光照效果;再结合狭义的材质概念就可以明白,在Ogre中,所谓的“材质”实际上就是物体表面对光照的响应最终结果的整体描述,它综合了现有图形学的各种技术手段。材质脚本是以.material为后缀的文本文件。
  • Program:各种shader语言都有自身的定义和表达方式,但不论用哪种语言写的shader,从调用者的角度看来,都需要知道shader的源文件在哪儿?是什么类型的?用的是什么版本?入口函数是什么?等方面的内容。Program脚本就负责回答这些问题,实际上program脚本是为Material脚本服务的,它们之间是“被引用者”和“引用者”之间的关系。Program脚本是以.program为后缀的文本文件。program脚本中定义的内容,有时也直接写在Material文本文件里。
  • Particle:Ogre中的粒子系统的实例化即是以此脚本文件为基础的。Particle脚本是以.particle为后缀的文本文件。
  • Compositor:游戏场景中的一些特殊光照效果,有时需要以不同的方式对场景进行多次渲染然后综合处理;有时要在上次渲染的结果上作进一步的图像处理,并把处理结果作为下一次处理的输入数据;或者用多次渲染与重复处理组合起来行成一个渲染链,渲染链的输出就是最后要的光照效果(比如常见的"Bloom"、"Flur"等)。这大概也是Ogre为什么把这种处理方法定义为Compositor的原因吧。底层的图形渲染引擎如DirectX,OpenGL等只提供对场景数据的直接渲染的支持,对这种特殊光效的处理过程鲜有现成的接口可以调用,而Ogre为了实现这种功能所引入的Compositor的概念,就是用来实现以上的组合渲染过程的。Compositor脚本是以.compositor为后缀的文本文件。
  • Overlay:作为覆盖层的Overlay的用途是多方面的。在渲染过程中,Overlay渲染队列是被放在最后进行处理的,所以Overlay对象总是会覆盖整个场景而显示在最前面。Overlay常用作系统中二维对象或特殊对象的显示,比如UI界面、游戏场景中的HUD(Head Up Display)等。Overlay中的contain对象可以相互叠加或嵌套。对场景中要显示的Overlay对象内容及其相互关系,Ogre用overlay脚本对其进行描述。overlay脚本是以.overlay为后缀的文本文件。
  • Font:在Ogre中显示的文字,都是以纹理的方式来处理的。其处理方法大致有两种,第一种是把要显示的文字内容处理成图片,然后作为贴图资源进行加载和处理;第二种方法是使用操作系统提供的或第三方提供的字库,Ogre会根据字库和用户要显示的内容,自动在内部渲染为纹理。如果要使用第二种方法,就要用Ogre提供的Font脚本。Font脚本是以.fontdef为后缀的文本文件。
  • 其他。Ogre还为脚本的扩展提供了可能。Ogre的某些插件使用的资源可以有自已的脚本格式,对这些脚本文件的解析同样可以依靠Ogre原有的脚本解析机制。

Ogre的资源管理器都是从ResourceManager派生出来的,而ResourceManager又以ScriptLoader为基类。有相应脚本的的资源管理器,在其构造函数中会调用ResourceGroupManager::_registerScriptLoader()函数,将资源管理器对象指针保存到ResourceGroupManager的mScriptLoaderOrderMap容器中。在之后的资源初始化过程中(见前面相关文章的讨论),ResourceGroupManager::initialiseResourceGroup()会调用ResourceGroupManager::parseResourceGroupScripts()函数,对相应ResourceGroup中的脚本文件进行解析。新版本的Ogre引入了ScriptCompilerManager类后,以“.material”、“.program”、“.particle”和“.compositor”为后缀的文件都统一由ScriptCompilerManager类型对象统一进行管理和解析(而原来各自对应的资源管理器不再注册到mScriptLoaderOrderMap中)。ScriptCompilerManager自身也是以ScriptLoader为基类的,Oger在初始化Root对象时,会创建一个ScriptCompilerManager对象,并通过ResourceGroupManager::_registerScriptLoader()函数,将它的指针保存到ResourceGroupManager的mScriptLoaderOrderMap容器中。因此,新版本的ResourceGroupManager的mScriptLoaderOrderMap中保存的第一个ScriptLoader对象,实际上就是ScriptCompilerManager的对象指针。

ResourceGroupManager::parseResourceGroupScripts()函数的代码如下:

 1     void ResourceGroupManager::parseResourceGroupScripts(ResourceGroup* grp)
2 {
3
4 LogManager::getSingleton().logMessage(
5 "Parsing scripts for resource group " + grp->name);
6
7 // Count up the number of scripts we have to parse
8 typedef list<FileInfoListPtr>::type FileListList;
9 typedef SharedPtr<FileListList> FileListListPtr;
10 typedef std::pair<ScriptLoader*, FileListListPtr> LoaderFileListPair;
11 typedef list<LoaderFileListPair>::type ScriptLoaderFileList;
12 ScriptLoaderFileList scriptLoaderFileList;
13 size_t scriptCount = 0;
14 // Iterate over script users in loading order and get streams
15 ScriptLoaderOrderMap::iterator oi;
16 for (oi = mScriptLoaderOrderMap.begin();
17 oi != mScriptLoaderOrderMap.end(); ++oi)
18 {
19 ScriptLoader* su = oi->second;
20 // MEMCATEGORY_GENERAL is the only category supported for SharedPtr
21 FileListListPtr fileListList(OGRE_NEW_T(FileListList, MEMCATEGORY_GENERAL)(), SPFM_DELETE_T);
22
23 // Get all the patterns and search them
24 const StringVector& patterns = su->getScriptPatterns();
25 for (StringVector::const_iterator p = patterns.begin(); p != patterns.end(); ++p)
26 {
27 FileInfoListPtr fileList = findResourceFileInfo(grp->name, *p);
28 scriptCount += fileList->size();
29 fileListList->push_back(fileList);
30 }
31 scriptLoaderFileList.push_back(
32 LoaderFileListPair(su, fileListList));
33 }
34 // Fire scripting event
35 fireResourceGroupScriptingStarted(grp->name, scriptCount);
36
37 // Iterate over scripts and parse
38 // Note we respect original ordering
39 for (ScriptLoaderFileList::iterator slfli = scriptLoaderFileList.begin();
40 slfli != scriptLoaderFileList.end(); ++slfli)
41 {
42 ScriptLoader* su = slfli->first;
43 // Iterate over each list
44 for (FileListList::iterator flli = slfli->second->begin(); flli != slfli->second->end(); ++flli)
45 {
46 // Iterate over each item in the list
47 for (FileInfoList::iterator fii = (*flli)->begin(); fii != (*flli)->end(); ++fii)
48 {
49 bool skipScript = false;
50 fireScriptStarted(fii->filename, skipScript);
51 if(skipScript)
52 {
53 LogManager::getSingleton().logMessage(
54 "Skipping script " + fii->filename);
55 }
56 else
57 {
58 LogManager::getSingleton().logMessage(
59 "Parsing script " + fii->filename);
60 DataStreamPtr stream = fii->archive->open(fii->filename);
61 if (!stream.isNull())
62 {
63 if (mLoadingListener)
64 mLoadingListener->resourceStreamOpened(fii->filename, grp->name, 0, stream);
65
66 if(fii->archive->getType() == "FileSystem" && stream->size() <= 1024 * 1024)
67 {
68 DataStreamPtr cachedCopy;
69 cachedCopy.bind(OGRE_NEW MemoryDataStream(stream->getName(), stream));
70 su->parseScript(cachedCopy, grp->name);
71 }
72 else
73 su->parseScript(stream, grp->name);
74 }
75 }
76 fireScriptEnded(fii->filename, skipScript);
77 }
78 }
79 }
80
81 fireResourceGroupScriptingEnded(grp->name);
82 LogManager::getSingleton().logMessage(
83 "Finished parsing scripts for resource group " + grp->name);
84 }

ScriptCompilerManager对象有一个StringVector mScriptPatterns成员,它里面主要保存着待解析的脚本文件类型信息,在ScriptCompilerManager被创建时,“.material”、“.program”、“.particle”、“.compositor”四个字符串会被保存在内。第27行的findResourceFileInfo()函数的代码展开如下:

 1     FileInfoListPtr ResourceGroupManager::findResourceFileInfo(const String& groupName,
2 const String& pattern, bool dirs)
3 {
4 OGRE_LOCK_AUTO_MUTEX
5 // MEMCATEGORY_GENERAL is the only category supported for SharedPtr
6 FileInfoListPtr vec(OGRE_NEW_T(FileInfoList, MEMCATEGORY_GENERAL)(), SPFM_DELETE_T);
7
8 // Try to find in resource index first
9 ResourceGroup* grp = getResourceGroup(groupName);
10 if (!grp)
11 {
12 OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,
13 "Cannot locate a resource group called '" + groupName + "'",
14 "ResourceGroupManager::findResourceFileInfo");
15 }
16
17 OGRE_LOCK_MUTEX(grp->OGRE_AUTO_MUTEX_NAME) // lock group mutex
18
19 // Iterate over the archives
20 LocationList::iterator i, iend;
21 iend = grp->locationList.end();
22 for (i = grp->locationList.begin(); i != iend; ++i)
23 {
24 FileInfoListPtr lst = (*i)->archive->findFileInfo(pattern, (*i)->recursive, dirs);
25 vec->insert(vec->end(), lst->begin(), lst->end());
26 }
27
28 return vec;
29 }

以FileSystemArchive为例,在findResourceFileInfo()函数中的第24行将会形成如下调用链:

1. FileSystemArchive::findFileInfo

                  ||
\/ 2. FileSystemArchive::findFiles(const String& pattern, bool recursive,
bool dirs, StringVector* simpleList, FileInfoList* detailList)

其中的调用“结点2”——findFiles()函数展开后为:

 1     void FileSystemArchive::findFiles(const String& pattern, bool recursive,
2 bool dirs, StringVector* simpleList, FileInfoList* detailList) const
3 {
4 intptr_t lHandle, res;
5 struct _finddata_t tagData;
6
7 // pattern can contain a directory name, separate it from mask
8 size_t pos1 = pattern.rfind ('/');
9 size_t pos2 = pattern.rfind ('\\');
10 if (pos1 == pattern.npos || ((pos2 != pattern.npos) && (pos1 < pos2)))
11 pos1 = pos2;
12 String directory;
13 if (pos1 != pattern.npos)
14 directory = pattern.substr (0, pos1 + 1);
15
16 String full_pattern = concatenate_path(mName, pattern);
17
18 lHandle = _findfirst(full_pattern.c_str(), &tagData);
19 res = 0;
20 while (lHandle != -1 && res != -1)
21 {
22 if ((dirs == ((tagData.attrib & _A_SUBDIR) != 0)) &&
23 ( !msIgnoreHidden || (tagData.attrib & _A_HIDDEN) == 0 ) &&
24 (!dirs || !is_reserved_dir (tagData.name)))
25 {
26 if (simpleList)
27 {
28 simpleList->push_back(directory + tagData.name);
29 }
30 else if (detailList)
31 {
32 FileInfo fi;
33 fi.archive = this;
34 fi.filename = directory + tagData.name;
35 fi.basename = tagData.name;
36 fi.path = directory;
37 fi.compressedSize = tagData.size;
38 fi.uncompressedSize = tagData.size;
39 detailList->push_back(fi);
40 }
41 }
42 res = _findnext( lHandle, &tagData );
43 }
44 // Close if we found any files
45 if(lHandle != -1)
46 _findclose(lHandle);
47
48 // Now find directories
49 if (recursive)
50 {
51 String base_dir = mName;
52 if (!directory.empty ())
53 {
54 base_dir = concatenate_path(mName, directory);
55 // Remove the last '/'
56 base_dir.erase (base_dir.length () - 1);
57 }
58 base_dir.append ("/*");
59
60 // Remove directory name from pattern
61 String mask ("/");
62 if (pos1 != pattern.npos)
63 mask.append (pattern.substr (pos1 + 1));
64 else
65 mask.append (pattern);
66
67 lHandle = _findfirst(base_dir.c_str (), &tagData);
68 res = 0;
69 while (lHandle != -1 && res != -1)
70 {
71 if ((tagData.attrib & _A_SUBDIR) &&
72 ( !msIgnoreHidden || (tagData.attrib & _A_HIDDEN) == 0 ) &&
73 !is_reserved_dir (tagData.name))
74 {
75 // recurse
76 base_dir = directory;
77 base_dir.append (tagData.name).append (mask);
78 findFiles(base_dir, recursive, dirs, simpleList, detailList);
79 }
80 res = _findnext( lHandle, &tagData );
81 }
82 // Close if we found any files
83 if(lHandle != -1)
84 _findclose(lHandle);
85 }
86 }

其作用是在本Archive指定的目录下搜索以pattern(findFiles的第一个参数)为后缀名的文件,并将满足条件的所有文件的相关信息(包括文件名、相应Archive对象指针、路径名及是否为压缩文件等)添加到名为detailList的FileInfoList中去并返回给调用函数。而ResourceGroupManager::findResourceFileInfo()函数会对保存在本group的locationList中的所有Archive对象进行如上操作(findResourceFileInfo()函数22-26行)。

因此,回到最开始的ResourceGroupManager::parseResourceGroupScripts()函数,可以知道第16-33行的作用是:对指定的resourceGroup进行搜索,并将保存在locationList中的所有Archive对象对应目录下,后缀名为资源管理器指定的文件类型(由mScriptPatterns来指定,对ScriptCompilerManager来说mScriptPattern中的值为:“.material”、“.program”、“.particle”、“.compositor”)的文件的相关信息,以文件为单位保存到scriptLoaderFileList(在第12行中定义)列表中。

  ResourceGroupManager::parseResourceGroupScripts()函数的第39-79行,则实现了对保存在scriptLoaderFileList列表中对应的脚本文件的解析。其中最核心的逻辑是DataStreamPtr stream = fii->archive->open(fii->filename)(60行)和su->parseScript()(70、73行)。第60行用于打开待解析的脚本文件,而su->parseScript()函数则会调用相应资源管理器的parseScript(DataStreamPtr& stream, const String& groupName)函数。以ScriptCompilerManager为例,其代码为:
 1     void ScriptCompilerManager::parseScript(DataStreamPtr& stream, const String& groupName)
2 {
3 #if OGRE_THREAD_SUPPORT
4 // check we have an instance for this thread (should always have one for main thread)
5 if (!OGRE_THREAD_POINTER_GET(mScriptCompiler))
6 {
7 // create a new instance for this thread - will get deleted when
8 // the thread dies
9 OGRE_THREAD_POINTER_SET(mScriptCompiler, OGRE_NEW ScriptCompiler());
10 }
11 #endif
12 // Set the listener on the compiler before we continue
13 {
14 OGRE_LOCK_AUTO_MUTEX
15 OGRE_THREAD_POINTER_GET(mScriptCompiler)->setListener(mListener);
16 }
17 OGRE_THREAD_POINTER_GET(mScriptCompiler)->compile(stream->getAsString(), stream->getName(), groupName);
18 }

其中,stream->getAsString()用来将脚本文件中的内容以字符串的形式加载到内存;stream->getName()用来返回待解析脚本文件的文件名。

转:Ogre源代码浅析——脚本及其解析(一)的更多相关文章

  1. Gradle 庖丁解牛(构建生命周期核心托付对象创建源代码浅析)

    [工匠若水 http://blog.csdn.net/yanbober 未经同意严禁转载,请尊重作者劳动成果.私信联系我] 1 背景 上一篇<Gradle 庖丁解牛(构建源头源代码浅析)> ...

  2. FFmpeg的HEVC解码器源代码简单分析:解析器(Parser)部分

    ===================================================== HEVC源代码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpe ...

  3. FFmpeg的H.264解码器源代码简单分析:解析器(Parser)部分

    ===================================================== H.264源代码分析文章列表: [编码 - x264] x264源代码简单分析:概述 x26 ...

  4. 【Spark】Stage生成和Stage源代码浅析

    引入 上一篇文章<DAGScheduler源代码浅析>中,介绍了handleJobSubmitted函数,它作为生成finalStage的重要函数存在.这一篇文章中,我将就DAGSched ...

  5. Android网络通信Volley框架源代码浅析(二)

    尊重原创 http://write.blog.csdn.net/postedit/25921795 在前面的一片文章Volley框架浅析(一)中我们知道在RequestQueue这个类中,有两个队列: ...

  6. VBA中使用JavaScript脚本语言解析JSON数据

    JSON:JavaScript 对象表示法(JavaScript Object Notation) 和xml相似,都是文本形式(保存在文本文件中或字符串等形式),比如: jsstr = {" ...

  7. 【Spark Core】任务运行机制和Task源代码浅析1

    引言 上一小节<TaskScheduler源代码与任务提交原理浅析2>介绍了Driver側将Stage进行划分.依据Executor闲置情况分发任务,终于通过DriverActor向exe ...

  8. Android网络通信Volley框架源代码浅析(三)

    尊重原创 http://write.blog.csdn.net/postedit/26002961 通过前面浅析(一)和浅析(二)的分析.相信大家对于Volley有了初步的认识,可是假设想更深入的理解 ...

  9. Android应用Preference相关及源代码浅析(SharePreferences篇)

    1 前言 在我们开发Android过程中数据的存储会有非常多种解决方式,譬如常见的文件存储.数据库存储.网络云存储等,可是Android系统为咱们提供了更加方便的一种数据存储方式.那就是SharePr ...

随机推荐

  1. ubuntu 安装 android studio

    总共分三个步骤: 安装JDK 这里引用下别人的安装过程http://blog.csdn.net/bhq2010/article/details/6839762 注:android studio 版本为 ...

  2. 不知不觉vs2012 update 4出来了

    今天早上起来原来看新闻说VISUAL STUIDO  2013 正式发布的日期是11月13日,今天打开微软VS2013下载页面,发现没有任何迹象,在浏览里面的新闻的时候发现了VS2012 UPDATA ...

  3. Java设计模式(六)合成模式 享元模式

    (十一)合成模式 Composite 合成模式是一组对象的组合,这些对象能够是容器对象,也能够是单对象.组对象同意包括单对象,也能够包括其它组对象,要为组合对象和单对象定义共同的行为.合成模式的意义是 ...

  4. lstm(一) 演化之路

    递归神经网络引入了时序的反馈机制,在语音.音乐等时序信号的分析上有重要的意义. Hochreiter(应该是Schmidhuber的弟子)在1991年分析了bptt带来的梯度爆炸和消失问题,给学习算法 ...

  5. 转:使用RNN解决NLP中序列标注问题的通用优化思路

    http://blog.csdn.net/malefactor/article/details/50725480 /* 版权声明:可以任意转载,转载时请标明文章原始出处和作者信息 .*/ author ...

  6. NLP系列(4)_朴素贝叶斯实战与进阶(转)

    http://blog.csdn.net/han_xiaoyang/article/details/50629608 作者: 寒小阳 && 龙心尘 时间:2016年2月. 出处:htt ...

  7. MFC对话框:模态对话框及其弹出过程

    From: http://www.jizhuomi.com/school/c/160.html 加法计算器对话框程序大家照着做一遍后,相信对基于对话框的程序有些了解了,有个好的开始对于以后的学习大有裨 ...

  8. 阿里云centos配置nginx和nodejs

    今天新买了阿里云,想把网站跑起来,于是记录跑起来的过程 1.购买域名 2.购买解析 3.购买ecs主机 4.ssh登录主机 5.安装vsftpd 6.配置ftp用户.文件夹.权限 7.安装nginx/ ...

  9. 10 个超炫绘制图表图形的 Javascript 插件【转载+整理】

    原文地址 现在,有很多在线绘制图表和图形(Charts and Graphs)的 JavaScript 插件,这些插件还都是免费,以及图表库.这些插件大量出现的原因是基于一个事实:人们不再依赖于 Fl ...

  10. logback-kafka-appender

    logback 日志写入kafka队列 logback-kafka-appender Logback incompatibility Warning Due to a bug in logback-c ...