一开始弄了好久没整明白这个Manipulator的初始摄像机位置,始终看不到物体不知道怎么调整(因为默认在(0, 0, 0)物体被剔除了),后来清楚了在此做个记录。
//动画完成回调
struct CompletedCallback : public osgGA::AnimationPathManipulator::AnimationCompletedCallback
{
CompletedCallback(osgViewer::Viewer* viewer)
{
_viewer = viewer;
}
void completed(const osgGA::AnimationPathManipulator* apm) override
{
_viewer->setCameraManipulator(new osgGA::TerrainManipulator);
}
osgViewer::Viewer* _viewer = nullptr;
};
int main(int argc, char *argv[])
{
osgViewer::Viewer viewer;
osg::ref_ptr<osg::Group> root = new osg::Group;
root->addChild(osgDB::readNodeFile("./cow.osg"));
viewer.setSceneData(root);
osgGA::AnimationPathManipulator* apm = new osgGA::AnimationPathManipulator;
osg::AnimationPath* path = new osg::AnimationPath;
for (int i = 0; i <= 36; i++)
{
float pi = 3.14159265;
float pi2 = 2 * pi;
float precision = 36.0;
float delta = pi2 / precision;
osg::Quat quat(-delta * i, osg::Vec3d(0.0, 0.0, 1.0));
//AnimationPathManipulator默认情况下,摄像机是在(0, 0, 0)并且视角朝下(z-),这里先绕x轴旋转45°
osg::Quat base(pi / 4.0, osg::Vec3d(1.0, 0.0, 0.0));
float radius = root->getBound().radius() * 3;//使摄像机离视角中心三倍包围盒半径的距离
float z = radius * cos(pi / 4.0);
float xy_radius = radius * sin(pi / 4.0);
osg::Matrixd m = osg::Matrixd::rotate(base * quat) * osg::Matrixd::translate(-xy_radius * sin(delta * i), -xy_radius * cos(delta * i), z);
osg::Vec3f pos = m.getTrans();
osg::Quat rot = m.getRotate();
path->insert(i / 3.6, osg::AnimationPath::ControlPoint(pos, rot));
}
path->setLoopMode(osg::AnimationPath::NO_LOOPING);
apm->setAnimationPath(path);
//添加动画完成后的回调,如果为LOOP或SWING就不要添加了
apm->setAnimationCompletedCallback(new CompletedCallback(&viewer));
viewer.setCameraManipulator(apm);
return viewer.run();
}
运行效果:摄像机俯角45°环绕物体一圈
注意:
如果在场景运行的过程中设置AnimationPathManipulator,会发现视角卡住,没有动画效果,这是因为AnimationPathManipulator是以场景frameTime为基准(另一个动画组件AnimationPathCallback则是会记录动画开始时的frameTime,然后以差值为基准),则如果动画设置的0-10s,然后当前frameTime为5s,那么只会执行其中5-10s的效果,如果当前frameTime超过了10s,那么什么效果都没有,解决方案是这里继承AnimationPathManipulator后做一点小修改。
class AnimationPathManipulator_Repaired :public osgGA::AnimationPathManipulator
{
public:
bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us) override
{
if (!valid())return false;
switch (ea.getEventType())
{
case osgGA::GUIEventAdapter::FRAME:
{
if (_firstTime < 0.0)
{
_firstTime = ea.getTime();
}
handleFrame(ea.getTime() - _firstTime);
return false;
}
default:
break;
}
return false;
}
float _firstTime = -1.0;
};