在做一个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文件也会出现不同的错误,头大。欢迎懂的朋友给点指导意见。
谢谢。
Ogre3D Mesh转换到FBX格式,布布扣,bubuko.com
原文:http://blog.csdn.net/shangguanwaner/article/details/24545385