现在来分析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 * | |
| The archive in which the file has been found (for info when performing multi-Archive searches, note you should still open through ResourceGroupManager). |
| |
| |
| |
size_t | |
| |
size_t | |
|
主要意思就是由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)。