游戏开发论坛

 找回密码
 立即注册
搜索
查看: 20834|回复: 13

骨骼动画(Skeketal Animation)(四)

[复制链接]

43

主题

155

帖子

171

积分

注册会员

Rank: 2

积分
171
发表于 2005-5-9 11:36:00 | 显示全部楼层 |阅读模式
我们的目标是根据骨骼动画来更新模型。看看手上的材料,

1)骨骼动画数据。上一节中我们已经读出了AnimationSet、Animation和AnimationKey这些动画数据,我们现在要做的就是把它们应用到骨骼上面去。AnimationSet只是标明了我们要播放的动画名称,关键的处理在Animation和AnimationKey上面。Animation包含了所对应的骨骼名称,下属的AnimationKey包含了坐标变换的类型以及对应的时间戳,我们也把AnimationKey看做一个关键帧。下面要做的就是根据当前时间判断动画落在哪两个关键帧中间,例如key1和key2,然后求出插值系数scaler,
  
  scaler = (当前时间-key1.时间)/(key2.时间-key1.时间)

求出插值系数后,骨骼的当前位置就可以用下面的方法求出,注意各种key类型求插值的方法不一样,

  switch( Key的类型 )
  {
    case 旋转:
      
      ...
      
      // 四元数插值
      D3DXQUATERNION RotationQuaternion;
      D3DXQuaternionSlerp(
        &RotationQuaternion,
        &pAnimationKey->pQuaternionKeys[Key1].Value,
        &pAnimationKey->pQuaternionKeys[Key2].Value,
        Scaler);

      // 应用旋转矩阵
      D3DXMATRIX RotationMatrix;
      D3DXMatrixRotationQuaternion( &RotationMatrix, &RotationQuaternion );
      pAnimation->pBone->TransformationMatrix *= RotationMatrix;

      break;

    case 平移和缩放:
      
      ...

      // 矢量插值
      D3DXVECTOR3 InterpolatedVector =
        pAnimationKey->pVectorKeys[Key1].Value + Scaler *
        ( pAnimationKey->pVectorKeys[Key2].Value - pAnimationKey->pVectorKeys[Key1].Value );

      if( pAnimationKey->Type == XAnimationKey::KeyType::Scaling )
      {
        // 应用缩放矩阵
        D3DXMATRIX ScalingMatrix;
        D3DXMatrixScaling(
          &ScalingMatrix, InterpolatedVector.x, InterpolatedVector.y, InterpolatedVector.z );
                       
        pAnimation->pBone->TransformationMatrix *= ScalingMatrix;
      }
      else
      {
        // 应用平移矩阵
        D3DXMATRIX TranslationMatrix;
        D3DXMatrixTranslation(
          &TranslationMatrix, InterpolatedVector.x, InterpolatedVector.y, InterpolatedVector.z );
        pAnimation->pBone->TransformationMatrix *= TranslationMatrix;
      }

      break;

    case 坐标变换矩阵:

      ...

      // 矩阵插值
      D3DXMATRIX TransformMatrix =
        pAnimationKey->pMatrixKeys[Key1].Value + Scaler *
        ( pAnimationKey->pMatrixKeys[Key2].Value - pAnimationKey->pMatrixKeys[Key1].Value );
                                               
      // 应用坐标变换矩阵
      pAnimation->pBone->TransformationMatrix *= TransformMatrix;

      break;
  } // switch

这样我们就把根据当前时间计算出来的插值坐标变换矩阵应用到骨骼上了。

