现在的位置: 首页 > 综合 > 正文

Ogre3D Mesh转换到FBX格式

2014年10月25日 ⁄ 综合 ⁄ 共 4944字 ⁄ 字号 评论关闭

在做一个Ogre3d 模型和骨骼动画转换到FBX格式的工具。中间遇到种种问题,下面将主要思路记录下来。

转换Mesh比较简单,就是遍历MeshPtr,获取顶点和索引缓冲、获取UV坐标,然后按FBX SDK的格式重新定义,然后保存即可。这个比较好弄。贴出关键代码:

bool Util::enumMeshVertex(const Ogre::String& strMesh, const Ogre::String& strGroupName, VERTEX_LIST& vecPos, INDEX_LIST& vecIndex)
    {
        Ogre::MeshPtr pMesh = Ogre::MeshManager::getSingleton().load(strMesh, strGroupName);

        int nVertexCount = 0;
        int nIndexCount = 0;
        DBGSTRING("NumSubMesh: %d", pMesh->getNumSubMeshes());

        for(int i=0; i<pMesh->getNumSubMeshes(); i++) {
            Ogre::SubMesh* pSubMesh = pMesh->getSubMesh(i);
            if(pSubMesh->useSharedVertices) {
                DBGSTRING("useSharedVertices");
                nVertexCount += pMesh->sharedVertexData->vertexCount;
            }else{
                nVertexCount += pSubMesh->vertexData->vertexCount;
            }
            nIndexCount += pSubMesh->indexData->indexCount;
            DBGSTRING("nIndexCount: %d", pSubMesh->indexData->indexCount);
        }

        DBGSTRING("nVertexCount: %d, nIndexCount: %d", nVertexCount, nIndexCount);

        for(int i=0; i<pMesh->getNumSubMeshes(); ++i) {
            Ogre::SubMesh* pSubMesh = pMesh->getSubMesh(i);

            Ogre::VertexData* pVertexData = pSubMesh->useSharedVertices ? pMesh->sharedVertexData : pSubMesh->vertexData;
            const Ogre::VertexElement* posElem = pVertexData->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);
            const Ogre::VertexElement* uvElem = pVertexData->vertexDeclaration->findElementBySemantic(Ogre::VES_TEXTURE_COORDINATES);
            //const Ogre::VertexElement* normalElem = pVertexData->vertexDeclaration->findElementBySemantic(Ogre::VES_NORMAL);

            Ogre::HardwareVertexBufferSharedPtr vbuf = pVertexData->vertexBufferBinding->getBuffer(posElem->getSource());
            Ogre::HardwareVertexBufferSharedPtr uvbuf = pVertexData->vertexBufferBinding->getBuffer(uvElem->getSource());
        
            unsigned char* pVertex = static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
        
            float* pReal;
            float* pUVReal;
            //读取顶点
            for(size_t j=0; j<pVertexData->vertexCount; ++j, pVertex += vbuf->getVertexSize()) {
                VERTEX_DATA vd;

                posElem->baseVertexPointerToElement(pVertex, &pReal);
                Ogre::Vector3 pos(pReal[0], pReal[1], pReal[2]);
                vd.position = pos;

                vecPos.push_back(vd);
            }
            vbuf->unlock();

            //读取UV
            unsigned char* pUV = static_cast<unsigned char*>(uvbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
            for(size_t j=0; j<pVertexData->vertexCount; ++j, pUV += uvbuf->getVertexSize()) {
                uvElem->baseVertexPointerToElement(pUV, &pUVReal);
                vecPos[j].uv = Ogre::Vector2(pUVReal[0], pUVReal[1]);
            }
            uvbuf->unlock();

            Ogre::IndexData* pIndexData = pSubMesh->indexData;
            Ogre::HardwareIndexBufferSharedPtr ibuf = pIndexData->indexBuffer;
            bool b32Bit = ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT;

            unsigned char* pIndex = static_cast<unsigned char*>(ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
            int nIndexSize = b32Bit ? 4 : 2;

            for(size_t k=0; k<pIndexData->indexCount; k++) {
                unsigned long ulIndex = 0;
                if(b32Bit) {
                    memcpy(&ulIndex, pIndex, 4);
                }else{
                    unsigned short usIndex = 0;
                    memcpy(&usIndex, pIndex, 2);
                    ulIndex = static_cast<unsigned long>(usIndex);
                }

                vecIndex.push_back(ulIndex);
                pIndex += nIndexSize;
            }
            ibuf->unlock();
        }

        return vecPos.size() > 0;
    }

bool Util::exportMesh(const char* szMesh, const char* szGroupName, const char* szFileName)
    {
        VERTEX_LIST vecPos;
        INDEX_LIST vecIndex;

        enumMeshVertex(szMesh, szGroupName, vecPos, vecIndex);

        if(vecPos.size() <= 0){
            DBGSTRING("获取Mesh顶点数据失败");
            return false;
        }

        FbxScene* pScene = FbxOgre::FbxSdk::getScene();
        pScene->Clear();

        FbxMesh* pMesh = FbxMesh::Create(pScene, szMesh);

        pMesh->InitControlPoints(vecPos.size());
        FbxVector4* pVertex = pMesh->GetControlPoints();
    
        for(int i=0; i<vecPos.size(); i++) {
            Ogre::Vector3 pos = vecPos[i].position;
            pVertex[i] = FbxVector4(pos.x, pos.y, pos.z);
        }

        //创建三角形
        int nTriangles = vecIndex.size() / 3;
    
        for(int i=0; i<nTriangles; i++) {
            int nIndex = i*3;
            pMesh->BeginPolygon();
            pMesh->AddPolygon(vecIndex[nIndex]);
            pMesh->AddPolygon(vecIndex[nIndex+1]);
            pMesh->AddPolygon(vecIndex[nIndex+2]);

            pMesh->EndPolygon();
        }

        FbxNode* pNode = FbxNode::Create(pScene, szMesh);

        pNode->SetNodeAttribute(pMesh);

        FbxGeometryElementUV* pUVElement = pMesh->CreateElementUV("uvset");
        pUVElement->SetMappingMode(FbxGeometryElement::eByControlPoint);
        pUVElement->SetReferenceMode(FbxGeometryElement::eDirect);
        pUVElement->GetDirectArray().Resize(vecPos.size());
        for(int i=0; i<vecPos.size(); i++) {
            FbxVector2 fbxuv(vecPos[i].uv.x, 1.0f - vecPos[i].uv.y);
            pUVElement->GetDirectArray().SetAt(i, fbxuv);
        }

        FbxNode* pRootNode = pScene->GetRootNode();
        pRootNode->AddChild(pNode);

        return FbxOgre::FbxSdk::saveScene(szFileName);
    }

FbxOgre是我自己进行的封装,其实就是把FBX SDK的Sample代码修改了一下,具体实现看FBX SDK。

现在主要的问题是Ogre骨骼动画数据的转换,按Ogre skeleton文件读出的数据直接写入FBX的话,导入3d max骨骼的位置和动画的效果惨不忍睹,有些是正的,有些是反的,不同的skeleton文件也会出现不同的错误,头大。欢迎懂的朋友给点指导意见。 

谢谢。

抱歉!评论已关闭.