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

  • 简介

这节课NeHe课程实现了在场景中进行碰撞检测以及简单的物理作用(碰撞之后的反弹),碰撞检测三维图形学中一个比较高级的主题,本课只涉及了简单的规则几何体之间的碰撞检测,更复杂的实例需要读者去更深入的学习。

  • 实现

本课中碰撞检测的算法包括线与平面的求交检测、线与圆柱体的求交、以及球体之间的碰撞检测,这些碰撞检测的原理可以参考NeHe中的介绍。

具体实现在如下几个函数中:

///////////////////////////线面相交////////////////////////////////
//////////////////////////////////////////////////////////////////////////
int TestIntersionPlane(const Plane& plane,const TVector& position,const TVector& direction, double& lamda, TVector& pNormal)
/////////////////////线与圆柱体相交///////////////////////////
//////////////////////////////////////////////////////////////////////////
int TestIntersionCylinder(const Cylinder& cylinder,const TVector& position,const TVector& direction, double& lamda, TVector& pNormal,TVector& newposition)
//////////////////////球体相交/////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
int FindBallCol(TVector& point, double& TimePoint, double Time2, int& BallNr1, int& BallNr2)
之后再每帧中计算了各球体与不同几何体之间的碰撞,具体代码在idle函数中

接下来创建本课中的几何体(包括墙面、地面、圆柱体等)

