[OpenGL游戏开发] (原)骨骼动画osgCal示例+osgCal编译(附源码)

经过近三天的纠结,终于将《 OpenSceneGraph 三维渲染引擎编程指南》中带有的骨骼动画 osgCal 示例运行成功。写篇总结纪念这逝去的三天,同时给各位童鞋做个参考,希望大家少走弯路。
  首先对 Cal3D 做一个简单地介绍。 Cal3D 是一个开源的骨骼动画引擎,它是基于关键帧的骨骼动画,数据文件分为骨骼( Skeleton )、网格( Mesh )和动画( Animation 3 个部分,由一个文本文件管理( cal3d.cfg) ,可以定义多个动作,但每个动画文件是相互独立的。它同时支持动作混合,即可以同时实现两个独立的动作协同一起。其中,每一个动作都有一个权重,即不同的动作优先级是不一样的,通常来说,运动类动作优先级会高于循环类动画。因此,它在渲染过程中的效果是比较好的,管理也是很方便,可以通过修改文本配置文件 (.cfg) 来管理动画制作信息。
1. 运行环境: win7+X86+vs2008( 这是我机器的运行环境,只供参考 )
2. 硬件要求:
2.1 CMake-2.8.7 (可以直接去官方下载安装版的 CMake ,只需一步 next 就可以轻易安装,下载地址: http://www.cmake.org/cmake/resources/software.html 
2.2 SVN 源码下载工具。这是一个非常常见的源码下载工具,很多开源的网站就直接提供 svn 链接,就可以轻易获取最新版本的源码;很多中小型软件开发团队也是会用到该工具,用于代码的上传更新,下载共享,实现多人维护代码。下载地址: http://tortoisesvn.net/ 
相关使用的教程: 
http://dev.baidu.com/wiki/app/index.php?title=SVN%E6%95%99%E7%A8%8B#.E6.96.B0.E5.A2.9E.E6.96.87.E4.BB.B6.E5.92.8C.E7.9B.AE.E5.BD.95 
2.3 编译好的 osg( 我用的是 OpenSceneGraph-2.8.2 ,虽然版本稍微有点老,但是不影响正常的使用,也就无所谓了。现在最新版本是,可以去中国官网下载:   http://www.osgchina.org/projects/osgcn/wiki.php  )
OSG 编译的教程: http://hi.baidu.com/kdlichao/blog/item/18852ad5d7cd95249a502702.html
2.4 编译好的 Cal3D
2.5 编译好的 osgCal
(下面将详细讲解 Cal3D osgCal 的编译。)
3. 编译 Cal3D ,目的就是得到 cal3d_d.lib cal3d_d.dll cal3d.lib cal3d.dll ,后面编译 osgCal 依赖 cal3d 库文件(本例中只用到库文件,动态库只是附属物)。
cal3d 源码获取,通过 SVN 获取最新的源码,最新源码下载地址:
http://svn.gna.org/svn/cal3d/trunk 。
下载的源码文件包含以下三个文件夹


打开 cal3d ,找到 cal3d.sln ,打开该工程,包含以下 12 个工程,


这里我们只需要build Cal3D 工程,选择该工程,右击,在弹出框中选中 build ,单击。等待几分钟就可以在 bin 文件下的 Debug Releas 得到 cal3d_d.lib cal3d_d.dll debug 模式)及 cal3d.lib cal3d.dll release 模式)。
cal3d 目录下新建 lib include 文件夹,将 cal3d_d.dll cal3d.dll 拷贝到 bin 文件; cal3d_d.lib cal3d.lib 拷贝到 lib 文件夹;将 src 的所以文件拷贝到 include 文件夹。
可以依据需要编译其他工程,如果编译


max maya )的 cal3d 导出插件,那么需要安装 max maya) 软件,并且在 Tools-Options-Projects and Solutions-C++ Directories 的包含文件添加 max(maya) 安装目录下的 include 路径,库文件添加对应的 lib 路径。其他的例子工程可以随便 build 了,但是我没有明白这些例子工程到底是干啥用的,留着以后探讨吧。
到这里编译 osgCal 的准备工作就完成了。
4. 编译 osgCal ,编译 osgCal 就是为了得到 osgCald.dll osgCald.lib osgCal.dll osgCal.lib (后面带 d 的是在 dubeg 模式下得到的,不带的是 release 模式)。
svn 获取最新源码,最新源码获取地址: 
https://osgcal.svn.sourceforge.net/svnroot/osgcal/trunk/osgCal 及
https://osgcal.svn.sourceforge.net/svnroot/osgcal/trunk/models 
(本示例中只用到 osgCal ,模型 models 没有用到,具体实现什么功能,目前没有整明白)
打开 Cmake ,生成 osgCal 解决方案。
osgCal 文件下的 CMakeLists.txt 拖到 CMake 2.8.7
点击 Configure ,选择编译的环境


