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

来源:互联网 发布:网络带来的利与弊800 编辑:程序博客网 时间:2024/05/18 22:47
  • 简介

本文实现从外部文件中加载场景,并进行简单的场景漫游操作。

一般来说,场景都是通过外部建模工具生成的,然后再通过osg进行读取加载,一方面直接通过顶点生成场景对程序员来说十分困难,另一方面通过外部建模的方式加载场景使得程序变得十分灵活。像我们玩的CS游戏就是通过加载一些地图文件实现在不同的场景之中战斗。

  • 实现

在NeHe教程中实现的过程介绍的非常清楚,不熟悉的读者可以参考NeHe教程第十课。在这里我们同样使用NeHe中读取文件的方式以及行进的方式,代码如下

typedef struct tagVERTEX{float x,y,z;float u,v;}VERTEX;typedef struct tagTRIANGLE{VERTEXvertex[3];}TRIANGLE;typedefstruct tagSECTOR{intiNumTriangle;TRIANGLE *pTriangle;}SECTOR;SECTORSector;void readstr(FILE *f,char *string){do{fgets(string, 255, f);} while ((string[0] == '/') || (string[0] == '\n'));}void SetupWorld(){float x, y, z, u, v;int numtriangles;FILE *filein;char oneline[255];filein = fopen("Data/world.txt", "rt");// File To Load World Data Fromreadstr(filein,oneline);sscanf(oneline, "NUMPOLLIES %d\n", &numtriangles);Sector.iNumTriangle = numtriangles;Sector.pTriangle = new TRIANGLE[numtriangles];for(int i = 0; i < numtriangles; i++){for(int j = 0; j < 3; j++){readstr(filein,oneline);sscanf(oneline, "%f %f %f %f %f", &x, &y, &z, &u, &v);Sector.pTriangle[i].vertex[j].x = x;Sector.pTriangle[i].vertex[j].y = y;Sector.pTriangle[i].vertex[j].z = z;Sector.pTriangle[i].vertex[j].u = u;Sector.pTriangle[i].vertex[j].v = v;}}fclose(filein);}

读取文件的方式有很多种,(在我们的Qt框架中可以通过QFile和QTextStream等方式读取)为了节省时间此处借用了NeHe源码中的读取方式

const float piover180 = 0.0174532925f;float heading; float xpos;float zpos;GLfloatyrot;GLfloat walkbias = 0;GLfloat walkbiasangle = 0;GLfloat lookupdown = 0.0f;
同样的定义了一些变量用来控制角色。之后为了实现在每一帧中修改角色的姿态,我们定义了三个UpdateCallback类用来响应向前行进、左右旋转(绕Y轴旋转)以及上下旋转
(绕X轴旋转)

class TransUpdateCallback : public osg::NodeCallback{public:TransUpdateCallback(){GLfloat xtrans = -xpos;GLfloat ztrans = -zpos;GLfloat ytrans = -walkbias-0.25f;_trans = osg::Vec3d(xtrans, ytrans, ztrans);}virtual void operator()(Node* node, NodeVisitor* nv){if (dynamic_cast<osg::MatrixTransform*>(node)){osg::MatrixTransform *moveMT = dynamic_cast<osg::MatrixTransform*>(node);moveMT->setMatrix(osg::Matrix::translate(_trans));}traverse(node, nv);}void setTrans(osg::Vec3d trans){_trans = trans;}osg::Vec3d _trans;};

class RotateXUpdateCallback : public osg::NodeCallback{public:RotateXUpdateCallback(){_pitch = lookupdown;}virtual void operator()(Node* node, NodeVisitor* nv){if (dynamic_cast<osg::MatrixTransform*>(node)){osg::MatrixTransform *rotateX = dynamic_cast<osg::MatrixTransform*>(node);rotateX->setMatrix(osg::Matrix::rotate(_pitch, osg::X_AXIS));}traverse(node, nv);}void setPitch(double pitch){_pitch = pitch;}double _pitch;};class RotateYUpdateCallback : public osg::NodeCallback{public:RotateYUpdateCallback(){_heading = osg::DegreesToRadians(360.0f) - yrot;}virtual void operator()(Node* node, NodeVisitor* nv){if (dynamic_cast<osg::MatrixTransform*>(node)){osg::MatrixTransform *rotateY = dynamic_cast<osg::MatrixTransform*>(node);rotateY->setMatrix(osg::Matrix::rotate(_heading, osg::Y_AXIS));}traverse(node, nv);}void setHeading(double heading){_heading = heading;}double _heading;};
接着定义了三个全局的变量,用来记录场景中角色的姿态,在键盘的响应中修改它们实现角色的正确移动和旋转

TransUpdateCallback *g_moveMTCB;RotateXUpdateCallback *g_xRotMTCB;RotateYUpdateCallback *g_yRotMTCB;
接下来构建我们的场景

osg::Group *root = new osg::Group;osg::Vec3Array *vertexArray = new osg::Vec3Array;osg::Vec2Array *textureArray = new osg::Vec2Array;unsigned int num = Sector.iNumTriangle;for (unsigned i = 0; i < num; ++i){vertexArray->push_back(osg::Vec3(Sector.pTriangle[i].vertex[0].x, Sector.pTriangle[i].vertex[0].y, Sector.pTriangle[i].vertex[0].z));vertexArray->push_back(osg::Vec3(Sector.pTriangle[i].vertex[1].x, Sector.pTriangle[i].vertex[1].y, Sector.pTriangle[i].vertex[1].z));vertexArray->push_back(osg::Vec3(Sector.pTriangle[i].vertex[2].x, Sector.pTriangle[i].vertex[2].y, Sector.pTriangle[i].vertex[2].z));textureArray->push_back(osg::Vec2(Sector.pTriangle[i].vertex[0].u, Sector.pTriangle[i].vertex[0].v));textureArray->push_back(osg::Vec2(Sector.pTriangle[i].vertex[1].u, Sector.pTriangle[i].vertex[1].v));textureArray->push_back(osg::Vec2(Sector.pTriangle[i].vertex[2].u, Sector.pTriangle[i].vertex[2].v));}osg::MatrixTransform *moveMT = new osg::MatrixTransform;TransUpdateCallback *tucb = new TransUpdateCallback;moveMT->setUpdateCallback(tucb);g_moveMTCB = tucb;osg::MatrixTransform *headingMT = new osg::MatrixTransform;RotateYUpdateCallback *ryuc = new RotateYUpdateCallback;headingMT->setUpdateCallback(ryuc);g_yRotMTCB = ryuc;osg::MatrixTransform *pitchMT = new osg::MatrixTransform;RotateXUpdateCallback *rxuc = new RotateXUpdateCallback;pitchMT->setUpdateCallback(rxuc);g_xRotMTCB = rxuc;headingMT->addChild(pitchMT);pitchMT->addChild(moveMT);osg::Geometry *triangleGeometry = new osg::Geometry;triangleGeometry->setVertexArray(vertexArray);triangleGeometry->setTexCoordArray(0, textureArray);triangleGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, createTexture(0));triangleGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, 108));osg::Geode *triangleGeode = new osg::Geode;triangleGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);triangleGeode->addDrawable(triangleGeometry);moveMT->addChild(triangleGeode);root->addChild(headingMT);
构建场景中我们将Callback记录在全局变量中,在EventHandler中修改它们