osg::Geode*	createWalls()
osg::Geode*	createFloor()
osg::Geode*	createCylinder()
osg::Geode*	createCylinder2()
osg::Geode*	createBalls(int i)
在ManipulatorSceneHandler中的Frame时间中不断地进行计算和判断每个球体的位置(调用idle)

		switch(ea.getEventType())
		{
		case (osgGA::GUIEventAdapter::FRAME):
			{
				idle();
				viewer->getCamera()->setViewMatrixAsLookAt(osg::Vec3(Pos.X(),Pos.Y(),Pos.Z()), osg::Vec3(Pos.X()+dir.X(),Pos.Y()+dir.Y(),Pos.Z()+dir.Z()), osg::Y_AXIS);
			}
最后把这些部分都添加到场景中:

在BuildScene中查看相应的代码

编译运行程序:

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

附:本课源码(源码中可能存在错误和不足,仅供参考)[其他头文件可以在NeHe课程中下载]

#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/Plane>
#include <osg/Texture2D>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/CullFace>

#include <osgGA/TrackballManipulator>
#include <osg/ShapeDrawable>

#include <osgGA/GUIEventHandler>
#include <osgGA/GUIEventAdapter>
#include <osg/AnimationPath>
#include <osg/BlendFunc>
#include <osg/Switch>
#include <osg/Material>

#include "Tray.h"
#include "Tvector.h"

//////////////////////////////////////////////////////////////////////////
GLfloat spec[]={1.0, 1.0 ,1.0 ,1.0};      //sets specular highlight of balls
GLfloat posl[]={0,400,0,1};               //position of ligth source
GLfloat amb[]={0.2f, 0.2f, 0.2f ,1.0f};   //global ambient
GLfloat amb2[]={0.3f, 0.3f, 0.3f ,1.0f};  //ambient of lightsource

TVector dir(0,0,-10);                     //initial direction of camera
TVector Pos(0,-50,1000);                  //initial position of camera
float camera_rotation=0;                  //holds rotation around the Y axis


TVector veloc(0.5,-0.1,0.5);              //initial velocity of balls
TVector accel(0,-0.05,0);                 //acceleration ie. gravity of balls

TVector ArrayVel[10];                     //holds velocity of balls
TVector ArrayPos[10];                     //position of balls
TVector OldPos[10];                       //old position of balls
int NrOfBalls;                            //sets the number of balls
double Time=0.6;                          //timestep of simulation
int hook_toball1=0, sounds=1;             //hook camera on ball, and sound on/off
//Plane structure
struct Plane{
	TVector _Position;
	TVector _Normal;
};
//Cylinder structure
struct Cylinder{                          
	TVector _Position;
	TVector _Axis;
	double _Radius;
};
//Explosion structure
struct Explosion{
	TVector _Position;
	float   _Alpha;
	float   _Scale;
};

Plane pl1,pl2,pl3,pl4,pl5;                //the 5 planes of the room
Cylinder cyl1,cyl2,cyl3;                  //the 2 cylinders of the room

GLuint texture[4], dlist;                 //stores texture objects and display list
Explosion ExplosionArray[20];             //holds max 20 explosions at once
//Perform Intersection tests with primitives

osg::Switch *g_SwitchNode;
//////////////////////////////////////////////////////////////////////////
///////////////////////////线面相交////////////////////////////////
//////////////////////////////////////////////////////////////////////////
int TestIntersionPlane(const Plane& plane,const TVector& position,const TVector& direction, double& lamda, TVector& pNormal)
{
	double DotProduct=direction.dot(plane._Normal);
	double l2;

	//determine if ray paralle to plane
	if ((DotProduct<ZERO)&&(DotProduct>-ZERO)) 
		return 0;

	l2=(plane._Normal.dot(plane._Position-position))/DotProduct;

	if (l2<-ZERO) 
		return 0;

	pNormal=plane._Normal;
	lamda=l2;
	return 1;
}


//////////////////////////////////////////////////////////////////////////
/////////////////////线与圆柱体相交///////////////////////////
//////////////////////////////////////////////////////////////////////////
int TestIntersionCylinder(const Cylinder& cylinder,const TVector& position,const TVector& direction, double& lamda, TVector& pNormal,TVector& newposition)
{
	TVector RC;
	double d;
	double t,s;
	TVector n,D,O;
	double ln;
	double in,out;


	TVector::subtract(position,cylinder._Position,RC);
	TVector::cross(direction,cylinder._Axis,n);

	ln=n.mag();

	if ( (ln<ZERO)&&(ln>-ZERO) ) return 0;

	n.unit();

	d= fabs( RC.dot(n) );

	if (d<=cylinder._Radius)
	{
		TVector::cross(RC,cylinder._Axis,O);
		t= - O.dot(n)/ln;
		TVector::cross(n,cylinder._Axis,O);
		O.unit();
		s= fabs( sqrt(cylinder._Radius*cylinder._Radius - d*d) / direction.dot(O) );

		in=t-s;
		out=t+s;

		if (in<-ZERO){
			if (out<-ZERO) return 0;
			else lamda=out;
		}
		else
			if (out<-ZERO) {
				lamda=in;
			}
			else
				if (in<out) lamda=in;
				else lamda=out;

				newposition=position+direction*lamda;
				TVector HB=newposition-cylinder._Position;
				pNormal=HB - cylinder._Axis*(HB.dot(cylinder._Axis));
				pNormal.unit();

				return 1;
	}

	return 0;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////球体相交/////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
int FindBallCol(TVector& point, double& TimePoint, double Time2, int& BallNr1, int& BallNr2)
{
	TVector RelativeV;
	TRay rays;
	double MyTime=0.0, Add=Time2/150.0, Timedummy=10000, Timedummy2=-1;
	TVector posi;

	//Test all balls against eachother in 150 small steps
	for (int i=0;i<NrOfBalls-1;i++)
	{
		for (int j=i+1;j<NrOfBalls;j++)
		{	
			RelativeV=ArrayVel[i]-ArrayVel[j];
			rays=TRay(OldPos[i],TVector::unit(RelativeV));
			MyTime=0.0;

			if ( (rays.dist(OldPos[j])) > 40) continue; 

			while (MyTime<Time2)
			{
				MyTime+=Add;
				posi=OldPos[i]+RelativeV*MyTime;
				if (posi.dist(OldPos[j])<=40) {
					point=posi;
					if (Timedummy>(MyTime-Add)) 
						Timedummy=MyTime-Add;
					BallNr1=i;
					BallNr2=j;
					break;
				}
			}
		}
	}

	if (Timedummy!=10000) { 
		TimePoint=Timedummy;
		return 1;
	}

	return 0;
}


void initVars()
{
	//create palnes
	pl1._Position=TVector(0,-300,0);
	pl1._Normal=TVector(0,1,0);
	pl2._Position=TVector(300,0,0);
	pl2._Normal=TVector(-1,0,0);
	pl3._Position=TVector(-300,0,0);
	pl3._Normal=TVector(1,0,0);
	pl4._Position=TVector(0,0,300);
	pl4._Normal=TVector(0,0,-1);
	pl5._Position=TVector(0,0,-300);
	pl5._Normal=TVector(0,0,1);


	//create cylinders
	cyl1._Position=TVector(0,0,0);
	cyl1._Axis=TVector(0,1,0);
	cyl1._Radius=60+20;
	cyl2._Position=TVector(200,-300,0);
	cyl2._Axis=TVector(0,0,1);
	cyl2._Radius=60+20;
	cyl3._Position=TVector(-200,0,0);
	cyl3._Axis=TVector(0,1,1);
	cyl3._Axis.unit();
	cyl3._Radius=30+20;

	//Set initial positions and velocities of balls
	//also initialize array which holds explosions
	NrOfBalls=10;
	ArrayVel[0]=veloc;
	ArrayPos[0]=TVector(199,180,10);
	ExplosionArray[0]._Alpha=0;
	ExplosionArray[0]._Scale=1;
	ArrayVel[1]=veloc;
	ArrayPos[1]=TVector(0,150,100);
	ExplosionArray[1]._Alpha=0;
	ExplosionArray[1]._Scale=1;
	ArrayVel[2]=veloc;
	ArrayPos[2]=TVector(-100,180,-100);
	ExplosionArray[2]._Alpha=0;
	ExplosionArray[2]._Scale=1;
	for (int i=3; i<10; i++)
	{
		ArrayVel[i]=veloc;
		ArrayPos[i]=TVector(-500+i*75, 300, -500+i*50);
		ExplosionArray[i]._Alpha=0;
		ExplosionArray[i]._Scale=1;
	}
	for (int i=10; i<20; i++)
	{
		ExplosionArray[i]._Alpha=0;
		ExplosionArray[i]._Scale=1;
	}
}


void idle()
{
	double rt,rt2,rt4,lamda=10000;
	TVector norm,uveloc;
	TVector normal,point,time;
	double RestTime,BallTime;
	TVector Pos2;
	int BallNr=0,dummy=0,BallColNr1,BallColNr2;
	TVector Nc;

	if (!hook_toball1)
	{
		camera_rotation+=0.1f;
		if (camera_rotation>360)
			camera_rotation=0;
	}

	RestTime=Time;
	lamda=1000;

	//Compute velocity for next timestep using Euler equations
	for (int j=0;j<NrOfBalls;j++)
		ArrayVel[j]+=accel*RestTime;

	//While timestep not over
	while (RestTime>ZERO)
	{
		lamda=10000;   //initialize to very large value

		//For all the balls find closest intersection between balls and planes/cylinders
		for (int i=0;i<NrOfBalls;i++)
		{
			//compute new position and distance
			OldPos[i]=ArrayPos[i];
			TVector::unit(ArrayVel[i],uveloc);
			ArrayPos[i]=ArrayPos[i]+ArrayVel[i]*RestTime;
			rt2=OldPos[i].dist(ArrayPos[i]);

			//Test if collision occured between ball and all 5 planes
			if (TestIntersionPlane(pl1,OldPos[i],uveloc,rt,norm))
			{  
				//Find intersection time
				rt4=rt*RestTime/rt2;

				//if smaller than the one already stored replace and in timestep
				if (rt4<=lamda)
				{ 
					if (rt4<=RestTime+ZERO)
						if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
						{
							normal=norm;
							point=OldPos[i]+uveloc*rt;
							lamda=rt4;
							BallNr=i;
						}
				}
			}

			if (TestIntersionPlane(pl2,OldPos[i],uveloc,rt,norm))
			{
				rt4=rt*RestTime/rt2;

				if (rt4<=lamda)
				{ 
					if (rt4<=RestTime+ZERO)
						if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
						{
							normal=norm;
							point=OldPos[i]+uveloc*rt;
							lamda=rt4;
							BallNr=i;
							dummy=1;
						}
				}

			}

			if (TestIntersionPlane(pl3,OldPos[i],uveloc,rt,norm))
			{
				rt4=rt*RestTime/rt2;

				if (rt4<=lamda)
				{ 
					if (rt4<=RestTime+ZERO)
						if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
						{
							normal=norm;
							point=OldPos[i]+uveloc*rt;
							lamda=rt4;
							BallNr=i;
						}
				}
			}

			if (TestIntersionPlane(pl4,OldPos[i],uveloc,rt,norm))
			{
				rt4=rt*RestTime/rt2;

				if (rt4<=lamda)
				{ 
					if (rt4<=RestTime+ZERO)
						if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
						{
							normal=norm;
							point=OldPos[i]+uveloc*rt;
							lamda=rt4;
							BallNr=i;
						}
				}
			}

			if (TestIntersionPlane(pl5,OldPos[i],uveloc,rt,norm))
			{
				rt4=rt*RestTime/rt2;

				if (rt4<=lamda)
				{ 
					if (rt4<=RestTime+ZERO)
						if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
						{
							normal=norm;
							point=OldPos[i]+uveloc*rt;
							lamda=rt4;
							BallNr=i;
						}
				}
			}

			//Now test intersection with the 3 cylinders
			if (TestIntersionCylinder(cyl1,OldPos[i],uveloc,rt,norm,Nc))
			{
				rt4=rt*RestTime/rt2;

				if (rt4<=lamda)
				{ 
					if (rt4<=RestTime+ZERO)
						if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
						{
							normal=norm;
							point=Nc;
							lamda=rt4;
							BallNr=i;
						}
				}

			}
			if (TestIntersionCylinder(cyl2,OldPos[i],uveloc,rt,norm,Nc))
			{
				rt4=rt*RestTime/rt2;

				if (rt4<=lamda)
				{ 
					if (rt4<=RestTime+ZERO)
						if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
						{
							normal=norm;
							point=Nc;
							lamda=rt4;
							BallNr=i;
						}
				}

			}
			if (TestIntersionCylinder(cyl3,OldPos[i],uveloc,rt,norm,Nc))
			{
				rt4=rt*RestTime/rt2;

				if (rt4<=lamda)
				{ 
					if (rt4<=RestTime+ZERO)
						if (! ((rt<=ZERO)&&(uveloc.dot(norm)>ZERO)) )
						{
							normal=norm;
							point=Nc;
							lamda=rt4;
							BallNr=i;
						}
				}

			}
		}

		//After all balls were teste with planes/cylinders test for collision
		//between them and replace if collision time smaller
		if (FindBallCol(Pos2,BallTime,RestTime,BallColNr1,BallColNr2))
		{
			//if (sounds)
			//	PlaySound("Data/Explode.wav",NULL,SND_FILENAME|SND_ASYNC);

			if ( (lamda==10000) || (lamda>BallTime) )
			{
				RestTime=RestTime-BallTime;

				TVector pb1,pb2,xaxis,U1x,U1y,U2x,U2y,V1x,V1y,V2x,V2y;
				double a,b;

				pb1=OldPos[BallColNr1]+ArrayVel[BallColNr1]*BallTime;
				pb2=OldPos[BallColNr2]+ArrayVel[BallColNr2]*BallTime;
				xaxis=(pb2-pb1).unit();

				a=xaxis.dot(ArrayVel[BallColNr1]);
				U1x=xaxis*a;
				U1y=ArrayVel[BallColNr1]-U1x;

				xaxis=(pb1-pb2).unit();
				b=xaxis.dot(ArrayVel[BallColNr2]);
				U2x=xaxis*b;
				U2y=ArrayVel[BallColNr2]-U2x;

				V1x=(U1x+U2x-(U1x-U2x))*0.5;
				V2x=(U1x+U2x-(U2x-U1x))*0.5;
				V1y=U1y;
				V2y=U2y;

				for (int j=0;j<NrOfBalls;j++)
					ArrayPos[j]=OldPos[j]+ArrayVel[j]*BallTime;

				ArrayVel[BallColNr1]=V1x+V1y;
				ArrayVel[BallColNr2]=V2x+V2y;

				//Update explosion array
				for(int j=0;j<20;j++)
				{
					if (ExplosionArray[j]._Alpha<=0)
					{
						ExplosionArray[j]._Alpha=1;
						ExplosionArray[j]._Position=ArrayPos[BallColNr1];
						ExplosionArray[j]._Scale=1;
						break;
					}
				}

				continue;
			}
		}

		//End of tests 
		//If test occured move simulation for the correct timestep
		//and compute response for the colliding ball
		if (lamda!=10000)
		{		 
			RestTime-=lamda;

			for (int j=0;j<NrOfBalls;j++)
				ArrayPos[j]=OldPos[j]+ArrayVel[j]*lamda;

			rt2=ArrayVel[BallNr].mag();
			ArrayVel[BallNr].unit();
			ArrayVel[BallNr]=TVector::unit( (normal*(2*normal.dot(-ArrayVel[BallNr]))) + ArrayVel[BallNr] );
			ArrayVel[BallNr]=ArrayVel[BallNr]*rt2;

			//Update explosion array
			for(int j=0;j<20;j++)
			{
				if (ExplosionArray[j]._Alpha<=0)
				{
					ExplosionArray[j]._Alpha=1;
					ExplosionArray[j]._Position=point;
					ExplosionArray[j]._Scale=1;
					break;
				}
			}
		}
		else RestTime=0;

	}

}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////


osg::Geode*	createWalls()
{
	osg::Geode *geode = new osg::Geode;
	osg::Geometry *geometry = new osg::Geometry;
	osg::Vec3Array *vertexArray = new osg::Vec3Array;
	osg::Vec2Array *textureArray = new osg::Vec2Array;
	osg::Vec3Array *colorArray = new osg::Vec3Array;

	colorArray->push_back(osg::Vec3(1,1,1));

	vertexArray->push_back(osg::Vec3(320,320,320));
	vertexArray->push_back(osg::Vec3(320,-320,320));
	vertexArray->push_back(osg::Vec3(-320,-320,320));
	vertexArray->push_back(osg::Vec3(-320,320,320));
	textureArray->push_back(osg::Vec2(1.0f, 0.0f));
	textureArray->push_back(osg::Vec2(1.0f, 1.0f));
	textureArray->push_back(osg::Vec2(0.0f, 1.0f));
	textureArray->push_back(osg::Vec2(0.0f, 0.0f));

	vertexArray->push_back(osg::Vec3(-320,320,-320));
	vertexArray->push_back(osg::Vec3(-320,-320,-320));
	vertexArray->push_back(osg::Vec3(320,-320,-320));
	vertexArray->push_back(osg::Vec3(320,320,-320));
	textureArray->push_back(osg::Vec2(1.0f, 0.0f));
	textureArray->push_back(osg::Vec2(1.0f, 1.0f));
	textureArray->push_back(osg::Vec2(0.0f, 1.0f));
	textureArray->push_back(osg::Vec2(0.0f, 0.0f));

	vertexArray->push_back(osg::Vec3(320,320,-320));
	vertexArray->push_back(osg::Vec3(320,-320,-320));
	vertexArray->push_back(osg::Vec3(320,-320,320));
	vertexArray->push_back(osg::Vec3(320,320,320));
	textureArray->push_back(osg::Vec2(1.0f, 0.0f));
	textureArray->push_back(osg::Vec2(1.0f, 1.0f));
	textureArray->push_back(osg::Vec2(0.0f, 1.0f));
	textureArray->push_back(osg::Vec2(0.0f, 0.0f));

	vertexArray->push_back(osg::Vec3(-320,320,320));
	vertexArray->push_back(osg::Vec3(-320,-320,320));
	vertexArray->push_back(osg::Vec3(-320,-320,-320));
	vertexArray->push_back(osg::Vec3(-320,320,-320));
	textureArray->push_back(osg::Vec2(1.0f, 0.0f));
	textureArray->push_back(osg::Vec2(1.0f, 1.0f));
	textureArray->push_back(osg::Vec2(0.0f, 1.0f));
	textureArray->push_back(osg::Vec2(0.0f, 0.0f));

	geometry->setVertexArray(vertexArray);
	geometry->setColorArray(colorArray, osg::Array::BIND_OVERALL);
	geometry->setTexCoordArray(0, textureArray, osg::Array::BIND_PER_VERTEX);

	osg::Texture2D *texture2D = new osg::Texture2D;
	texture2D->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
	texture2D->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
	texture2D->setImage(osgDB::readImageFile("Data/wand.bmp"));
	geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture2D);
	geometry->getOrCreateStateSet()->setAttributeAndModes(new osg::CullFace());

	geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, vertexArray->size()));
	geode->addDrawable(geometry);

	return geode;
}


