美文网首页
cocos2d源码分析(十二):Sprite3D之网格顶点数据

cocos2d源码分析(十二):Sprite3D之网格顶点数据

作者: 奔向火星005 | 来源:发表于2019-01-21 22:20 被阅读0次

    在cocos2d中,主要用Sprite3D对象来渲染3D模型。它的主要结构如下:


    从图中可以看出它的成员主要有Skeleton3D对象_skeleton,MeshVerterData对象数组_meshVertexDatas,和Mesh对象数组_meshs。本文主要分析MeshVerterData对象。

    MeshVerterData从命名上看,即网格顶点数据。它主要用于管理顶点数据,如顶点位置,法线,纹理坐标的内存数据等,以及OpenGL的vbo。前面的加载c3t文件,加载obj文件中分析过,当cocos2d把obj(或c3t,c3b)文件解析后,最终会把数据存储到MeshDatas,MaterialDatas和NodeDatas这三个数据结构中,Sprite3D中的MeshVerterData成员的数据,主要是来自MeshDatas中的数据。下面看下Sprite3D的创建过程:

    bool Sprite3D::initWithFile(const std::string& path)
    {
        _aabbDirty = true;
        _meshes.clear();
        _meshVertexDatas.clear();
        CC_SAFE_RELEASE_NULL(_skeleton);
        removeAllAttachNode();
        
        if (loadFromCache(path))
            return true;
        
        MeshDatas* meshdatas = new (std::nothrow) MeshDatas();
        MaterialDatas* materialdatas = new (std::nothrow) MaterialDatas();
        NodeDatas* nodeDatas = new (std::nothrow) NodeDatas();
        
        //加载模型文件,将信息存到meshdatas,materialdatas,nodeDatas中
        if (loadFromFile(path, nodeDatas, meshdatas, materialdatas))
        {
            //根据meshdatas,materialdatas,nodeDatas初始化Sprite3D
            if (initFrom(*nodeDatas, *meshdatas, *materialdatas))
            {
                //add to cache
                auto data = new (std::nothrow) Sprite3DCache::Sprite3DData();
                data->materialdatas = materialdatas;
                data->nodedatas = nodeDatas;
                data->meshVertexDatas = _meshVertexDatas;
                for (const auto mesh : _meshes) {
                    data->glProgramStates.pushBack(mesh->getGLProgramState());
                }
                
                Sprite3DCache::getInstance()->addSprite3DData(path, data);
                CC_SAFE_DELETE(meshdatas);
                _contentSize = getBoundingBox().size;
                return true;
            }
        }
        CC_SAFE_DELETE(meshdatas);
        CC_SAFE_DELETE(materialdatas);
        CC_SAFE_DELETE(nodeDatas);
        
        return false;
    }
    

    initFrom函数如下:

    bool Sprite3D::initFrom(const NodeDatas& nodeDatas, const MeshDatas& meshdatas, const MaterialDatas& materialdatas)
    {
        for(const auto& it : meshdatas.meshDatas)  {
            if(it)  {
    //            Mesh* mesh = Mesh::create(*it);
    //            _meshes.pushBack(mesh);
                
                //通过meshDatas,创建MeshVertexData
                auto meshvertex = MeshVertexData::create(*it);
                _meshVertexDatas.pushBack(meshvertex);
            }
        }  //初始化顶点和索引数据(_meshVertexDatas)
    
         //创建骨骼
        _skeleton = Skeleton3D::create(nodeDatas.skeleton); 
        CC_SAFE_RETAIN(_skeleton);
        
        auto size = nodeDatas.nodes.size();
        for(const auto& it : nodeDatas.nodes) {
            if(it)  {
                createNode(it, this, materialdatas, size == 1);
            }
        }
        for(const auto& it : nodeDatas.skeleton) {
            if(it) {
                 createAttachSprite3DNode(it,materialdatas);
            }
        }
        genMaterial(); //根据attrib绑定值,材质文件,纹理等创建Material
        
        return true;
    }
    

    MeshVertexData::create函数如下:

    MeshVertexData* MeshVertexData::create(const MeshData& meshdata)
    {
        auto vertexdata = new (std::nothrow) MeshVertexData();
        
        //每个顶点占多大内存,例如,一个顶点包含有位置,法线和纹理坐标,则position(3*4)+normal(3*4)+texcoord(2*4),其中4=sizeof(float)
        int pervertexsize = meshdata.getPerVertexSize();
        
        //VertexBuffer内部会创建vbo(glGenBuffers),并向opengl申请存储顶点的内存(glBufferData)
        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)
        {
            //将顶点数据从CPU传到GPU, 通过(glBufferSubData)
            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, size = meshdata.subMeshIndices.size(); i < size; ++i) {
    
            auto& index = meshdata.subMeshIndices[i];
            
            //内部创建vbo(glGenBuffers),并向OpenGL申请内存(glBufferData)来存储索引数据
            auto indexBuffer = IndexBuffer::create(IndexBuffer::IndexType::INDEX_TYPE_SHORT_16, (int)(index.size()));
            
            //将索引数据从CPU传到GPU(glBufferSubData)
            indexBuffer->updateIndices(&index[0], (int)index.size(), 0);
            std::string id = (i < meshdata.subMeshIds.size() ? meshdata.subMeshIds[i] : "");
            MeshIndexData* indexdata = nullptr;
            
            //计算AABB
            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;
    }
    

    它的类结构大致如下:


    以orc.c3t为例,分析下顶点内存数据的布局,orc.c3t相关文件相关内容如下:

        "meshes": [
            {
                "attributes": [{
                        "size":   3, 
                        "type": "GL_FLOAT", 
                        "attribute": "VERTEX_ATTRIB_POSITION"
                    }, {
                        "size":   3, 
                        "type": "GL_FLOAT", 
                        "attribute": "VERTEX_ATTRIB_NORMAL"
                    }, {
                        "size":   2, 
                        "type": "GL_FLOAT", 
                        "attribute": "VERTEX_ATTRIB_TEX_COORD"
                    }, {
                        "size":   4, 
                        "type": "GL_FLOAT", 
                        "attribute": "VERTEX_ATTRIB_BLEND_WEIGHT"
                    }, {
                        "size":   4, 
                        "type": "GL_FLOAT", 
                        "attribute": "VERTEX_ATTRIB_BLEND_INDEX"
                    }], 
                "vertices": [
                    -4.087269, -0.284269,  2.467412, -0.182764, -0.799652,  0.571974,  0.309707,  0.734820,  0.500000,  0.500000,  0.000000,  0.000000,  0.000000,  1.000000,  0.000000,  0.000000, 
                    -3.801909, -0.138538, -0.349688, -0.335480, -0.819229, -0.465099,  0.333359,  0.578025,  1.000000,  0.000000,  0.000000,  0.000000,  0.000000,  0.000000,  0.000000,  0.000000,
    ...//省略了
    

    attributes对应opengl着色器中的attributes属性类型,orc.c3t文件中一个顶点有5个属性内容,VERTEX_ATTRIB_POSITION是顶点的位置信息,也就是xyz坐标,占3个字节,VERTEX_ATTRIB_NORMAL是顶点法线,VERTEX_ATTRIB_TEX_COORD是纹理坐标,VERTEX_ATTRIB_BLEND_WEIGHT是骨骼蒙皮是要用到的,意思是每个骨骼对该顶点影响的权重是多少,VERTEX_ATTRIB_BLEND_INDEX是骨骼的索引,在后面分析骨骼蒙皮时再讨论。因此一个顶点占的字节数为43+43+42+44+4*4=64。也就是VertexBuffer的_sizePerVertex成员。

    我们也可以提前看一下orc.c3t的顶点着色器内的attribute变量,在ccShader_3D_PositionTex.vert文件,如下:

    const char* cc3D_SkinPositionTex_vert = R"(
    attribute vec3 a_position;        //对应VERTEX_ATTRIB_POSITION
    
    attribute vec4 a_blendWeight;  //对应VERTEX_ATTRIB_BLEND_WEIGHT
    attribute vec4 a_blendIndex;    //对应VERTEX_ATTRIB_BLEND_INDEX
    
    attribute vec2 a_texCoord;   //对应VERTEX_ATTRIB_TEX_COORD
    
    const int SKINNING_JOINT_COUNT = 60;
    // Uniforms
    uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * 3];
    
    // Varyings
    varying vec2 TextureCoordOut;
    
    vec4 getPosition()
    {
        float blendWeight = a_blendWeight[0];
    
        int matrixIndex = int (a_blendIndex[0]) * 3;
    
    //省略...
    

    顶点数据的内存图如下,


    因为cc3D_SkinPositionTex_vert着色器中并没有用到法线数据,所以图中VERTEX_ATTRIB_NORMAL下没有写在着色器中对应的attribute变量。

    相关文章

      网友评论

          本文标题:cocos2d源码分析(十二):Sprite3D之网格顶点数据

          本文链接:https://www.haomeiwen.com/subject/hpahjqtx.html