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

来源:互联网 发布:淘宝微淘如何发买家秀 编辑:程序博客网 时间:2024/05/01 04:28
  • 简介

这节课NeHe课程主要向我们展示了将物理运动规律引入到三维场景中,模拟真实物体的位置变化过程。这节课分别模拟了如下几种运动方式:

(1)在重力作用下的抛物线运动;

(2)匀速运动

(3)一种类似于弹簧一样的来回运动

这些运动的方程在Physics.h头文件中描述的很清楚,稍有物理知识应该不难理解。

  • 实现

首先我们在场景中添加各种几何体:

root->addChild(createBackGroundGeode()); //网状背景root->addChild(constantVelocityNode());  //匀速模拟root->addChild(underGravitationNode());  // 重力模拟root->addChild(massConnectedWithSpringNode()); //弹簧来回运动模拟
另外需要添加场景中的说明文字:

osg::Node *timeEllapsedNode = glPrint(-15.0f, 14, 0, osg::Vec4(1,1,1,1), "Time elapsed (seconds): %.2f", timeElapsed);timeEllapsedNode->addUpdateCallback(new TimeEllapsedUpdateCallback);root->addChild(timeEllapsedNode);osg::Node *ratioNode = glPrint(-15.0f, 13, 0, osg::Vec4(1,1,1,1),"Slow motion ratio: %.2f", slowMotionRatio);ratioNode->addUpdateCallback(new RatioUpdateCallback);root->addChild(ratioNode);root->addChild(glPrint(-15.0f, 12, 0, osg::Vec4(1,1,1,1),"Press F2 for normal motion"));root->addChild(glPrint(-15.0f, 11, 0, osg::Vec4(1,1,1,1),"Press F3 for slow motion"));

接下来便是一系列的更新回调,按照物理运动的规律更新节点位置:

//Constant Callbackclass ConstantPointUpdateCallback : public osg::Drawable::UpdateCallback{public:virtual void update(osg::NodeVisitor*, 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)return;vertexArray->clear();for (int a = 0; a < constantVelocity->numOfMasses; ++a){Mass* mass = constantVelocity->getMass(a);Vector3D* pos = &mass->pos;vertexArray->push_back(osg::Vec3(pos->x, pos->y, pos->z));}geometry->setVertexArray(vertexArray);vertexArray->dirty();}};class ConstantWordUpdateCallback : public osg::NodeCallback{public:virtual void operator()(osg::Node* node, osg::NodeVisitor* nv){osg::MatrixTransform *mt = dynamic_cast<osg::MatrixTransform*>(node);if (!mt)return;for (int a = 0; a < constantVelocity->numOfMasses; ++a){Mass* mass = constantVelocity->getMass(a);Vector3D* pos = &mass->pos;mt->setMatrix(osg::Matrix::translate(pos->x, pos->y + 1, pos->z));}traverse(node, nv);}};
为了篇幅限制,这里只给出匀速运动的回调代码,其他部分参看附录中的源码。

我们需要在每帧中计算时间以及在每帧中更新物理运动的参数,这部分在交互的代码FRAME(帧循环事件)中给出:

case (osgGA::GUIEventAdapter::FRAME):{double dt = 0.0;_currentTick = osg::Timer::instance()->tick();if (_currentTick != _lastTick){dt = osg::Timer::instance()->delta_s(_lastTick, _currentTick);_lastTick = _currentTick;}dt /= slowMotionRatio;timeElapsed += dt;float maxPossible_dt = 0.1f;int numOfIterations = (int)(dt / maxPossible_dt) + 1;if (numOfIterations != 0)dt = dt / numOfIterations;for (int a = 0; a < numOfIterations; ++a){constantVelocity->operate(dt);motionUnderGravitation->operate(dt);massConnectedWithSpring->operate(dt);}}
此外在按下F2和F3时调整运动的速度:

if (ea.getKey() == osgGA::GUIEventAdapter::KEY_F2){slowMotionRatio = 1.0f;}if (ea.getKey() == osgGA::GUIEventAdapter::KEY_F3){slowMotionRatio = 10.0f;}
编译运行程序:


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