osg::Geode*	createFloor()
{
	osg::Geode *geode = new osg::Geode;
	osg::Geometry *geometry = new osg::Geometry;

	osg::Vec3Array *vertexArray = new osg::Vec3Array;
	osg::Vec2Array *textureArray = new osg::Vec2Array;
	osg::Vec3Array *colorArray = new osg::Vec3Array;
	colorArray->push_back(osg::Vec3(1,1,1));
	vertexArray->push_back(osg::Vec3(-320,-320,320));
	vertexArray->push_back(osg::Vec3(320,-320,320));
	vertexArray->push_back(osg::Vec3(320,-320,-320));
	vertexArray->push_back(osg::Vec3(-320,-320,-320));
	textureArray->push_back(osg::Vec2(1.0f, 0.0f));
	textureArray->push_back(osg::Vec2(1.0f, 1.0f));
	textureArray->push_back(osg::Vec2(0.0f, 1.0f));
	textureArray->push_back(osg::Vec2(0.0f, 0.0f));
	geometry->setVertexArray(vertexArray);
	geometry->setColorArray(colorArray, osg::Array::BIND_OVERALL);
	geometry->setTexCoordArray(0, textureArray, osg::Array::BIND_PER_VERTEX);

	osg::Texture2D *texture2D = new osg::Texture2D;
	texture2D->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
	texture2D->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
	texture2D->setImage(osgDB::readImageFile("Data/boden.bmp"));
	geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture2D);

	geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, vertexArray->size()));
	geode->addDrawable(geometry);

	return geode;
}



