|
楼主 |
发表于 2013-7-10 18:09:23
|
显示全部楼层
骨骼动画osgCal示例+osgCal编译(yuanloo 发表于 2012-4-11 14:46:17 )
经过近三天的纠结,终于将《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/in ... C.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/blo ... cd95249a502702.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/downloads15 ... sgVR/main.cpp__.htm
骨骼模型下载地址:
http://ftp.jaist.ac.jp/pub/sourc ... /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]
|
|