前面两章已经创建root、加载dll、创建渲染系统、创建渲染窗口。这章,我们该往整个框架里塞东西了。ogre1.8用的单线程,不支持后台加载。关于资源的操作就是:
创建资源管理器->继承不同资源管理器的创建->向资源管理器添加路径->初始化已添加路径的资源->加载资源->渲染资源。
前面两步已经在前面实现,接下来的开始添加路径。
1、ogre资源的路径添加
ogre中有两种方式实现路径加载,一种傻瓜式加载,直接传入一个cfg文件名称,解析了就把解析的资源目录添加到资源管理器中,另一种就是手动添加,直接调用资源管理器向里面添加资源。
resouce.cfg
第一种,傻瓜式的。
先看下resouce.cfg里的内容
[Essential] #资源分组名称,这个里面是ogre自带的最基础的资源
#这里有traymanager里所需的资源,包括overlay、鼠标、字体
Zip=../../media/packs/SdkTrays.zip
Zip=../../media/packs/profiler.zip
FileSystem=../../media/thumbnails
# Common sample resources needed by many of the samples.
# Rarely used resources should be separately loaded by the
# samples which require them.
[Popular]
FileSystem=../../media/fonts
FileSystem=../../media/materials/programs
FileSystem=../../media/materials/scripts
FileSystem=../../media/materials/textures
FileSystem=../../media/materials/textures/nvidia
FileSystem=../../media/models
FileSystem=../../media/particle
FileSystem=../../media/DeferredShadingMedia
FileSystem=../../media/PCZAppMedia
FileSystem=../../media/RTShaderLib
FileSystem=../../media/RTShaderLib/materials
FileSystem=../../media/materials/scripts/SSAO
FileSystem=../../media/materials/textures/SSAO
Zip=../../media/packs/cubemap.zip
Zip=../../media/packs/cubemapsJS.zip
Zip=../../media/packs/dragon.zip
Zip=../../media/packs/fresneldemo.zip
Zip=../../media/packs/ogretestmap.zip
Zip=../../media/packs/ogredance.zip
Zip=../../media/packs/Sinbad.zip
Zip=../../media/packs/skybox.zip
[General]
FileSystem=../../media
# Materials for visual tests
[Tests]
FileSystem=../../media/../../Tests/Media
[Popular]
Zip=../../media/packs/NxOgre.zip
结构上就是:
[分组组名]
Zip=**.zip #zip文件目录
FileSystem=** #文件夹
解析并加载cfg文件
void RenderScene::setupResources(Ogre::String cfgFile)
{
ConfigFile cf;
cf.load(cfgFile);//加载并解析
// Go through all sections & settings in the file
ConfigFile::SectionIterator seci = cf.getSectionIterator();
String secName, typeName, archName;
while (seci.hasMoreElements())
{
secName = seci.peekNextKey();
ConfigFile::SettingsMultiMap *settings = seci.getNext();
ConfigFile::SettingsMultiMap::iterator i;
for (i = settings->begin(); i != settings->end(); ++i)
{
typeName = i->first;
archName = i->second;
//资源管理器中添加资源路径
ResourceGroupManager::getSingleton().addResourceLocation(
archName, typeName, secName);
}
}
}
.cfg文件的解析,最终的实现:
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,转换成ogredatastream,代码不难,就是用windows的方法获取大小,再用模板类转换成datasteam,可以自己看。
DataStreamPtr stream(OGRE_NEW FileStreamDataStream(filename, &fp, false));
load(stream, separators, trimWhitespace);//继续,根儿上的解析
根儿上的解析
void ConfigFile::load(const DataStreamPtr& stream, const String& separators,
bool trimWhitespace)
{
clear();//清理之前加载的东西
String currentSection = StringUtil::BLANK;
SettingsMultiMap* currentSettings = OGRE_NEW_T(SettingsMultiMap, MEMCATEGORY_GENERAL)();//用ogre_new智能指针新建,不用担心释放了就
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//不是组名的添加到当前组中
{
Ogre::String::size_type separator_pos = line.find_first_of(separators, 0);
if (separator_pos != Ogre::String::npos)
{
optName = line.substr(0, separator_pos);//等号前的name
Ogre::String::size_type nonseparator_pos = line.find_first_not_of(separators, separator_pos);
optVal = (nonseparator_pos == Ogre::String::npos) ? "" : line.substr(nonseparator_pos);//等号后的value
if (trimWhitespace)
{
StringUtil::trim(optVal);
StringUtil::trim(optName);
}
currentSettings->insert(SettingsMultiMap::value_type(optName, optVal));//当前组中添加name和value
}
}
}
}
}
添加到资源管理器
解析出的信息通过addResourceLocation添加到资源管理器中,添加过程:
获取resoucegroup->有则获取无则创建->文件管理器加载
//获取group
ResourceGroup* grp = getResourceGroup(resGroup);
if (!grp)
{
createResourceGroup(resGroup);
grp = getResourceGroup(resGroup);
}
//锁资源分组
OGRE_LOCK_MUTEX(grp->OGRE_AUTO_MUTEX_NAME) // lock group mutex
// Get archive
Archive* pArch = ArchiveManager::getSingleton().load( name, locType );
// Add to location list
ResourceLocation* loc = OGRE_NEW_T(ResourceLocation, MEMCATEGORY_RESOURCE);
loc->archive = pArch;
loc->recursive = recursive;
grp->locationList.push_back(loc);
// Index resources
StringVectorPtr vec = pArch->find("*", recursive);//获取文件路径中所有文件
for( StringVector::iterator it = vec->begin(); it != vec->end(); ++it )
grp->addToIndex(*it, pArch);//通过文件名保存archive。已判断资源存在不存在、文件对象是啥等等。
先解析ArchiveManager::getSingleton().load这个函数,实现由注册的文件工厂(文件类型)根据文件路径创建文件并加载
其中核心代码:
ArchiveFactoryMap::iterator it = mArchFactories.find(archiveType);//前面root创建时我们已经注册过FileSystemArchiveFactory、ZipArchiveFactory、EmbeddedZipArchiveFactory
if (it == mArchFactories.end())
。。。
//没找到就报错。
pArch = it->second->createInstance(filename);//创建文件对象
pArch->load(); //加载
mArchives[filename] = pArch;
其中FileSystemArchive的load代码如下
OGRE_LOCK_AUTO_MUTEX
String testPath = concatenate_path(mName, "__testwrite.ogre");
std::ofstream writeStream;
writeStream.open(testPath.c_str());
if (writeStream.fail())
mReadOnly = true;
else
{
mReadOnly = false;
writeStream.close();
::remove(testPath.c_str());
}
我的妈呀,发现里面就是虚晃一枪,就是检查了下路径存在不存在,是不是只读,别的啥也没干啊
再看下ZipArchive的load
OGRE_LOCK_AUTO_MUTEX
if (!mZzipDir)
{
zzip_error_t zzipError;
mZzipDir = zzip_dir_open_ext_io(mName.c_str(), &zzipError, 0, mPluginIo);//打开
checkZzipError(zzipError, "opening archive");
// Cache names
ZZIP_DIRENT zzipEntry;
while (zzip_dir_read(mZzipDir, &zzipEntry))//读取
{
FileInfo info;
info.archive = this;
// Get basename / path
StringUtil::splitFilename(zzipEntry.d_name, info.basename, info.path);
info.filename = zzipEntry.d_name;
// Get sizes
info.compressedSize = static_cast<size_t>(zzipEntry.d_csize);
info.uncompressedSize = static_cast<size_t>(zzipEntry.st_size);
// folder entries
if (info.basename.empty())
{
info.filename = info.filename.substr (0, info.filename.length () - 1);
StringUtil::splitFilename(info.filename, info.basename, info.path);
info.compressedSize = size_t (-1);
}
mFileList.push_back(info);//添加zip中的内容到zip archive的文件列表中
}
}
zip的倒是干了点事儿,那就是读取zip中文件列表并保存,以备后期真正的加载。
在虚加载之后,就是把路径中所有文件及对应的archive对象保存,以备后面使用。
简单描述虚加载就是:读取文件路径、zip,添加到各自的group,并且保存里面的文件与archive对象的对应。
虚加载之后,就是资源的初始化,下一章开始。