Ogre3D Mesh轉換到FBX格式

在做一個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文件也會出現不同的錯誤,頭大。歡迎懂的朋友給點指導意見。 

謝謝。


發佈了69 篇原創文章 · 獲贊 32 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章