zoukankan      html  css  js  c++  java
  • Ogre源代码浅析——资源管理逻辑结构(一)

          Ogre把资源分为“Font”、“GpuProgram”、“Material”、“Mesh”、“Skeleton”和“Texture”等类型,它们分别用Font、GpuProgram、Material、Mesh、Skeleton、Texture等同名的类对象来描述,这些类都直接从Resource基类派生。Ogre的Resource对象都由ResourceManager来管理。不同类型资源的管理,分别由不同的资源管理器来实现,比如以上各种类型资源都对应着各自的资源管理器,FontManager、GpuProgramManager、MaterialManager、MeshManager、SkeletonManager、TextureManager等,它们都以ResourceManager作为自已的基类。各种类型资源类对象的创建、Load/Unload、销毁等操作,都由相应的ResourceManager来完成。但Ogre的对资源的管理还不仅限于此。为了更方便资源的使用,提高资源的使用的效率,Ogre还引入了多种技术手段,下面将结合相关代码对此作一些简单的分析。

          最令人印象深刻的大概要属Ogre引入的Group概念。Ogre中有一个被称为ResourceGroupManager的类,其中内嵌了一个ResourceGroup的结构定义,很明显定义ResourceGroup只是为了ResourceGroupManager内部使用。在需要进行3D场景展示的一般应用中,经常会遇到需要进行场景切换的时候,比如游戏中的关卡切换时,虚拟现实中角色由一个地点转换到另一个地点时等等。而在渲染每个场景时所需的资源往往涉及了所有的资源类型,一旦场景发生切换,当前所使用的大量资源都需要被逐一卸载,而新的场景所需的各类资源要逐一被加载。在游戏编程时,可以在自已编写的关卡管理器中处理类似工作,这明显会产生额外的工作量,更麻烦的是这部分代码逻辑可能需要在每个应用中被重复编写,而如果借助Ogre提供的ResourceGroup就可以直接方便地实现类似功能了。以下是ResourceGroup定义的主要部分:

    struct ResourceGroup
    {
    	enum Status
    	{
    		UNINITIALSED = 0,
    		INITIALISING = 1,
    		INITIALISED = 2,
    		LOADING = 3,
    		LOADED = 4
    	};
    
    	/// Group name
    	String name;
    	/// Group status
    	Status groupStatus;
    	/// List of possible locations to search
    	LocationList locationList;
    	/// Index of resource names to locations, built for speedy access (case sensitive archives)
    	ResourceLocationIndex resourceIndexCaseSensitive;
            /// Index of resource names to locations, built for speedy access (case insensitive archives)
            ResourceLocationIndex resourceIndexCaseInsensitive;
    	/// Pre-declared resources, ready to be created
    	ResourceDeclarationList resourceDeclarations;
    	/// Created resources which are ready to be loaded / unloaded
    	// Group by loading order of the type (defined by ResourceManager)
    	// (e.g. skeletons and materials before meshes)
    	typedef map<Real, LoadUnloadResourceList*>::type LoadResourceOrderMap;
    	LoadResourceOrderMap loadResourceOrderMap;
            /// Linked world geometry, as passed to setWorldGeometry
            String worldGeometry;
            /// Scene manager to use with linked world geometry
            SceneManager* worldGeometrySceneManager;
    	// in global pool flag - if true the resource will be loaded even a different	group was requested in the load method as a parameter.
    	bool inGlobalPool;
    
    	void addToIndex(const String& filename, Archive* arch);
    	void removeFromIndex(const String& filename, Archive* arch);
    	void removeFromIndex(Archive* arch);
    };
    

      其中的name变量,用来标识group对象的名称。在生成ResourceGroup对象后,对象指针会被保存在ResourceGroupManager的mResourceGroupMap容器中并以name为key。来看下它的定义:

    /// Map from resource group names to groups
    typedef map<String, ResourceGroup*>::type ResourceGroupMap;
    ResourceGroupMap mResourceGroupMap;

      再来看ResourceGroup中LocationList的定义:

    struct ResourceLocation
    {
    	/// Pointer to the archive which is the destination
    	Archive* archive;
    	/// Whether this location was added recursively
    	bool recursive;
    };
    /// List of possible file locations
    typedef list<ResourceLocation*>::type LocationList;
    

      可以看到ResourceLocation对象与Archive对象相比,只多了一个变量recursive,它主要用来表示对相应的Archive是否要进行递归操作(当Archive表示的目录中含有子目录时,一般要进行递归操作)因此ResourceLocation对象更完整地描述了资源所在目录的情况。

         再来看ResourceGroup中ResourceLocationIndex和LoadUnloadResourceList的定义:

    /// Resource index entry, resourcename->location 
    typedef map<String, Archive*>::type ResourceLocationIndex;
    
    /// List of resources which can be loaded / unloaded
    typedef list<ResourcePtr>::type LoadUnloadResourceList;
    

      在Ogre中对资源进行使用和加载前先要对其进行定位,也就是要把待使用的资源的资源名、资源类型以及资源所在的路径关联起来。由于Ogre是以资源组(ResourceGroup)为单位对资源进行使用的,所以这个定位工作一般通过ResourceGroupManager对象来完成。ResourceGroupManager提供了addResourceLocation()方法来实现这一功能。

    void ResourceGroupManager::addResourceLocation(const String& name, 
            const String& locType, const String& resGroup, bool recursive)
    {
        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);
            
        StringUtil::StrStreamType msg;
        msg << "Added resource location '" << name << "' of type '" << locType
                << "' to resource group '" << resGroup << "'";
        if (recursive)
            msg << " with recursive option";
             LogManager::getSingleton().logMessage(msg.str());
    
    }

           addResourceLocation()的第一个参数“name”对应的资源所在目录的路径字符串;第二个参数locType对应着此目录下资源的类型——普通文件还是压缩文件;第三个参数resGroup表器要操作的group名称。Ogre会先根据resGroup在mResourceGroupMap中搜索相应的group,如果没找到就创建一个保存到mResourceGroupMap中;然后根据头两个参数生成相应的Archive对象和ResourceLocation对象,并把此ResourceLocation对象指针保存到第三个参数指定的group的locationList成员容器中。

          接下来,Ogre会遍历Archive所表示的路径中的所有资源文件,并以资源文件的文件名为key将相应的Archive对象指针保存到ResourceGroup中的resourceIndexCaseSensitive和resourceIndexCaseInsensitive中(前者对资源文件名大小写敏感,后者不敏感),这样就把资源文件名与资源所在路径关联到了一起,为之后的资源加载作好准备,这是第一步。这一步工作可以手动逐条传入目录字符串来分别进行,也可以象例程中那样,通过外部配制文件来自动批处理。

        Ogre中资源在加载到内存后,资源数据是保存在相应的资源对象中的,因此Ogre首先要根据需要生成资源对象,这是紧接上面第一步完成后第二步要做的工作。第二步完成后,Ogre会在内存中生成当前需要的所有的资源类对象,但此时的资源类对象中的数据是空的,相应的外部资源数据还没有真正加载进内存。这一步工作又被称为资源的初始化(initialise)。Ogre可以通过ResouceGroupManager的initialiseResourceGroup(const String& name)方法来对指定的group进行初始化,也可以通过initialiseAllResourceGroups()一次性对所有保存在mResourceGroupMap中的group中的资源进行初始化。来看相关代码:

    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;
            LogManager::getSingleton().logMessage("Creating resources for group " + name);
            createDeclaredResources(grp);
            grp->groupStatus = ResourceGroup::INITIALISED;
            LogManager::getSingleton().logMessage("All done");
            // Reset current group
            mCurrentGroup = 0;
        }
    }

          可以看到,在初始化开始之前,Ogre先将group的状态设为INITIALISIN,在初始化完成后状态被设成了INITIALISED。具体的初始化工作分为两个部分,一个是加载并实例化脚本,由于有些资源如材质(Material)、Shader(GpuProgram)等是由脚本文件描述的,所以对它们来说需要进行脚本加载和处理工作,这部分工作主要由parseResourceGroupScripts(grp)来完成;另一个工作如前所述是根据需要创建资源对象,这部分工作主要由createDeclareResources(grp)函数来完成。来看一下其中的代码:

    void ResourceGroupManager::createDeclaredResources(ResourceGroup* grp)
    {
    
        for (ResourceDeclarationList::iterator i = grp->resourceDeclarations.begin();
                i != grp->resourceDeclarations.end(); ++i)
        {
            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;
            }
            else
            {
                loadList = li->second;
            }
            loadList->push_back(res);
    
       }
    
    }

      Ogre在之前的资源定位中,把所有待加载的资源与它所在的目录关联起来了。但在实际应用中,同一目录下的资源在某一阶段不需要全部加载,常常只需要加载其中的一部分。因此在上面所说的初始化过程中,就不能根据目录或者说Archive对象来创建资源对象,而应该根据实际需要来指定。指定要被创建的资源对象的信息就被保存在ResourceGroup中的resourceDeclaration里。看一下它的相关类型定义:

    struct ResourceDeclaration
    {
         String resourceName;
         String resourceType;
         ManualResourceLoader* loader;
        NameValuePairList parameters;
    };
    /// List of resource declarations
    typedef list<ResourceDeclaration>::type ResourceDeclarationList;

          可以清楚地看到ResourceDeclaration中主要保存的是要被创建的资源的名称。因此ResourceGroupManager::createDeclaredResources()函数会根据事先保存在各group的resourceDeclaration中的资源(这些资源被称为“已声明的”)的名称来创建资源对象。需要强调的是,此时仅是创建资源对象而已,并未加载相应的资源数据。

    作者:yzwalkman
    转载请注明出处。
  • 相关阅读:
    菜单无限极分类核心代码
    获取页面中更新删除传过来的id
    CI循环数组问题
    ci框架model中的进行增删改的写法
    MySQL DBA的修炼与未来(参考篇)
    Linux学习笔记(13)linux软件安装rpm与yum--理论篇
    Linux学习笔记(12)linux文件目录与用户管理
    Linux学习笔记(11)linux网络管理与配置之一——配置路由与默认网关,双网卡绑定(5-6)
    Linux学习笔记(10)linux网络管理与配置之一——主机名与IP地址,DNS解析与本地hosts解析(1-4)
    自定义流水号,前置加0
  • 原文地址:https://www.cnblogs.com/yzwalkman/p/2830117.html
Copyright © 2011-2022 走看看