-
简介
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();
}