魔兽mdx文件导出为Ogre Mesh的小进展

来源:互联网 发布:格拉斯哥艺术学院知乎 编辑:程序博客网 时间:2024/05/01 01:40
最近一直在试图把魔兽3的mdx文件转为Ogre Mesh,学习一下基础的3D编程。Ogre Mesh的导出在很久之前也曾试图做过,并且还把WOW的m2模型以及WMO模型导入到了Max中,但是只做到了骨架的导入,动画数据始终出不来,于是放弃。

 

    这次依然是碰到这里的问题,导出静态的Mesh很快就完成,包括模型与材质,代码也比较简单。

 

// 模型数据bool ModelLoaderMdx::loadGeosets(Ogre::MeshPtr model, MdxDataStreamPtr dataStream, int size){    unsigned int index = 0;    while(size > 0)    {        int geosetSize = dataStream->read<int>();        size -= geosetSize;        Ogre::String meshName = m_modelName + Ogre::String("_sub_") + Ogre::StringConverter::toString(index++);        Ogre::SubMesh* subMesh = model->createSubMesh(meshName);            // 硬件缓冲编号        // 分别为顶点坐标 法线 贴图坐标#define HARDWARE_BUFFER_SOURCE_VERTEX 0#define HARDWARE_BUFFER_SOURCE_NORMAL 1#define HARDWARE_BUFFER_SOURCE_TEXPOS 2        //        // 顶点数据        //        if(!expectTag(dataStream, 'VRTX'))         {            OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR, "Expect VRTX data for geoset",                "ModelLoaderMdx::loadGeosets");        }        unsigned int vertexCount = dataStream->read<unsigned int>();        // 不使用共享顶点数据        // 每个SubMesh都创建自己的VertexData        subMesh->useSharedVertices = false;        subMesh->vertexData = OGRE_NEW Ogre::VertexData();        subMesh->vertexData->vertexStart = 0;        subMesh->vertexData->vertexCount = vertexCount;        subMesh->vertexData->vertexDeclaration->addElement(            HARDWARE_BUFFER_SOURCE_VERTEX, 0, Ogre::VET_FLOAT3, Ogre::VES_POSITION);        size_t vertexSize = sizeof(float) * 3;        assert(subMesh->vertexData->vertexDeclaration->getVertexSize(HARDWARE_BUFFER_SOURCE_VERTEX) == vertexSize);        if (subMesh->vertexData->vertexDeclaration->getVertexSize(HARDWARE_BUFFER_SOURCE_VERTEX) != vertexSize)        {            OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR, "VertexSize error",                 "ModelLoaderMdx::loadGeoset");        }        Ogre::HardwareVertexBufferSharedPtr vertexBuffer;        vertexBuffer = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(            vertexSize,            vertexCount,            Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);        void* vertexBufferData = vertexBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD);        unsigned int vertexBufferDataPos = 0;        for (unsigned int i = 0; i < vertexCount; ++i)        {            Ogre::Vector3 data(                dataStream->read<float>(),                dataStream->read<float>(),                dataStream->read<float>()                );            transformCoord(data);            memcpy((char*)vertexBufferData + vertexBufferDataPos, &data, sizeof(Ogre::Vector3));            vertexBufferDataPos += sizeof(Ogre::Vector3);        }        vertexBuffer->unlock();        subMesh->vertexData->vertexBufferBinding->setBinding(HARDWARE_BUFFER_SOURCE_VERTEX, vertexBuffer);        //        // 法线数据        //        if(!expectTag(dataStream, 'NRMS'))        {            OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR, "Expect NRMS data for material",                "ModelLoaderMdx::loadGeosets");        }        unsigned int normalCount = dataStream->read<unsigned int>();        if(normalCount != vertexCount)        {            std::stringstream stream;            stream << "Normal count mismatch, " << normalCount << " normals for " << vertexCount << " vertices)!";            OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR, stream.str(),                 "ModelLoaderMdx::loadGeoset");        }        subMesh->vertexData->vertexDeclaration->addElement(            HARDWARE_BUFFER_SOURCE_NORMAL, 0, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);        size_t normalSize = sizeof(float) * 3;        assert(subMesh->vertexData->vertexDeclaration->getVertexSize(HARDWARE_BUFFER_SOURCE_NORMAL) == normalSize);        if (subMesh->vertexData->vertexDeclaration->getVertexSize(HARDWARE_BUFFER_SOURCE_NORMAL) != normalSize)        {            OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR, "NormalSize error",                 "ModelLoaderMdx::loadGeoset");        }        Ogre::HardwareVertexBufferSharedPtr normalBuffer;        normalBuffer = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(            normalSize,            normalCount,            Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);        void* normalBufferData = normalBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD);        unsigned int normalBufferDataPos = 0;        for (unsigned int i = 0; i < normalCount; ++i)        {            Ogre::Vector3 data(                dataStream->read<float>(),                dataStream->read<float>(),                dataStream->read<float>()                );            transformCoord(data);            memcpy((char*)normalBufferData + normalBufferDataPos, &data, sizeof(Ogre::Vector3));            normalBufferDataPos += sizeof(Ogre::Vector3);        }        normalBuffer->unlock();        subMesh->vertexData->vertexBufferBinding->setBinding(HARDWARE_BUFFER_SOURCE_NORMAL, normalBuffer);…………
        //        // 顶点索引        //        if(!expectTag(dataStream, 'PVTX'))         {            OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR, "Expect PVTX data for geoset",                "ModelLoaderMdx::loadGeosets");        }        unsigned int indexCount = dataStream->read<unsigned int>();        assert(totalIndexCount == indexCount);        if (totalIndexCount != indexCount)        {            std::stringstream stream;            stream << "indexCount is " << indexCount << ", but totalIndexCount for all faces is " << totalIndexCount;            OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR, stream.str(),                 "ModelLoaderMdx::loadGeoset");        }        subMesh->indexData->indexStart = 0;        subMesh->indexData->indexCount = indexCount;        Ogre::HardwareIndexBufferSharedPtr indexBuffer;        indexBuffer = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(            Ogre::HardwareIndexBuffer::IT_16BIT,            indexCount,            Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);        void* indexBufferData = indexBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD);        dataStream->read(indexBufferData, indexCount * sizeof(unsigned short));        // 三角形反转, 将原来的反面朝外        unsigned short* tmpData = (unsigned short*)indexBufferData;        for (unsigned int i = 0; i < indexCount; i += 3)        {            unsigned short tmp = tmpData[i + 1];            tmpData[i + 1] = tmpData[i + 2];            tmpData[i + 2] = tmp;        }        indexBuffer->unlock();        subMesh->indexData->indexBuffer = indexBuffer;        subMesh->operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST;…………        // 材质ID        unsigned int materialID = dataStream->read<unsigned int>();        Ogre::String materialName = m_modelName + Ogre::String("_") + boost::lexical_cast<Ogre::String>(materialID);        subMesh->setMaterialName(materialName);…………        //        // 贴图坐标        //        if(!expectTag(dataStream, 'UVBS'))         {            OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR, "Expect UVBS data for geoset",                "ModelLoaderMdx::loadGeosets");        }        unsigned int texturePositionCount = dataStream->read<unsigned int>();        if(texturePositionCount != vertexCount)        {            std::stringstream stream;            stream << "Texture position count mismatch, " << texturePositionCount << " texture positions for " << vertexCount << " vertices)!";            OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR, stream.str(),                 "ModelLoaderMdx::loadGeoset");        }        // TextureCoord Data        subMesh->vertexData->vertexDeclaration->addElement(            HARDWARE_BUFFER_SOURCE_TEXPOS, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES);        size_t texPosSize = sizeof(float) * 2;        assert(subMesh->vertexData->vertexDeclaration->getVertexSize(HARDWARE_BUFFER_SOURCE_TEXPOS) == texPosSize);        if (subMesh->vertexData->vertexDeclaration->getVertexSize(HARDWARE_BUFFER_SOURCE_TEXPOS) != texPosSize)        {            OGRE_EXCEPT(Ogre::Exception::ERR_INTERNAL_ERROR, "TexturePositionSize error",                 "ModelLoaderMdx::loadGeoset");        }        Ogre::HardwareVertexBufferSharedPtr texPosBuffer;        texPosBuffer = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(            texPosSize,            texturePositionCount,            Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY);        void* texPosBufferData = texPosBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD);        dataStream->read(texPosBufferData, texPosSize * texturePositionCount);        texPosBuffer->unlock();        subMesh->vertexData->vertexBufferBinding->setBinding(HARDWARE_BUFFER_SOURCE_TEXPOS, texPosBuffer);    }    return true;}

 

    但是到了动画数据这里问题又出来了,而且mdx模型与m2模型还有些差别,mdx模型中没有骨骼数据,只有max中最简单的三种变换数据,Ogre中只有MorphAnimation能够实现此种动画。

 

    Ogre的MorphAnimation的第一个KeyFrame必须带有完整的顶点信息,而mdx模型中旋转、缩放与位移是分开的,也就是一个KeyFrame上可能只有一种变换,或者多种。而实际上在max中制作动画的时候这三种变换也是独立开的,Ogre的论坛上找到一篇讨论有人提到了这个问题,可惜制作者的回复是MorphAnimation只实现到这样……

 

    也确实,现在除了一些小物件的动画外,主角,怪物的动画都用skeleton了,也许MorphAnimation就快退出历史的舞台,在Ogre中看不到了,也不能指望会有什么改进。

 

    继续实现之,那就只能在有KeyFrame的地方把三种变换都计算一次,然后取得最终变换后的位置数据,也就是做人工的动画帧采样。在War3EditorSource的基础上做了些修改,终于,一帧帧的动画计算出来了。

 

mdx2mesh_anim

 

    不过问题依然还有很多,比如mdx模型,尤其是怪物和角色模型中大量用到了ReplacableTexture,这些需要通过读配置文件来获取可用的贴图,另外模型上附带的粒子特效、纹理动画等都还没有导出,看看这个没有贴图的攻击中的蝎子,前面的路仍然很远。

 

mdx2mesh_anim_attack

原创粉丝点击