OSG:从源码看Viewer::run() 一

刚刚入门OSG不久,一直弄不清楚它到底是如何显示一副图片的,简单从源码看下。
:所用OSG的版本为3.4 release版!

1、前言

在OSG程序中,简单展示一副图片用:

    osg::ref_ptr viewer = new osgViewer::Viewer;
    osg::ref_ptr node = osgDB::readNodeFile("glider.osg");
    viewer->setSceneData(node);
    return viewer->run();

然后在我们的计算机显示器(屏幕中心)上就会展现滑翔机的图片,并且移动鼠标、滚轮等设备还能实现对该滑翔机的旋转、缩放、平移等一系列的操作。在OpenGL中我们知道,要实现对模型的旋转缩放等功能,需要设置相机的各种参数(相机位恣,旋转角度、平移步长、转动速率等等)。osg中简简单单的几行代码,就实现了如此之多的功能,不禁要问:run()函数里边到底发生了什么。

2、第一步

打开OSG的源码,在Viewer.cpp文件中找到Viewer::run()函数的定义:

int Viewer::run()
{
    if (!getCameraManipulator() && getCamera()->getAllowEventFocus())
    {
        setCameraManipulator(new osgGA::TrackballManipulator());
    }

    setReleaseContextAtEndOfFrameHint(false);

    return ViewerBase::run();
}

可见,将一副图片的节点读入osg的场景中后,在场景的run中首先会判断该场景中有没有漫游器(getCameraManipulator返回一个osgGA::CameraManipulator),如果该场景中不存在漫游器,则调用函数setCameraManipulator创建一个跟踪球 TrackballManipulator的场景漫游器。关键来看默认的漫游器osgGA::TrackballManipulator()做了什么!

2.1 默认漫游器

先看osgGA::TrackballManipulator()的默认构造函数:

TrackballManipulator::TrackballManipulator( int flags )
   : inherited( flags )
{
    setVerticalAxisFixed( false );
}

将该类当做参数传递给了setCameraManipulator,重点来看setCameraManipulator干了什么,源码:

void View::setCameraManipulator(osgGA::CameraManipulator* manipulator, bool resetPosition)
{
    _cameraManipulator = manipulator;

    if (_cameraManipulator.valid())
    {
        _cameraManipulator->setCoordinateFrameCallback(new ViewerCoordinateFrameCallback(this));

        if (getSceneData()) _cameraManipulator->setNode(getSceneData());

        if (resetPosition)
        {
            osg::ref_ptr dummyEvent = _eventQueue->createEvent();
            _cameraManipulator->home(*dummyEvent, *this);
        }
    }
}

可以看出,该函数主要设置了当前场景的主相机的动作,设置了当前场景节点的坐标系以及场景中的数据,同时通过设置home--漫游器初始位置。
紧接着通过setReleaseContextAtEndOfFrameHint()设置了渲染场景的上下文关系。

3、再看 ViewerBase::run()

源码:

int ViewerBase::run()
{
    if (!isRealized())
    {
        realize();
    }

    const char* run_frame_count_str = getenv("OSG_RUN_FRAME_COUNT"); //getenv()从环境变量中去字符串
    unsigned int runTillFrameNumber = run_frame_count_str==0 ? osg::UNINITIALIZED_FRAME_NUMBER : atoi(run_frame_count_str);

    while(!done() && (run_frame_count_str==0 || getViewerFrameStamp()->getFrameNumber()0.0 ? 1.0/_runMaxFrameRate : 0.0;
        osg::Timer_t startFrameTick = osg::Timer::instance()->tick();
        if (_runFrameScheme==ON_DEMAND)
        {
            if (checkNeedToDoFrame())
            {
                frame();
            }
            else
            {
                // we don't need to render a frame but we don't want to spin the run loop so make sure the minimum
                // loop time is 1/100th of second, if not otherwise set, so enabling the frame microSleep below to
                // avoid consume excessive CPU resources.
                if (minFrameTime==0.0) minFrameTime=0.01;
            }
        }
        else
        {
            frame();
        }

        // work out if we need to force a sleep to hold back the frame rate
        osg::Timer_t endFrameTick = osg::Timer::instance()->tick();
        double frameTime = osg::Timer::instance()->delta_s(startFrameTick, endFrameTick);
        if (frameTime < minFrameTime) OpenThreads::Thread::microSleep(static_cast(1000000.0*(minFrameTime-frameTime)));
    }

    return 0;
}

在该函数中,实现了每一帧的渲染、计算帧率等。
frame()中:

void ViewerBase::frame(double simulationTime)
{
    if (_done) return;

    // OSG_NOTICE<

可见,在osg的每一帧的绘制中,实现了对事件的遍历(eventTraversal())、更新( updateTraversal())和渲染(renderingTraversals()),同时这也是漫游器真正被添加进来起作用的地方。至于每一帧中到底如何处理事件的(eventTraversal()函数作用),可参看源码文件Viewer.cpp中void Viewer::eventTraversal()的具体定义。

主要参考文章:http://blog.csdn.net/csxiaosh...
http://blog.csdn.net/popy007/...

未完 待续。。。

你可能感兴趣的:(c++)