【Ogre引擎架构】 第四讲 模型文件渲染数据的存储机制
2015-03-22 06:40
639 查看
不论是你看到的模型,骨骼动画,或是各种让你愉悦感爆棚的特效,在这些一切被渲染都屏幕之前,你需要知道的是它们都存储在一个叫做HardwareBuffer的地方,每个单独的可渲染对象被渲染系统RenderSystem渲染的时候,都会单独把自己的HardwareBuffer传给RenderSystem,正是这样优美的架构设计,让开发者可以迅速的将各种素材应用到自己的游戏之中而不需要做多余的事,下面让我们一起进入数据的容器HardwareBuffer
下面首先欣赏几张由作者亲手创造的美图,读者可以直观感受一下HardwareBuffer的魅力
接上一讲对MeshSerializer的分析,在读取Sinbad.mesh.xml的模型数据时,需要将内容存储到HardwareBuffer供RenderSystem使用,首先MeshSerializer::Import将传入的文件名指定的Sinbad.mesh.xml文件交给XmlDocument打开,在前两讲已经介绍了这个文件内容以及存储形式,打开后接着就是把数据读取保存到Mesh里的HardwareBuffer,读者注意此函数是作者根据源代码自形编写的简化版本,和原代码不一致,重在理清其读取流程,
DefaultHardwareBufferManager顾名思义就是管理HardwareBuffer的类,HardwareBuffer本身是一个抽象类需要对其进行派生实现才能写入和读取数据,DefaultHardwareBuffer便是其继承类,在前面几讲里有提到Mesh类内部是由若干SubMesh组成的,而SubMesh直接持有各种HardwareBuffer,在渲染时将HardwareBuffer的数据提交给RenderSystem进行渲染
上一讲作者已经强调,学习Ogre首先需要学习OpenGL或DirectX,在这里作者再次强调,如果你还没有掌握,请不要学习Ogre,因为Ogre的实现完全是基于渲染架构的,禁忌急于求成,一定要注重基础,不可走弯路导致大量浪费时间
在读取顶点相关数据时,进入MeshSerializer::ReadGeometry,通过HardwareVertexBuffer进行读写
下面首先欣赏几张由作者亲手创造的美图,读者可以直观感受一下HardwareBuffer的魅力
接上一讲对MeshSerializer的分析,在读取Sinbad.mesh.xml的模型数据时,需要将内容存储到HardwareBuffer供RenderSystem使用,首先MeshSerializer::Import将传入的文件名指定的Sinbad.mesh.xml文件交给XmlDocument打开,在前两讲已经介绍了这个文件内容以及存储形式,打开后接着就是把数据读取保存到Mesh里的HardwareBuffer,读者注意此函数是作者根据源代码自形编写的简化版本,和原代码不一致,重在理清其读取流程,
DefaultHardwareBufferManager顾名思义就是管理HardwareBuffer的类,HardwareBuffer本身是一个抽象类需要对其进行派生实现才能写入和读取数据,DefaultHardwareBuffer便是其继承类,在前面几讲里有提到Mesh类内部是由若干SubMesh组成的,而SubMesh直接持有各种HardwareBuffer,在渲染时将HardwareBuffer的数据提交给RenderSystem进行渲染
//import xml file void MeshSerializer::Import(const char* filename,Mesh* pMesh) { //HardwareBuffer* p=OGRE_NEW HardwareIndexBuffer; DefaultHardwareBufferManager* pHBMgr= static_cast<DefaultHardwareBufferManager*>(HardwareBufferManager::GetSingletonPtr()); if(NULL == filename) return ; m_pXmlDoc = new XmlDocument(filename); if(!m_pXmlDoc->LoadFile()){ //fail LOGERROR("MeshSerializer::Import Load "+string(filename)+" fail"); return ; } m_pMesh=pMesh; const XmlElement* rootElem=m_pXmlDoc->RootElement(); if(!rootElem){ LOGERROR("MeshSerializer::Import rootElem is NULL"); return ; } //submeshes 读取Submeshes const XmlElement* elem=rootElem->FirstChildElement("submeshes"); if(elem) ReadSubMeshes(elem); //skeleton link elem = rootElem->FirstChildElement("skeletonlink"); if(elem) ReadSkeletonLink(elem,pMesh); delete m_pXmlDoc; m_pXmlDoc=NULL; //delete pHBMgr; }前面已经讲过Sinbad.mesh.xml模型文件读取和保存的过程,下面来直接进入ReadSubMesh函数,一探究竟,流程比较清晰,读者只需仔细对照源码,理清其流程即可。
void MeshSerializer::ReadSubMeshes(const XmlElement* mElem) { for(const XmlElement* smElem=mElem->FirstChildElement();smElem;smElem=smElem->NextSiblingElement()){ SubMesh* sm=m_pMesh->CreateSubMesh(); //material const char* material=smElem->Attribute("material"); if(material) sm->SetMaterialName(string(material)); //usesharedvertices const char* useSharedVertices=smElem->Attribute("usesharedvertices"); if(useSharedVertices) sm->m_bUseSharedVertices=StringConverter::ParseBool(useSharedVertices); //operation type bool bReadFaces=true;//判断是否需要读取面信息 const char* operType=smElem->Attribute("operationtype"); if(operType){ if(!strcmp(operType,"triangle_list")) sm->m_OperationType=RenderOperation::OT_TRIANGLE_LIST; } //index bits 16 or 32 bool bUse32bitIndex=false; const char* tmp=smElem->Attribute("use32bitindexes"); if(tmp) bUse32bitIndex=StringConverter::ParseBool(tmp); //Faces if(bReadFaces){ //判断一下面的数量是否正确 int actualCount=0; const XmlElement* faces=smElem->FirstChildElement("faces"); if(faces){ for(const XmlElement* face=faces->FirstChildElement();face;face=face->NextSiblingElement()) ++actualCount; const char* claimCount=faces->Attribute("count"); if(claimCount){ int iClaimCount=StringConverter::ParseInt(claimCount); if(actualCount != iClaimCount){ LOGERROR("ReadSubMeshes faces actualCount!=iClaimCount"); return ; } } //把数据存入索引数据块内 if(actualCount>0){ switch(sm->m_OperationType){ case RenderOperation::OT_TRIANGLE_LIST: sm->m_pIndexData->m_IndexCount=3*actualCount; break; } } } //allocate space HardwareIndexBufferSharedPtr ptrIndexBuffer=HardwareBufferManager::GetSingleton().CreateIndexBuffer( bUse32bitIndex?HardwareIndexBuffer::IT_32BIT:HardwareIndexBuffer::IT_16BIT, sm->m_pIndexData->m_IndexCount,HardwareBuffer::HBU_DYNAMIC); sm->m_pIndexData->m_IndexBufferPtr=ptrIndexBuffer; //将索引数据装入已分配的内存 unsigned int *pInt=0; unsigned short *pShort=0; if(bUse32bitIndex){ pInt=static_cast<unsigned int*>(ptrIndexBuffer->Lock(HardwareBuffer::HBL_DISCARD)); }else{ pShort=static_cast<unsigned short*>(ptrIndexBuffer->Lock(HardwareBuffer::HBL_DISCARD)); } for(const XmlElement* face=faces->FirstChildElement();face;face=face->NextSiblingElement()){ if(bUse32bitIndex){ *(pInt++)=StringConverter::ParseInt(face->Attribute("v1")); *(pInt++)=StringConverter::ParseInt(face->Attribute("v2")); *(pInt++)=StringConverter::ParseInt(face->Attribute("v3")); }else{ *(pShort++)=StringConverter::ParseInt(face->Attribute("v1")); *(pShort++)=StringConverter::ParseInt(face->Attribute("v2")); *(pShort++)=StringConverter::ParseInt(face->Attribute("v3")); } } ptrIndexBuffer->Unlock(); } //geometry if(!sm->m_bUseSharedVertices){ const XmlElement* geoElem=smElem->FirstChildElement("geometry"); if(geoElem) ReadGeometry(geoElem,sm->m_pVertexData); } //bone assignments const XmlElement* boneAssignElem = smElem->FirstChildElement("boneassignments"); if(boneAssignElem) ReadBoneAssignments(boneAssignElem,sm); } }作者重点介绍一下HardwareBuffer的两个派生类,HardwareIndexBuffer和HardwareVertexBuffer
上一讲作者已经强调,学习Ogre首先需要学习OpenGL或DirectX,在这里作者再次强调,如果你还没有掌握,请不要学习Ogre,因为Ogre的实现完全是基于渲染架构的,禁忌急于求成,一定要注重基础,不可走弯路导致大量浪费时间
class HardwareBuffer:public BufferAlloc{ public: enum Usage{ HBU_STATIC=1, HBU_DYNAMIC=2, HBU_DYNAMIC_WRITE_ONLY=4, HBU_WRITE_ONLY=8, HBU_STATIC_WRITE_ONLY=16 }; enum LockOptions{ HBL_DISCARD, HBL_READ_ONLY }; //only called in HardwareBufferManager HardwareBuffer(Usage usage):m_Usage(usage),m_SizeInBytes(0),m_LockStart(0),m_LockSize(0),m_bIsLocked(false){} virtual ~HardwareBuffer(){} Usage GetUsage() const{return m_Usage;} size_t GetSizeInBytes() const {return m_SizeInBytes;} virtual void WriteData(size_t dstOffset,size_t length,const void* pSource) = 0; virtual void CopyData(HardwareBuffer& srcBuf,size_t srcOffset,size_t dstOffset,size_t length){ const void* pSource = srcBuf.Lock(srcOffset,length,HBL_READ_ONLY); this->WriteData(dstOffset,length,pSource); srcBuf.Unlock(); } protected: size_t m_SizeInBytes; Usage m_Usage; size_t m_LockStart; size_t m_LockSize; bool m_bIsLocked; //Internal Implement virtual void* LockImpl(size_t offset,size_t length,LockOptions options)=0; virtual void UnlockImpl()=0; public: virtual void* Lock(size_t offset,size_t length,LockOptions options){ // void* ret=NULL; ret=LockImpl(offset,length,options); m_bIsLocked=true; m_LockStart=offset; m_LockSize=length; return ret; } void* Lock(LockOptions options){ m_bIsLocked = true; return this->Lock(0,m_SizeInBytes,options); } virtual void Unlock(){ UnlockImpl(); m_bIsLocked=false; } };我们可以看到,HardwareBuffer有两个主要接口Lock和Unlock,在准备开始读写时,Lock返回HardwareBuffer持有的那块buffer的首地址,只有在Lock之后才可进行读写,Unlock之后将禁止读写
class HardwareIndexBuffer:public HardwareBuffer{ public: enum IndexType{ IT_16BIT, IT_32BIT }; HardwareIndexBuffer(IndexType itype,size_t numIndexes,HardwareBuffer::Usage usage); virtual ~HardwareIndexBuffer(); size_t GetIndexSize() const {return m_IndexSize;} IndexType GetType() const {return m_IndexType;} protected: size_t m_NumIndex; size_t m_IndexSize;//size of each index IndexType m_IndexType; }; class HardwareIndexBufferSharedPtr:public SharedPtr<HardwareIndexBuffer> { public: HardwareIndexBufferSharedPtr():SharedPtr<HardwareIndexBuffer>(){} explicit HardwareIndexBufferSharedPtr(HardwareIndexBuffer* buf); }; }HardwareIndexBuffer持有的便是顶点的索引缓冲区,在RenderSystem绘制时, 直接使用这里的内容
在读取顶点相关数据时,进入MeshSerializer::ReadGeometry,通过HardwareVertexBuffer进行读写
void MeshSerializer::ReadGeometry(const XmlElement* geoElem,VertexData* pVertexData) { int iClaimCount=StringConverter::ParseInt(geoElem->Attribute("vertexcount")); //assign vertex count pVertexData->m_VertexCount=iClaimCount; VertexDeclaration* pDecl=pVertexData->m_pVertexDeclaration; VertexBufferBinding* pBindings=pVertexData->m_pVertexBufferBinding; unsigned short bufCount(0); unsigned char* pVert(0); float* pFloat(0); for(const XmlElement* vbElem=geoElem->FirstChildElement();vbElem;vbElem=vbElem->NextSiblingElement()){ if(vbElem){ int iActualCount(0); for(const XmlElement* vertex=vbElem->FirstChildElement();vertex;vertex=vertex->NextSiblingElement()) ++iActualCount; if(iActualCount != iClaimCount){ //generate exception LOGERROR("MeshSerializer::ReadGeometry iActualCount!=iClaimCount"); return ; } size_t offset(0); //analyse declaration const char* attrib=vbElem->Attribute("positions"); if(attrib && StringConverter::ParseBool(attrib)){ pDecl->AddElement(bufCount,offset,VES_POSITION,VET_FLOAT3); offset += VertexElement::GetTypeSize(VET_FLOAT3); } attrib=vbElem->Attribute("normals"); if(attrib && StringConverter::ParseBool(attrib)){ pDecl->AddElement(bufCount,offset,VES_NORMAL,VET_FLOAT3); offset += VertexElement::GetTypeSize(VET_FLOAT3); } attrib=vbElem->Attribute("texture_coords"); if(attrib && StringConverter::ParseInt(attrib)){ unsigned short uNum=StringConverter::ParseInt(attrib); char str[200]={0}; for(unsigned short i=0;i<uNum;++i){ sprintf_s(str,200,"texture_coord_dimensions_%d",i); attrib=vbElem->Attribute(str); int dims(0); if(attrib) dims=StringConverter::ParseInt(attrib); else dims=2; VertexElementType elemType=VertexElement::MultiplyTypeCount(VET_FLOAT1,dims); pDecl->AddElement(bufCount,offset,VES_TEXTURECOORD,elemType); offset += VertexElement::GetTypeSize(elemType); } } //create vertex buffer pVertexData->m_VertexCount=iActualCount; HardwareVertexBufferSharedPtr ptrVertexBuffer=HardwareBufferManager::GetSingleton().CreateVertexBuffer( offset,pVertexData->m_VertexCount,HardwareBuffer::HBU_STATIC_WRITE_ONLY); pBindings->SetBinding(bufCount,ptrVertexBuffer); pVert=static_cast<unsigned char*>(ptrVertexBuffer->Lock(HardwareBuffer::HBL_DISCARD)); //analyse vertexes VertexDeclaration::VertexElementList lstElem=pDecl->FindElementBySource(bufCount); for(const XmlElement* vertElem=vbElem->FirstChildElement();vertElem;vertElem=vertElem->NextSiblingElement()){ if(vertElem){ for(VertexDeclaration::VertexElementList::iterator i=lstElem.begin();i!=lstElem.end();++i){ const VertexElement& elem=*i; switch(elem.GetSemantic()){ case VES_POSITION: { const XmlElement* posElem=vertElem->FirstChildElement("position"); if(!posElem){ LOGERROR("MeshSerializer::ReadGeometry VES_POSITION file error"); return ; } elem.BaseVertexPointerToElement(pVert,&pFloat); *pFloat++ = StringConverter::ParseReal(posElem->Attribute("x")); *pFloat++ = StringConverter::ParseReal(posElem->Attribute("y")); *pFloat++ = StringConverter::ParseReal(posElem->Attribute("z")); //calculate the bounding box // } break; case VES_NORMAL: { const XmlElement* normElem=vertElem->FirstChildElement("normal"); if(!normElem){ LOGERROR("MeshSerializer::ReadGeometry VES_NORMAL file error"); return ; } elem.BaseVertexPointerToElement(pVert,&pFloat); *pFloat++ = StringConverter::ParseReal(normElem->Attribute("x")); *pFloat++ = StringConverter::ParseReal(normElem->Attribute("y")); *pFloat++ = StringConverter::ParseReal(normElem->Attribute("z")); if(VET_FLOAT4 == elem.GetType()) *pFloat++ =StringConverter::ParseReal(normElem->Attribute("w")); } break; case VES_TEXTURECOORD: { const XmlElement* texElem=vertElem->FirstChildElement("texcoord"); if(!texElem){ LOGERROR("MeshSerializer::ReadGeometry VES_TEXTURECOORD file error"); return ; } elem.BaseVertexPointerToElement(pVert,&pFloat); *pFloat++ = StringConverter::ParseReal(texElem->Attribute("u")); if(VertexElement::GetTypeCount(elem.GetType())>1) *pFloat++ = StringConverter::ParseReal(texElem->Attribute("v")); else if(VertexElement::GetTypeCount(elem.GetType())>2) *pFloat++ = StringConverter::ParseReal(texElem->Attribute("w")); else if(VertexElement::GetTypeCount(elem.GetType())>3) *pFloat++ = StringConverter::ParseReal(texElem->Attribute("x")); } break; } } pVert += ptrVertexBuffer->GetVertexSize(); } } ++bufCount; ptrVertexBuffer->Unlock(); } } }下面列出HardwareVertexBuffer相关类
class HardwareVertexBuffer:public HardwareBuffer { protected: HardwareBufferManagerBase* m_pBufferManager; size_t m_NumVertices; size_t m_VertexSize;//single vertex size public: HardwareVertexBuffer(HardwareBufferManagerBase* pMgr,size_t vertSize,size_t numVertices,HardwareBuffer::Usage usage); virtual ~HardwareVertexBuffer(); size_t GetVertexSize() const {return m_VertexSize;} size_t GetVertexCount() const {return m_NumVertices;} HardwareBufferManagerBase* GetManager() const {return m_pBufferManager;} }; class HardwareVertexBufferSharedPtr:public SharedPtr<HardwareVertexBuffer> { public: HardwareVertexBufferSharedPtr():SharedPtr<HardwareVertexBuffer>(){} explicit HardwareVertexBufferSharedPtr(HardwareVertexBuffer* buf); };HardwareVertexBuffer里面的顶点数据存储格式,通过下面几个类进行定义和管理
enum VertexElementSemantic{ VES_POSITION=1, VES_DIFFUSE, VES_SPECULAR, VES_NORMAL, VES_BLEND_INDICES, VES_BLEND_WEIGHTS, VES_TEXTURECOORD, VES_TANGENT, VES_BINORMAL }; enum VertexElementType{ VET_FLOAT1=0, VET_FLOAT2, VET_FLOAT3, VET_FLOAT4, VET_FLOAT5, VET_UBYTE4, VET_COLOR }; class VertexElement { protected: //source buffer unsigned short m_uSource; //offset from start size_t m_Offset; //element semantic VertexElementSemantic m_VertexElementSemantic; //element type VertexElementType m_VertexElementType; public: //should not be called directly VertexElement(){} VertexElement(unsigned short uSource,size_t offset,VertexElementSemantic semantic, VertexElementType elemType); virtual ~VertexElement(); size_t GetOffset() const {return m_Offset;} unsigned short GetSource() const {return m_uSource;} VertexElementType GetType() const {return m_VertexElementType;} VertexElementSemantic GetSemantic() const {return m_VertexElementSemantic;} inline void BaseVertexPointerToElement(void* pBase,float** ppFloat) const{ *ppFloat = static_cast<float*>( static_cast<void*>( static_cast<unsigned char*>(pBase)+m_Offset)); } inline void BaseVertexPointerToElement(void* pBase,unsigned char** ppElem) const{ *ppElem = static_cast<unsigned char*>(pBase)+m_Offset; } size_t GetSize() const; static size_t GetTypeSize(VertexElementType elemType); static size_t GetTypeCount(VertexElementType elemType); size_t GetTypeCount(); static VertexElementType MultiplyTypeCount(VertexElementType BaseType,unsigned short count); }; class VertexDeclaration { public: typedef list<VertexElement> VertexElementList; protected: VertexElementList m_lstElement; public: VertexDeclaration(); virtual ~VertexDeclaration(); const VertexElement& AddElement(unsigned short uSource,size_t offset,VertexElementSemantic semantic,VertexElementType elemType); const VertexElement& InsertElement(unsigned short atPosition,unsigned short uSource,size_t offset,VertexElementSemantic semantic,VertexElementType elemType); void RemoveElement(VertexElementSemantic ves); VertexElementList FindElementBySource(unsigned short uSource); const VertexElement* FindElementBySemantic(VertexElementSemantic ves); VertexElementList& GetElements() {return m_lstElement;} size_t GetElementCount() {return m_lstElement.size();} const VertexElement* GetElement(unsigned short index); size_t GetVertexSize(unsigned short uSource); }; class VertexBufferBinding { public: typedef map<unsigned short,HardwareVertexBufferSharedPtr> VertexBufferBindingMap; protected: VertexBufferBindingMap m_mBindingMap; public: VertexBufferBinding(); virtual ~VertexBufferBinding(); void SetBinding(unsigned short index,const HardwareVertexBufferSharedPtr& buffer); void UnsetBinding(unsigned short index); const HardwareVertexBufferSharedPtr& GetBuffer(unsigned short index); const VertexBufferBindingMap& GetBindings() const; unsigned short GetNextIndex() const; };通过以上内容的讲解,读者已经可以把Sinbad成功存入HardwareBuffer了,下一步就是如何把它们渲染出来的问题了
相关文章推荐
- 【Ogre引擎架构】 第三讲 角色模型文件的渲染数据化
- 【Ogre引擎架构】第一讲 从零开始 解析角色模型文件(一)
- 【Ogre引擎架构】第二讲 从零开始 解析角色模型文件(二)
- 一共81个,开源大数据处理工具汇总:查询引擎、流式计算、迭代计算、离线计算、键值存储、表格存储、文件存储、资源管理、日志收集系统、消息系统、分布式服务、集群管理、基础设施、搜索引擎、数据挖掘=监控
- D16 HBase数据模型/命令行/存储机制
- 【Ogre引擎架构】第十一讲 渲染系统封装OpenGL 2.x (一)
- Zookeeper系列(4)--ZK概述,数据模型,节点特性,Watcher机制、ACL及数据存储
- Innodb引擎数据与索引文件单独存储方式
- 在proe模型文件里面存储用户数据
- MongoDB 存储引擎和数据模型设计
- 用Windows的文件映射机制,实现大批量数据的快速存储
- [开发总结]系统架构及数据模型----AutoDesk文件格式转换篇(五)
- 一共81个,开源大数据处理工具汇总:查询引擎、流式计算、迭代计算、离线计算、键值存储、表格存储、文件存储、资源管理、日志收集系统、消息系统、分布式服务、集群管理、基础设施、搜索引擎、数据挖掘=监控
- 存储在HDFS上的文件,存储机制及数据安全性如何保证。
- 用Windows的文件映射机制,实现大批量数据的快速存储
- OSISoft实时/历史数据库PI的数据存储机制分析
- 第四讲 DICOM介质存储功能与文件格式
- MySQL存储引擎和数据类型
- 将MotionCapture数据转换为OGRE可读写的数据文件格式
- 数据结构教程 第四课 算法效率的度量和存储空间需求