Havok的Animation组件是比较强大的,并且可以和物理场景进行交互,其实可以完全不用Ogre的骨骼动画系统,动画的处理完全交给Havok,Ogre只管显示即可。
花了点时间,做了个测试程序,将二者结合到一起,导入Havok的一个角色动画,然后播放这个动画,Ogre实时渲染出来。截图如下:
这里吐槽下,Havok SDK自带的Demo,特别是一些和图形渲染相关的代码,很多都不提供源代码和参考例子。写代码中间几次卡住,抓狂到极点。无耐只能自己跟踪程序,看他的一些关键点的输出,进行分析。
例子是Havok自带的例子改过来的,见Havok SDK目录Demo\Demos\Animation\Api\MeshAndDeformation\Skinning。我就说下和Ogre的整合方法。最主要的是hkxMeshSection这个数据结构,它包含了Mesh的顶点、Normal、UV坐标等等数据,把他导出,在Ogre中手动建立Mesh,再实时的在Ogre中更新这个Mesh即可。给出主要的功能代码:
int SkinningMesh::AllocateMesh(hkxMeshSection* meshsec)
{
const hkxVertexDescription& vdesc = meshsec->m_vertexBuffer->getVertexDesc();
const hkxVertexDescription::ElementDecl* pdecl = vdesc.getElementDecl(hkxVertexDescription::HKX_DU_POSITION, 0);
const hkxVertexDescription::ElementDecl* ndecl = vdesc.getElementDecl(hkxVertexDescription::HKX_DU_NORMAL, 0);
const hkxVertexDescription::ElementDecl* tdecl = vdesc.getElementDecl(hkxVertexDescription::HKX_DU_TEXCOORD, 0);
hkxVertexBuffer* vbuf = meshsec->m_vertexBuffer;
Ogre::String strName = "SkinningMesh" + Ogre::StringConverter::toString(m_nMeshNum++);
int nVertexCount = vbuf->getNumVertices();
//DBGSTRING("nVertexCount: %d", nVertexCount);
VERTEXSET_DATA* pVertexSetData = new VERTEXSET_DATA();
pVertexSetData->vertexSet.SetVertexNum(nVertexCount);
pVertexSetData->mesh = Ogre::MeshManager::getSingleton().createManual(
strName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
Ogre::SubMesh* submesh = pVertexSetData->mesh->createSubMesh();
submesh->useSharedVertices = false;
//顶点缓冲区
submesh->vertexData = new Ogre::VertexData();
submesh->vertexData->vertexStart = 0;
submesh->vertexData->vertexCount = nVertexCount;
Ogre::VertexDeclaration* vdecl = submesh->vertexData->vertexDeclaration;
Ogre::VertexBufferBinding* vbind = submesh->vertexData->vertexBufferBinding;
//数据格式声明
vdecl->addElement(0, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
vdecl->addElement(1, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
vdecl->addElement(2, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES); //UV坐标
//建立顶点缓冲区
Ogre::HardwareVertexBufferSharedPtr posVertexBuffer =
Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(3*sizeof(Ogre::Real),
nVertexCount,
Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE);
vbind->setBinding(0, posVertexBuffer);
//建立Normal
Ogre::HardwareVertexBufferSharedPtr normalVertexBuffer =
Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(3*sizeof(Ogre::Real),
nVertexCount,
Ogre::HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY_DISCARDABLE);
vbind->setBinding(1, normalVertexBuffer);
//建立UV
Ogre::HardwareVertexBufferSharedPtr uvVertexBuffer =
Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(2*sizeof(Ogre::Real),
nVertexCount,
Ogre::HardwareBuffer::HBU_STATIC);
vbind->setBinding(2, uvVertexBuffer);
pVertexSetData->posVertexBuffer = posVertexBuffer;
pVertexSetData->normalVertexBuffer = normalVertexBuffer;
Ogre::AxisAlignedBox meshBounds(0, 0, 0, 10, 0, 10);
pVertexSetData->mesh->_setBounds(meshBounds);
//写入顶点数据
//顶点
if(pdecl){
void* pBuffer = (meshsec->m_vertexBuffer->getVertexDataPtr((*pdecl)));
if(pBuffer == NULL){
::MessageBoxA(NULL, "GetRenderData getVertexDataPtr 顶点数据 失败", "Havok System Error", MB_OK|MB_ICONSTOP);
return -1;
}
int nNum = meshsec->m_vertexBuffer->getNumVertices();
void* pOutBuf = malloc(posVertexBuffer->getSizeInBytes());
memset(pOutBuf, 0, posVertexBuffer->getSizeInBytes());
for(int i=0; i<nNum; i++){
const float* pCurr = reinterpret_cast<float*>((char*)pBuffer + (pdecl->m_byteStride * i));
memcpy((char*)pOutBuf + i*3*sizeof(float), pCurr, 3*sizeof(float));
}
posVertexBuffer->writeData(0,
posVertexBuffer->getSizeInBytes(),
pOutBuf,
true);
free(pOutBuf);
}
//Normal
if(ndecl){
void* pBuffer = (meshsec->m_vertexBuffer->getVertexDataPtr((*ndecl)));
if(pBuffer == NULL){
::MessageBoxA(NULL, "GetRenderData getVertexDataPtr Normal数据 失败", "Havok System Error", MB_OK|MB_ICONSTOP);
return -1;
}
int nNum = meshsec->m_vertexBuffer->getNumVertices();
void* pOutBuf = malloc(normalVertexBuffer->getSizeInBytes());
memset(pOutBuf, 0, normalVertexBuffer->getSizeInBytes());
for(int i=0; i<nNum; i++){
const float* pCurr = reinterpret_cast<float*>((char*)pBuffer + (ndecl->m_byteStride * i));
memcpy((char*)pOutBuf + i*3*sizeof(float), pCurr, 3*sizeof(float));
}
normalVertexBuffer->writeData(0,
normalVertexBuffer->getSizeInBytes(),
pOutBuf,
true);
free(pOutBuf);
}
//TextureCoord
if(tdecl){
void* pBuffer = (meshsec->m_vertexBuffer->getVertexDataPtr((*tdecl)));
if(pBuffer == NULL){
::MessageBoxA(NULL, "GetRenderData getVertexDataPtr Normal数据 失败", "Havok System Error", MB_OK|MB_ICONSTOP);
return -1;
}
void* pOutBuf = malloc(2*sizeof(float)*nVertexCount);
memset(pOutBuf, 0, 2*sizeof(float)*nVertexCount);
hkxVertexDescription::DataType TextDataType = vdesc.getElementType(hkxVertexDescription::HKX_DU_TEXCOORD, 0);
int nNum = meshsec->m_vertexBuffer->getNumVertices();
for(int i=0; i<nNum; i++){
if(TextDataType == hkxVertexDescription::HKX_DT_INT16){
unsigned short* pCurr = reinterpret_cast<unsigned short*>((char*)pBuffer + (tdecl->m_byteStride * i));
float uv[2] = { 0.0f};
uv[0] = pCurr[0] / 3276.7f;
uv[1] = 1 - (pCurr[1] / 3276.7f);
memcpy((char*)pOutBuf + i*2*sizeof(float), (const void*)uv, 2*sizeof(float));
}else if(TextDataType == hkxVertexDescription::HKX_DT_FLOAT){
float* pCurr = reinterpret_cast<float*>((char*)pBuffer + (tdecl->m_byteStride * i));
float uv[2] = { 0.0f};
uv[0] = pCurr[0];
uv[1] = pCurr[1];
memcpy((char*)pOutBuf + i*2*sizeof(float), (const void*)uv, 2*sizeof(float));
}
}
uvVertexBuffer->writeData(0,
uvVertexBuffer->getSizeInBytes(),
pOutBuf,
true);
free(pOutBuf);
}
//索引缓冲区
int nTriCount = meshsec->getNumTriangles();
if(nTriCount > 0){
Ogre::HardwareIndexBufferSharedPtr indexBuffer =
Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(
Ogre::HardwareIndexBuffer::IT_16BIT,
nTriCount*3, //三角形数
Ogre::HardwareBuffer::HBU_STATIC, true);
//锁定mesh缓存,写入索引数据
unsigned short* faceVertexIndices = (unsigned short*)indexBuffer->lock(
0, nTriCount*3*2, Ogre::HardwareBuffer::HBL_DISCARD);
int nIndex = 0;
for(int i=0; i<nTriCount; i++){
hkUint32 a, b, c;
meshsec->getTriangleIndices(i, a, b, c);
//DBGSTRING("Triangle (%d, %d, %d)", a, b, c);
faceVertexIndices[nIndex++] = (unsigned short)a;
faceVertexIndices[nIndex++] = (unsigned short)b;
faceVertexIndices[nIndex++] = (unsigned short)c;
}
indexBuffer->unlock();
submesh->indexData->indexBuffer = indexBuffer;
submesh->indexData->indexStart = 0;
submesh->indexData->indexCount = nTriCount*3;
}
hkxMaterial* material = meshsec->m_material;
if(material != HK_NULL){
DBGSTRING("MaterialName: %s", material->m_name.cString());
Ogre::String strMatName = material->m_name.cString();
submesh->setMaterialName(strMatName);
}
pVertexSetData->mesh->load();
pVertexSetData->mesh->touch();
m_map.insert(VERTEXSET_MAP::value_type(meshsec, pVertexSetData));
return m_nMeshNum -1;
}
代码下载地址在 这里