您的位置:首页 > 运维架构 > 网站架构

【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进行渲染

//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了,下一步就是如何把它们渲染出来的问题了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