例子源码

这个例子主要演示了两个功能:

  • 如何动态修改StateSet
  • 如何动态创建动画路径

动态修改StateSet

StateSet的动态修改使用osg::StateSet::Callback来完成。

这里简单的展开一下,在osg里有很多预置的NodeCallbackCallback,这些类我称为回调类,它们会在osg的每一帧当中被调用一次或多次。你可以模仿这些预置的类来打造自己的回调类。要实现一个回调类,除了从相关的NodeCallbackCallback派生外,最重要的就是重载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++;
}
```

几乎毫无区别!

所以我觉得这个例子也许“年久失修”,在某次的修改中,注释已经和代码不一致了。看来老外写代码也和我们一样,没那么严谨。


chaoswong
48 声望2 粉丝

引用和评论

0 条评论