Here we want to display the normals of the model in a OpenGL context and control their lengths. For that we traverse the GL scene graph and draw the normals of the X3DTK::GL::IndexedFaceSet
node.
Some important functions and classes:
X3DTK::StateVariables
and add methods to set and get the length.enterTransform
and leaveTransform
operating on the X3DTK::GL::Transform
node that belongs to the Grouping component.X3DTK::GL::IndexedFaceSet
node which already has the vertex arrays 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 X3DTK::GL::NormalRendererStateVariables
is given by X3DTK::X3DProcessor
which controls the number of instances.#ifndef GLNORMALRENDERERSTATEVARIABLES_H #define GLNORMALRENDERERSTATEVARIABLES_H #include <X3DTK/kernel.h> namespace X3DTK { namespace GL { // State variables for the GL::Renderer processor. class NormalRendererStateVariables : public StateVariables { public: NormalRendererStateVariables(); void setNormalLength(float value); float getNormalLength() const {return _normalLength;}; private: float _normalLength; }; } } #endif
#include "GL_NormalRendererStateVariables.h" namespace X3DTK { namespace GL { NormalRendererStateVariables::NormalRendererStateVariables() : StateVariables(), _normalLength(1.0f) { } void NormalRendererStateVariables::setNormalLength(float value) { _normalLength = value; } } }
#ifndef GLNORMALRENDERERGROUPINGVISITOR_H #define GLNORMALRENDERERGROUPINGVISITOR_H #include <X3DTK/GL/scenegraph.h> namespace X3DTK { namespace GL { // Visitor for the Grouping component of the GL::NormalRenderer processor. class NormalRendererGroupingVisitor : public GroupingVisitor { public: NormalRendererGroupingVisitor(); static void enterTransform(Transform *T); static void leaveTransform(Transform *T); }; } } #endif
#include "GL_NormalRendererGroupingVisitor.h" namespace X3DTK { namespace GL { NormalRendererGroupingVisitor::NormalRendererGroupingVisitor() : GroupingVisitor() { // Enter functions. define(Recorder<Transform>::getEnterFunction(&NormalRendererGroupingVisitor::enterTransform)); // Leave functions define(Recorder<Transform>::getLeaveFunction(&NormalRendererGroupingVisitor::leaveTransform)); } void NormalRendererGroupingVisitor::enterTransform(Transform *T) { // Changing the coordinates system. glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMultMatrixf(&T->getTransformMatrix().front()); } void NormalRendererGroupingVisitor::leaveTransform(Transform *) { // Returning to the old coordinates system. glMatrixMode(GL_MODELVIEW); glPopMatrix(); } } }
#ifndef GLNORMALRENDERERGEOMETRY3DVISITOR_H #define GLNORMALRENDERERGEOMETRY3DVISITOR_H #include "GL_NormalRendererStateVariables.h" #include <X3DTK/GL/scenegraph.h> namespace X3DTK { namespace GL { // Visitor for the Geometry3D component of the GL::NormalRenderer processor. class NormalRendererGeometry3DVisitor : public Geometry3DVisitor { public: NormalRendererGeometry3DVisitor(); static void enterIndexedFaceSet(IndexedFaceSet *I); }; } } #endif
#include "GL_NormalRendererGeometry3DVisitor.h" #include <vector> using namespace std; namespace X3DTK { namespace GL { NormalRendererGeometry3DVisitor::NormalRendererGeometry3DVisitor() : Geometry3DVisitor() { // Enter function. define(Recorder<IndexedFaceSet>::getEnterFunction(&NormalRendererGeometry3DVisitor::enterIndexedFaceSet)); } void NormalRendererGeometry3DVisitor::enterIndexedFaceSet(IndexedFaceSet *G) { // State variables assignation. NormalRendererStateVariables *stateVariables = Singleton<NormalRendererStateVariables>::getInstance(); float coef = stateVariables->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 in order not to copy 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 "GL_NormalRendererStateVariables.h" #include <X3DTK/GL/scenegraph.h> namespace X3DTK { namespace GL { // GL::NormalRenderer processor. class NormalRenderer : public X3DOnePassProcessor { public: NormalRenderer(); void setNormalLength(float value); virtual void render(SFNode N) const; }; } } #endif
#include "GL_NormalRenderer.h" #include "GL_NormalRendererGeometry3DVisitor.h" #include "GL_NormalRendererGroupingVisitor.h" namespace X3DTK { namespace GL { NormalRenderer::NormalRenderer() { // The algorithm for drawing the normals is based upon a DFS graph traversal // of the GL scene graph. setGraphTraversal(new DFSGraphTraversal()); // Setting the component visitor to the tree traversal algorithm. setComponentVisitor(new NormalRendererGeometry3DVisitor()); setComponentVisitor(new NormalRendererGroupingVisitor()); } void NormalRenderer::setNormalLength(float value) { Singleton<NormalRendererStateVariables>::getInstance()->setNormalLength(value); } void NormalRenderer::render(SFNode N) const { glDisable(GL_COLOR_MATERIAL); traverse(N); } } }
#ifndef NORMALX3DGLSCENE_H #define NORMALX3DGLSCENE_H #include "GL_NormalRenderer.h" #include <X3DTK/simplex3dglscene.h> namespace X3DTK { // Class extending SimpleX3DGLscene for drawing the normals. class NormalX3DGLScene : public SimpleX3DGLScene { public: 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(Singleton<GL::NormalRenderer>::getInstance()), _normal(false) { } void NormalX3DGLScene::setNormal(bool value) { _normal = value; } void NormalX3DGLScene::setNormalLength(float value) { // Setting the length parameter of the GL::NormalRenderer processor. _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/GL/renderer.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() { // Releases scene graphs of scene. 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); // QGLViewer settings setSceneBoundingBox(scene.getBBoxMin().f_data(), scene.getBBoxMax().f_data()); showEntireScene(); } void Viewer::init() { #ifdef GL_RESCALE_NORMAL glEnable(GL_RESCALE_NORMAL); #endif about(); loadFile(); } void Viewer::draw() { // Draws the scene. 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(); }