Havok动画渲染Demo(使用Ogre)

Havok的Animation组件是比较强大的,并且可以和物理场景进行交互,其实可以完全不用Ogre的骨骼动画系统,动画的处理完全交给Havok,Ogre只管显示即可。

花了点时间,做了个测试程序,将二者结合到一起,导入Havok的一个角色动画,然后播放这个动画,Ogre实时渲染出来。截图如下:

Havok动画渲染Demo(使用Ogre)

Havok动画渲染Demo(使用Ogre)

Havok动画渲染Demo(使用Ogre)

Havok动画渲染Demo(使用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;
}


代码下载地址在 这里