安装下面的要求,配置参数。


这是重点,一不小心,就在载在这里了!!
这里的 include 路径及 lib 文件几乎都是手动加过去的。
对几个重要的参数作个解释:
CAL3D_INCLUDE_DIR      cal3d include 文件的路径( E:/cal3d/cal3d/include
CAL3D_        LIBRARY          cal3d lib 文件( E:/cal3d/cal3d/lib/cal3d_d.lib
OPEN_THREADS_INCLUDE_DIR 
OpenThreads include 路径 
(D:/osgSDK/OpenSceneGraph-2.8.2/include/OpenThreads)
OPEN_THREADS_LIBTARY
OpenThreads lib 路径 (D:/osgSDK/OpenSceneGraph-2.8.2/lib/OpenThreads) 
OSG_INCLUDE_DIR         
osg include 文件路径( D:/osgSDK/OpenSceneGraph-2.8.2/include
OSG_LIBRARY             
osg lib 文件( D:/osgSDK/OpenSceneGraph-2.8.2/lib/osg.lib
根据 SDK 所在的位置指定正确的路径。
参数配置完毕后,第二次点击 configure
确定无误后点击 Generate ,显示 Configuring done,表明配置成功,将生成解决方案。
注意:这里 include lib 路径要与 osgCal 工程下的 Tools-Options-Projects and Solutions-C++ Directories include lib 路径相一致。要是在 CMake 中未添加这些 include lib 路径(前提是 Generating done ),在工程中再添加 include lib 路径也是可以的。 Cmake 中允许有警告,但是不要有错误,否则编译会通不过去。
打开 osgCal.sln 解决方案,


注意:检查Tools-Options-Projects and Solutions-C++ Directories 中的 include lib 路径是否正确。
Build ALL_BUILD 。等待十几分钟,显示 6 succeeded,0 faild ,表明编译成功。
osgCald.dll osgCal.dll 拷贝到 bin 文件; osgCald.lib osgCal.lib 拷贝到 lib 文件夹。
经过千辛万苦,终于得到了所需要的 osgCald.dll osgCal.dll osgCald.lib osgCal.lib 。下面可以开始跑例子了,检验一下生成的库文件及动态库是否正确。
5. 配置 osgCal 环境,运行 osgCal 骨骼示例。
开始 - 控制面板 - 系统和安全 - 系统 - 高级系统设置 - 高级 - 环境变量 - 用户变量 -PATH 添加 osgCald.dll osgCal.dll 的路径。


新建工程 win32 控制台工程,工程命名 skeleton ,将代码粘贴至 skeleton.cpp 。代码下载地址:
http://read.pudn.com/downloads156/sourcecode/windows/opengl/695085/Cal3D%E5%8A%A8%E7%94%BB%E7%9A%84%E5%8A%A0%E8%BD%BD%E3%80%81%E6%98%BE%E7%A4%BA%E5%8F%8A%E6%8E%A7%E5%88%B6%E7%A4%BA%E4%BE%8B/osgVR/main.cpp__.htm 
骨骼模型下载地址: 
http://ftp.jaist.ac.jp/pub/sourceforge/o/project/os/osgcal/test_models/0.0.1/ 
Tools-Options-Projects and Solutions-C++ Directories 添加 osgCal include bin 路径。
Project-Properties-Configuration Properties-Linker-Input-Additional 添加依赖库
osg.lib
osgDB.lib
osgGA.lib
osgUtil.lib
osgViewer.lib
osgCal.lib
这里选择的是 Release 模式。
编译工程,运行。将会展示如下的


示例的骨骼动画是非常生动的。有以下几种模式:
animation=skeleton_idle.caf
animation=skeleton_strut.caf
animation=skeleton_walk.caf
animation=skeleton_jog.caf
animation=skeleton_wave.caf
animation=skeleton_shoot_arrow.caf
animation=skeleton_hiphop.caf

按“ 1 ”对应的是 idle ,按“ 2 ”对应的是 strut ,按“ 3 ”对应的是 walk ....

好了!我们的 osgCal 例子运行成功, enjoy !!

最后附上源码:

/**********************************************************  
*Write by FlySky  
* zzuxp@163.com   http://www.OsgChina.org     
**********************************************************/   

#include <osgViewer/Viewer>    
#include <osgViewer/ViewerEventHandlers>    

#include <osg/Node>    
#include <osg/Geode>    
#include <osg/Geometry>    
#include <osg/Group>    

#include <osgDB/ReadFile>    
#include <osgDB/WriteFile>    

#include <osgGA/TrackballManipulator>    
#include <osg/BoundingSphere>    
#include <osgUtil/Optimizer>    

#include <osgCal/CoreModel>    
#include <osgCal/Model>    

#include <iostream>    

//加载模型    
osg::Node* makeModel( osgCal::CoreModel* cm,osgCal::BasicMeshAdder* ma, int animNum = -1 )   
{   
        osgCal::Model* model = new osgCal::Model();   

        //从CoreModel中创建模型    
        model->load( cm, ma );   

        if ( animNum != -1 )   
        {   
                //实现动画    
                model->blendCycle( animNum, 1.0f, 0 );   
        }   

        return model;   
}   

//动画控制事件处理器    
class AnimationToggleHandler : public osgGA::GUIEventHandler    
{   
public:    
        //构造函数    
        AnimationToggleHandler( osgCal::Model* m,   
                const std::vector <std::string >& an )   
                : model( m )   
                , animationNames( an )   
                , currentAnimation( -1 )   
        {   
                //    
        }   
        //事件处理函数    
        bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)   
        {   
                osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(&aa);   
                if (!viewer) return false;   

                switch(ea.getEventType())   
                {   
                case(osgGA::GUIEventAdapter::KEYDOWN):   
                        {   
                                if ( ea.getModKeyMask() & osgGA::GUIEventAdapter::MODKEY_CTRL )   
                                {   
                                        if ( ea.getKey() >= '1' &&   
                                                ea.getKey() < '1' + (int)animationNames.size() )   
                                        {   
                                                //执行动作    
                                                model->executeAction( ea.getKey()- '1' );   
                                        }   
                                }   
                                else if ( ea.getKey() >= '1' &&   
                                        ea.getKey() < '1' + (int)animationNames.size() )   
                                {      
                                        //重新初始化    
                                        if ( currentAnimation != -1 )   
                                        {   
                                                model->clearCycle( currentAnimation, 1.0 );   
                                        }   

                                        currentAnimation = ea.getKey() - '1';   
                                        //循环动作    
                                        model->blendCycle(currentAnimation, 1.0f, 1.0 );                           
                                }   
                                //清除当前动作,重置初始动作    
                                else if ( ea.getKey() == '0' )   
                                {   
                                        model->clearCycle( currentAnimation, 0.0 );    

                                        currentAnimation = -1;   
                                }   
                        }   
                default: break;   
                }   

                return false;   
        }   

private:   
        //动画模型    
        osgCal::Model*              model;   
        //动画动作名称    
        std::vector <std::string >  animationNames;   
        //当前动作    
        int                         currentAnimation;   

};   

//暂停或者渲染状态切换事件处理器    
class ToggleHandler : public osgGA::GUIEventHandler    
{   
public:    

        ToggleHandler( bool& toggleVar,char  key):    
          toggleVar( &toggleVar ),   
                  key( key )   
          {   
                  //    
          }   
          //事件处理函数    
          bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)   
          {   
                  osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(&aa);   
                  if (!viewer) return false;   

                  switch(ea.getEventType())   
                  {   
                  case(osgGA::GUIEventAdapter::KEYDOWN):   
                          {   
                                  if ( ea.getKey() == key )   
                                  {   
                                          //切换状态    
                                          *toggleVar = !(*toggleVar);   
                                  }   
                          }   
                  default: break;   
                  }   

                  return false;   
          }   

private:   
        //当前状态    
        bool*       toggleVar;   
        //按键    
        char        key;   
};   

int main()   
{   
        //创建场景浏览器    
        osgViewer::Viewer* viewer = new osgViewer::Viewer();   

        osg::Group* root = new osg::Group();   

        //一系列动作名    
        std::vector <std::string > animationNames;   
        //动画模型名    
        std::string fn("E:\\OSG\\osgCalByflySky\\test_models\\thug\\cal3d.cfg");   //skeleton\\skeleton hero\\hero
        //初始化静止模型动作    
        int animNum = -1;   

        //动画模型    
        osgCal::CoreModel* coreModel = new osgCal::CoreModel();   

        //基本网格    
        osgCal::BasicMeshAdder* meshAdder= new osgCal: efaultMeshAdder ();   

        //网格参数    
        osgCal::MeshParameters* p = new osgCal::MeshParameters();   
        //采用深度,用于减少子网被遮盖的不可见的像素    
        p->useDepthFirstMesh = true ;   
        //平滑模式    
        p->software = true ;   

        //加载外部模型文件    
        coreModel->load( fn, p);   

        //添加模型    
        root->addChild( makeModel( coreModel,meshAdder,animNum ) );   

        //得到一系列动作名称    
        animationNames = coreModel->getAnimationNames();   

        //动作切换控制    
        viewer->addEventHandler( new AnimationToggleHandler( (osgCal::Model*)root->getChild(0), animationNames ) );   

        //暂停控制    
        bool paused = false;   
        viewer->addEventHandler( new ToggleHandler( paused, 'p' ) );   

        //优化场景数据    
        osgUtil::Optimizer optimizer ;   
        optimizer.optimize(root);   

        viewer->setSceneData(root);   

        viewer->setCameraManipulator(new osgGA::TrackballManipulator());   

        viewer->realize();   

        //添加时间控制    
        osg::Timer_t startTick = osg::Timer::instance()->tick();   

        //初始化一个状态枚举变量,控制暂停或者渲染    
        //默认为渲染状态    
        enum PauseState { Unpaused, Paused };   
        PauseState   pauseState = Unpaused;   
        osg::Timer_t pauseStartTick = 0;   
        double       totalPauseTime = 0;    

        //通过控制当前帧显示时间,来控制是否暂停或者渲染    
        while ( !viewer->done() )   
        {   
                osg::Timer_t tick = osg::Timer::instance()->tick();   

                //暂停    
                if ( pauseState == Unpaused && paused )   
                {   
                        pauseState = Paused;   
                        pauseStartTick = tick;   
                }   
                //渲染    
                if ( pauseState == Paused && !paused )   
                {   
                        pauseState = Unpaused;   
                        //得到暂停时间    
                        totalPauseTime += osg::Timer::instance()->delta_s( pauseStartTick, tick );   
                }   

                //当前时间    
                double currentTime = osg::Timer::instance()->delta_s(    
                        startTick,   
                        pauseState == Unpaused ? tick : pauseStartTick );   

                //继续暂停帧开始渲染    
                viewer->frame( currentTime - totalPauseTime );   
        }   

        return 0;   

[/code]

再给大家提供一个cal3d格式的骨骼模型。enjoy!!


欢迎转载,转载请注明: http://www.cnkinect.com/forum.php?mod=viewthread&tid=15697&page=1&extra=
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本人主要从事图形图象工作,空闲之余接触了一些游戏编程,特写一些编程心得,本文 适合没有接触过人物动画编程的初学者,希望游戏制作的大虾们指点,交流。 在以前还有没接触人物动画编程的时候,觉得通过编程让人物动起来一定是一件很麻烦 的事情,尤其是初学者,大都会摸不着头脑,碰到诸如骨骼动画之类,似乎无从下手。但是 当你了解了它们的格式,就会发现其实真正的人物动画的制作并不是在编程阶段,而是在模 型构建阶段,程序员主要做工作的是掌握模型文件的格式,将存储在人物模型中的各种信息, 如顶点,面片,材质,骨骼或顶点运动的关键帧序列等信息读入内存然后用你熟悉的 SDK 绘制出来,再根据时间采用线性或者球形插值对动作序列的关键帧进行插值,不断变换顶点 坐标,从而得到了一系列连续的人物动画,听起来确实不难吧!当然你也可以在程序里自己 控制人物每一帧每一个关节的运动,我想做游戏的很少有人这么做吧。下面我向大家介绍一 下自己是如何编写人物动画程序的。本人从事的图形图象开发主要是基于 OpenGL 和 OSG 因此范例程序将采用 OpenGL 或 OSG。先声明一下,本人的语言表达能力很差,请大家多 多谅解指正。 考虑到没有接触过人物模型的朋友,我首先从人物模型的结构讲起,游戏人物编程主要 采用的人物模型格式有 Quake 里的md2,md3,Half Life 里的 mdl,Doom里的 md5,还有 典型的骨骼动画模型 ms3d…,至于3dmax 的模型,本人觉得太麻烦!在此我说两个有代表 性的 Md3,和 ms3d,其它的模型都大同小异,只要你了解了它们的格式,程序实现都不难。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值