ogre 引擎 框架追踪 第三章 资源加载之虚加载
前面写到的,资源的操作过程如下:
创建资源管理器->继承不同资源管理器的创建->向资源管理器添加路径->初始化已添加路径的资源->初始化资源->加载资源->渲染资源。
第二章的虚加载,已经将资源的路径添加到group并且保存好文件名与archive对象的对应关系。
接下来跟一下资源的初始化。
以笨蛋化的初始化为例
Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
初始化所有资源,获取资源组的map,然后一个一个的初始化
//资源操作时,一般都先上锁
OGRE_LOCK_AUTO_MUTEX
// Intialise all declared resource groups
ResourceGroupMap::iterator i, iend;
iend = mResourceGroupMap.end();
for (i = mResourceGroupMap.begin(); i != iend; ++i)
{
ResourceGroup* grp = i->second;
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
mCurrentGroup = grp;
parseResourceGroupScripts(grp);//解析资源组中所有脚本
LogManager::getSingleton().logMessage("Creating resources for group " + i->first);
createDeclaredResources(grp);//创建声明了的资源。
grp->groupStatus = ResourceGroup::INITIALISED;//标记为已初始化
mCurrentGroup = 0;
}
}
资源初始化之脚本解析
先解析资源组中所有脚本parseResourceGroupScripts
// Count up the number of scripts we have to parse
typedef list<FileInfoListPtr>::type FileListList;
typedef SharedPtr<FileListList> FileListListPtr;
typedef std::pair<ScriptLoader*, FileListListPtr> LoaderFileListPair;
typedef list<LoaderFileListPair>::type 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)//脚本加载map是在各个有脚本资源管理器创建时注册到map中的,有FontManager(*.fontdef)、ScriptCompilerManager(它解析的格式*.program、*.material、*.particle、*.compositor、*.os)、OverlayManager(*.overlay)、MaterialManager、ParticleSystemManager,后面两个的解析交给脚本编译管理器了
{
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);//获取该格式的所有文件列表。包含archive对象指针(包含了文件的名称、绝对位置、文件类型)
scriptCount += fileList->size();
fileListList->push_back(fileList);
}
scriptLoaderFileList.push_back(
LoaderFileListPair(su, fileListList));//将脚本加载器跟他所有的文件列表保存
}
// Fire scripting event
fireResourceGroupScriptingStarted(grp->name, scriptCount);//通知下开始解析,有进度条的就开始显示了 start parse 。。。
// 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);//告知sdktray,输出下开始解析的文件名称,获取下是否文件被过滤掉
if(skipScript)
{
LogManager::getSingleton().logMessage("Skipping script " + fii->filename);
}
else
{
LogManager::getSingleton().logMessage("Parsing script " + fii->filename);
DataStreamPtr stream = fii->archive->open(fii->filename);//通过自己的archive打开文件,返回数据
if (!stream.isNull())
{
if (mLoadingListener)//若添加了资源加载的监听者,就会告诉它文件打开
mLoadingListener->resourceStreamOpened(fii->filename, grp->name, 0, stream);
if(fii->archive->getType() == "FileSystem" && stream->size() <= 1024 * 1024)
{
DataStreamPtr cachedCopy;
cachedCopy.bind(OGRE_NEW MemoryDataStream(stream->getName(), stream));
su->parseScript(cachedCopy, grp->name);//解析文件夹中的脚本
}
else
su->parseScript(stream, grp->name);
}
}
fireScriptEnded(fii->filename, skipScript);//解析结束告知
}
}
}
fireResourceGroupScriptingEnded(grp->name);
LogManager::getSingleton().logMessage("Finished parsing scripts for resource group " + grp->name);
以*.program文件为例,在解析的底层调用了ScriptParser::parse,解析成ConcreteNodeListPtr(包含了命令及参数等);解析之后就编译ScriptCompiler::compile。不同的脚本不同的解析、编译方法,后面再细说。尤其是材质、program、cg、hlgl等。其实就是解析完了后,就创建对应的对象,比如材质(包括tecnique、pass等)、overlay等。创建完之后,就告诉资源管理器,这个资源我们已经创建了。
资源初始化之创建声明了的资源
createDeclaredResources
跟进代码
for (ResourceDeclarationList::iterator i = grp->resourceDeclarations.begin();
i != grp->resourceDeclarations.end(); ++i)//resourceDeclarations里面一般为空,除非通过ResourceGroupManager::declareResource调用,以添加声明。声明了的资源提前于其他资源非ogre自建资源而创建了。
{
ResourceDeclaration& dcl = *i;
// Retrieve the appropriate manager
ResourceManager* mgr = _getResourceManager(dcl.resourceType);
// Create the resource
ResourcePtr res = mgr->create(dcl.resourceName, grp->name,
dcl.loader != 0, dcl.loader, &dcl.parameters);//创建资源,记着,是创建,不是加载
// Add resource to load list
ResourceGroup::LoadResourceOrderMap::iterator li =
grp->loadResourceOrderMap.find(mgr->getLoadingOrder());
LoadUnloadResourceList* loadList;
if (li == grp->loadResourceOrderMap.end())
{
loadList = OGRE_NEW_T(LoadUnloadResourceList, MEMCATEGORY_RESOURCE)();
grp->loadResourceOrderMap[mgr->getLoadingOrder()] = loadList;//向加载顺序map中添加
}
else
{
loadList = li->second;
}
loadList->push_back(res);//资源组的加载列表中存入资源指针
}
资源的创建、加载操作一般分布在三个时期:
第一个时期是root初始化时,材质、mesh的管理器也做了初始化,在他们初始化时创建了ogre系统默认的材质(default)和mesh(cube、sphere),不仅仅创建了,还加载了;这个时期的创建是为了不至于不加载外部资源而不能使用。
第二个时期就是当前的时期,资源初始化时,(1)在脚本解析过程中,解析了的脚本对象就创建了(材质、overlay等),(2)先把声明了的资源创建了。这个时期的就是创建,没有加载。
第三个时期就是在资源初始化完成,在外部创建entity、material(等)的时候。而且加载主要集中在这个时期。也就是下章的 实加载。
总结资源初始化干的事儿,就是在前期资源文件、文件类型、archive读取并对应之后,把资源组中所有脚本文件解析并创建对应的对象;创建资源组中已声明了的资源。
本章引出的扩展文章为(1)关于不同的脚本不同的解析、编译方法(2)关于资源组管理器、资源管理器、资源的关系及ogre中关于资源的继承关系。