2)骨骼数据。在从文件中读出来的时候,我们已经利用pFrameSibling和pFrameFirstChild两个字段构造了一个层次结构。注意骨骼中的TransformationMatrix包含的是当前骨骼相对于父骨骼的坐标变换,应用到mesh上的时候,我们需要的相对于根骨骼的坐标变换。因此我们要做一下处理,简单的一个递归调用,

     //-----------------------------------------------------------------------------
     // 名称: UpdateHierarchy
     // 描述: 计算本节点及所有兄弟、子节点相对于根节点的偏移矩阵
     //-----------------------------------------------------------------------------
     void UpdateHierarchy( D3DXMATRIX *matTrans = NULL )
     {
       D3DXMATRIX matIdentity;

       // 根节点的偏移矩阵为单位矩阵
       if( NULL == matTrans )
       {
         D3DXMatrixIdentity( &matIdentity );
         matTrans = &matIdentity;
       }

       // 计算偏移矩阵
       matCombined = TransformationMatrix * ( *matTrans );

       // 更新兄弟节点
       if( pFrameSibling )                       
         ( ( D3DXFRAME_EX* )pFrameSibling )->UpdateHierarchy( matTrans );                       

       // 更新子节点
       if( pFrameFirstChild )
         ( ( D3DXFRAME_EX* )pFrameFirstChild )->UpdateHierarchy( &matCombined );
     }

通过在根骨骼上的一次调用,我们就可以在自定义的matCombined字段中得到各个骨骼相对于根骨骼的坐标变换矩阵.

3)mesh数据。mesh数据相对简单,ID3DXMesh和ID3DXSkinInfo接口为我们了做大部分的工作。不过天底下没有免费的午餐,为了让它们运转起来,我们还是要做一些额外的努力。在步骤1里面我们已经通过插值得到了骨骼当前的坐标变换矩阵,不过这个坐标变换是相对于模型本地坐标的,为了应用到mesh上,我们需要将坐标对齐到mesh的中心,

     // 首先bone转换到以mesh中心为坐标原点的坐标系,然后再应用frame的坐标变换矩阵
     for( DWORD i=0; i<m_pRootMeshContainer->pSkinInfo->GetNumBones(); i++ )
     {
       m_pRootMeshContainer->pBoneMatrices =
         *( m_pRootMeshContainer->pSkinInfo->GetBoneOffsetMatrix( i ) );               

       if( m_pRootMeshContainer->ppFrameMatrices )
         m_pRootMeshContainer->pBoneMatrices *= *m_pRootMeshContainer->ppFrameMatrices;               
     }

这样所有的数据都准备好了,用ID3DXSkinInfo的方法来更新骨骼关联的每个顶点的坐标,(每个顶点根据关联的所有骨骼的坐标变换矩阵乘以对应的权重再相加来得到最终应用到顶点上的坐标变换矩阵)

     m_pRootMeshContainer->pSkinInfo->UpdateSkinnedMesh(
       m_pRootMeshContainer->pBoneMatrices, NULL, pSrcVertex, pDestVertex );

到此一切准备都结束了! 绘制Mesh的动作和平常一样,设置材质和纹理,然后调用DrawSubSet方法,这个想来对大家是没有什么难度的事情。怎么样,是不是想从头再看一遍回味一下呢? 呵呵。

(附件里面是测试工程,编译环境为VS.NET2003, DXSDK(October 2004)。大家有什么不明白或者我哪里说错的,欢迎给我写信或者留言。)

sf_20055911366.rar

435.84 KB, 下载次数:

2

主题

53

帖子

65

积分

注册会员

Rank: 2

积分
65
发表于 2005-5-11 13:51:00 | 显示全部楼层

Re:骨骼动画(Skeketal Animation)(四)

不错的文章!

0

主题

10

帖子

16

积分

新手上路

Rank: 1

积分
16
发表于 2005-6-14 03:34:00 | 显示全部楼层

Re:骨骼动画(Skeketal Animation)(四)

谢谢你的代码,不过我发现两个问题,一、如果有多个AnimationSet就只能播放第一个了,二、m_pMeshFile 和 m_pTexutrePath两个变量释放内存的时候会出错,不过在你的代码里面好像不会有错误。
以下是我加的代码,解决第一个问题~
在XSkinMesh::load中
将这两句
if( m_pRootAnimationSet && m_pRootFrame )
MapAnimationBone( m_pRootFrame, m_pRootAnimationSet );
改为       
        if( m_pRootAnimationSet && m_pRootFrame )
        {
                LPXAnimationSet tmpAnimationSet;

                tmpAnimationSet = m_pRootAnimationSet;
                while( tmpAnimationSet )
                {
                        MapAnimationBone( m_pRootFrame, tmpAnimationSet );

                        tmpAnimationSet = tmpAnimationSet->pNext;
                }
        }