osg::Geode*	createCylinder()
{
	osg::Geode *cylinderGeode = new osg::Geode;
	osg::ShapeDrawable *shapeDrawable = new osg::ShapeDrawable;
	osg::Texture2D *texture2D = new osg::Texture2D;
	texture2D->setImage(osgDB::readImageFile("Data/Marble.bmp"));
	shapeDrawable->getOrCreateStateSet()->setTextureAttributeAndModes(0,texture2D);
	shapeDrawable->setColor(osg::Vec4(0.5,0.5,0.5, 1.0));
	shapeDrawable->setShape(new osg::Cylinder(osg::Vec3(), 60, 2000));
	cylinderGeode->addDrawable(shapeDrawable);
	
	return cylinderGeode;
}

osg::Geode*	createCylinder2()
{
	osg::Geode *cylinderGeode = new osg::Geode;
	osg::ShapeDrawable *shapeDrawable = new osg::ShapeDrawable;

	osg::Texture2D *texture2D = new osg::Texture2D;
	texture2D->setImage(osgDB::readImageFile("Data/Marble.bmp"));
	shapeDrawable->getOrCreateStateSet()->setTextureAttributeAndModes(0,texture2D);
	shapeDrawable->setColor(osg::Vec4(0.5,0.5,0.5, 1.0));
	shapeDrawable->setShape(new osg::Cylinder(osg::Vec3(), 30, 2000));
	cylinderGeode->addDrawable(shapeDrawable);

	return cylinderGeode;
}

