-
简介
这节课我们将创建一个以正弦波方式飘动的旗帜。本课所用到的知识在前面的课程中都有讲解,并没有什么新的内容
-
实现
首先创建我们的场景,关于旗帜的顶点坐标在NeHe教程中已经有非常详细的介绍,本文就不在赘述了。
float points[45][45][3];
for(int x=0; x<45; x++)
{
for(int y=0; y<45; y++)
{
points[x][y][0]=float((x/5.0f)-4.5f);
points[x][y][1]=float((y/5.0f)-4.5f);
points[x][y][2]=float(sin((((x/5.0f)*40.0f)/360.0f)*3.141592654*2.0f));
}
}
osg::Group *root = new osg::Group;
osg::MatrixTransform *zoomMT = new osg::MatrixTransform;
zoomMT->setMatrix(osg::Matrix::translate(0.0, 0.0, -12.0));
osg::MatrixTransform *xRotMT = new osg::MatrixTransform;
xRotMT->setUpdateCallback(new XRotCallback);
osg::MatrixTransform *yRotMT = new osg::MatrixTransform;
yRotMT->setUpdateCallback(new YRotCallback);
osg::MatrixTransform *zRotMT = new osg::MatrixTransform;
zRotMT->setUpdateCallback(new ZRotCallback);
osg::Geometry *flagGeometry = new osg::Geometry;
flagGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
osg::PolygonMode *polygonMode = new osg::PolygonMode(osg::PolygonMode::FRONT, osg::PolygonMode::LINE);
flagGeometry->getOrCreateStateSet()->setAttribute(polygonMode);
osg::Vec3Array *flagVertexArray = new osg::Vec3Array;
osg::Vec2Array *textureArray = new osg::Vec2Array;
for(int x = 0; x < 44; x++ )
{
for(int y = 0; y < 44; y++ )
{
double float_x, float_y, float_xb, float_yb;
float_x = float(x)/44.0f;
float_y = float(y)/44.0f;
float_xb = float(x+1)/44.0f;
float_yb = float(y+1)/44.0f;
textureArray->push_back(osg::Vec2( float_x, float_y));
textureArray->push_back(osg::Vec2( float_x, float_yb ));
textureArray->push_back(osg::Vec2( float_xb, float_yb ));
textureArray->push_back(osg::Vec2( float_xb, float_y ));
flagVertexArray->push_back(osg::Vec3( points[x][y][0], points[x][y][1], points[x][y][2]));
flagVertexArray->push_back(osg::Vec3( points[x][y+1][0], points[x][y+1][1], points[x][y+1][2]));
flagVertexArray->push_back(osg::Vec3( points[x+1][y+1][0], points[x+1][y+1][1], points[x+1][y+1][2]));
flagVertexArray->push_back(osg::Vec3( points[x+1][y][0], points[x+1][y][1], points[x+1][y][2]));
}
}
osg::Image *textureImage = osgDB::readImageFile("Data/Tim.bmp");
osg::Texture2D *texture2D = new osg::Texture2D;
texture2D->setImage(textureImage);
texture2D->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
texture2D->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
flagGeometry->setTexCoordArray(0, textureArray);
flagGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture2D);
flagGeometry->setVertexArray(flagVertexArray);
flagGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 7744));
flagGeometry->setUpdateCallback(new GeometryCallback);
flagGeometry->setUseDisplayList(false);
flagGeometry->setUseVertexBufferObjects(true);
osg::Geode *flagGeode = new osg::Geode;
flagGeode->addDrawable(flagGeometry);
root->addChild(zoomMT);
zoomMT->addChild(xRotMT);
xRotMT->addChild(yRotMT);
yRotMT->addChild(zRotMT);
zRotMT->addChild(flagGeode);
return root;
为了实现OpenGL中glPolygonMode的效果(实现旗帜的一面用线框显示), 代码中使用了osg::PolygonMode来完成设置
为了实现动态的旋转效果,和前面课程一样定义了UpdateCallback
class XRotCallback : public osg::NodeCallback
{
public:
XRotCallback() : _angle(0){}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (dynamic_cast<osg::MatrixTransform*>(node))
{
osg::MatrixTransform *rot = dynamic_cast<osg::MatrixTransform*>(node);
rot->setMatrix(osg::Matrix::rotate(_angle, osg::X_AXIS));
_angle += osg::DegreesToRadians(0.3);
}
traverse(node, nv);
}
double _angle;
};
为了实现旗帜的飘动效果,与NeHe教程中类似,我们需要时刻去修改顶点数据,通过osg::Drawable的UpdateCallback可以做到,它与osg::Node的UpdateCallback类似,所不同的是需要重载的函数为update函数而不是Node中的operator()函数,在代码中我使用了NeHe的points数组来实现(之前直接修改vertexArray总是失败,希望读者可以给出直接修改vertexArray方式的代码)
class GeometryCallback : public osg::Drawable::UpdateCallback
{
public:
virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable)
{
osg::Geometry *geometry = dynamic_cast<osg::Geometry*>(drawable);
if (!geometry)
{
return;
}
osg::Vec3Array *vertexArray = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
if (vertexArray)
{
float points[45][45][3];
int i = 0;
for(int x = 0; x < 44; x++ )
{
for(int y = 0; y < 44; y++ )
{
points[x][y][0] = vertexArray->at(i).x();
points[x][y][1] = vertexArray->at(i).y();
points[x][y][2] = vertexArray->at(i).z();
++i;
points[x][y+1][0] = vertexArray->at(i).x();
points[x][y+1][1] = vertexArray->at(i).y();
points[x][y+1][2] = vertexArray->at(i).z();
++i;
points[x+1][y+1][0] = vertexArray->at(i).x();
points[x+1][y+1][1] = vertexArray->at(i).y();
points[x+1][y+1][2] = vertexArray->at(i).z();
++i;
points[x+1][y][0] = vertexArray->at(i).x();
points[x+1][y][1] = vertexArray->at(i).y();
points[x+1][y][2] = vertexArray->at(i).z();
++i;
}
}
static int wiggle_count = 0;
float hold;
if( wiggle_count == 2 )
{
for(int y = 0; y < 45; y++ )
{
hold=points[0][y][2];
for(int x = 0; x < 44; x++)
{
points[x][y][2] = points[x+1][y][2];
}
points[44][y][2]=hold;
}
wiggle_count = 0;
}
wiggle_count++;
vertexArray->clear();
for(int x = 0; x < 44; x++ )
{
for(int y = 0; y < 44; y++ )
{
vertexArray->push_back(osg::Vec3( points[x][y][0], points[x][y][1], points[x][y][2]));
vertexArray->push_back(osg::Vec3( points[x][y+1][0], points[x][y+1][1], points[x][y+1][2]));
vertexArray->push_back(osg::Vec3( points[x+1][y+1][0], points[x+1][y+1][1], points[x+1][y+1][2]));
vertexArray->push_back(osg::Vec3( points[x+1][y][0], points[x+1][y][1], points[x+1][y][2]));
}
}
vertexArray->dirty();
}
}
};
另外一个需要注意的地方:需要将flagGeomerty的加载方式修改为VBO的方式,不要使用DisplayList,代码中已经设置。
最后编译运行程序,一个飘动的旗帜出现了
附:本课源码(源码中可能存在错误和不足,仅供参考)
#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 <osgDB/ReadFile>
#include <osg/Texture2D>
#include <osg/PolygonMode>
//
//RotCallback
class XRotCallback : public osg::NodeCallback
{
public:
XRotCallback() : _angle(0){}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (dynamic_cast<osg::MatrixTransform*>(node))
{
osg::MatrixTransform *rot = dynamic_cast<osg::MatrixTransform*>(node);
rot->setMatrix(osg::Matrix::rotate(_angle, osg::X_AXIS));
_angle += osg::DegreesToRadians(0.3);
}
traverse(node, nv);
}
double _angle;
};
class YRotCallback : public osg::NodeCallback
{
public:
YRotCallback() : _angle(0){}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (dynamic_cast<osg::MatrixTransform*>(node))
{
osg::MatrixTransform *rot = dynamic_cast<osg::MatrixTransform*>(node);
rot->setMatrix(osg::Matrix::rotate(_angle, osg::Y_AXIS));
_angle += osg::DegreesToRadians(0.2);
}
traverse(node, nv);
}
double _angle;
};
class ZRotCallback : public osg::NodeCallback
{
public:
ZRotCallback() : _angle(0){}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
if (dynamic_cast<osg::MatrixTransform*>(node))
{
osg::MatrixTransform *rot = dynamic_cast<osg::MatrixTransform*>(node);
rot->setMatrix(osg::Matrix::rotate(_angle, osg::Z_AXIS));
_angle += osg::DegreesToRadians(0.4);
}
traverse(node, nv);
}
double _angle;
};
//End
//
//
//变换Geometry的顶点
class GeometryCallback : public osg::Drawable::UpdateCallback
{
public:
virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable)
{
osg::Geometry *geometry = dynamic_cast<osg::Geometry*>(drawable);
if (!geometry)
{
return;
}
osg::Vec3Array *vertexArray = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
if (vertexArray)
{
float points[45][45][3];
int i = 0;
for(int x = 0; x < 44; x++ )
{
for(int y = 0; y < 44; y++ )
{
points[x][y][0] = vertexArray->at(i).x();
points[x][y][1] = vertexArray->at(i).y();
points[x][y][2] = vertexArray->at(i).z();
++i;
points[x][y+1][0] = vertexArray->at(i).x();
points[x][y+1][1] = vertexArray->at(i).y();
points[x][y+1][2] = vertexArray->at(i).z();
++i;
points[x+1][y+1][0] = vertexArray->at(i).x();
points[x+1][y+1][1] = vertexArray->at(i).y();
points[x+1][y+1][2] = vertexArray->at(i).z();
++i;
points[x+1][y][0] = vertexArray->at(i).x();
points[x+1][y][1] = vertexArray->at(i).y();
points[x+1][y][2] = vertexArray->at(i).z();
++i;
}
}
static int wiggle_count = 0;
float hold;
if( wiggle_count == 2 )
{
for(int y = 0; y < 45; y++ )
{
hold=points[0][y][2];
for(int x = 0; x < 44; x++)
{
points[x][y][2] = points[x+1][y][2];
}
points[44][y][2]=hold;
}
wiggle_count = 0;
}
wiggle_count++;
vertexArray->clear();
for(int x = 0; x < 44; x++ )
{
for(int y = 0; y < 44; y++ )
{
vertexArray->push_back(osg::Vec3( points[x][y][0], points[x][y][1], points[x][y][2]));
vertexArray->push_back(osg::Vec3( points[x][y+1][0], points[x][y+1][1], points[x][y+1][2]));
vertexArray->push_back(osg::Vec3( points[x+1][y+1][0], points[x+1][y+1][1], points[x+1][y+1][2]));
vertexArray->push_back(osg::Vec3( points[x+1][y][0], points[x+1][y][1], points[x+1][y][2]));
}
}
vertexArray->dirty();
}
}
};
//
class ViewerWidget : public QWidget, public osgViewer::Viewer
{
public:
ViewerWidget(osg::Node *scene = NULL)
{
QWidget* renderWidget = getRenderWidget( createGraphicsWindow(0,0,100,100), 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->setProjectionMatrixAsPerspective(45.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 0.1f, 100.0f );
camera->setViewMatrixAsLookAt(osg::Vec3d(0, 0, 1), osg::Vec3d(0, 0, 0), osg::Vec3d(0, 1, 0));
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()
{
float points[45][45][3];
for(int x=0; x<45; x++)
{
for(int y=0; y<45; y++)
{
points[x][y][0]=float((x/5.0f)-4.5f);
points[x][y][1]=float((y/5.0f)-4.5f);
points[x][y][2]=float(sin((((x/5.0f)*40.0f)/360.0f)*3.141592654*2.0f));
}
}
osg::Group *root = new osg::Group;
osg::MatrixTransform *zoomMT = new osg::MatrixTransform;
zoomMT->setMatrix(osg::Matrix::translate(0.0, 0.0, -12.0));
osg::MatrixTransform *xRotMT = new osg::MatrixTransform;
xRotMT->setUpdateCallback(new XRotCallback);
osg::MatrixTransform *yRotMT = new osg::MatrixTransform;
yRotMT->setUpdateCallback(new YRotCallback);
osg::MatrixTransform *zRotMT = new osg::MatrixTransform;
zRotMT->setUpdateCallback(new ZRotCallback);
osg::Geometry *flagGeometry = new osg::Geometry;
flagGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
osg::PolygonMode *polygonMode = new osg::PolygonMode(osg::PolygonMode::FRONT, osg::PolygonMode::LINE);
flagGeometry->getOrCreateStateSet()->setAttribute(polygonMode);
osg::Vec3Array *flagVertexArray = new osg::Vec3Array;
osg::Vec2Array *textureArray = new osg::Vec2Array;
for(int x = 0; x < 44; x++ )
{
for(int y = 0; y < 44; y++ )
{
double float_x, float_y, float_xb, float_yb;
float_x = float(x)/44.0f;
float_y = float(y)/44.0f;
float_xb = float(x+1)/44.0f;
float_yb = float(y+1)/44.0f;
textureArray->push_back(osg::Vec2( float_x, float_y));
textureArray->push_back(osg::Vec2( float_x, float_yb ));
textureArray->push_back(osg::Vec2( float_xb, float_yb ));
textureArray->push_back(osg::Vec2( float_xb, float_y ));
flagVertexArray->push_back(osg::Vec3( points[x][y][0], points[x][y][1], points[x][y][2]));
flagVertexArray->push_back(osg::Vec3( points[x][y+1][0], points[x][y+1][1], points[x][y+1][2]));
flagVertexArray->push_back(osg::Vec3( points[x+1][y+1][0], points[x+1][y+1][1], points[x+1][y+1][2]));
flagVertexArray->push_back(osg::Vec3( points[x+1][y][0], points[x+1][y][1], points[x+1][y][2]));
}
}
osg::Image *textureImage = osgDB::readImageFile("Data/Tim.bmp");
osg::Texture2D *texture2D = new osg::Texture2D;
texture2D->setImage(textureImage);
texture2D->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
texture2D->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
flagGeometry->setTexCoordArray(0, textureArray);
flagGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture2D);
flagGeometry->setVertexArray(flagVertexArray);
flagGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 7744));
flagGeometry->setUpdateCallback(new GeometryCallback);
flagGeometry->setUseDisplayList(false);
flagGeometry->setUseVertexBufferObjects(true);
osg::Geode *flagGeode = new osg::Geode;
flagGeode->addDrawable(flagGeometry);
root->addChild(zoomMT);
zoomMT->addChild(xRotMT);
xRotMT->addChild(yRotMT);
yRotMT->addChild(zRotMT);
zRotMT->addChild(flagGeode);
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();
}