这节课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中查看相应的代码
编译运行程序:
附:本课源码(源码中可能存在错误和不足,仅供参考)[其他头文件可以在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(); }