osg::Geode*	createBalls(int i)
{
	osg::Geode *cylinderSphere = new osg::Geode;
	osg::ShapeDrawable *shapeDrawable = new osg::ShapeDrawable;

	switch(i){
		case 1: shapeDrawable->setColor(osg::Vec4(1.0f,1.0f,1.0f, 1.0f));
			break;
		case 2: shapeDrawable->setColor(osg::Vec4(1.0f,1.0f,0.0f, 1.0f));
			break;
		case 3: shapeDrawable->setColor(osg::Vec4(0.0f,1.0f,1.0f, 1.0f));
			break;
		case 4: shapeDrawable->setColor(osg::Vec4(0.0f,1.0f,0.0f, 1.0f));
			break;
		case 5: shapeDrawable->setColor(osg::Vec4(0.0f,0.0f,1.0f, 1.0f));
			break;
		case 6: shapeDrawable->setColor(osg::Vec4(0.65f,0.2f,0.3f, 1.0f));
			break;
		case 7: shapeDrawable->setColor(osg::Vec4(1.0f,0.0f,1.0f, 1.0f));
			break;
		case 8: shapeDrawable->setColor(osg::Vec4(0.0f,0.7f,0.4f,1.0f));
			break;
		default: shapeDrawable->setColor(osg::Vec4(1.0f, 0.0, 0.0, 1.0));
	}
	shapeDrawable->setShape(new osg::Sphere(osg::Vec3(), 20));
	cylinderSphere->addDrawable(shapeDrawable);

	return cylinderSphere;
}


