osgearth_pick示例,展示了鼠标移动(或者鼠标单击)时,触及到的矢量特征、绘制的线、遮盖图等内容,会将这些内容的信息打印输出到屏幕上。
执行命令
// 三维地图或者二维地图都可。
// 三维图
osgearth_pickd.exe earth_image\china-simple.earth
// 二维图
osgearth_pickd.exe earth_image\world_projected.earth
鼠标移动到某条线上(线条会变色),获取到特征边界线的信息,显示在左上角。左下角显示矢量图。
鼠标移动到遮盖图上(或者线条上,线条会变色),获取遮盖图信息,现实在左上角下。左下角显示图元信息。
本节示例,代码逻辑比前几篇更难理解一些。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LC "[rttpicker] "
using namespace osgEarth;
using namespace osgEarth::Util;
using namespace osgEarth::Features;
using namespace osgEarth::Annotation;
namespace ui = osgEarth::Util::Controls;
//-----------------------------------------------------------------------
//! Application-wide data.
struct App
{
App(osg::ArgumentParser& args) : viewer(args), mainView(NULL), rttView(NULL), mapNode(NULL), picker(NULL) { }
// 多视景器,且通过args初始化viewer
osgViewer::CompositeViewer viewer;
osgViewer::View* mainView;// 主视景器
osgViewer::View* rttView; // rtt视景器
osgEarth::MapNode* mapNode;
osgEarth::Util::RTTPicker* picker;// rtt拾取类
ui::LabelControl* fidLabel;
ui::LabelControl* nameLabel;
osg::Uniform* highlightUniform;// 高亮的变量
};
//! Callback that you install on the RTTPicker.
// 安装RTTPicker后,提供RTTPicker的回调方法
struct MyPickCallback : public RTTPicker::Callback
{
App& _app;
MyPickCallback(App& app) : _app(app) { }
void onHit(ObjectID id)
{
// First see whether it's a feature:
FeatureIndex* index = Registry::objectIndex()->get(id).get();
Feature* feature = index ? index->getFeature( id ) : 0L;
// 有feature标签,则拾取feature标签
if ( feature )
{
_app.fidLabel->setText( Stringify() << "Feature ID = " << feature->getFID() << " (oid = " << id << ")" );
_app.nameLabel->setText( Stringify() << "Name = " << feature->getString("name") );
}
else // 存在annotation标注,则拾取annotation标注
{
// Check whether it's an annotation:
AnnotationNode* anno = Registry::objectIndex()->get(id).get();
if ( anno )
{
_app.fidLabel->setText( Stringify() << "ObjectID = " << id );
_app.nameLabel->setName( Stringify() << "Name = " << anno->getName() );
}
// None of the above.. clear. 什么也没有获取到
else
{
_app.fidLabel->setText( Stringify() << "unknown oid = " << id );
_app.nameLabel->setText( " " );
}
}
_app.highlightUniform->set( id );// 将对象id设置给高亮变量
}
// 取消拾取时,回复标签显示
void onMiss()
{
_app.fidLabel->setText( "No pick." );
_app.nameLabel->setText( " " );
_app.highlightUniform->set( 0u );
}
// pick whenever the mouse moves.
// 当鼠标移动时,获取。也可以改为鼠标点击时获取
bool accept(const osgGA::GUIEventAdapter& ea, const osgGA::GUIActionAdapter& aa)
{
// return ea.getEventType() == ea.MOVE;// 移动时获取,移动到空白区域,则取消选中和高亮。
return ea.getEventType() == ea.RELEASE;// 点击时获取,再点击其他地方,则取消选中和高亮。
}
};
//-----------------------------------------------------------------------
// Shaders that will highlight the currently "picked" feature.
// 顶点着色器和片段着色器
const char* highlightVert =
"#version " GLSL_VERSION_STR "\n"
"uniform uint objectid_to_highlight; \n"
"uint oe_index_objectid; // Stage global containing object id \n"
"flat out int selected; \n"
"void checkForHighlight(inout vec4 vertex) \n"
"{ \n"
" selected = (objectid_to_highlight > 1u && objectid_to_highlight == oe_index_objectid) ? 1 : 0; \n"
"} \n";
const char* highlightFrag =
"#version " GLSL_VERSION_STR "\n"
"flat in int selected; \n"
"void highlightFragment(inout vec4 color) \n"
"{ \n"
" if ( selected == 1 ) \n"
" color.rgb = mix(color.rgb, clamp(vec3(0.5,2.0,2.0)*(1.0-color.rgb), 0.0, 1.0), 0.5); \n"
"} \n";
// 安装高亮器到app
void installHighlighter(App& app)
{
osg::StateSet* stateSet = app.mapNode->getOrCreateStateSet();
// 索引geoemtry时要使用的顶点属性绑定位置。
int attrLocation = Registry::objectIndex()->getObjectIDAttribLocation();
// This shader program will highlight the selected object.
// 着色器程序会使选中的object高亮
VirtualProgram* vp = VirtualProgram::getOrCreate(stateSet);
vp->setFunction( "checkForHighlight", highlightVert, ShaderComp::LOCATION_VERTEX_CLIP );
vp->setFunction( "highlightFragment", highlightFrag, ShaderComp::LOCATION_FRAGMENT_COLORING );
// Since we're accessing object IDs, we need to load the indexing shader as well:
// 由于我们正在访问对象ID,因此还需要加载索引着色器:
Registry::objectIndex()->loadShaders( vp );
// A uniform that will tell the shader which object to highlight:
// 通过uniform变量,告诉着色器哪个对象要被高亮
app.highlightUniform = new osg::Uniform("objectid_to_highlight", 0u);
stateSet->addUniform(app.highlightUniform );
}
//------------------------------------------------------------------------
// Configures a window that lets you see what the RTT camera sees.
// 配置一个窗口,使您可以看到RTT相机所看到的内容。
void
setupRTTView(osgViewer::View* view, osg::Texture* rttTex)
{
view->setCameraManipulator(0L);
view->getCamera()->setName( "osgearth_pick RTT view" );
view->getCamera()->setViewport(0,0,256,256);
view->getCamera()->setClearColor(osg::Vec4(1,1,1,1));
view->getCamera()->setProjectionMatrixAsOrtho2D(-.5,.5,-.5,.5);// 二维正投影
view->getCamera()->setViewMatrixAsLookAt(osg::Vec3d(0,-1,0), osg::Vec3d(0,0,0), osg::Vec3d(0,0,1));
view->getCamera()->setProjectionResizePolicy(osg::Camera::FIXED);// 固定
// v 和 t,都是通过绘制2个三角形拼接为正方形
osg::Vec3Array* v = new osg::Vec3Array(6);
(*v)[0].set(-.5,0,-.5); (*v)[1].set(.5,0,-.5); (*v)[2].set(.5,0,.5); (*v)[3].set((*v)[2]); (*v)[4].set(-.5,0,.5);(*v)[5].set((*v)[0]);
osg::Vec2Array* t = new osg::Vec2Array(6);
(*t)[0].set(0,0); (*t)[1].set(1,0); (*t)[2].set(1,1); (*t)[3].set((*t)[2]); (*t)[4].set(0,1); (*t)[5].set((*t)[0]);
osg::Geometry* g = new osg::Geometry();
g->setUseVertexBufferObjects(true);
g->setUseDisplayList(false);
g->setVertexArray( v ); // 几何顶点坐标
g->setTexCoordArray( 0, t );// 纹理坐标
g->addPrimitiveSet( new osg::DrawArrays(GL_TRIANGLES, 0, 6) );// 通过绘制三角形方式绘制
osg::Geode* geode = new osg::Geode();
geode->addDrawable( g );
osg::StateSet* stateSet = geode->getOrCreateStateSet();
stateSet->setDataVariance(osg::Object::DYNAMIC);// 动态的
stateSet->setTextureAttributeAndModes(0, rttTex, 1);
rttTex->setUnRefImageDataAfterApply( false );
rttTex->setResizeNonPowerOfTwoHint(false);
GLUtils::setLighting(stateSet, 0);
stateSet->setMode(GL_CULL_FACE, 0);
stateSet->setAttributeAndModes(new osg::BlendFunc(GL_ONE, GL_ZERO), 1);
const char* fs =
"#version " GLSL_VERSION_STR "\n"
"void swap(inout vec4 c) { c.rgba = c==vec4(0)? vec4(1) : vec4(vec3((c.r+c.g+c.b+c.a)/4.0),1); }\n";
osgEarth::Registry::shaderGenerator().run(geode);
VirtualProgram::getOrCreate(geode->getOrCreateStateSet())->setFunction("swap", fs, ShaderComp::LOCATION_FRAGMENT_COLORING);
view->setSceneData( geode );
}
void startPicker(App& app)
{
// Note! Must stop and restart threading when removing the picker
// because it changes the OSG View/Slave configuration.
// 当移动拾取器时,必须停止并重置线程。
app.viewer.stopThreading();
// 创建rtt拾取器
app.picker = new RTTPicker();
app.mainView->addEventHandler(app.picker);
// add the graph that will be picked.
// 添加将要被拾取的mapNode
app.picker->addChild(app.mapNode);
// install a callback that controls the picker and listens for hits.
// 安装回调方法,它控制拾取器并侦听命中。
app.picker->setDefaultCallback(new MyPickCallback(app));
// Make a view that lets us see what the picker sees.
// 创建视景器,让我们看到拾取器看到了什么。
if (app.rttView == NULL)
{
app.rttView = new osgViewer::View();
app.rttView->getCamera()->setGraphicsContext(app.mainView->getCamera()->getGraphicsContext());
app.rttView->getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
app.viewer.addView(app.rttView); // 子视景器加入到主视景器中
}
setupRTTView(app.rttView, app.picker->getOrCreateTexture(app.mainView));
app.rttView->getCamera()->setNodeMask(~0);
app.viewer.startThreading();// 开启主视景器线程
}
// 停止拾取
void stopPicker(App& app)
{
// Note! Must stop and restart threading when removing the picker
// because it changes the OSG View/Slave configuration.
app.viewer.stopThreading();
//app.viewer.removeView(app.rttView);
app.rttView->getCamera()->setNodeMask(0);
app.mainView->removeEventHandler(app.picker);
app.picker = 0L;// 停止拾取的状态
app.viewer.startThreading();
}
struct TogglePicker : public ui::ControlEventHandler
{
App& _app;
TogglePicker(App& app) : _app(app) { }
// 点击拾取时,需要判断是否为拾取状态。处于停止状态,则开启;反之,则停止。
void onClick(Control* button)
{
if (_app.picker == 0L)// 当前处于停止拾取的状态
startPicker(_app);
else
stopPicker(_app);
}
};
//-----------------------------------------------------------------------
int
usage(const char* name)
{
OE_NOTICE
<< "\nUsage: " << name << " file.earth" << std::endl
<< MapNodeHelper().usage() << std::endl;
return 0;
}
int
main(int argc, char** argv)
{
osg::ArgumentParser arguments(&argc,argv);
if ( arguments.read("--help") )
return usage(argv[0]);
App app(arguments);
// 创建主视景器
app.mainView = new osgViewer::View();
app.mainView->setUpViewInWindow(30, 30, 1024, 1024, 0);
app.mainView->getCamera()->setSmallFeatureCullingPixelSize(-1.0f);
app.viewer.addView(app.mainView);
app.mainView->getDatabasePager()->setUnrefImageDataAfterApplyPolicy( false, false );
app.mainView->setCameraManipulator( new EarthManipulator() );
// Made some UI components: 制作一些ui控件
ui::VBox* uiContainer = new ui::VBox();
uiContainer->setAlign( ui::Control::ALIGN_LEFT, ui::Control::ALIGN_TOP );
uiContainer->setAbsorbEvents( true );
uiContainer->setBackColor(0,0,0,0.8);
uiContainer->addControl( new ui::LabelControl("RTT Picker Test", osg::Vec4(1,1,0,1)) );
uiContainer->addControl( new ui::ButtonControl("Toggle picker", new TogglePicker(app)) );// 控制是否允许拾取
app.fidLabel = new ui::LabelControl("---");
uiContainer->addControl( app.fidLabel );
app.nameLabel = uiContainer->addControl( new ui::LabelControl( "---" ) );
// Load up the earth file. 加载earth文件
osg::Node* node = MapNodeHelper().load( arguments, &app.viewer, uiContainer );
if ( node )
{
app.mainView->setSceneData( node );
app.mapNode = MapNode::get(node);
// start with a picker running 开始拾取
startPicker(app);
// Highlight features as we pick'em.
installHighlighter(app); // 高亮
app.mainView->getCamera()->setName( "Main view" );
return app.viewer.run();
}
else
{
return usage(argv[0]);
}
}