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.
#ifndef GLNORMALRENDERERGLOBALVARIABLES_H #define GLNORMALRENDERERGLOBALVARIABLES_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(); virtual void enterTransform(Transform *T) const; virtual void leaveTransform(Transform *T) const; }; } } #endif
#include "GL_NormalRendererGroupingVisitor.h" namespace X3DTK { namespace GL { NormalRendererGroupingVisitor::NormalRendererGroupingVisitor() : GroupingVisitor() { // Enter functions. defineEnterFunction(&NormalRendererGroupingVisitor::enterTransform); // Leave functions defineLeaveFunction(&NormalRendererGroupingVisitor::leaveTransform); } void NormalRendererGroupingVisitor::enterTransform(Transform *T) const { // Changing the coordinates system. glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMultMatrixf(&T->getTransformMatrix().front()); } void NormalRendererGroupingVisitor::leaveTransform(Transform *) const { // 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(); virtual void enterIndexedFaceSet(IndexedFaceSet *I) const; protected: NormalRendererStateVariables *stateVariables; }; } } #endif
#include "GL_NormalRendererGeometry3DVisitor.h" #include <vector> using namespace std; namespace X3DTK { namespace GL { NormalRendererGeometry3DVisitor::NormalRendererGeometry3DVisitor() : Geometry3DVisitor() { // Enter function. defineEnterFunction(&NormalRendererGeometry3DVisitor::enterIndexedFaceSet); // State variables assignation. stateVariables = GraphTraversal::getInstanceOf<NormalRendererStateVariables>(); } void NormalRendererGeometry3DVisitor::enterIndexedFaceSet(IndexedFaceSet *G) const { 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(); virtual ~NormalRenderer(); void setNormalLength(float value); virtual void render(SFNode N) const; protected: NormalRendererStateVariables *stateVariables; }; } } #endif
#include "GL_NormalRenderer.h" #include "GL_NormalRendererGeometry3DVisitor.h" #include "GL_NormalRendererGroupingVisitor.h" #include <iostream> using namespace std; namespace X3DTK { namespace GL { NormalRenderer::NormalRenderer() { // Getting the state variables to initialize its values. stateVariables = GraphTraversal::getInstanceOf<NormalRendererStateVariables>(); // The algorithm for drawing the normals is based upon a DFS graph traversal // of the GL scene graph. graphTraversal = new DFSGraphTraversal(); // Setting the component visitor to the tree traversal algorithm. graphTraversal->setComponentVisitor(new NormalRendererGeometry3DVisitor()); graphTraversal->setComponentVisitor(new NormalRendererGroupingVisitor()); } NormalRenderer::~NormalRenderer() { // Removing the state variables. GraphTraversal::removeInstanceOf<NormalRendererStateVariables>(); delete graphTraversal; } void NormalRenderer::setNormalLength(float value) { stateVariables->setNormalLength(value); } void NormalRenderer::render(SFNode N) const { glDisable(GL_COLOR_MATERIAL); graphTraversal->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(X3DProcessor::getInstanceOf<GL::NormalRenderer>()), _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(); }