//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
class ManipulatorSceneHandler : public osgGA::GUIEventHandler
{
public:
	ManipulatorSceneHandler()
	{
	}

public:
	virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
	{
		osgViewer::Viewer *viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
		if (!viewer)
			return false;
		if (!viewer->getSceneData())
			return false;
		if (ea.getHandled()) 
			return false;

		osg::Group *root = viewer->getSceneData()->asGroup();

		switch(ea.getEventType())
		{
		case (osgGA::GUIEventAdapter::FRAME):
			{
				idle();
				viewer->getCamera()->setViewMatrixAsLookAt(osg::Vec3(Pos.X(),Pos.Y(),Pos.Z()), osg::Vec3(Pos.X()+dir.X(),Pos.Y()+dir.Y(),Pos.Z()+dir.Z()), osg::Y_AXIS);
			}
		case(osgGA::GUIEventAdapter::KEYDOWN):
			{
				if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left)
				{
				}

				if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right)
				{
				}

				if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Space)
				{
				}
			}
		default: break;
		}
		return false;
	}
};

//////////////////////////////////////////////////////////////////////////

class PostionUpdateCallback : public osg::NodeCallback
{
public:
	PostionUpdateCallback(int index) : _index(index){ }

	virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
	{
		osg::MatrixTransform *mt = dynamic_cast<osg::MatrixTransform*>(node);
		if (!mt)
			return;

		mt->setMatrix(osg::Matrix::translate(ArrayPos[_index].X(),ArrayPos[_index].Y(),ArrayPos[_index].Z()));
		
		traverse(node, nv);
	}
	
	int _index;
};


class CollisionSwitchUpdateCallback : public osg::NodeCallback
{
public:
	CollisionSwitchUpdateCallback(int index) : _index(index){ }

	virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
	{
		osg::Switch *st = dynamic_cast<osg::Switch*>(node);
		if (!st)
			return;

		if (ExplosionArray[_index]._Alpha >= 0)
		{
			ExplosionArray[_index]._Alpha-=0.01f;
			ExplosionArray[_index]._Scale+=0.03f;
			st->setAllChildrenOn();
		}
		else
		{
			st->setAllChildrenOff();
		}
		
		

		traverse(node, nv);
	}

	int _index;
};




class CollisionScaleUpdateCallback : public osg::NodeCallback
{
public:
	CollisionScaleUpdateCallback(int index) : _index(index){ }

	virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
	{
		osg::MatrixTransform *mt = dynamic_cast<osg::MatrixTransform*>(node);
		if (!mt)
			return;
		
		if (ExplosionArray[_index]._Alpha >= 0)
		{
			mt->setMatrix(osg::Matrix::scale(ExplosionArray[_index]._Scale,ExplosionArray[_index]._Scale,ExplosionArray[_index]._Scale));
		}

		traverse(node, nv);
	}

	int _index;
};


class CollisionTransUpdateCallback : public osg::NodeCallback
{
public:
	CollisionTransUpdateCallback(int index) : _index(index){ }

	virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
	{
		osg::MatrixTransform *mt = dynamic_cast<osg::MatrixTransform*>(node);
		if (!mt)
			return;

		if (ExplosionArray[_index]._Alpha >= 0)
		{
			mt->setMatrix(osg::Matrix::translate(ArrayPos[_index].X(),ArrayPos[_index].Y(),ArrayPos[_index].Z()));
		}	

		traverse(node, nv);
	}

	int _index;
};