就可以了。
第二个问题:
m_pMeshFile = new char[strlen( pMeshFile ) + 1];
m_pTexutrePath = new char[strlen( pTexturePath ) + 1];
后面都多加了一位~~~~
不知道qlwuu大大有没有发现这些问题~

43

主题

155

帖子

171

积分

注册会员

Rank: 2

积分
171
 楼主| 发表于 2005-6-14 17:31:00 | 显示全部楼层

Re: Re:骨骼动画(Skeketal Animation)(四)

CTuo: Re:骨骼动画(Skeketal Animation)(四)

谢谢你的代码,不过我发现两个问题,一、如果有多个AnimationSet就只能播放第一个了,二、m_pMeshFile 和 m...


改得好,确实如此,呵呵。因为只是做个demo所以没有认真的改下去,这就是为什么需要个引擎的原因了,到时候要好好的封装一下的。

132

主题

1341

帖子

1341

积分

金牌会员

Rank: 6Rank: 6

积分
1341
发表于 2005-6-19 12:13:00 | 显示全部楼层

Re:骨骼动画(Skeketal Animation)(四)

你这篇文章写得很好。
我有个问题,为什么坐标变换矩阵那里:
      // 应用坐标变换矩阵
      pAnimation->pBone->TransformationMatrix *= TransformMatrix;
要“ *=  ”?

11

主题

123

帖子

123

积分

注册会员

Rank: 2

积分
123
发表于 2005-7-22 12:01:00 | 显示全部楼层

Re:骨骼动画(Skeketal Animation)(四)

楼主你要帮帮我啊,我怎么不能登陆你说的ftp上下载那本老外的高级动画书,
  我很需要啊,请帮帮我啊,我的游戏到这卡住了 啊,怎么下载啊?

55

主题

331

帖子

337

积分

中级会员

Rank: 3Rank: 3

积分
337
发表于 2005-8-14 16:54:00 | 显示全部楼层

Re:骨骼动画(Skeketal Animation)(四)

Re:骨骼动画(Skeketal Animation)(四)

你这篇文章写得很好。
我有个问题,为什么坐标变换矩阵那里:
      // 应用坐标变换矩阵
      pAnimation->pBone->TransformationMatrix *= TransformMatrix;
要“ *=  ”?


骨骼不是有继承性吗???TransformationMatrix *= TransformMatrix 就是把
TransformMatrix 应用到  TransformationMatrix 上

29

主题

97

帖子

99

积分

注册会员

Rank: 2

积分
99
发表于 2005-9-5 00:30:00 | 显示全部楼层

Re:骨骼动画(Skeketal Animation)(四)

//原码为:
//注册标准模板
hr=lpd3dxfile->RegisterTemplates( (LPVOID)XSKINEXP_TEMPLATES, sizeof( XSKINEXP_TEMPLATES ) - 1 );
if FAILED(hr)
{
lpd3dxfile->Release();
return false;
}

//报错为:
storm error LNK2005: "unsigned char * D3DRM_XTEMPLATES" (?D3DRM_XTEMPLATES@@3PAEA) 已经在 ParseX.obj 中定义

//我没有在PARSEx中定义过这些东西啊。不知何解????

29

主题

97

帖子

99

积分

注册会员

Rank: 2

积分
99
发表于 2005-11-1 19:27:00 | 显示全部楼层

Re:骨骼动画(Skeketal Animation)(四)

我发现问题了,如果你把这个X文件放进去,运行看看,惨不忍睹,我不知道这么贴附件,把你的EMAIL给我,给你看看

29

主题

97

帖子

99

积分

注册会员

Rank: 2

积分
99
发表于 2005-11-1 19:40:00 | 显示全部楼层

Re:骨骼动画(Skeketal Animation)(四)

我已经发到到你的HOTMAIL的邮箱了,望赐教
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-6-8 05:46

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表