Havok动画渲染Demo(使用Ogre)

来源:互联网 发布:淘宝在线人工客服时间 编辑:程序博客网 时间:2024/04/29 13:21

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;
}


代码下载地址在 这里