这节课我们将创建一个以正弦波方式飘动的旗帜。本课所用到的知识在前面的课程中都有讲解,并没有什么新的内容
首先创建我们的场景,关于旗帜的顶点坐标在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; };
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(); }