class CollisionGeometryUpdateCallback : public  osg::Drawable::UpdateCallback
{
public:
	CollisionGeometryUpdateCallback(int index) : _index(index){ }

	virtual void update(osg::NodeVisitor*, osg::Drawable* drawable)
	{
		osg::Geometry *geo = dynamic_cast<osg::Geometry*>(drawable);
		if (!geo)
			return;

		osg::Vec4Array *colorArray = dynamic_cast<osg::Vec4Array*>(geo->getColorArray());
		if(!colorArray)
			return;
		
		colorArray->clear();
		colorArray->push_back(osg::Vec4(1,1,0,ExplosionArray[_index]._Alpha));
		geo->setColorArray(colorArray, osg::Array::BIND_OVERALL);
	}

	int _index;
};



//////////////////////////////////////////////////////////////////////////
osg::Node*	createExplorsionNode(int i)
{
	osg::MatrixTransform *rotMT = new osg::MatrixTransform;
	rotMT->setMatrix(osg::Matrix::rotate(osg::DegreesToRadians(-45.0), osg::Y_AXIS));

	osg::Geode *geode = new osg::Geode;
	osg::Geometry *geometry = new osg::Geometry;
	geometry->setUpdateCallback(new CollisionGeometryUpdateCallback(i));
	osg::Vec3Array *vertexArray = new osg::Vec3Array;
	osg::Vec3Array *normalArray = new osg::Vec3Array;
	osg::Vec2Array *textureArray = new osg::Vec2Array;
	osg::Vec4Array *colorArray = new osg::Vec4Array;
	colorArray->push_back(osg::Vec4(0,0,0,1));

	vertexArray->push_back(osg::Vec3(-50,-40,0));
	vertexArray->push_back(osg::Vec3(50,-40,0));
	vertexArray->push_back(osg::Vec3(50,40,0));
	vertexArray->push_back(osg::Vec3(-50,40,0));
	for (int j = 0; j < 4; ++j)
	{
		normalArray->push_back(osg::Vec3(0,0,1));
	}
	textureArray->push_back(osg::Vec2(0.0f, 0.0f));
	textureArray->push_back(osg::Vec2(0.0f, 1.0f));
	textureArray->push_back(osg::Vec2(1.0f, 1.0f));
	textureArray->push_back(osg::Vec2(1.0f, 0.0f));

	vertexArray->push_back(osg::Vec3(-50,40,0));
	vertexArray->push_back(osg::Vec3(50,40,0));
	vertexArray->push_back(osg::Vec3(50,-40,0));
	vertexArray->push_back(osg::Vec3(-50,-40,0));
	for (int j = 0; j < 4; ++j)
	{
		normalArray->push_back(osg::Vec3(0,0,-1));
	}
	textureArray->push_back(osg::Vec2(0.0f, 0.0f));
	textureArray->push_back(osg::Vec2(0.0f, 1.0f));
	textureArray->push_back(osg::Vec2(1.0f, 1.0f));
	textureArray->push_back(osg::Vec2(1.0f, 0.0f));

	vertexArray->push_back(osg::Vec3(0,-40,50));
	vertexArray->push_back(osg::Vec3(0,-40,-50));
	vertexArray->push_back(osg::Vec3(0,40,-50));
	vertexArray->push_back(osg::Vec3(0,40,50));
	for (int j = 0; j < 4; ++j)
	{
		normalArray->push_back(osg::Vec3(1,0,0));
	}
	textureArray->push_back(osg::Vec2(0.0f, 0.0f));
	textureArray->push_back(osg::Vec2(0.0f, 1.0f));
	textureArray->push_back(osg::Vec2(1.0f, 1.0f));
	textureArray->push_back(osg::Vec2(1.0f, 0.0f));

	vertexArray->push_back(osg::Vec3(0,40,50));
	vertexArray->push_back(osg::Vec3(0,40,-50));
	vertexArray->push_back(osg::Vec3(0,-40,-50));
	vertexArray->push_back(osg::Vec3(0,-40,50));
	for (int j = 0; j < 4; ++j)
	{
		normalArray->push_back(osg::Vec3(-1,0,0));
	}
	textureArray->push_back(osg::Vec2(0.0f, 0.0f));
	textureArray->push_back(osg::Vec2(0.0f, 1.0f));
	textureArray->push_back(osg::Vec2(1.0f, 1.0f));
	textureArray->push_back(osg::Vec2(1.0f, 0.0f));

	osg::Texture2D *texture2D = new osg::Texture2D;
	texture2D->setImage(osgDB::readImageFile("Data/spark.bmp"));

	osg::BlendFunc *blendFunc = new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE);

	osg::Material *material = new osg::Material();
	material->setShininess(osg::Material::FRONT,100.0);
	material->setSpecular(osg::Material::FRONT, osg::Vec4(1.0, 1.0 ,1.0 ,1.0));
	material->setColorMode(osg::Material::AMBIENT_AND_DIFFUSE);

	geometry->setVertexArray(vertexArray);
	geometry->setNormalArray(normalArray, osg::Array::BIND_PER_VERTEX);
	geometry->setTexCoordArray(0, textureArray, osg::Array::BIND_PER_VERTEX);
	geometry->setColorArray(colorArray, osg::Array::BIND_OVERALL);
	geometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture2D);
	geometry->getOrCreateStateSet()->setAttributeAndModes(blendFunc);
	geometry->getOrCreateStateSet()->setAttributeAndModes(material);
	geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, vertexArray->size()));
	geometry->setUseDisplayList(false);
	geode->addDrawable(geometry);
	rotMT->addChild(geode);

	return rotMT;
}

