NeHe教程在这节课中向我们介绍了如何读取显卡支持的OpenGL的扩展,如何使用Targa(TGA)图像文件作为纹理,以及如何利用OpenGL的剪裁区域来滚动屏幕文字。osg支持tga格式的文件读取,因此不需要像NeHe课程那样解析TGA格式,有兴趣的读者可以查看osgPlugins库中的ReaderWriterTGA.cpp文件了解osg是如何读取TGA图片格式的。
用纹理贴图来创建字体在第十七课中已经使用过了,过程相对比较繁琐,本课的主要目的是学习如何查看OpenGL的扩展,为了简便,本课使用osgText来显示字体。
本课中NeHe使用的是坐标原点在左上角的坐标系统,但是在osg中设置同样的投影坐标系会导致文字反转,在osg论坛中对此也有描述:osgText::Text and screen
按照帖子中修改之后发现字体消失,希望知道的读者指点一下。为此本课还是采用坐标原点在左下角的坐标系统,定义了TX和TY宏以便于使用NeHe中的位置数据
#define TX(x) (x) #define TY(y) (480-(y))首先定义创建字体的函数:(字体位置和颜色以及文字内容)
osg::Group* createText(int xPos, int yPos, osg::Vec4 colors, const char *contents) { osgText::Text *text = new osgText::Text; text->setText(contents); text->setFont("Fonts/Arial.ttf"); text->setCharacterSize(15.0); text->setColor(colors); osg::Geode *textGeode = new osg::Geode; textGeode->addDrawable(text); osg::MatrixTransform *mt = new osg::MatrixTransform; mt->setMatrix(osg::Matrix::translate(xPos, yPos, 0.0)); mt->addChild(textGeode); return mt; }之后创建边框线:
osg::Group * createDisplayBorder()
为了获取显卡的扩展,需要我们在GraphicContext创建好之后获取,因此在Viewer视景器Realize的时候来获取是一个不错的时机。本课通过设置Realize的一个operation来获取扩展信息。Operation是一个自定义的操作,在osg中可以在某个过程完成之后定义一些操作。只需要重载Operation中的()操作符即可实现自定义操作:
在osg中有一个类用来获取扩展的信息:osg::GL2Extensions可以查看当前显卡支持的各种扩展以及支持的OpenGL版本,但是在它里面未找到显卡供应商和显卡型号的接口,我们可以用OpenGL的glGetString来获取。
virtual void operator () (osg::GraphicsContext* gc) { if (_isInitialized) { return; } OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex); unsigned int contextID = gc->getState()->getContextID(); osg::GL2Extensions* gl2ext = osg::GL2Extensions::Get(contextID,true); if( gl2ext ) { char *render = (char*)glGetString(GL_RENDERER); char *vendor = (char*)glGetString(GL_VENDOR); char *version = (char*)glGetString(GL_VERSION); ... } ... }接着我们需要定义裁剪范围,在osg中osg::Scissor封装了OpenGL中glScissor的功能,它是一个StateAttribute,通过给节点设置StateAttribute进一步控制节点在视口
中的显示范围:
osg::Group *scissorGroup = new osg::Group; g_scissorGroup = scissorGroup; osg::Scissor *scissor = new osg::Scissor; scissor->setScissor(1, TY(417), g_width-2 , 289); g_scissorGroup->getOrCreateStateSet()->setAttributeAndModes(scissor);
if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Down) { if((scroll<32*(maxtokens-9))) { scroll += 2; for (unsigned i = 0, j = 0; i < g_scissorGroup->getNumChildren(); i +=2) { osg::MatrixTransform *mt = dynamic_cast<osg::MatrixTransform*>(g_scissorGroup->getChild(i)); if (!mt) return false; //扩展的序号:如1 2 3 等 mt->setMatrix(osg::Matrix::translate(TX(0), TY(115+(j*32)-scroll), 0.0)); osg::MatrixTransform *mt2 = dynamic_cast<osg::MatrixTransform*>(g_scissorGroup->getChild(i+1)); if (!mt2) return false; //扩展的内容 如GL_ARB_clear_buffer_object等 mt2->setMatrix(osg::Matrix::translate(TX(50), TY(115+(j*32)-scroll), 0.0)); ++j; } } }
附:本课源码(源码中可能存在错误和不足,仅供参考)
#include "../osgNeHe.h" #include <QtCore/QTimer> #include <QtGui/QApplication> #include <QtGui/QVBoxLayout> #include <osgViewer/Viewer> #include <osgDB/ReadFile> #include <osgQt/GraphicsWindowQt> #include <osg/MatrixTransform> #include <osg/Geode> #include <osgText/Text> #include <osg/Scissor> #include <osg/GraphicsThread> #define TX(x) (x) #define TY(y) (480.0-(y)) char *glExtensionString = NULL; int scroll; int maxtokens; int g_height = 480; int g_width = 640; osg::Group *g_scissorGroup; ////////////////////////////////////////////////////////////////////////// //在指定位置显示文本 osg::Group* createText(int xPos, int yPos, osg::Vec4 colors, const char *contents) { osgText::Text *text = new osgText::Text; text->setText(contents); text->setFont("Fonts/Arial.ttf"); text->setCharacterSize(15.0); text->setColor(colors); osg::Geode *textGeode = new osg::Geode; textGeode->addDrawable(text); osg::MatrixTransform *mt = new osg::MatrixTransform; mt->setMatrix(osg::Matrix::translate(xPos, yPos, 0.0)); mt->addChild(textGeode); return mt; } ////////////////////////////////////////////////////////////////////////// osg::Group * createDisplayBorder() { // osg::Group *borderGroup = new osg::Group; osg::Geode* borderGeode1 = new osg::Geode; osg::Geometry *borderGeometry1 = new osg::Geometry; osg::Vec2Array *verticesArray1 = new osg::Vec2Array; verticesArray1->push_back(osg::Vec2(639,TY(417))); verticesArray1->push_back(osg::Vec2( 0,TY(417))); verticesArray1->push_back(osg::Vec2( 0,TY(480))); verticesArray1->push_back(osg::Vec2(639,TY(480))); verticesArray1->push_back(osg::Vec2(639,TY(128))); borderGeometry1->setVertexArray(verticesArray1); borderGeometry1->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, 5)); osg::Vec3Array *colorsArray1 = new osg::Vec3Array; colorsArray1->push_back(osg::Vec3(1.0f,1.0f,1.0f)); borderGeometry1->setColorArray(colorsArray1, osg::Array::BIND_OVERALL); borderGeode1->addDrawable(borderGeometry1); borderGroup->addChild(borderGeode1); osg::Geode* borderGeode2 = new osg::Geode; osg::Geometry *borderGeometry2 = new osg::Geometry; osg::Vec2Array *verticesArray2 = new osg::Vec2Array; verticesArray2->push_back(osg::Vec2( 0,TY(128))); verticesArray2->push_back(osg::Vec2(639,TY(128))); verticesArray2->push_back(osg::Vec2(639, TY(1))); verticesArray2->push_back(osg::Vec2( 0, TY(1))); verticesArray2->push_back(osg::Vec2( 0,TY(417))); borderGeometry2->setVertexArray(verticesArray2); borderGeometry2->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, 5)); osg::Vec3Array *colorsArray2 = new osg::Vec3Array; colorsArray2->push_back(osg::Vec3(1.0f,1.0f,1.0f)); borderGeometry2->setColorArray(colorsArray2, osg::Array::BIND_OVERALL); borderGeode2->addDrawable(borderGeometry2); borderGroup->addChild(borderGeode2); return borderGroup; } ////////////////////////////////////////////////////////////////////////// //获取OpenGL扩展 class GetOpenGLExtensionsOperation: public osg::GraphicsOperation { public: GetOpenGLExtensionsOperation(const std::string& name, bool keep, osg::Group *root) : osg::GraphicsOperation("TestSupportOperation",false), _isInitialized(false), _root(root){ } virtual void operator () (osg::GraphicsContext* gc) { if (_isInitialized) { return; } OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex); unsigned int contextID = gc->getState()->getContextID(); osg::GL2Extensions* gl2ext = osg::GL2Extensions::Get(contextID,true); if( gl2ext ) { char *render = (char*)glGetString(GL_RENDERER); char *vendor = (char*)glGetString(GL_VENDOR); char *version = (char*)glGetString(GL_VERSION); _root->addChild(createText(TX(200), TY(16), osg::Vec4(1.0f,0.7f,0.4f, 1.0f), render)); _root->addChild(createText(TX(200), TY(48), osg::Vec4(1.0f,0.7f,0.4f, 1.0f), vendor)); _root->addChild(createText(TX(200), TY(80), osg::Vec4(1.0f,0.7f,0.4f, 1.0f), version)); glExtensionString = (char *)malloc(strlen((char *)glGetString(GL_EXTENSIONS))+1); strcpy (glExtensionString,(char *)glGetString(GL_EXTENSIONS)); osg::Group *scissorGroup = new osg::Group; g_scissorGroup = scissorGroup; osg::Scissor *scissor = new osg::Scissor; scissor->setScissor(1, TY(417), g_width-2 , 289); g_scissorGroup->getOrCreateStateSet()->setAttributeAndModes(scissor); _root->addChild(g_scissorGroup); char *token; int cnt = 0; token=strtok(glExtensionString," "); while(token!=NULL) { cnt++; if (cnt>maxtokens) { maxtokens=cnt; } std::stringstream os; std::string str; os.precision(2); os << std::fixed << cnt; str = os.str(); scissorGroup->addChild(createText(TX(0), TY(115+(cnt*32)-scroll), osg::Vec4(0.5f, 1.0f, 0.5f, 1.0f), str.c_str())); scissorGroup->addChild(createText(TX(50), TY(115+(cnt*32)-scroll), osg::Vec4(1.0f, 1.0f, 0.5f, 1.0f), token)); token=strtok(NULL," "); } } _isInitialized = true; } OpenThreads::Mutex _mutex; bool _isInitialized; osg::Group *_root; }; ////////////////////////////////////////////////////////////////////////// class SceneEventHandler : public osgGA::GUIEventHandler { public: SceneEventHandler(){} virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) { osgViewer::Viewer *viewer = dynamic_cast<osgViewer::Viewer*>(&aa); if (!viewer) return false; osg::Group *root = dynamic_cast<osg::Group*>(viewer->getSceneData()); if (!root) return false; if (ea.getHandled()) return false; switch(ea.getEventType()) { case (osgGA::GUIEventAdapter::KEYDOWN): { if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Down) { if((scroll<32*(maxtokens-9))) { scroll += 2; for (unsigned i = 0, j = 0; i < g_scissorGroup->getNumChildren(); i +=2) { osg::MatrixTransform *mt = dynamic_cast<osg::MatrixTransform*>(g_scissorGroup->getChild(i)); if (!mt) return false; //扩展的序号:如1 2 3 等 mt->setMatrix(osg::Matrix::translate(TX(0), TY(115+(j*32)-scroll), 0.0)); osg::MatrixTransform *mt2 = dynamic_cast<osg::MatrixTransform*>(g_scissorGroup->getChild(i+1)); if (!mt2) return false; //扩展的内容 如GL_ARB_clear_buffer_object等 mt2->setMatrix(osg::Matrix::translate(TX(50), TY(115+(j*32)-scroll), 0.0)); ++j; } } } if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Up) { if (scroll >= 0) { scroll -= 2; for (unsigned i = 0, j = 0; i < g_scissorGroup->getNumChildren(); i +=2) { osg::MatrixTransform *mt = dynamic_cast<osg::MatrixTransform*>(g_scissorGroup->getChild(i)); if (!mt) return false; mt->setMatrix(osg::Matrix::translate(TX(0), TY(115+(j*32)-scroll), 0.0)); osg::MatrixTransform *mt2 = dynamic_cast<osg::MatrixTransform*>(g_scissorGroup->getChild(i+1)); if (!mt2) return false; mt2->setMatrix(osg::Matrix::translate(TX(50), TY(115+(j*32)-scroll), 0.0)); ++j; } } } } case (osgGA::GUIEventAdapter::RESIZE): { g_height = ea.getWindowHeight(); g_width = ea.getWindowWidth(); } default: break; } return false; } }; ////////////////////////////////////////////////////////////////////////// class ViewerWidget : public QWidget, public osgViewer::Viewer { public: ViewerWidget(osg::Node *scene = NULL) { QWidget* renderWidget = getRenderWidget( createGraphicsWindow(0,0,640,480), scene); QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(renderWidget); layout->setContentsMargins(0, 0, 0, 1); setLayout( layout ); connect( &_timer, SIGNAL(timeout()), this, SLOT(update()) ); _timer.start( 10 ); } QWidget* getRenderWidget( osgQt::GraphicsWindowQt* gw, osg::Node* scene ) { osg::Camera* camera = this->getCamera(); camera->setGraphicsContext( gw ); const osg::GraphicsContext::Traits* traits = gw->getTraits(); camera->setClearColor( osg::Vec4(0.0, 0.0, 0.0, 1.0) ); camera->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) ); camera->setProjectionMatrixAsOrtho(0.0, 640.0, 0.0, 480.0, -1.0, 1.0); camera->setViewMatrixAsLookAt(osg::Vec3d(0, 0, 1), osg::Vec3d(0, 0, 0), osg::Vec3d(0, 1, 0)); setRealizeOperation(new GetOpenGLExtensionsOperation("OpenGLExtension", false, scene->asGroup())); addEventHandler(new SceneEventHandler()); this->setSceneData( scene ); return gw->getGLWidget(); } osgQt::GraphicsWindowQt* createGraphicsWindow( int x, int y, int w, int h, const std::string& name="", bool windowDecoration=false ) { osg::DisplaySettings* ds = osg::DisplaySettings::instance().get(); osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits; traits->windowName = name; traits->windowDecoration = windowDecoration; traits->x = x; traits->y = y; traits->width = w; traits->height = h; traits->doubleBuffer = true; traits->alpha = ds->getMinimumNumAlphaBits(); traits->stencil = ds->getMinimumNumStencilBits(); traits->sampleBuffers = ds->getMultiSamples(); traits->samples = ds->getNumMultiSamples(); return new osgQt::GraphicsWindowQt(traits.get()); } virtual void paintEvent( QPaintEvent* event ) { frame(); } protected: QTimer _timer; }; osg::Node* buildScene() { osg::Group *root = new osg::Group; root->addChild(createText(TX(60), TY(16), osg::Vec4(1.0f,0.5f,0.5f, 1.0f), "Renderer")); root->addChild(createText(TX(80), TY(48), osg::Vec4(1.0f,0.5f,0.5f, 1.0f), "Vendor")); root->addChild(createText(TX(70), TY(80), osg::Vec4(1.0f,0.5f,0.5f, 1.0f), "Version")); root->addChild(createText(TX(192), TY(432), osg::Vec4(0.5f,0.5f,1.0f, 1.0f), "NeHe Productions")); root->addChild(createDisplayBorder()); return root; } int main( int argc, char** argv ) { QApplication app(argc, argv); ViewerWidget* viewWidget = new ViewerWidget(buildScene()); viewWidget->setGeometry( 100, 100, 640, 480 ); viewWidget->show(); return app.exec(); }