glNormalViewer

Introduction

This an example of how to write a new processor for the GL scene graph. A processor is the combination of a scene graph traversal algorithm and of a scene graph node visitor. X3D nodes are divided into components, and there exists a visitor for each component.

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:

GL::NormalRendererStateVariables

To control the lengths of the normals displayed on screen, we have to memorize a global variable to the traversal of the scene graph. That's why we derive X3DTK::StateVariables and add methods to set and get the length.

GL::NormalRendererGroupingVisitor

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 X3DTK::GL::Transform node that belongs to the Grouping component.

GL::NormalRendererGeometry3DVisitor

To draw the normals we have to visit the 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.

GL::NormalRenderer

This is the facade of the processor which aggregates the visitors of the different components.

NormalX3DGLScene

We customize X3DTK::SimpleX3DGLScene by adding methods relative to the display of the normals.

Code

GL_NormalRendererStateVariables.h

#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

GL_NormalRendererStateVariables.cpp

#include "GL_NormalRendererStateVariables.h"

namespace X3DTK {
namespace GL {

NormalRendererStateVariables::NormalRendererStateVariables()
: StateVariables(), _normalLength(1.0f)
{
}

void NormalRendererStateVariables::setNormalLength(float value)
{
  _normalLength = value;
}

}
}

GL_NormalRendererGroupingVisitor.h

#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

GL_NormalRendererGroupingVisitor.cpp

#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();  
}

}
}

GL_NormalRendererGeometry3DVisitor.h

#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

GL_NormalRendererGeometry3DVisitor.cpp

#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);
}

}
}

GL_NormalRenderer.h

#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

GL_NormalRenderer.cpp

#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);
}

}
}

NormalX3DGLScene.h

#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

NormalX3DGLScene.cpp

#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);
}

}

Viewer.h

#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

Viewer.cpp

#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();
}

Generated on Fri Jul 30 12:02:31 2004 for X3DToolKit by doxygen 1.3.6