-
简介
NeHe教程在这节课中向我们介绍了如何从文件加载3D模型,并且平滑的从一个模型变换为另一个模型。这个过程又称为逐点动画,点之间的变化过程算法十分简单,采用的是线性的算法。
首先我们仿照NeHe课程中定义了表示点的结构体以及读取文件的结构体OBJECT并读取文件
- void initMorphObject()
- {
- objload("Data/sphere.txt",&morph1);
- objload("Data/torus.txt",&morph2);
- objload("Data/tube.txt",&morph3);
- objallocate(&morph4,486);
- morph4.verts = 486;
- for(int i=0;i<486;i++)
- {
- morph4.points[i].x=((float)(rand()%14000)/1000)-7;
- morph4.points[i].y=((float)(rand()%14000)/1000)-7;
- morph4.points[i].z=((float)(rand()%14000)/1000)-7;
- }
- morphGeometry1 = new osg::Geometry;
- osg::Vec3Array *vertexArray1 = new osg::Vec3Array;
- for (unsigned i = 0; i < morph1.verts; ++i)
- {
- vertexArray1->push_back(osg::Vec3(morph1.points[i].x, morph1.points[i].y, morph1.points[i].z));
- }
- morphGeometry1->setVertexArray(vertexArray1);
- morphGeometry1->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, 486));
- morphGeometry1->setUseDisplayList(false);
- srcMorphGeometry = dynamic_cast<osg::Geometry*>(morphGeometry1->clone(osg::CopyOp::DEEP_COPY_ALL));
- destMorphGeometry = morphGeometry1;
- ...
- }
上述的处理过程在几何体的更新回调MorphUpdateCallback之中
- virtual void update(osg::NodeVisitor*, osg::Drawable* drawable)
- {
- osg::Geometry *geom = dynamic_cast<osg::Geometry*>(drawable);
- if (!geom)
- return;
- osg::Vec3Array *vertices = dynamic_cast<osg::Vec3Array*>(geom->getVertexArray());
- osg::Vec3Array *destVertices = dynamic_cast<osg::Vec3Array*>(_destGeometry->getVertexArray());
- if (vertices && destVertices)
- {
- for (unsigned i = 0; i < vertices->size(); ++i)
- {
- osg::Vec3 delta = (destVertices->at(i) - vertices->at(i)) / _steps;
- vertices->at(i) += delta;
- }
- vertices->dirty();
- }
- }
- if (ea.getKey() == osgGA::GUIEventAdapter::KEY_1)
- {
- MorphUpdateCallback *muc = dynamic_cast<MorphUpdateCallback*>(srcMorphGeometry->getUpdateCallback());
- if (!muc)
- return false;
- muc->setDestGeometry(morphGeometry1);
- }
- if (ea.getKey() == osgGA::GUIEventAdapter::KEY_2)
- {
- MorphUpdateCallback *muc = dynamic_cast<MorphUpdateCallback*>(srcMorphGeometry->getUpdateCallback());
- if (!muc)
- return false;
- muc->setDestGeometry(morphGeometry2);
- }
通过设置几何体变换的开始形状(srcGeometry)和最终变换到的目标几何体(destGeometry),在回调中不断调整顶点位置完成动画效果。
编译运行程序:
附:本课源码(源码中可能存在错误和不足,仅供参考)
- /************************************************************************\
- * osgNeHe - Copyright (C) 2013-2014 Frank He
- *
- * This library is open source and may be redistributed and/or modified under
- * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
- * (at your option) any later version. The full license is in LICENSE file
- * included with this distribution, and on the openscenegraph.org website.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- *
- * A HUGE thanks to NeHe for the OpenGL tutorials< http://nehe.gamedev.net >
- *
- * If you've found this code useful, Please let me know.
- * My E-mail: < hzhxfrank@gmail.com >
- * My Blog: < http://blog.youkuaiyun.com/csxiaoshui >
- *
- \************************************************************************/
- #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/Geometry>
- GLfloat cx,cy,cz = -15.0f;
- int step=0;
- int steps = 200;
- bool morph = false;
- osg::MatrixTransform *g_transMT = NULL;
- osg::MatrixTransform *g_rotateX = NULL;
- osg::MatrixTransform *g_rotateY = NULL;
- osg::MatrixTransform *g_rotateZ = NULL;
- struct VERTEX
- {
- float x, y, z;
- };
- struct OBJECT
- {
- int verts;
- VERTEX *points;
- } ;
- OBJECT morph1,morph2,morph3,morph4;
- osg::Geometry *morphGeometry1, *morphGeometry2, *morphGeometry3, *morphGeometry4;
- osg::Geometry *srcMorphGeometry, *destMorphGeometry;
- void objallocate(OBJECT *k,int n)
- {
- k->points=(VERTEX*)malloc(sizeof(VERTEX)*n);
- }
- void objfree(OBJECT *k)
- {
- free(k->points);
- }
- void readstr(FILE *f,char *string)
- {
- do
- {
- fgets(string, 255, f);
- } while ((string[0] == '/') || (string[0] == '\n'));
- return;
- }
- void objload(char *name,OBJECT *k)
- {
- int ver;
- float rx,ry,rz;
- FILE *filein;
- char oneline[255];
- filein = fopen(name, "rt");
- readstr(filein,oneline);
- sscanf(oneline, "Vertices: %d\n", &ver);
- k->verts=ver;
- objallocate(k,ver);
- for (int i=0;i<ver;i++)
- {
- readstr(filein,oneline);
- sscanf(oneline, "%f %f %f", &rx, &ry, &rz);
- k->points[i].x = rx;
- k->points[i].y = ry;
- k->points[i].z = rz;
- }
- fclose(filein);
- }
- //
- class MorphUpdateCallback : public osg::Drawable::UpdateCallback
- {
- public:
- MorphUpdateCallback(osg::Geometry *dest, int steps) : _destGeometry(dest), _steps(steps)
- {
- }
- void setDestGeometry(osg::Geometry *destGeometry)
- {
- _destGeometry = destGeometry;
- }
- virtual void update(osg::NodeVisitor*, osg::Drawable* drawable)
- {
- osg::Geometry *geom = dynamic_cast<osg::Geometry*>(drawable);
- if (!geom)
- return;
- osg::Vec3Array *vertices = dynamic_cast<osg::Vec3Array*>(geom->getVertexArray());
- osg::Vec3Array *destVertices = dynamic_cast<osg::Vec3Array*>(_destGeometry->getVertexArray());
- if (vertices && destVertices)
- {
- for (unsigned i = 0; i < vertices->size(); ++i)
- {
- osg::Vec3 delta = (destVertices->at(i) - vertices->at(i)) / _steps;
- vertices->at(i) += delta;
- }
- vertices->dirty();
- }
- }
- int _steps;
- osg::Geometry* _destGeometry;
- };
- void initMorphObject()
- {
- objload("Data/sphere.txt",&morph1);
- objload("Data/torus.txt",&morph2);
- objload("Data/tube.txt",&morph3);
- objallocate(&morph4,486);
- morph4.verts = 486;
- for(int i=0;i<486;i++)
- {
- morph4.points[i].x=((float)(rand()%14000)/1000)-7;
- morph4.points[i].y=((float)(rand()%14000)/1000)-7;
- morph4.points[i].z=((float)(rand()%14000)/1000)-7;
- }
- morphGeometry1 = new osg::Geometry;
- osg::Vec3Array *vertexArray1 = new osg::Vec3Array;
- for (unsigned i = 0; i < morph1.verts; ++i)
- {
- vertexArray1->push_back(osg::Vec3(morph1.points[i].x, morph1.points[i].y, morph1.points[i].z));
- }
- morphGeometry1->setVertexArray(vertexArray1);
- morphGeometry1->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, 486));
- morphGeometry1->setUseDisplayList(false);
- srcMorphGeometry = dynamic_cast<osg::Geometry*>(morphGeometry1->clone(osg::CopyOp::DEEP_COPY_ALL));
- destMorphGeometry = morphGeometry1;
- morphGeometry2 = new osg::Geometry;
- osg::Vec3Array *vertexArray2 = new osg::Vec3Array;
- for (unsigned i = 0; i < morph2.verts; ++i)
- {
- vertexArray2->push_back(osg::Vec3(morph2.points[i].x, morph2.points[i].y, morph2.points[i].z));
- }
- morphGeometry2->setVertexArray(vertexArray2);
- morphGeometry2->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, 486));
- morphGeometry2->setUseDisplayList(false);
- morphGeometry3 = new osg::Geometry;
- osg::Vec3Array *vertexArray3 = new osg::Vec3Array;
- for (unsigned i = 0; i < morph3.verts; ++i)
- {
- vertexArray3->push_back(osg::Vec3(morph3.points[i].x, morph3.points[i].y, morph3.points[i].z));
- }
- morphGeometry3->setVertexArray(vertexArray3);
- morphGeometry3->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, 486));
- morphGeometry3->setUseDisplayList(false);
- morphGeometry4 = new osg::Geometry;
- osg::Vec3Array *vertexArray4 = new osg::Vec3Array;
- for (unsigned i = 0; i < morph4.verts; ++i)
- {
- vertexArray4->push_back(osg::Vec3(morph4.points[i].x, morph4.points[i].y, morph4.points[i].z));
- }
- morphGeometry4->setVertexArray(vertexArray4);
- morphGeometry4->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, 486));
- morphGeometry4->setUseDisplayList(false);
- }
- //
- class RotAxisCallback : public osg::NodeCallback
- {
- public:
- RotAxisCallback(const osg::Vec3& axis, double rotSpeed = 0.0, double currentAngle = 0.0)
- : _rotAxis(axis), _rotSpeed(rotSpeed), _currentAngle(currentAngle){ }
- virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
- {
- osg::MatrixTransform *rotMT = dynamic_cast<osg::MatrixTransform*>(node);
- if (!rotMT)
- return;
- rotMT->setMatrix(osg::Matrix::rotate(_currentAngle, _rotAxis));
- _currentAngle += _rotSpeed;
- traverse(node, nv);
- }
- void setRotateSpeed(double speed)
- {
- _rotSpeed = speed;
- }
- double getRotateSpeed() const
- {
- return _rotSpeed;
- }
- private:
- osg::Vec3 _rotAxis;
- double _currentAngle;
- double _rotSpeed;
- };
- //
- 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_Left)
- {
- if (!g_rotateX)
- return false;
- RotAxisCallback *rotCallback = dynamic_cast<RotAxisCallback*>(g_rotateX->getUpdateCallback());
- if (!rotCallback)
- return false;
- double speed = rotCallback->getRotateSpeed();
- speed -= 0.02;
- rotCallback->setRotateSpeed(speed);
- }
- if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Right)
- {
- if (!g_rotateX)
- return false;
- RotAxisCallback *rotCallback = dynamic_cast<RotAxisCallback*>(g_rotateX->getUpdateCallback());
- if (!rotCallback)
- return false;
- double speed = rotCallback->getRotateSpeed();
- speed += 0.02;
- rotCallback->setRotateSpeed(speed);
- }
- if (ea.getKey() == osgGA::GUIEventAdapter::KEY_Up)
- {
- if (!g_rotateY)
- return false;
- RotAxisCallback *rotCallback = dynamic_cast<RotAxisCallback*>(g_rotateY->getUpdateCallback());
- if (!rotCallback)
- return false;
- double speed = rotCallback->getRotateSpeed();
- speed -= 0.02;
- rotCallback->setRotateSpeed(speed);
- }
- if (ea.getKey()== osgGA::GUIEventAdapter::KEY_Down)
- {
- if (!g_rotateY)
- return false;
- RotAxisCallback *rotCallback = dynamic_cast<RotAxisCallback*>(g_rotateY->getUpdateCallback());
- if (!rotCallback)
- return false;
- double speed = rotCallback->getRotateSpeed();
- speed += 0.02;
- rotCallback->setRotateSpeed(speed);
- }
- if (ea.getKey()== osgGA::GUIEventAdapter::KEY_Page_Up)
- {
- if (!g_rotateZ)
- return false;
- RotAxisCallback *rotCallback = dynamic_cast<RotAxisCallback*>(g_rotateZ->getUpdateCallback());
- if (!rotCallback)
- return false;
- double speed = rotCallback->getRotateSpeed();
- speed -= 0.02;
- rotCallback->setRotateSpeed(speed);
- }
- if (ea.getKey()== osgGA::GUIEventAdapter::KEY_Page_Down)
- {
- if (!g_rotateZ)
- return false;
- RotAxisCallback *rotCallback = dynamic_cast<RotAxisCallback*>(g_rotateZ->getUpdateCallback());
- if (!rotCallback)
- return false;
- double speed = rotCallback->getRotateSpeed();
- speed += 0.02;
- rotCallback->setRotateSpeed(speed);
- }
- if (ea.getKey()== osgGA::GUIEventAdapter::KEY_A)
- {
- if (!g_transMT)
- return false;
- osg::Matrix transMatrix = g_transMT->getMatrix();
- transMatrix.setTrans(transMatrix.getTrans() + osg::Vec3(-0.1, 0, 0));
- g_transMT->setMatrix(transMatrix);
- }
- if (ea.getKey()== osgGA::GUIEventAdapter::KEY_D)
- {
- if (!g_transMT)
- return false;
- osg::Matrix transMatrix = g_transMT->getMatrix();
- transMatrix.setTrans(transMatrix.getTrans() + osg::Vec3(0.1, 0, 0));
- g_transMT->setMatrix(transMatrix);
- }
- if (ea.getKey()== osgGA::GUIEventAdapter::KEY_W)
- {
- if (!g_transMT)
- return false;
- osg::Matrix transMatrix = g_transMT->getMatrix();
- transMatrix.setTrans(transMatrix.getTrans() + osg::Vec3(0, 0.1, 0));
- g_transMT->setMatrix(transMatrix);
- }
- if (ea.getKey()== osgGA::GUIEventAdapter::KEY_S)
- {
- if (!g_transMT)
- return false;
- osg::Matrix transMatrix = g_transMT->getMatrix();
- transMatrix.setTrans(transMatrix.getTrans() + osg::Vec3(0, -0.1, 0));
- g_transMT->setMatrix(transMatrix);
- }
- if (ea.getKey()== osgGA::GUIEventAdapter::KEY_Q)
- {
- if (!g_transMT)
- return false;
- osg::Matrix transMatrix = g_transMT->getMatrix();
- transMatrix.setTrans(transMatrix.getTrans() + osg::Vec3(0, 0, -0.1));
- g_transMT->setMatrix(transMatrix);
- }
- if (ea.getKey()== osgGA::GUIEventAdapter::KEY_Z)
- {
- if (!g_transMT)
- return false;
- osg::Matrix transMatrix = g_transMT->getMatrix();
- transMatrix.setTrans(transMatrix.getTrans() + osg::Vec3(0, 0, 0.1));
- g_transMT->setMatrix(transMatrix);
- }
- if (ea.getKey() == osgGA::GUIEventAdapter::KEY_1)
- {
- MorphUpdateCallback *muc = dynamic_cast<MorphUpdateCallback*>(srcMorphGeometry->getUpdateCallback());
- if (!muc)
- return false;
- muc->setDestGeometry(morphGeometry1);
- }
- if (ea.getKey() == osgGA::GUIEventAdapter::KEY_2)
- {
- MorphUpdateCallback *muc = dynamic_cast<MorphUpdateCallback*>(srcMorphGeometry->getUpdateCallback());
- if (!muc)
- return false;
- muc->setDestGeometry(morphGeometry2);
- }
- if (ea.getKey() == osgGA::GUIEventAdapter::KEY_3)
- {
- MorphUpdateCallback *muc = dynamic_cast<MorphUpdateCallback*>(srcMorphGeometry->getUpdateCallback());
- if (!muc)
- return false;
- muc->setDestGeometry(morphGeometry3);
- }
- if (ea.getKey() == osgGA::GUIEventAdapter::KEY_4)
- {
- MorphUpdateCallback *muc = dynamic_cast<MorphUpdateCallback*>(srcMorphGeometry->getUpdateCallback());
- if (!muc)
- return false;
- muc->setDestGeometry(morphGeometry4);
- }
- }
- default: break;
- }
- return false;
- }
- };
- class ViewerWidget : public QWidget, public osgViewer::Viewer
- {
- public:
- ViewerWidget(osg::Node *scene = NULL)
- {
- QWidget* renderWidget = getRenderWidget( createGraphicsWindow(0,0,640,480), 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()
- {
- initMorphObject();
- osg::Group *root = new osg::Group;
- root->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE);
- osg::MatrixTransform *mt = new osg::MatrixTransform;
- g_transMT = mt;
- mt->setMatrix(osg::Matrix::translate(0, 0, cz));
- osg::MatrixTransform *rotX = new osg::MatrixTransform;
- g_rotateX = rotX;
- rotX->addUpdateCallback(new RotAxisCallback(osg::X_AXIS));
- osg::MatrixTransform *rotY = new osg::MatrixTransform;
- g_rotateY = rotY;
- rotY->addUpdateCallback(new RotAxisCallback(osg::Y_AXIS));
- osg::MatrixTransform *rotZ = new osg::MatrixTransform;
- g_rotateZ = rotZ;
- rotZ->addUpdateCallback(new RotAxisCallback(osg::Z_AXIS));
- osg::Geode *geode = new osg::Geode;
- geode->addDrawable(srcMorphGeometry);
- srcMorphGeometry->setUpdateCallback(new MorphUpdateCallback(destMorphGeometry,200));
- root->addChild(mt);
- mt->addChild(rotX);
- rotX->addChild(rotY);
- rotY->addChild(rotZ);
- rotZ->addChild(geode);
- 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();
- }