动画
动画是一种常见的动画形式(Frame ByFrame),其原理是在连续的关键帧中分解动画动作,从另一个方面来说,也就是在时间轴的每帧上逐顿绘制不同的内容,使其连续播放而形成动画。
因为帧动画的帧序列内容不一样,不但给制作增加了负担,而且最终输出的文件量也很大,但它的优势也很明显:帧动画具有非常大的灵活性,几乎可以表现任何想表现的内容,而它类似于电影的播放模式,很适合于表演细腻的动画,如说话、飘动等。
osg::Sequence类
osg::Sequence类直接继承自osg::Group类它也可以作为一个组节点,继承关系图如图10-6所示。
图10-6 osg::Sequence 的继承关系图
作为场景的一个组节点,它可以添加其他的节点。帧动画通常有图片帧动画、模型帧动画和文字帧动画3种。无论是哪种动画,在OSG中,都需要把它们做成一个节点示例添加到场景中。图片可以创建一个四边形纹理、文字可以添加到Geode示例,模型就可以直接添加。
osg::Sequence 类主要负责动画的管理染操作,如加入节点、设置动画模式、设置动画状态及设置动画速度等。
可以通过调用下面的函数设置帧动画的模式类型:
- void setInterval(LoopMode mode, int begin, int end)
- // 第一个参数动画的模式类型,第二、三个参数分别是开始和终止时间
- void getInterval(LoopMode &mode, int &begin, int &end) const
- enum LoopMode
- {
- LOOP//循环
- SWING//单摆
- }
可以通过调用下面的函数设置帧动画的渲染状态模式
- void setMode(SequenceMode mode)
- SequenceMode getMode 0 const
- enum SequenceMode
- {
- START,//开始
- STOP//停止
- PAUSE//暂停
- RESUME//继续
- }
可以通过调用下面的函数设置帧动画的速度和重复次数:
- //第一个参数是动画的速度,第二个参数是重复次数,默认为-1(无限次)
- void setDuration(float speed, int nreps = -1);
- void getDuration(float &speed, int &nreps) const;
有时由于对帧动画的手动控制,可能导致帧动画不能与帧渲染时间一一对应,可以通过调用一个类的成员函数来完成与帧的同步,默认情况是不同步的,成员函数如下
- void setSync (bool sync)// 默认为 false
- void getSync (bool &sync) const
在第10.2.2节的示例中会用到一系列的模型来演示一个动画,但只是教读者如何来创建和实现帧动画及其控制,在实际应用中需要使用更加精细的方法(在后面介绍自定义粒子系统时会讲到,要耐心阅读。
动画显示与控制示例
动画(osg::Sequcnce)显示与控制示例的代码如程序清单10-4所示
1. /// 帧动画 //
2. // 对节点进行适当的缩放
3. osg::ref_ptr<osg::Node> createScaledNode(osg::ref_ptr<osg::Node>, float targetScale);
4.
5. // 创建帧动画
6. osg::ref_ptr<osg::Sequence> createSequenece(const string &strDataFolder);
7.
8. // 帧动画事件控制
9. class SequenceEventHandler :public osgGA::GUIEventHandler
10. {
11. public:
12. // 构造函数
13. SequenceEventHandler(osg::ref_ptr<osg::Sequence> seq)
14. {
15. _seq = seq;
16. }
17.
18. virtual bool handle(const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &aa)
19. {
20. osg::Sequence::SequenceMode mode = _seq->getMode();
21. if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN)
22. {
23. switch (ea.getKey())
24. {
25. // 暂停
26. case 'p':
27. {
28. mode = osg::Sequence::PAUSE;
29. _seq->setMode(mode);
30. break;
31. }
32. // 开始
33. case 's':
34. {
35. mode = osg::Sequence::START;
36. _seq->setMode(mode);
37. break;
38. }
39. // 继续
40. case 'r':
41. {
42. mode = osg::Sequence::RESUME;
43. _seq->setMode(mode);
44. break;
45. }
46. // 停止
47. case 'l':
48. {
49. mode = osg::Sequence::STOP;
50. _seq->setMode(mode);
51. break;
52. }
53. default:
54. break;
55. }
56. }
57.
58. return false;
59. }
60. private:
61. osg::ref_ptr<osg::Sequence> _seq;
62. };
63.
64. void sequence_10_4(const string &strDataFolder);
65. /// 帧动画 //
66. // 对节点进行适当的缩放
67. osg::ref_ptr<osg::Node> createScaledNode(osg::ref_ptr<osg::Node> node, float targetScale)
68. {
69. // 通过包围盒确定合适的缩放
70. const osg::BoundingSphere &bsphere = node->getBound();
71. float scale = targetScale / bsphere._radius;
72.
73. osg::ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform();
74. pat->setPosition(osg::Vec3(0.0, 0.0, 0.0));
75. pat->setScale(osg::Vec3(scale, scale, scale));
76. pat->setDataVariance(osg::Object::DYNAMIC);
77. pat->addChild(node.get());
78.
79. // 法线归一化,保证光照明暗均匀
80. osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
81. stateset = pat->getOrCreateStateSet();
82. stateset->setMode(GL_NORMALIZE, osg::StateAttribute::ON);
83.
84. return pat.get();
85. }
86.
87. // 创建帧动画
88. osg::ref_ptr<osg::Sequence> createSequenece(const string &strDataFolder)
89. {
90. // 创建帧动画对象
91. osg::ref_ptr<osg::Sequence> seq = new osg::Sequence;
92.
93. // 文件名向量对象
94. typedef std::vector<std::string> Filenames;
95. Filenames filenames;
96.
97. // 添加模型名
98. filenames.push_back(strDataFolder + "cow.osg");
99. filenames.push_back(strDataFolder + "spaceship.osg");
100. filenames.push_back(strDataFolder + "dumptruck.osg");
101. filenames.push_back(strDataFolder + "cessna.osg");
102. filenames.push_back(strDataFolder + "glider.osg");
103.
104. for (Filenames::iterator itr = filenames.begin(); itr != filenames.end(); ++itr)
105. {
106. // 加载模型
107. osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(*itr);
108. if (node)
109. {
110. // 添加子节点
111. seq->addChild(createScaledNode(node, 100.0));
112.
113. // 设置节点的持续时间
114. seq->setTime(seq->getNumChildren() - 1, 1.0);
115. }
116. }
117.
118. // 设置帧动画持续的时间
119. seq->setInterval(osg::Sequence::LOOP, 0, -1);
120.
121. // 设置播放的速度及重复的次数
122. seq->setDuration(1.0, -1);
123.
124. // 开始播放
125. seq->setMode(osg::Sequence::START);
126.
127. return seq.get();
128. }
129.
130. void sequence_10_4(const string &strDataFolder)
131. {
132. osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
133. osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
134. traits->x = 40;
135. traits->y = 40;
136. traits->width = 600;
137. traits->height = 480;
138. traits->windowDecoration = true;
139. traits->doubleBuffer = true;
140. traits->sharedContext = 0;
141.
142. osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
143.
144. osg::ref_ptr<osg::Camera> camera = viewer->getCamera();
145. camera->setGraphicsContext(gc.get());
146. camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
147. GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
148. camera->setDrawBuffer(buffer);
149. camera->setReadBuffer(buffer);
150.
151. osg::ref_ptr<osg::Group> root = new osg::Group();
152.
153. // 向场景中添加帧动画
154. osg::ref_ptr<osg::Sequence> sq = new osg::Sequence();
155. sq = createSequenece(strDataFolder);
156.
157. root->addChild(sq.get());
158.
159. // 添加帧动画控制事件
160. viewer->addEventHandler(new SequenceEventHandler(sq.get()));
161.
162. osgUtil::Optimizer optimize;
163. optimize.optimize(root.get());
164.
165. viewer->setSceneData(root.get());
166.
167. viewer->realize();
168.
169. viewer->run();
170. }
运行程序,截图如图 10-7 所示
图10-7 动画显示与控制示例截图