#include "../osgNeHe.h"#include "Physics1.h"#include <QtCore/QTimer>#include <QtGui/QApplication>#include <QtGui/QVBoxLayout>#include <osgViewer/Viewer>#include <osgDB/ReadFile>#include <osgQt/GraphicsWindowQt>#include <osg/MatrixTransform>#include <osgText/Text>#include <osg/Point>#include <osgGA/TrackballManipulator>//////////////////////////////////////////////////////////////////////////ConstantVelocity* constantVelocity = new ConstantVelocity();MotionUnderGravitation* motionUnderGravitation = new MotionUnderGravitation(Vector3D(0.0f, -9.81f, 0.0f));MassConnectedWithSpring* massConnectedWithSpring = new MassConnectedWithSpring(2.0f);float slowMotionRatio = 10.0f;float timeElapsed = 0;osg::Node* glPrint(GLfloat x, GLfloat y, GLfloat z, const osg::Vec4& fontColor, const char *string, ...){chartext[256];va_listap;if (string == NULL)return NULL;va_start(ap, string);vsprintf(text, string, ap);va_end(ap);osg::MatrixTransform *posMT = new osg::MatrixTransform;posMT->setMatrix(osg::Matrix::translate(x, y, z));osgText::Text *textWords = new osgText::Text;textWords->setColor(fontColor);textWords->setText(text);textWords->setFont("Fonts/Arial.ttf");textWords->setCharacterSize(1.0f);osg::Geode *fontGeode = new osg::Geode;fontGeode->addDrawable(textWords);posMT->addChild(fontGeode);return posMT;}//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////class ManipulatorSceneHandler : public osgGA::GUIEventHandler{public:ManipulatorSceneHandler(){_lastTick  =  osg::Timer::instance()->tick();_currentTick = _lastTick;}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):{double dt = 0.0;_currentTick = osg::Timer::instance()->tick();if (_currentTick != _lastTick){dt = osg::Timer::instance()->delta_s(_lastTick, _currentTick);_lastTick = _currentTick;}dt /= slowMotionRatio;timeElapsed += dt;float maxPossible_dt = 0.1f;int numOfIterations = (int)(dt / maxPossible_dt) + 1;if (numOfIterations != 0)dt = dt / numOfIterations;for (int a = 0; a < numOfIterations; ++a){constantVelocity->operate(dt);motionUnderGravitation->operate(dt);massConnectedWithSpring->operate(dt);}}break;case(osgGA::GUIEventAdapter::KEYDOWN):{if (ea.getKey() == osgGA::GUIEventAdapter::KEY_F2){slowMotionRatio = 1.0f;}if (ea.getKey() == osgGA::GUIEventAdapter::KEY_F3){slowMotionRatio = 10.0f;}}break;default: break;}return false;}osg::Timer_t _lastTick;osg::Timer_t _currentTick;};////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////Constant Callbackclass ConstantPointUpdateCallback : public osg::Drawable::UpdateCallback{public:virtual void update(osg::NodeVisitor*, 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)return;vertexArray->clear();for (int a = 0; a < constantVelocity->numOfMasses; ++a){Mass* mass = constantVelocity->getMass(a);Vector3D* pos = &mass->pos;vertexArray->push_back(osg::Vec3(pos->x, pos->y, pos->z));}geometry->setVertexArray(vertexArray);vertexArray->dirty();}};class ConstantWordUpdateCallback : public osg::NodeCallback{public:virtual void operator()(osg::Node* node, osg::NodeVisitor* nv){osg::MatrixTransform *mt = dynamic_cast<osg::MatrixTransform*>(node);if (!mt)return;for (int a = 0; a < constantVelocity->numOfMasses; ++a){Mass* mass = constantVelocity->getMass(a);Vector3D* pos = &mass->pos;mt->setMatrix(osg::Matrix::translate(pos->x, pos->y + 1, pos->z));}traverse(node, nv);}};//Gravity Callbackclass GravitationPointUpdateCallback : public osg::Drawable::UpdateCallback{public:virtual void update(osg::NodeVisitor*, 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)return;vertexArray->clear();for (int a = 0; a < motionUnderGravitation->numOfMasses; ++a){Mass* mass = motionUnderGravitation->getMass(a);Vector3D* pos = &mass->pos;vertexArray->push_back(osg::Vec3(pos->x, pos->y, pos->z));}geometry->setVertexArray(vertexArray);vertexArray->dirty();}};class GravitationWordUpdateCallback : public osg::NodeCallback{public:virtual void operator()(osg::Node* node, osg::NodeVisitor* nv){osg::MatrixTransform *mt = dynamic_cast<osg::MatrixTransform*>(node);if (!mt)return;for (int a = 0; a < motionUnderGravitation->numOfMasses; ++a){Mass* mass = motionUnderGravitation->getMass(a);Vector3D* pos = &mass->pos;mt->setMatrix(osg::Matrix::translate(pos->x, pos->y + 1, pos->z));}traverse(node, nv);}};//SpringCallbackclass SpringPointUpdateCallback : public osg::Drawable::UpdateCallback{public:virtual void update(osg::NodeVisitor*, 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)return;vertexArray->clear();for (int a = 0; a < massConnectedWithSpring->numOfMasses; ++a){Mass* mass = massConnectedWithSpring->getMass(a);Vector3D* pos = &mass->pos;vertexArray->push_back(osg::Vec3(pos->x, pos->y, pos->z));}geometry->setVertexArray(vertexArray);vertexArray->dirty();}};class SpringWordUpdateCallback : public osg::NodeCallback{public:virtual void operator()(osg::Node* node, osg::NodeVisitor* nv){osg::MatrixTransform *mt = dynamic_cast<osg::MatrixTransform*>(node);if (!mt)return;for (int a = 0; a < massConnectedWithSpring->numOfMasses; ++a){Mass* mass = massConnectedWithSpring->getMass(a);Vector3D* pos = &mass->pos;mt->setMatrix(osg::Matrix::translate(pos->x, pos->y + 1, pos->z));}traverse(node, nv);}};class SpringLineUpdateCallback : public osg::Drawable::UpdateCallback{public:virtual void update(osg::NodeVisitor*, 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)return;vertexArray->clear();for (int a = 0; a < massConnectedWithSpring->numOfMasses; ++a){Mass* mass = massConnectedWithSpring->getMass(a);Vector3D* pos = &mass->pos;vertexArray->push_back(osg::Vec3(pos->x, pos->y, pos->z));Vector3D *tmp = &massConnectedWithSpring->connectionPos;vertexArray->push_back(osg::Vec3(tmp->x, tmp->y, tmp->z));}geometry->setVertexArray(vertexArray);vertexArray->dirty();}};class TimeEllapsedUpdateCallback : public osg::NodeCallback{public:virtual void operator()(osg::Node* node, osg::NodeVisitor* nv){osg::MatrixTransform *mt = dynamic_cast<osg::MatrixTransform*>(node);if (!mt)return;osg::Geode *geode = dynamic_cast<osg::Geode*>(mt->getChild(0));if (!geode)return;osgText::Text *text = dynamic_cast<osgText::Text*>(geode->getDrawable(0));if (!text)return;std::stringstream os;std::string str;os.precision(2);os << std::fixed << "Time elapsed (seconds): " << timeElapsed;str = os.str();text->setText(str);traverse(node, nv);}};class RatioUpdateCallback : public osg::NodeCallback{public:virtual void operator()(osg::Node* node, osg::NodeVisitor* nv){osg::MatrixTransform *mt = dynamic_cast<osg::MatrixTransform*>(node);if (!mt)return;osg::Geode *geode = dynamic_cast<osg::Geode*>(mt->getChild(0));if (!geode)return;osgText::Text *text = dynamic_cast<osgText::Text*>(geode->getDrawable(0));if (!text)return;std::stringstream os;std::string str;os.precision(2);os << std::fixed << "Slow motion ratio: " << slowMotionRatio;str = os.str();text->setText(str);traverse(node, nv);}};//绘制背景网格osg::Geode*createBackGroundGeode(){osg::Geode *geode = new osg::Geode;osg::Geometry *geometry = new osg::Geometry;osg::Vec3Array *vertexArray = new osg::Vec3Array;osg::Vec3Array *colorArray = new osg::Vec3Array;colorArray->push_back(osg::Vec3(0, 0, 1.0));geometry->setColorArray(colorArray, osg::Array::BIND_OVERALL);for (float x = -20; x <= 20; x += 1.0f){vertexArray->push_back(osg::Vec3(x, 20, 0));vertexArray->push_back(osg::Vec3(x,-20, 0));}for (float y = -20; y <= 20; y += 1.0f){vertexArray->push_back(osg::Vec3(20, y, 0));vertexArray->push_back(osg::Vec3(-20, y, 0));}geometry->setVertexArray(vertexArray);geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, vertexArray->size()));geode->addDrawable(geometry);return geode;}osg::Geode*createPoint(const osg::Vec3& pos, const osg::Vec3& color, float size){osg::Geode *geode = new osg::Geode;osg::Geometry *geometry = new osg::Geometry;osg::Vec3Array *vertexArray = new osg::Vec3Array;osg::Vec3Array *colorArray = new osg::Vec3Array;colorArray->push_back(color);geometry->setColorArray(colorArray, osg::Array::BIND_OVERALL);vertexArray->push_back(pos);geometry->setVertexArray(vertexArray);geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, vertexArray->size()));osg::Point *point = new osg::Point;point->setSize(size);geometry->getOrCreateStateSet()->setAttributeAndModes(point);geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);geode->addDrawable(geometry);return geode;}osg::Geode*createLine(const osg::Vec3& posBeg, const osg::Vec3& posEnd, const osg::Vec3& color){osg::Geode *geode = new osg::Geode;osg::Geometry *geometry = new osg::Geometry;osg::Vec3Array *vertexArray = new osg::Vec3Array;osg::Vec3Array *colorArray = new osg::Vec3Array;vertexArray->push_back(posBeg);vertexArray->push_back(posEnd);geometry->setVertexArray(vertexArray);colorArray->push_back(color);geometry->setColorArray(colorArray, osg::Array::BIND_OVERALL);geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, vertexArray->size()));geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);geode->addDrawable(geometry);return geode;}//绘制Constant Velocityosg::Node*constantVelocityNode(){osg::Group *constantVelocityGroup = new osg::Group;for (int a = 0; a < constantVelocity->numOfMasses; ++a){Mass* mass = constantVelocity->getMass(a);Vector3D* pos = &mass->pos;osg::Node *node = glPrint(pos->x, pos->y + 1, pos->z, osg::Vec4(1,0,0,1), "Mass with constant vel");osg::Geode *geode = createPoint(osg::Vec3(pos->x, pos->y, pos->z), osg::Vec3(1,0,0), 4);geode->getDrawable(0)->setUpdateCallback(new ConstantPointUpdateCallback);node->addUpdateCallback(new ConstantWordUpdateCallback);constantVelocityGroup->addChild(node);constantVelocityGroup->addChild(geode);}return constantVelocityGroup;}//绘制motionUnderGravitationosg::Node* underGravitationNode(){osg::Group *group = new osg::Group;for (int a = 0; a < motionUnderGravitation->numOfMasses; ++a){Mass* mass = motionUnderGravitation->getMass(a);Vector3D* pos = &mass->pos;osg::Node *wordNode = glPrint(pos->x, pos->y + 1, pos->z, osg::Vec4(1,1,0,1), "Motion under gravitation");osg::Geode *pointNode = createPoint(osg::Vec3(pos->x, pos->y, pos->z), osg::Vec3(1,1,0), 4);wordNode->addUpdateCallback(new GravitationWordUpdateCallback);pointNode->getDrawable(0)->setUpdateCallback(new GravitationPointUpdateCallback);group->addChild(wordNode);group->addChild(pointNode);}return group;}//绘制massConnectedWithSpringosg::Node* massConnectedWithSpringNode(){osg::Group *group = new osg::Group;for (int a = 0; a < massConnectedWithSpring->numOfMasses; ++a){Mass* mass = massConnectedWithSpring->getMass(a);Vector3D* pos = &mass->pos;osg::Node *wordNode = glPrint(pos->x, pos->y + 1, pos->z, osg::Vec4(0,1,0,1), "Mass connected with spring");osg::Geode *pointNode = createPoint(osg::Vec3(pos->x, pos->y, pos->z), osg::Vec3(0,1,0), 8);group->addChild(wordNode);group->addChild(pointNode);wordNode->addUpdateCallback(new SpringWordUpdateCallback);pointNode->getDrawable(0)->setUpdateCallback(new SpringPointUpdateCallback);Vector3D *tmp = &massConnectedWithSpring->connectionPos;osg::Geode *lineNode = createLine(osg::Vec3(pos->x, pos->y, pos->z), osg::Vec3(tmp->x, tmp->y, tmp->z), osg::Vec3(0,1,0));//Force the lineNode Draw Upon BackgroundlineNode->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);group->addChild(lineNode);lineNode->getDrawable(0)->setUpdateCallback(new SpringLineUpdateCallback);}return group;}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, 40), osg::Vec3d(0, 0, 0), osg::Vec3d(0, 1, 0));//OSG默认会将点裁剪掉camera->setCullingMode(camera->getCullingMode() &~osg::CullSettings::SMALL_FEATURE_CULLING); addEventHandler(new ManipulatorSceneHandler);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(){osg::Group *root = new osg::Group;root->addChild(createBackGroundGeode());root->addChild(constantVelocityNode());root->addChild(underGravitationNode());root->addChild(massConnectedWithSpringNode());osg::Node *timeEllapsedNode = glPrint(-15.0f, 14, 0, osg::Vec4(1,1,1,1), "Time elapsed (seconds): %.2f", timeElapsed);timeEllapsedNode->addUpdateCallback(new TimeEllapsedUpdateCallback);root->addChild(timeEllapsedNode);osg::Node *ratioNode = glPrint(-15.0f, 13, 0, osg::Vec4(1,1,1,1),"Slow motion ratio: %.2f", slowMotionRatio);ratioNode->addUpdateCallback(new RatioUpdateCallback);root->addChild(ratioNode);root->addChild(glPrint(-15.0f, 12, 0, osg::Vec4(1,1,1,1),"Press F2 for normal motion"));root->addChild(glPrint(-15.0f, 11, 0, osg::Vec4(1,1,1,1),"Press F3 for slow motion"));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