3dsmax表情动画的导出

 

基本概念

       1,Morpher Modifier

       3dsmax中,使用Morpher Modifier可以改变mesh, patch, NURBS model的形状,同时支持Material morphing,通常用来实现复杂的表情动画。

       Mesh添加Morpher Modifier:选择一个mesh->Modify面板->Modifier List->Morpher

      

      2,Channel

       每个channel对应一个不同的Mesh,代表一种表情。

  

       通过指定在不同关健帧时每个channel的权重,来实现不同表情的组合过渡。

准备工作

       3dsmax6 sdk并没有暴露访问morpher modifier的接口,但是我们可以在3dsmax6/maxsdk/samples/modifiers/morpher/wm3.h中找到它的定义和ClassID

  #define MR3_CLASS_ID             Class_ID(0x17bb6854, 0xa5cba2a3)

  …

  class MorphR3 : public Modifier, TimeChangeCallback

  {

  …

  };

      

 将此文件include到你的工程中就可以访问morpher modifier接口了。注意,把下面两行代码注释才可以联接通过:  

      

      //static GetMorphMod theModPickmode;

      

      //static GetMorphNode thePickMode;

 

实现代码

      1,得到morpher modifier

 

 

      Modifier *wxFindMorpherModifier(INode *pINode)

       {

       #if MAX_RELEASE >= 4000

              // get the object reference of the node

              Object *pObject;

              pObject = pINode->GetObjectRef();

              if(pObject == 0) return 0;

      

              // loop through all derived objects

              while(pObject->SuperClassID() == GEN_DERIVOB_CLASS_ID)

              {

                     IDerivedObject *pDerivedObject;

                     pDerivedObject = static_cast(pObject);

      

                     // loop through all modifiers

                     int stackId;

                     for(stackId = 0; stackId < pDerivedObject->NumModifiers(); stackId++)

                     {

                            // get the modifier

                            Modifier *pModifier;

                            pModifier = pDerivedObject->GetModifier(stackId);

      

                            // check if we found the morpher modifier

                            if(pModifier->ClassID() == MR3_CLASS_ID) return pModifier;

                     }

      

                     // continue with next derived object

                     pObject = pDerivedObject->GetObjRef();

              }

       #endif

      

              return 0;

       }

 

       2,得到每个channel相对于原始Mesh的变化顶点和偏移量:

     MorphR3 *pMorpherModifier = static_cast< MorphR3* >( wxFindMorpherModifier(pNode) );

       if( pMorpherModifier != NULL )

       {

              int iNumPoses = (int)pMorpherModifier->chanBank.size();

              for( int i = 0; i < iNumPoses; ++i )

              {

                     morphChannel &currChannel = pMorpherModifier->chanBank[i];

                     if( !currChannel.mActive || !currChannel.mActiveOverride || !currChannel.cblock || !currChannel.mNumPoints  )

                     {

                            continue;

                     }

 

                     wxPose pose;

                     // 表情的名称

                     memcpy( pose.chName, currChannel.mName, sizeof(char)*64 );

                     wxPose::wxOffsetVertex offsetVertex;

                     // 遍历所有顶点

                     for( int iVertexID = 0; iVertexID < currChannel.mNumPoints; ++iVertexID )

                     {

                            // 判断是否有偏移

                            if( currChannel.mDeltas[iVertexID] == Point3(0,0,0) )

                                   continue;

 

                            // 保存顶点ID和偏移量

                            offsetVertex.index = iVertexID;

                            offsetVertex.pos = currChannel.mPoints[iVertexID] - m_aVertexArray[iVertexID].vPos;                                     

                         pose.m_VertexOffsetMap.push_back(offsetVertex);

                     }

      

        3,得到channel的关健帧信息

                     // 权重的变化范围

                     float fRange = currChannel.mSpinmax - currChannel.mSpinmin;

                     Control *Controller = currChannel.cblock->GetController(0);

                     IKeyControl *pIKeyControl = GetKeyControlInterface(Controller);

                     // 关健帧数目

                     int iNumKeys = pIKeyControl->GetNumKeys();

                     for( int j = 0; j < iNumKeys; ++j )

                     {

                            IBezFloatKey FKey;

                            pIKeyControl->GetKey( j, &FKey);

                            // 权重

                            fInfluence = FKey.val / fRange;

                            // 时间

                            fTime = FKey.time;

                     }

TODO

       1,morphChannel类的数据成员mDeltasmWeights的具体含义。发现mDeltas里面近似保存了每个顶点的偏移量乘以0.01,有何作用?mWeights按名字理解是顶点权重,不理解。

       // Actual morphable points

       std::vector              mPoints;

       std::vector              mDeltas;

       std::vector              mWeights;

 

      2,求权重范围的方法。目前的方法是求SpinControl的取值范围:

   float fRange = currChannel.mSpinmax - currChannel.mSpinmin;

   在实验过程中发现这个范围并不准确,当SpinControl取值范围为{0,100}时,会得到大于100和小于0的数。

      

      3,支持导出material morpher数据。支持material morpher可以做到材质的混合与过渡,实现更逼真的效果。比如在笑的同时脸色变红润,眼角出现皱纹等等。

  4,目前我将表情动画的数据序列化到模型格式里,但是3dsmax6不能将morpher modifier编辑的结果存成单独的文件(类似于骨骼动画里的.bip文件),如果一个模型对应多个morph动画文件就不容易序列化了。不知道高版本的max有没有此功能。

   5, 各个MorphTarget法向量的计算

你可能感兴趣的:(游戏编程实践)