用OpenSceneGraph实现的NeHe OpenGL教程 - 第十一课

  • 简介

这节课我们将创建一个以正弦波方式飘动的旗帜。本课所用到的知识在前面的课程中都有讲解,并没有什么新的内容


  • 实现

首先创建我们的场景,关于旗帜的顶点坐标在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,代码中已经设置。

最后编译运行程序,一个飘动的旗帜出现了

用OpenSceneGraph实现的NeHe OpenGL教程 - 第十一课_第1张图片

附:本课源码(源码中可能存在错误和不足,仅供参考)

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





你可能感兴趣的:(用OpenSceneGraph实现的NeHe OpenGL教程 - 第十一课)