Cocos2d-x 3.x 图形学渲染系列十七

笔者介绍:姜雪伟IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人;已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

顶点索引数据是游戏开发中经常需要加载处理的,游戏中的顶点数据最终是通过流传出去的,类中定义了一个结构体,专用于对指定的顶点属性进行绘制,它与glVertexAttribPointer是相关的。这个结构体是Cocos2d-x引擎自己定义的,结构体定义代码如下所示:

struct CC_DLL VertexStreamAttribute
{
	/**
		构造函数.
     */
	VertexStreamAttribute()
    : _normalize(false),_offset(0),_semantic(0),_type(0),_size(0)
    {
    }
	/**
	构造函数
	@参数offset 属性的便宜
	@参数semantic 属性的(位置, 纹理, 颜色等).
	@参数type属性的类型, 可以是GL_FLOAT类型.
	@参数size 描述属性有多少参数类型.
     */
	VertexStreamAttribute(int offset, int semantic, int type, int size)
    : _normalize(false),_offset(offset),_semantic(semantic),_type(type),_size(size)
    {
    }
	/**
		构造函数
		@参数 offset 属性的便宜
		 @参数 semantic 属性的(位置, 纹理, 颜色等).
		 @参数 type 属性的类型, 可以是GL_FLOAT类型.
		 @参数 size 描述属性有多少参数类型.
	@参数normalize 如果是true, 数据将被255除     */
	VertexStreamAttribute(int offset, int semantic, int type, int size, bool normalize)
    : 	_normalize(normalize),_offset(offset),_semantic(semantic),_type(type),_size(size)
    {
    }
		/**
			属性是否被标准化
     */
	bool _normalize;
	/**
		在属性缓存中的偏移值
     */
	int _offset;
	/**
		描述属性的用处, 可能是位置,颜色等.
     */
	int _semantic;
	/**
		属性类型的描述, 可能是 GL_FLOAT, GL_UNSIGNED_BYTE 等.
     */
	int _type;
	/**
		描述在属性中有多少元素的类型
     */
	int _size;
};

以上定义的是用于传输的数据流,这个也是引擎底层对数据的处理方式。另外还定义了一个VertexData类,该类主要是用于指定输入流到GPU渲染管道,一个VertexData数据是由几个流数据组成的,每个流包含一个VertexStreamAttribute和绑定的顶点缓存。

     如果要把定义好的VertexData数据输入到GPU中,首先要做的事情是创建VertexData数据,函数代码如下:

VertexData* VertexData::create()
{
	VertexData* result = new (std::nothrow) VertexData();
	if(result)
    {
        result->autorelease();
		return result;
    }

	CC_SAFE_DELETE(result);
	return nullptr;
}

VertexData 数据创建好后,需要将其设置到流里面,调用函数 setStream 如下所示:

bool VertexData::setStream(VertexBuffer* buffer, const VertexStreamAttribute& stream)
{
	if( buffer == nullptr ) return false;
		auto iter = _vertexStreams.find(stream._semantic);
	if(iter == _vertexStreams.end())
    {
        buffer->retain();
		auto& bufferAttribute = _vertexStreams[stream._semantic];
		bufferAttribute._buffer = buffer;
		bufferAttribute._stream = stream;
    }
	else
    {
        buffer->retain();
        iter->second._buffer->release();
        iter->second._stream = stream;
        iter->second._buffer = buffer;
    }

	return true;
}

函数实现了数据流的设置,缓存的设置操作,这些接口都是引擎底层实现的,写逻辑时一般不会直接使用。开发者编写逻辑时,直接填充数据就可以了,引擎底层会将其转成数据流的形式传到GPU中处理。

引擎提供了两个类用于实现模型顶点数据处理:

VertexIndexData类与MeshVertexIndexData类,二者是不同的,前者主要是针对数据流的定义,而后者是具体到模型Mesh的顶点索引数据定义。MeshVertexIndexData类主要是针对模型Mesh的顶点和索引信息的定义,在它里面定义了一个MeshIndexData类,这个类包含了mesh所有的索引数据,与MeshIndexData类对应的是MeshVertexData类,这个类包含了mesh所需要的顶点数据。这两个类对于模型来说非常重要,Unity引擎中也有相关定义,下面具体说一下其内部实现,在MeshVertexData类中有create函数,用于创建模型顶点数据,以及申请顶点缓存用于存放顶点数据,具体实现函数如下所示:

