OSG:从源码看Viewer::run() 一
刚刚入门OSG
不久,一直弄不清楚它到底是如何显示一副图片的,简单从源码看下。
注:所用OSG的版本为3.4 release版!
1、前言
在OSG程序中,简单展示一副图片用:
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer; osg::ref_ptr<osg::Node> 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<osgGA::GUIEventAdapter> 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()<runTillFrameNumber)) { double minFrameTime = _runMaxFrameRate>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<unsigned int>(1000000.0*(minFrameTime-frameTime))); } return 0; }
在该函数中,实现了每一帧的渲染、计算帧率等。
在frame()
中:
void ViewerBase::frame(double simulationTime) { if (_done) return; // OSG_NOTICE<<std::endl<<"CompositeViewer::frame()"<<std::endl<<std::endl; if (_firstFrame) { viewerInit(); if (!isRealized()) { realize(); } _firstFrame = false; } advance(simulationTime); eventTraversal(); updateTraversal(); renderingTraversals(); }
可见,在osg的每一帧的绘制中,实现了对事件的遍历(eventTraversal()
)、更新( updateTraversal()
)和渲染(renderingTraversals()
),同时这也是漫游器真正被添加进来起作用的地方。至于每一帧中到底如何处理事件的(eventTraversal()
函数作用),可参看源码文件Viewer.cpp中void Viewer::eventTraversal()
的具体定义。
主要参考文章:http://blog.csdn.net/csxiaosh...
http://blog.csdn.net/popy007/...
未完 待续。。。