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