This an example of how to animate an X3D model. Here we want to deform the model globally. This is realized by deforming directly the GL graph scene. At each time step, the tree is traversed and the vertices of the GL::IndexedFaceSet
nodes are moved.
To animate the model, we need to store the time as a global variable to the traversal, as well as the bounding box of the scene, necessary to the deformation function.
We have to visit the GL::IndexedFaceSet
nodes. To be general, we have to process the different possible types of vertex arrays, implying that the code is redundant. There are 8 cases: 4 vertex formats plus the fact that the vertices can be duplicated. Indeed sometimes the model requires that the GL vertices are duplicated. It means that for a vertex of the X3D::IndexedFaceSet
node, there are one or more vertices for the GL::IndexedFaceSet
node. That is why, to find the relation between the X3D vertices and the GL vertices, we use the X3DToGLIndex.
This is the facade of the processor.
We customize SimpleX3DGLScene
by adding methods relative to the animation of the model.
#ifndef GLSIMPLEANIMATORGLOBALVARIABLES_H #define GLSIMPLEANIMATORGLOBALVARIABLES_H #include <X3DTK/kernel.h> namespace X3DTK { namespace GL { class SimpleAnimatorStateVariables : public StateVariables { public: SimpleAnimatorStateVariables(); virtual ~SimpleAnimatorStateVariables(); void setBBoxSize(const SFVec3f &size); void setTime(float time); float getTime() const {return _time;}; inline SFVec3f getBBoxSize() const {return _bboxSize;}; private: float _time; SFVec3f _bboxSize; }; } } #endif
#include "GL_SimpleAnimatorStateVariables.h" namespace X3DTK { namespace GL { SimpleAnimatorStateVariables::SimpleAnimatorStateVariables() : StateVariables(), _time(0.0f) { } SimpleAnimatorStateVariables::~SimpleAnimatorStateVariables() { } void SimpleAnimatorStateVariables::setBBoxSize(const SFVec3f &size) { _bboxSize = size; } void SimpleAnimatorStateVariables::setTime(float time) { _time = time; } } }
#ifndef GLSIMPLEANIMATORGEOMETRY3DVISITOR_H #define GLSIMPLEANIMATORGEOMETRY3DVISITOR_H #include "GL_SimpleAnimatorStateVariables.h" #include <X3DTK/gl_scenegraph.h> namespace X3DTK { namespace GL { class IndexedFaceSet; class SimpleAnimatorGeometry3DVisitor : public Geometry3DVisitor { public: SimpleAnimatorGeometry3DVisitor(); virtual ~SimpleAnimatorGeometry3DVisitor(); virtual void enterIndexedFaceSet(IndexedFaceSet *I) const; protected: SimpleAnimatorStateVariables *stateVariables; }; } } #endif
#include "GL_SimpleAnimatorGeometry3DVisitor.h" #include <iostream> #include <cmath> using namespace std; namespace X3DTK { namespace GL { SimpleAnimatorGeometry3DVisitor::SimpleAnimatorGeometry3DVisitor() : Geometry3DVisitor() { // Enter functions. defineEnterFunction(&SimpleAnimatorGeometry3DVisitor::enterIndexedFaceSet); // StateVariables assignation. stateVariables = GraphTraversal::getInstanceOf<SimpleAnimatorStateVariables>(); } SimpleAnimatorGeometry3DVisitor::~SimpleAnimatorGeometry3DVisitor() { } void SimpleAnimatorGeometry3DVisitor::enterIndexedFaceSet(IndexedFaceSet *I) const { //Parameters for the animation. SFVec3f size = stateVariables->getBBoxSize(); float mz = size.z; float mxy = (size.x < size.y ? size.y : size.x); float a = mxy*mxy*mz*0.1f*cosf(stateVariables->getTime()); float b = mxy*mxy; float c = 1.0f; float d = a/b; //switching to the appropriate case. 8 cases: 4 vertex formats, plus the fact that //the vertices can be duplicated when necessary. When duplicated, it means that two //vertices have the same coordinates, but different other properties like color... if (I->getVertexFormat() == GL_N3F_V3F) { vector<N3F_V3F> &vertexArray = I->N3F_V3F_vertexArray(); if (I->getVerticesDuplicated()) { //getting the list of vertices duplicated. const vector<MFInt32> &X3DToGLIndex = I->getX3DToGLIndex(); for (vector<MFInt32>::const_iterator itIndexArray = X3DToGLIndex.begin(); itIndexArray != X3DToGLIndex.end(); ++itIndexArray) { if ((*itIndexArray).size() > 0) { //computing the new coordinates for the first vertex. N3F_V3F *V = &vertexArray[(*itIndexArray).front()]; V->vertex.y = V->vertex.y - d + a/(b + c*(V->vertex.x*V->vertex.x + V->vertex.z*V->vertex.z)); //copying the coordinates for the others. for (MFInt32::const_iterator itIndex = ++((*itIndexArray).begin()); itIndex != (*itIndexArray).end(); ++itIndex) vertexArray[*itIndex].vertex = V->vertex; } } } else { //the vertices are not duplicated. for (vector<N3F_V3F>::iterator itVertex = vertexArray.begin(); itVertex != vertexArray.end(); ++itVertex) (*itVertex).vertex.y = (*itVertex).vertex.y - d + a/(b + c*((*itVertex).vertex.x*(*itVertex).vertex.x + (*itVertex).vertex.z*(*itVertex).vertex.z)); } } if (I->getVertexFormat() == GL_C4F_N3F_V3F) { vector<C4F_N3F_V3F> &vertexArray = I->C4F_N3F_V3F_vertexArray(); if (I->getVerticesDuplicated()) { //getting the list of vertices duplicated. const vector<MFInt32> &X3DToGLIndex = I->getX3DToGLIndex(); for (vector<MFInt32>::const_iterator itIndexArray = X3DToGLIndex.begin(); itIndexArray != X3DToGLIndex.end(); ++itIndexArray) { if ((*itIndexArray).size() > 0) { //computing the new coordinates for the first vertex. C4F_N3F_V3F *V = &vertexArray[(*itIndexArray).front()]; V->vertex.y = V->vertex.y - d + a/(b + c*(V->vertex.x*V->vertex.x + V->vertex.z*V->vertex.z)); //copying the coordinates for the others. for (MFInt32::const_iterator itIndex = ++((*itIndexArray).begin()); itIndex != (*itIndexArray).end(); ++itIndex) vertexArray[*itIndex].vertex = V->vertex; } } } else { //the vertices are not duplicated. for (vector<C4F_N3F_V3F>::iterator itVertex = vertexArray.begin(); itVertex != vertexArray.end(); ++itVertex) (*itVertex).vertex.y = (*itVertex).vertex.y - d + a/(b + c*((*itVertex).vertex.x*(*itVertex).vertex.x + (*itVertex).vertex.z*(*itVertex).vertex.z)); } } if (I->getVertexFormat() == GL_T2F_N3F_V3F) { vector<T2F_N3F_V3F> &vertexArray = I->T2F_N3F_V3F_vertexArray(); if (I->getVerticesDuplicated()) { //getting the list of vertices duplicated. const vector<MFInt32> &X3DToGLIndex = I->getX3DToGLIndex(); for (vector<MFInt32>::const_iterator itIndexArray = X3DToGLIndex.begin(); itIndexArray != X3DToGLIndex.end(); ++itIndexArray) { if ((*itIndexArray).size() > 0) { //computing the new coordinates for the first vertex. T2F_N3F_V3F *V = &vertexArray[(*itIndexArray).front()]; V->vertex.y = V->vertex.y - d + a/(b + c*(V->vertex.x*V->vertex.x + V->vertex.z*V->vertex.z)); //copying the coordinates for the others. for (MFInt32::const_iterator itIndex = ++((*itIndexArray).begin()); itIndex != (*itIndexArray).end(); ++itIndex) vertexArray[*itIndex].vertex = V->vertex; } } } else { //the vertices are not duplicated. for (vector<T2F_N3F_V3F>::iterator itVertex = vertexArray.begin(); itVertex != vertexArray.end(); ++itVertex) (*itVertex).vertex.y = (*itVertex).vertex.y - d + a/(b + c*((*itVertex).vertex.x*(*itVertex).vertex.x + (*itVertex).vertex.z*(*itVertex).vertex.z)); } } if (I->getVertexFormat() == GL_T2F_C4F_N3F_V3F) { vector<T2F_C4F_N3F_V3F> &vertexArray = I->T2F_C4F_N3F_V3F_vertexArray(); if (I->getVerticesDuplicated()) { //getting the list of vertices duplicated. const vector<MFInt32> &X3DToGLIndex = I->getX3DToGLIndex(); for (vector<MFInt32>::const_iterator itIndexArray = X3DToGLIndex.begin(); itIndexArray != X3DToGLIndex.end(); ++itIndexArray) { if ((*itIndexArray).size() > 0) { //computing the new coordinates for the first vertex. T2F_C4F_N3F_V3F *V = &vertexArray[(*itIndexArray).front()]; V->vertex.y = V->vertex.y - d + a/(b + c*(V->vertex.x*V->vertex.x + V->vertex.z*V->vertex.z)); //copying the coordinates for the others. for (MFInt32::const_iterator itIndex = ++((*itIndexArray).begin()); itIndex != (*itIndexArray).end(); ++itIndex) vertexArray[*itIndex].vertex = V->vertex; } } } else { //the vertices are not duplicated. for (vector<T2F_C4F_N3F_V3F>::iterator itVertex = vertexArray.begin(); itVertex != vertexArray.end(); ++itVertex) (*itVertex).vertex.y = (*itVertex).vertex.y - d + a/(b + c*((*itVertex).vertex.x*(*itVertex).vertex.x + (*itVertex).vertex.z*(*itVertex).vertex.z)); } } //recomputing the normals. I->computeNormals(); } } }
#ifndef GLSIMPLEANIMATOR_H #define GLSIMPLEANIMATOR_H #include "GL_SimpleAnimatorStateVariables.h" #include <X3DTK/gl_scenegraph.h> namespace X3DTK { namespace GL { class SimpleAnimator : public X3DOnePassProcessor { public: SimpleAnimator(); virtual ~SimpleAnimator(); void setBBoxSize(const SFVec3f &size); virtual void animate(SFNode N, float time); protected: SimpleAnimatorStateVariables *stateVariables; }; } } #endif
#include "GL_SimpleAnimator.h" #include "GL_SimpleAnimatorGeometry3DVisitor.h" namespace X3DTK { namespace GL { SimpleAnimator::SimpleAnimator() : X3DOnePassProcessor() { //Getting the global variables. stateVariables = GraphTraversal::getInstanceOf<SimpleAnimatorStateVariables>(); graphTraversal = new DFSGraphTraversal(); graphTraversal->setComponentVisitor(new SimpleAnimatorGeometry3DVisitor()); } SimpleAnimator::~SimpleAnimator() { GraphTraversal::removeInstanceOf<SimpleAnimatorStateVariables>(); delete graphTraversal; } void SimpleAnimator::setBBoxSize(const SFVec3f &size) { stateVariables->setBBoxSize(size); } void SimpleAnimator::animate(SFNode N, float time) { // Setting the time. stateVariables->setTime(time); graphTraversal->traverse(N); } } }
#ifndef SIMPLEANIMATIONSCENE_H #define SIMPLEANIMATIONSCENE_H #include "GL_SimpleAnimator.h" #include <X3DTK/simplex3dglscene.h> #include <X3DTK/x3d_worldcoordtranslator.h> namespace X3DTK { class SimpleAnimationScene : public SimpleX3DGLScene { public: SimpleAnimationScene(); virtual ~SimpleAnimationScene(); virtual void load(const char *file, bool fileValidation = true); virtual void init(); virtual void animate(); private: GL::SimpleAnimator *_glSimpleAnimator; X3D::WorldCoordTranslator *_translator; float _time; }; } #endif
#include "SimpleAnimationScene.h" #include <iostream> using namespace std; namespace X3DTK { SimpleAnimationScene::SimpleAnimationScene() : SimpleX3DGLScene(), _time(0.0f) { _glSimpleAnimator = X3DProcessor::getInstanceOf<GL::SimpleAnimator>(); _translator = X3DProcessor::getInstanceOf<X3D::WorldCoordTranslator>(); } SimpleAnimationScene::~SimpleAnimationScene() { } void SimpleAnimationScene::load(const char *file, bool fileValidation) { //Overloading the load method, to apply the WorldCoordTranslator processor to the scene. loadFile(file, fileValidation); _translator->translate(scene); computeBBox(); buildGLScene(); } void SimpleAnimationScene::init() { _time = 0.0f; if (scene != 0) _glSimpleAnimator->setBBoxSize(scene->getBBoxSize()); } void SimpleAnimationScene::animate() { _time += 0.03f; _glSimpleAnimator->animate(glscene, _time); } }
#ifndef VIEWER_H #define VIEWER_H #include <QGLViewer/qglviewer.h> #include "SimpleAnimationScene.h" class Viewer : public QGLViewer { public: Viewer(const char *file); ~Viewer(); protected : void loadFile(); void keyPressEvent(QKeyEvent *e); void init(); void animate(); void draw(); void about(); QString helpString() const; void help() const; private: X3DTK::SimpleAnimationScene scene; X3DTK::BBox BB; char *x3dfile; }; #endif
#include "Viewer.h" #include <math.h> #include <iostream> #include <qfiledialog.h> #include <qmessagebox.h> using namespace X3DTK; using namespace std; Viewer::Viewer(const char *file) { x3dfile = (char *)file; } Viewer::~Viewer() { scene.release(); } void Viewer::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_L : loadFile(); break; case Qt::Key_S : startAnimation(); break; case Qt::Key_P : stopAnimation(); break; default: QGLViewer::keyPressEvent(e); } updateGL(); } void Viewer::loadFile() { QString name = QFileDialog::getOpenFileName("", "X3D files (*.x3d *.X3D);;All files (*)", this); // In case of Cancel if (name.isEmpty()) return; // Loads the file name. scene.load(name, false); scene.init(); setSceneBoundingBox(scene.getBBoxMin().f_data(), scene.getBBoxMax().f_data()); setSceneRadius(2.0f*sceneRadius()); showEntireScene(); } void Viewer::init() { #ifdef GL_RESCALE_NORMAL glEnable(GL_RESCALE_NORMAL); #endif about(); loadFile(); } void Viewer::animate() { //Animates the scene. scene.animate(); } void Viewer::draw() { //traverse the scene graph scene.draw(); } void Viewer::about() { QMessageBox::about(this, "about the simpleAnimationViewer", "this is an example showing how to animate a simple X3D scene.Type 'h' to display help"); } QString Viewer::helpString() const { QString message(""); message += "<b>S</b>" + QString(" starts the animation<br>"); message += "<b>P</b>" + QString(" pauses the animation<br>"); message += "<b>L</b>" + QString(" loads a new file<br>"); message += QGLViewer::helpString(); return message; } void Viewer::help() const { QMessageBox *mb = new QMessageBox("help", helpString(), QMessageBox::NoIcon,QMessageBox::Ok | QMessageBox::Default, QMessageBox::NoButton,QMessageBox::NoButton, NULL, "Help", false,Qt::WStyle_DialogBorder | Qt::WType_Dialog | Qt::WDestructiveClose); mb->show(); }