MeshVertexData* MeshVertexData::create(const MeshData& meshdata)
{
	auto vertexdata = new (std::nothrow) MeshVertexData();
	int pervertexsize = meshdata.getPerVertexSize();
    vertexdata->_vertexBuffer = VertexBuffer::create(pervertexsize, 	(int)(meshdata.vertex.size() / (pervertexsize / 4)));
    vertexdata->_vertexData = VertexData::create();
	CC_SAFE_RETAIN(vertexdata->_vertexData);
	CC_SAFE_RETAIN(vertexdata->_vertexBuffer);

	int offset = 0;
	for (const auto&it : meshdata.attribs) {
	vertexdata->_vertexData->setStream(vertexdata->_vertexBuffer, 	VertexStreamAttribute(offset, it.vertexAttrib, it.type, it.size));
        offset += it.attribSizeBytes;
    }

    vertexdata->_attribs = meshdata.attribs;

	if(vertexdata->_vertexBuffer)
    {
        vertexdata->_vertexBuffer->updateVertices((void*)&meshdata.vertex[0], 	(int)meshdata.vertex.size() * 4 / vertexdata->_vertexBuffer->getSizePerVertex(), 0);
    }

	bool needCalcAABB = (meshdata.subMeshAABB.size() != 	meshdata.subMeshIndices.size());
	for (size_t i = 0; i <meshdata.subMeshIndices.size(); i++) {

	auto& index = meshdata.subMeshIndices[i];
	auto indexBuffer = 	IndexBuffer::create(IndexBuffer::IndexType::INDEX_TYPE_SHORT_16, (int)(index.size()));
        indexBuffer->updateIndices(&index[0], (int)index.size(), 0);
std::string id = (i < meshdata.subMeshIds.size() ? meshdata.subMeshIds[i] : "");
MeshIndexData* indexdata = nullptr;
		if (needCalcAABB)
        {
			auto aabb = Bundle3D::calculateAABB(meshdata.vertex, 			meshdata.getPerVertexSize(), index);
            indexdata = MeshIndexData::create(id, vertexdata, indexBuffer, aabb);
        }
		else
            indexdata = MeshIndexData::create(id, vertexdata, indexBuffer, meshdata.subMeshAABB[i]);

        vertexdata->_indexs.pushBack(indexdata);
    }

	vertexdata->autorelease();
	return vertexdata;
}

在函数中,创建了顶点数据缓存和索引数据缓存,最终要将其加入到vertexdata表中,其中函数的参数MeshData是在类文件CCBundle3DData.h中定义的模型结构体数据, MeshData结构体网格数据定义如下所示:

struct MeshData
{
	typedef std::vector<unsigned short> IndexArray;
	std::vector<float> vertex;
	int vertexSizeInFloat;
	std::vector<IndexArray> subMeshIndices;
	std::vector<std::string> subMeshIds; //子网格名字
	std::vector<AABB> subMeshAABB;
	int numIndex;
	std::vector<MeshVertexAttrib> attribs;
	int attribCount;

	public:
	/**
     * 获取到每个顶点的尺寸大小
     * @return 返回所有属性大小的总和
     */
	int getPerVertexSize() const
    {
		int vertexsize = 0;
		for(const auto& attrib : attribs)
        {
            vertexsize += attrib.attribSizeBytes;
        }
		return vertexsize;
    }

	/**
     * 重置数据
     */
	void resetData()
    {
		vertex.clear();
		subMeshIndices.clear();
		subMeshAABB.clear();
		attribs.clear();
		vertexSizeInFloat = 0;
		numIndex = 0;
		attribCount = 0;
}
	/**
     * 构造函数
     */

	MeshData()
    : vertexSizeInFloat(0)
    , numIndex(0)
    , attribCount(0)
    {
    }
    ~MeshData()
    {
		resetData();
    }
};

存放的模型顶点是用vector向量列表定义的,另外子网格索引也是用vector定义的。vector在引擎开发中使用还是非常广泛的,引擎中的MeshData结构体是针对单一的模型顶点定义的,而模型包含的数据是非常多的,举个例子,一个模型包含很多个顶点,给读者展示一下c3t模型内容如下图:


图中只截取了一小部分数据只是为了告诉读者引擎中定义的MeshData结构体对应的就是图中vetices一行的数据,正如读者所看到的文中有很多行数据,所以需要定义一个可以填充整个模型数据的结构体,其实就是把MeshData重新封装了一个vector,引擎中针对模型数据定义的结构体如下所示:

struct MeshDatas
{
	std::vector<MeshData*> meshDatas;

	void resetData()
    {
		for(auto& it : meshDatas)
        {
			delete it;
        }
		meshDatas.clear();
    }
    ~MeshDatas()
    {
		resetData();
    }
};

结构体用于存储模型的所有数据信息,它也是引擎底层的实现方式。有了数据后,还需要定义缓存用于存放,VertexIndexBuffer类主要是解决这个问题,将在后面介绍。



已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页