This an example of how to write a new processor. Here we want to define a processor displaying in a GL context the normals of the model and controlling the length of the normals.
To control the length of the normals displayed to screen, we have to memorize a variable global to the traverse of the scene graph. That's why we derive GlobalVariables
and add methods to set and get the length.
To draw the normals in the correct coordinates, we have to push the Transform matrices. Thus we define the methods enterTransform
and leaveTransform
operating on the GL::Transform
node that belongs to the Grouping component.
To draw the normals we have to visit the GL::IndexedFaceSet
node which already has the vertexArrays updated to be rendered by OpenGL. We just have to get the normals from the arrays. That is here that we get the length of the normals. The instance of the GL::NormalRendererGlobalVariables
is given by GVManager
which controls the number of instances.
This is the facade of the processor which aggregates the visitors of the different components.
We customize SimpleX3DGLScene
by adding methods relative to the display of the normals.
#ifndef GLNORMALRENDERERGLOBALVARIABLES_H #define GLNORMALRENDERERGLOBALVARIABLES_H #include <X3DTK/kernel.h> namespace X3DTK { namespace GL { class NormalRendererGlobalVariables : public GlobalVariables { public: NormalRendererGlobalVariables(); virtual ~NormalRendererGlobalVariables(); void setNormalLength(float value); float getNormalLength() const {return _normalLength;}; private: float _normalLength; }; } } #endif
#include "GLNormalRendererGlobalVariables.h" namespace X3DTK { namespace GL { NormalRendererGlobalVariables::NormalRendererGlobalVariables() : GlobalVariables(), _normalLength(1.0f) { } NormalRendererGlobalVariables::~NormalRendererGlobalVariables() { } void NormalRendererGlobalVariables::setNormalLength(float value) { _normalLength = value; } } }
#ifndef GLNORMALRENDERERGROUPINGVISITOR_H #define GLNORMALRENDERERGROUPINGVISITOR_H #include <X3DTK/glscenegraph.h> namespace X3DTK { namespace GL { class NormalRendererGroupingVisitor : public GroupingVisitor { public: NormalRendererGroupingVisitor(); virtual ~NormalRendererGroupingVisitor(); virtual void enterTransform(Transform *T) const; virtual void leaveTransform(Transform *T) const; }; } } #endif
#include "GLNormalRendererGroupingVisitor.h" namespace X3DTK { namespace GL { NormalRendererGroupingVisitor::NormalRendererGroupingVisitor() : GroupingVisitor() { // Enter functions. defineNewEnterFunction(&NormalRendererGroupingVisitor::enterTransform); // Leave functions defineNewLeaveFunction(&NormalRendererGroupingVisitor::leaveTransform); } NormalRendererGroupingVisitor::~NormalRendererGroupingVisitor() { } void NormalRendererGroupingVisitor::enterTransform(Transform *T) const { // Changing the coordinates system. glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMultMatrixf(&T->getTransformMatrix().front()); } void NormalRendererGroupingVisitor::leaveTransform(Transform *T) const { // Returning to the old coordinates system. glMatrixMode(GL_MODELVIEW); glPopMatrix(); } } }
#ifndef GLNORMALRENDERERGEOMETRY3DVISITOR_H #define GLNORMALRENDERERGEOMETRY3DVISITOR_H #include "GLNormalRendererGlobalVariables.h" #include <X3DTK/glscenegraph.h> namespace X3DTK { namespace GL { class NormalRendererGeometry3DVisitor : public Geometry3DVisitor { public: NormalRendererGeometry3DVisitor(); virtual ~NormalRendererGeometry3DVisitor(); virtual void enterIndexedFaceSet(IndexedFaceSet *I) const; protected: NormalRendererGlobalVariables *globalVariables; }; } } #endif
#include "GLNormalRendererGeometry3DVisitor.h" #include <vector> using namespace std; namespace X3DTK { namespace GL { NormalRendererGeometry3DVisitor::NormalRendererGeometry3DVisitor() : Geometry3DVisitor() { // Enter functions. defineNewEnterFunction(&NormalRendererGeometry3DVisitor::enterIndexedFaceSet); // GlobalVariables assignation. globalVariables = GVManager::getInstanceOf<NormalRendererGlobalVariables>(); } NormalRendererGeometry3DVisitor::~NormalRendererGeometry3DVisitor() { } void NormalRendererGeometry3DVisitor::enterIndexedFaceSet(IndexedFaceSet *G) const { float coef = globalVariables->getNormalLength(); glColor3f(1.0f, 0.0f, 0.0f); glDisable(GL_LIGHTING); // enumerating all the vertex formats. The method is simple: Get the normal vector of // vertex and drawing the line. // It is important to get a reference to the vertex array, to avoid a copy of the datas. if ((G->getColor()) && (G->getTexCoord())) { const vector<T2F_C4F_N3F_V3F> &vertexArray = G->T2F_C4F_N3F_V3F_vertexArray(); glBegin(GL_LINES); for (vector<T2F_C4F_N3F_V3F>::const_iterator it = vertexArray.begin(); it != vertexArray.end(); ++it) { SFVec3f vertex = (*it).vertex; SFVec3f vnormal = vertex + coef*(*it).normal; glVertex3fv(vertex.f_data()); glVertex3fv(vnormal.f_data()); } glEnd(); } if ((G->getColor()) && (!G->getTexCoord())) { const vector<C4F_N3F_V3F> &vertexArray = G->C4F_N3F_V3F_vertexArray(); glBegin(GL_LINES); for (vector<C4F_N3F_V3F>::const_iterator it = vertexArray.begin(); it != vertexArray.end(); ++it) { SFVec3f vertex = (*it).vertex; SFVec3f vnormal = vertex + coef*(*it).normal; glVertex3fv(vertex.f_data()); glVertex3fv(vnormal.f_data()); } glEnd(); } if ((!G->getColor()) && (G->getTexCoord())) { const vector<T2F_N3F_V3F> &vertexArray = G->T2F_N3F_V3F_vertexArray(); glBegin(GL_LINES); for (vector<T2F_N3F_V3F>::const_iterator it = vertexArray.begin(); it != vertexArray.end(); ++it) { SFVec3f vertex = (*it).vertex; SFVec3f vnormal = vertex + coef*(*it).normal; glVertex3fv(vertex.f_data()); glVertex3fv(vnormal.f_data()); } glEnd(); } if ((!G->getColor()) && (!G->getTexCoord())) { const vector<N3F_V3F> &vertexArray = G->N3F_V3F_vertexArray(); glBegin(GL_LINES); for (vector<N3F_V3F>::const_iterator it = vertexArray.begin(); it != vertexArray.end(); ++it) { SFVec3f vertex = (*it).vertex; SFVec3f vnormal = vertex + coef*(*it).normal; glVertex3fv(vertex.f_data()); glVertex3fv(vnormal.f_data()); } glEnd(); } glEnable(GL_LIGHTING); } } }
#ifndef GLNORMALRENDERER_H #define GLNORMALRENDERER_H #include "GLNormalRendererGlobalVariables.h" #include <X3DTK/glscenegraph.h> namespace X3DTK { namespace GL { class NormalRenderer : public X3DOnePassProcessor { public: NormalRenderer(); virtual ~NormalRenderer(); void setNormalLength(float value); virtual void render(SFNode N) const; protected: NormalRendererGlobalVariables *globalVariables; }; } } #endif
#include "GLNormalRenderer.h" #include "GLNormalRendererGeometry3DVisitor.h" #include "GLNormalRendererGroupingVisitor.h" #include <iostream> using namespace std; namespace X3DTK { namespace GL { NormalRenderer::NormalRenderer() { // The algorithm for drawing the normals is based upon a graphTraversal traversal of the // GL scene graph. graphTraversal = new DFSGraphTraversal(); // Getting the GlobalVariables to initialize its values. globalVariables = GVManager::getInstanceOf<NormalRendererGlobalVariables>(); // Setting the component visitor to the tree traversal algorithm. graphTraversal->setComponentVisitor(new NormalRendererGeometry3DVisitor()); graphTraversal->setComponentVisitor(new NormalRendererGroupingVisitor()); } NormalRenderer::~NormalRenderer() { delete graphTraversal; } void NormalRenderer::setNormalLength(float value) { globalVariables->setNormalLength(value); } void NormalRenderer::render(SFNode N) const { glDisable(GL_COLOR_MATERIAL); graphTraversal->traverse(N); } } }
#ifndef NORMALX3DGLSCENE_H #define NORMALX3DGLSCENE_H #include "GLNormalRenderer.h" #include <X3DTK/simplex3dglscene.h> namespace X3DTK { class NormalX3DGLScene : public SimpleX3DGLScene { public: NormalX3DGLScene(); virtual ~NormalX3DGLScene(); void setNormal(bool value); void setNormalLength(float value); virtual void draw(); private: GL::NormalRenderer *_normalRenderer; bool _normal; }; } #endif
#include "NormalX3DGLScene.h" namespace X3DTK { NormalX3DGLScene::NormalX3DGLScene() : SimpleX3DGLScene(), _normalRenderer(new GL::NormalRenderer()), _normal(false) { } NormalX3DGLScene::~NormalX3DGLScene() { delete _normalRenderer; } void NormalX3DGLScene::setNormal(bool value) { _normal = value; } void NormalX3DGLScene::setNormalLength(float value) { _normalRenderer->setNormalLength(value); } void NormalX3DGLScene::draw() { // call to the super class draw method. SimpleX3DGLScene::draw(); // Second pass rendering for the normals. if (_normal) _normalRenderer->render(glscene); } }
#ifndef VIEWER_H #define VIEWER_H #include "NormalX3DGLScene.h" #include <QGLViewer/qglviewer.h> class Viewer : public QGLViewer { public: Viewer(const char *file); ~Viewer(); protected : void loadFile(); void keyPressEvent(QKeyEvent *e); void init(); void draw(); void about(); QString helpString() const; void help() const; private: float normLength; X3DTK::NormalX3DGLScene scene; X3DTK::BBox BB; char *x3dfile; bool normal; }; #endif
#include "Viewer.h" #include <X3DTK/glrenderer.h> #include <math.h> #include <iostream> #include <qfiledialog.h> #include <qmessagebox.h> using namespace X3DTK; using namespace std; Viewer::Viewer(const char *file) : normLength(1.0f), normal(false) { x3dfile = (char *)file; } Viewer::~Viewer() { scene.release(); } void Viewer::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_L : loadFile(); break; case Qt::Key_N : normal = !normal; scene.setNormal(normal); break; case Qt::Key_Minus : normLength /= 2.0f; scene.setNormalLength(normLength); break; case Qt::Key_Plus : normLength *= 2.0f; scene.setNormalLength(normLength); 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); setSceneBoundingBox(scene.getBBoxMin().f_data(), scene.getBBoxMax().f_data()); showEntireScene(); } void Viewer::init() { glEnable(GL_RESCALE_NORMAL); about(); loadFile(); } void Viewer::draw() { //traverse the scene graph scene.draw(); } void Viewer::about() { QMessageBox::about(this, "about the glNormalViewer", "this is an example showing how to create a new processor that displays the normals of a model.Type 'h' to display help"); } QString Viewer::helpString() const { QString message(""); message += "<b>N</b>" + QString(" enables or disables the display of normals<br>"); message += "<b>+</b>" + QString(" increases the length of normals<br>"); message += "<b>-</b>" + QString(" decreases the length of normals<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(); }