//////////////////////////////////////////////////////////////////////////
/////////////////////////////osgNeHe框架/////////////////////////
//////////////////////////////////////////////////////////////////////////

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, 0.0) );
		camera->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) );
		camera->setProjectionMatrixAsPerspective(50.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 10.0f, 1000.0f);
		this->setSceneData(scene);
		addEventHandler(new ManipulatorSceneHandler);

		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()
{
	initVars();
	osg::Group *root = new osg::Group;

	osg::Light *light = new osg::Light;
	light->setAmbient(osg::Vec4(amb2[0], amb2[1],amb2[2], amb2[3]));
	light->setLightNum(0);
	light->setPosition(osg::Vec4(posl[0],posl[1],posl[2],posl[3]) );
	osg::LightSource *lightSource = new osg::LightSource;
	lightSource->setLight(light);
	root->addChild(lightSource);
	

	osg::MatrixTransform *rotMT = new osg::MatrixTransform;
	rotMT->addUpdateCallback(new osg::AnimationPathCallback(osg::Vec3(), osg::Y_AXIS, 0.5));
	rotMT->addChild(createWalls());
	rotMT->addChild(createFloor());

	osg::MatrixTransform *cylinderRotMT1 = new osg::MatrixTransform;
	osg::MatrixTransform *cylinderTransMT1 = new osg::MatrixTransform;
	cylinderRotMT1->setMatrix(osg::Matrix::rotate(osg::PI_2, osg::X_AXIS));
	cylinderTransMT1->setMatrix(osg::Matrix::translate(0, 0, -500));
	cylinderTransMT1->addChild(createCylinder());
	cylinderRotMT1->addChild(cylinderTransMT1);
	
	osg::MatrixTransform *cylinderTransMT2 = new osg::MatrixTransform;
	cylinderTransMT2->setMatrix(osg::Matrix::translate(200,-300,-500));
	cylinderTransMT2->addChild(createCylinder());

	osg::MatrixTransform *cylinderTransMT31 = new osg::MatrixTransform;
	osg::MatrixTransform *cylinderRotMT3 = new osg::MatrixTransform;
	osg::MatrixTransform *cylinderTransMT32 = new osg::MatrixTransform;
	cylinderTransMT31->setMatrix(osg::Matrix::translate(-200, 0, 0));
	cylinderRotMT3->setMatrix(osg::Matrix::rotate(osg::DegreesToRadians(135.0), osg::X_AXIS));
	cylinderTransMT32->setMatrix(osg::Matrix::translate(0, 0, -500));
	cylinderTransMT32->addChild(createCylinder2());
	cylinderTransMT31->addChild(cylinderRotMT3);
	cylinderRotMT3->addChild(cylinderTransMT32);

	rotMT->addChild(cylinderRotMT1);
	rotMT->addChild(cylinderTransMT2);
	rotMT->addChild(cylinderTransMT31);


	for (int i = 0; i < NrOfBalls; ++i)
	{
		osg::MatrixTransform *mt = new osg::MatrixTransform;
		mt->addUpdateCallback(new PostionUpdateCallback(i));
		mt->addChild(createBalls(i));
		rotMT->addChild(mt);
	}

	//////////////////////////////////////////////////////////////////////////
	
	for (int i = 0; i < 20; ++i)
	{
		osg::Switch *switchNode = new osg::Switch;
		g_SwitchNode = switchNode;
		switchNode->addUpdateCallback(new CollisionSwitchUpdateCallback(i));
		osg::MatrixTransform *collisionScale = new osg::MatrixTransform;
		collisionScale->addUpdateCallback(new CollisionScaleUpdateCallback(i));
		osg::MatrixTransform *collisionMT = new osg::MatrixTransform;
		collisionMT->addUpdateCallback(new CollisionTransUpdateCallback(i));
		osg::Node *collisionNode = createExplorsionNode(i);
		collisionScale->addChild(collisionMT);
		collisionMT->addChild(collisionNode);
		switchNode->addChild(collisionScale);
		rotMT->addChild(switchNode);
	}
	
	root->addChild(rotMT);

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


你可能感兴趣的:(C++,qt,OpenGL,nehe,OSG)