这个例子主要演示了两个功能:
- 如何动态修改StateSet
- 如何动态创建动画路径
动态修改StateSet
StateSet的动态修改使用osg::StateSet::Callback
来完成。
这里简单的展开一下,在osg里有很多预置的NodeCallback
和Callback
,这些类我称为回调类,它们会在osg的每一帧当中被调用一次或多次。你可以模仿这些预置的类来打造自己的回调类。要实现一个回调类,除了从相关的NodeCallback
和Callback
派生外,最重要的就是重载void operator()(osg::Node* node, osg::NodeVisitor* nv)
这个虚函数。
operator函数的第一个参数是节点,由于Callback类一般都是设在某个节点上的,所以这个传进来的Node一般都是这个某个节点。第二个参数是框架生成的一个visitor,它上面带着很多当前这个时间点的信息,比如这个例子里用到的getVisitorType()
、getFrameStamp()->getFrameNumber()
,后一个看也知道指的是当前是第多少帧,咱们看一下前面这个值,它返回的是一个枚举,找到它的定义:
enum VisitorType
{
NODE_VISITOR = 0,
UPDATE_VISITOR,
EVENT_VISITOR,
COLLECT_OCCLUDER_VISITOR,
CULL_VISITOR
};
从这里,我们就可以猜想出osg在运行到不同的阶段时会生成不同的visitor,比如UPDATE_VISITOR
就表示模型更新阶段、EVENT_VISITOR
表示系统事件处理阶段等等。所以说我们写的*Callback在一帧中是会被调用很多次的,你可以按照本例那样,只在自己想要处理的阶段,进行处理即可。
这里我们是要修改节点的颜色,例子里采用的是修改材质的方式来实现,这也是常用的方法。颜色的插值函数用的是osgAnimation::Vec4LinearSampler
这个类,这类类的用法,在上一个例子里就见识过了,很简单,只需要指定某一关键帧时对应的值就可以了,用的时候调getValueAt
就可以得到对应的插值结果,而不必自己去写插值算法(这个在算法比较复杂的时候会很好用^_^)。
小问题:
在创建完材质后,例子里还打开了混合模式(setMode(GL_BLEND, true)
),这个我试着关掉,也没发现问题。在我给小球本身贴上一张纹理后,关闭这个选项也没发现问题。也许这个选项在这里只是为了解决某些兼容性的问题吧。
动态创建动画路径
动画路径的生成用的是osg::NodeCallback
,例子里给了两个实现,一个是等时间段生成一个点,一个是等距离生成一个点。
两个例子的开头都给出了一段注释,但我发现这个注释很有问题:
等时间段的注释是这样写的:
This won't really give good results in any situation, but it does demonstrate on possible "fast" usage...
这种写法在某些情况下不能正常工作,这里这样写只是演示一下“最快”的用法。
等距离的注释:
This will give great results if you DO NOT have VSYNC enabled and can generate decent FPS.
如果你想关闭垂直同步,获得更高的FPS,那么就可以用这种写法。
看注释,明显等距离的写法效率应该更高才对,但对比核心代码:
```c++
//等时间
float t = osg::Timer::instance()->delta_s(_startTime, _currentTime);
if(_lastAdd + _addSeconds <= t && t <= 8.0f)
{
osg::Vec3 pos;
_sampler->getValueAt(t, pos);
_geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(pos, 0.5f)));
_geode->dirtyBound();
_lastAdd += _addSeconds;
}
//等距离
float t = osg::Timer::instance()->delta_s(_startTime, _currentTime);
osg::Vec3 pos;
_sampler->getValueAt(t, pos);
osg::Vec3 distance = _lastAdd - pos;
if(t <= 8.0f && distance.length() >= _threshold)
{
_geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(pos, 0.25f)));
_lastAdd = pos;
_count++;
}
```
几乎毫无区别!
所以我觉得这个例子也许“年久失修”,在某次的修改中,注释已经和代码不一致了。看来老外写代码也和我们一样,没那么严谨。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。