switch(ea.getEventType()){case(osgGA::GUIEventAdapter::KEYDOWN):{if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Up){xpos -= (float)sin(heading*piover180) * 0.05f;zpos -= (float)cos(heading*piover180) * 0.05f;if (walkbiasangle >= 359.0f){walkbiasangle = 0.0f;}else{walkbiasangle+= 10;}walkbias = (float)sin(walkbiasangle * piover180)/20.0f;GLfloat ytrans = -walkbias-0.25f;g_moveMTCB->setTrans(osg::Vec3d(-xpos, ytrans, -zpos));}if (ea.getKey()== osgGA::GUIEventAdapter::KEY_Down){xpos += (float)sin(heading*piover180) * 0.05f;zpos += (float)cos(heading*piover180) * 0.05f;if (walkbiasangle <= 1.0f){walkbiasangle = 359.0f;}else{walkbiasangle-= 10;}walkbias = (float)sin(walkbiasangle * piover180)/20.0f;GLfloat ytrans = -walkbias-0.25f;g_moveMTCB->setTrans(osg::Vec3d(-xpos, ytrans, -zpos));}if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left){heading -= 1.0f;yrot = heading;double direction = osg::DegreesToRadians(360.0f) - osg::DegreesToRadians(yrot);g_yRotMTCB->setHeading(direction);}if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right){heading += 1.0f;yrot = heading;double direction = osg::DegreesToRadians(360.0f) - osg::DegreesToRadians(yrot);g_yRotMTCB->setHeading(direction);}if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Page_Up){lookupdown += 1.0f;double direction = osg::DegreesToRadians(lookupdown);g_xRotMTCB->setPitch(direction);}if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Page_Down){lookupdown -= 1.0f;double direction = osg::DegreesToRadians(lookupdown);g_xRotMTCB->setPitch(direction);}}default: break;}
行进以及旋转的方式参考了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/Texture2D>#include <osgGA/TrackballManipulator>using namespace osg;////////////////////////////////////////////////////////////////////////////Copied From NeHe Tutorialtypedef struct tagVERTEX{float x,y,z;float u,v;}VERTEX;typedef struct tagTRIANGLE{VERTEXvertex[3];}TRIANGLE;typedefstruct tagSECTOR{intiNumTriangle;TRIANGLE *pTriangle;}SECTOR;SECTORSector;void readstr(FILE *f,char *string){do{fgets(string, 255, f);} while ((string[0] == '/') || (string[0] == '\n'));}void SetupWorld(){float x, y, z, u, v;int numtriangles;FILE *filein;char oneline[255];filein = fopen("Data/world.txt", "rt");// File To Load World Data Fromreadstr(filein,oneline);sscanf(oneline, "NUMPOLLIES %d\n", &numtriangles);Sector.iNumTriangle = numtriangles;Sector.pTriangle = new TRIANGLE[numtriangles];for(int i = 0; i < numtriangles; i++){for(int j = 0; j < 3; j++){readstr(filein,oneline);sscanf(oneline, "%f %f %f %f %f", &x, &y, &z, &u, &v);Sector.pTriangle[i].vertex[j].x = x;Sector.pTriangle[i].vertex[j].y = y;Sector.pTriangle[i].vertex[j].z = z;Sector.pTriangle[i].vertex[j].u = u;Sector.pTriangle[i].vertex[j].v = v;}}fclose(filein);}//End//////////////////////////////////////////////////////////////////////////const float piover180 = 0.0174532925f;float heading;float xpos;float zpos;GLfloatyrot;GLfloat walkbias = 0;GLfloat walkbiasangle = 0;GLfloat lookupdown = 0.0f;////////////////////////////////////////////////////////////////////////////TransUpdateCallbackclass TransUpdateCallback : public osg::NodeCallback{public:TransUpdateCallback(){GLfloat xtrans = -xpos;GLfloat ztrans = -zpos;GLfloat ytrans = -walkbias-0.25f;_trans = osg::Vec3d(xtrans, ytrans, ztrans);}virtual void operator()(Node* node, NodeVisitor* nv){if (dynamic_cast<osg::MatrixTransform*>(node)){osg::MatrixTransform *moveMT = dynamic_cast<osg::MatrixTransform*>(node);moveMT->setMatrix(osg::Matrix::translate(_trans));}traverse(node, nv);}void setTrans(osg::Vec3d trans){_trans = trans;}osg::Vec3d _trans;};//End//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////RotateUpdateCallback class RotateXUpdateCallback : public osg::NodeCallback{public:RotateXUpdateCallback(){_pitch = lookupdown;}virtual void operator()(Node* node, NodeVisitor* nv){if (dynamic_cast<osg::MatrixTransform*>(node)){osg::MatrixTransform *rotateX = dynamic_cast<osg::MatrixTransform*>(node);rotateX->setMatrix(osg::Matrix::rotate(_pitch, osg::X_AXIS));}traverse(node, nv);}void setPitch(double pitch){_pitch = pitch;}double _pitch;};class RotateYUpdateCallback : public osg::NodeCallback{public:RotateYUpdateCallback(){_heading = osg::DegreesToRadians(360.0f) - yrot;}virtual void operator()(Node* node, NodeVisitor* nv){if (dynamic_cast<osg::MatrixTransform*>(node)){osg::MatrixTransform *rotateY = dynamic_cast<osg::MatrixTransform*>(node);rotateY->setMatrix(osg::Matrix::rotate(_heading, osg::Y_AXIS));}traverse(node, nv);}void setHeading(double heading){_heading = heading;}double _heading;};//End//////////////////////////////////////////////////////////////////////////TransUpdateCallback *g_moveMTCB;RotateXUpdateCallback *g_xRotMTCB;RotateYUpdateCallback *g_yRotMTCB;osg::Texture2D*  createTexture(int mode){osg::Image *textureImage = osgDB::readImageFile("Data/Mud.bmp");osg::Texture2D *texture2D = new osg::Texture2D;texture2D->setImage(textureImage);if (mode == 0){texture2D->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST);texture2D->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST);}else if (mode == 1){texture2D->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);texture2D->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);}else if (mode == 2){texture2D->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_NEAREST);texture2D->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);}texture2D->setWrap(osg::Texture::WRAP_R, osg::Texture::REPEAT);texture2D->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);texture2D->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);return texture2D;}//////////////////////////////////////////////////////////////////////////class ManipulatorSceneHandler : public osgGA::GUIEventHandler{public:ManipulatorSceneHandler(){}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::KEYDOWN):{if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Up){xpos -= (float)sin(heading*piover180) * 0.05f;zpos -= (float)cos(heading*piover180) * 0.05f;if (walkbiasangle >= 359.0f){walkbiasangle = 0.0f;}else{walkbiasangle+= 10;}walkbias = (float)sin(walkbiasangle * piover180)/20.0f;GLfloat ytrans = -walkbias-0.25f;g_moveMTCB->setTrans(osg::Vec3d(-xpos, ytrans, -zpos));}if (ea.getKey()== osgGA::GUIEventAdapter::KEY_Down){xpos += (float)sin(heading*piover180) * 0.05f;zpos += (float)cos(heading*piover180) * 0.05f;if (walkbiasangle <= 1.0f){walkbiasangle = 359.0f;}else{walkbiasangle-= 10;}walkbias = (float)sin(walkbiasangle * piover180)/20.0f;GLfloat ytrans = -walkbias-0.25f;g_moveMTCB->setTrans(osg::Vec3d(-xpos, ytrans, -zpos));}if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Left){heading -= 1.0f;yrot = heading;double direction = osg::DegreesToRadians(360.0f) - osg::DegreesToRadians(yrot);g_yRotMTCB->setHeading(direction);}if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right){heading += 1.0f;yrot = heading;double direction = osg::DegreesToRadians(360.0f) - osg::DegreesToRadians(yrot);g_yRotMTCB->setHeading(direction);}if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Page_Up){lookupdown += 1.0f;double direction = osg::DegreesToRadians(lookupdown);g_xRotMTCB->setPitch(direction);}if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Page_Down){lookupdown -= 1.0f;double direction = osg::DegreesToRadians(lookupdown);g_xRotMTCB->setPitch(direction);}}default: break;}return false;}};//////////////////////////////////////////////////////////////////////////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 );this->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(){SetupWorld();osg::Group *root = new osg::Group;osg::Vec3Array *vertexArray = new osg::Vec3Array;osg::Vec2Array *textureArray = new osg::Vec2Array;unsigned int num = Sector.iNumTriangle;for (unsigned i = 0; i < num; ++i){vertexArray->push_back(osg::Vec3(Sector.pTriangle[i].vertex[0].x, Sector.pTriangle[i].vertex[0].y, Sector.pTriangle[i].vertex[0].z));vertexArray->push_back(osg::Vec3(Sector.pTriangle[i].vertex[1].x, Sector.pTriangle[i].vertex[1].y, Sector.pTriangle[i].vertex[1].z));vertexArray->push_back(osg::Vec3(Sector.pTriangle[i].vertex[2].x, Sector.pTriangle[i].vertex[2].y, Sector.pTriangle[i].vertex[2].z));textureArray->push_back(osg::Vec2(Sector.pTriangle[i].vertex[0].u, Sector.pTriangle[i].vertex[0].v));textureArray->push_back(osg::Vec2(Sector.pTriangle[i].vertex[1].u, Sector.pTriangle[i].vertex[1].v));textureArray->push_back(osg::Vec2(Sector.pTriangle[i].vertex[2].u, Sector.pTriangle[i].vertex[2].v));}osg::MatrixTransform *moveMT = new osg::MatrixTransform;TransUpdateCallback *tucb = new TransUpdateCallback;moveMT->setUpdateCallback(tucb);g_moveMTCB = tucb;osg::MatrixTransform *headingMT = new osg::MatrixTransform;RotateYUpdateCallback *ryuc = new RotateYUpdateCallback;headingMT->setUpdateCallback(ryuc);g_yRotMTCB = ryuc;osg::MatrixTransform *pitchMT = new osg::MatrixTransform;RotateXUpdateCallback *rxuc = new RotateXUpdateCallback;pitchMT->setUpdateCallback(rxuc);g_xRotMTCB = rxuc;headingMT->addChild(pitchMT);pitchMT->addChild(moveMT);osg::Geometry *triangleGeometry = new osg::Geometry;triangleGeometry->setVertexArray(vertexArray);triangleGeometry->setTexCoordArray(0, textureArray);triangleGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, createTexture(0));triangleGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, 108));osg::Geode *triangleGeode = new osg::Geode;triangleGeometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);triangleGeode->addDrawable(triangleGeometry);moveMT->addChild(triangleGeode);root->addChild(headingMT);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();}



0 0