Ogre源代码浅析——脚本及其解析(⼀)
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脚本是以positor为后缀的⽂本⽂件。
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”和“positor”为后缀的⽂件都统⼀由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 != d(); ++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 != d(); ++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 != d(); ++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
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”、“positor”四个字符串会被保存在内。第27⾏的findResour
ceFileInfo()函数的代码展开如下:
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-&d();
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                    fipressedSize = 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
50        {
51            String base_dir = mName;
52            if (!pty ())
53            {
54                base_dir = concatenate_path(mName, directory);
55                // Remove the last '/'
56                ase (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”、“positor”)的⽂件的相关信息,以⽂件为单位保存到scriptLoaderFileList(在第12⾏中定义)列表中。
ResourceGroupManager::parseResourceGroupScripts()函数的第39-79⾏,则实现了对保存在scriptLoa
derFileList列表中对应的脚本⽂件的解析。其中最核⼼的逻辑是DataStreamPtr stream = fii->archiv  1void 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)
5if (!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()⽤来返回待解析脚本⽂件的⽂件名。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。