icosahedronViewer

Introduction

This is an example on how to extend the X3D language by creating a new X3D node, as well as its GL drawable equivalent and apply common X3DTK::X3DProcessor to it by deriving the existing ones.

Here we want to define an icosahedron node and render it by the X3DTK::GL::Renderer processor. It implies to define an X3D::Icosahedron node belonging to the X3D scene graph, to extend the X3DTK::X3D::Loader to load it, to extend the X3DTK::X3D::BBoxUpdater to compute its bounding box necessary for the viewer and to define a X3DTK::GL::Icosahedron inheriting a X3DTK::GL::X3DNode belonging to the GL scene graph to draw it.

Some important functions and classes:

Defining X3D::Icosahedron

Here we define a simple node that only has a single atomic attribute. In the constructor, the type name and the attribute are recorded.

Loading X3D::Icosahedron

To load the node, we have to derive the creator for the nodes belonging to the Geometry3D, because X3DTK::X3D::Icosahedron belongs to it.

Computing the bounding box

To render the node, the bounding box must be computed. That is why we extend the X3DTK::X3D::BBoxUpdater processor. We derive X3DTK::X3D::BBoxUpdaterGeometry3DVisitor to add the enterIcosahedron function.

Defining GL::Icosahedron

To render X3DTK::X3D::Icosahedron, we first have to define its GL equivalent node, X3DTK::GL::Icosahedron. Notice that there are different namespaces for the X3D and GL scene graphs. The special methods update and draw have to be redefined.

building GL::Icosahedron

We derive the X3DTK::X3D::GLBuilderGeometry3DVisitor, which will create the X3DTK::GL::Icosahedron from an X3DTK::X3D::Icosahedron.

Code

X3D_Icosahedron.h

#ifndef ICOSAHEDRON_H
#define ICOSAHEDRON_H

#include <X3DTK/X3D/scenegraph.h>

namespace X3DTK {
namespace X3D {

// New X3D node.

class Icosahedron : public X3DGeometryNode
{
public:
  Icosahedron();
  virtual ~Icosahedron();
  
  void setRadius(const SFFloat &radius);
  inline const SFFloat &getRadius() const {return _radius;};

private:
  SFFloat _radius;
};

}
}

#endif

X3D_Icosahedron.cpp

#include "X3D_Icosahedron.h"

#include <iostream>

using namespace std;

namespace X3DTK {
namespace X3D {

Icosahedron::Icosahedron()
: X3DGeometryNode()
{
  // Defines the tag of the node. This string is the one read in the X3D file.
  define(Recorder<Icosahedron>::getTypeName("Icosahedron", "Geometry3D"));
  
  // Defines an attribute of the node.
  define(Recorder<Icosahedron, SFFloat>::getAttribute("radius", &Icosahedron::_radius, 1.0f));
}

Icosahedron::~Icosahedron()
{
}

void Icosahedron::setRadius(const SFFloat &radius)
{
  _radius = radius;
}
  
}
}

X3D_MyGeometry3DCreator.h

#ifndef MYGEOMETRY3DCREATOR_H
#define MYGEOMETRY3DCREATOR_H

#include <X3DTK/X3D/scenegraph.h>

namespace X3DTK {
namespace X3D {

// Deriving Geometry3DCreator to define the new node X3D::Icosahedron.

class MyGeometry3DCreator : public Geometry3DCreator
{
public:
  MyGeometry3DCreator();
};

}
}

#endif

X3D_MyGeometry3DCreator.cpp

#include "X3D_MyGeometry3DCreator.h"
#include "X3D_Icosahedron.h"

namespace X3DTK {
namespace X3D {

MyGeometry3DCreator::MyGeometry3DCreator()
: Geometry3DCreator()
{
  // Defines a new creation function for the Icosahedron node.
  define(Recorder<Icosahedron>::getCreationFunction());
}

}
}

X3D_MyBBoxUpdaterGeometry3DVisitor.h

#ifndef MYBBOXUPDATERGEOMETRY3DVISITOR_H
#define MYBBOXUPDATERGEOMETRY3DVISITOR_H

#include <X3DTK/X3D/bboxupdater.h>

namespace X3DTK {
namespace X3D {

class Icosahedron;

// Visitor for the Geometry3D component of the MyBBoxUpdater processor.

class MyBBoxUpdaterGeometry3DVisitor : public BBoxUpdaterGeometry3DVisitor
{
public:
  MyBBoxUpdaterGeometry3DVisitor();

  static void enterIcosahedron(Icosahedron *I);
};

}
}

#endif

X3D_MyBBoxUpdaterGeometry3DVisitor.cpp

#include "X3D_MyBBoxUpdaterGeometry3DVisitor.h"
#include "X3D_Icosahedron.h"

#include <iostream>

using namespace std;

namespace X3DTK {
namespace X3D {

MyBBoxUpdaterGeometry3DVisitor::MyBBoxUpdaterGeometry3DVisitor()
: BBoxUpdaterGeometry3DVisitor()
{
  // Defines the new enter function for the X3DAbstractNode.
  define(Recorder<Icosahedron>::getEnterFunction(&MyBBoxUpdaterGeometry3DVisitor::enterIcosahedron));
}

void MyBBoxUpdaterGeometry3DVisitor::enterIcosahedron(Icosahedron *I)
{
  // StateVariables assignation
  BBoxUpdaterStateVariables *stateVariables = Singleton<BBoxUpdaterStateVariables>::getInstance();
  
  // Checking whether the BBox of the node has already been computed or not.
  BBox *BB = stateVariables->getBBox(I);
  if (BB == 0)
  {
    // Creating a new BBox.
    BB = new BBox(SFVec3f(0.0f, 0.0f, 0.0f), SFVec3f(I->getRadius(), I->getRadius(), I->getRadius()));
    // Recording the couple (I, BB) to avoid to create a new BBox 
    // if I is visited a second time. 
    stateVariables->addBBox(I, BB);
    // Setting the current BBox.
    stateVariables->setShapeBBox(*BB);
  }
}

}
}

GL_Icosahedron.h

#ifndef GL_ICOSAHEDRON_H
#define GL_ICOSAHEDRON_H

#include <X3DTK/GL/scenegraph.h>

namespace X3DTK {
namespace GL {

// Class providing an implementation of the GL node corresponding to  
// X3D::Icosahedron.

class Icosahedron : public X3DGeometryNode
{
public:
  Icosahedron();
  virtual ~Icosahedron();

  void setRadius(const SFFloat &radius);
  inline const SFFloat &getRadius() const {return _radius;};

  virtual void update();
  virtual void draw() const;

private:  
  MFVec3f _vertices;
  short _indexes[12][5];
  MFVec3f _normal;
  SFFloat _radius; 
};

}
}

#endif

GL_Icosahedron.cpp

#include "GL_Icosahedron.h"
#include "X3D_Icosahedron.h"

#include <cmath>

#include <iostream>

namespace X3DTK {
namespace GL {

Icosahedron::Icosahedron()
: X3DGeometryNode()
{
  // Defines the tag of the node. 
  define(Recorder<Icosahedron>::getTypeName("Icosahedron", "Geometry3D"));
  
  // Defines an attribute of the node.
  define(Recorder<Icosahedron, SFFloat>::getAttribute("radius", &Icosahedron::_radius, 1.0f));
}

Icosahedron::~Icosahedron()
{
}

void Icosahedron::update()
{
  X3D::Icosahedron *I = static_cast<X3D::Icosahedron *>(x3dReference);
  
  // updating the attributes.
  _radius = I->getRadius();
  
  // building the vertex array.
  const float c = cos(PI/3.0);
  const float s = sin(PI/3.0);

  _vertices = MFVec3f(20);

  _vertices[0] = SFVec3f(c+2.0*s, s, 0.0);
  _vertices[1] = SFVec3f(-c-2.0*s, s, 0.0);
  _vertices[2] = SFVec3f(c+2.0*s, -s, 0.0);
  _vertices[3] = SFVec3f(-c-2.0*s, -s, 0.0);
  _vertices[4] = SFVec3f(0.0, 2.0*s+c,  s);
  _vertices[5] = SFVec3f(0.0, -2.0*s-c,  s);
  _vertices[6] = SFVec3f(0.0, 2.0*s+c, -s);
  _vertices[7] = SFVec3f(0.0, -2.0*s-c, -s);
  _vertices[8] = SFVec3f(-c-s,  -c-s, c+s);
  _vertices[9] = SFVec3f(-c-s,  c+s,  c+s);
  _vertices[10] = SFVec3f(-c-s, -c-s, -c-s);
  _vertices[11] = SFVec3f(-c-s, c+s,  -c-s);
  _vertices[12] = SFVec3f(c+s, -c-s, c+s);
  _vertices[13] = SFVec3f(c+s, c+s,  c+s);
  _vertices[14] = SFVec3f(c+s, -c-s, -c-s);
  _vertices[15] = SFVec3f(c+s, c+s,  -c-s);
  _vertices[16] = SFVec3f(-s, 0.0, -2.0*s-c);
  _vertices[17] = SFVec3f(s, 0.0, -2.0*s-c);
  _vertices[18] = SFVec3f(-s, 0.0, 2.0*s+c);
  _vertices[19] = SFVec3f(s, 0.0, 2.0*s+c);

  static short array[12][5] = {
    { 0, 13, 19, 12, 2 },
    { 0, 2, 14, 17, 15 } ,
    { 0, 15, 6, 4, 13 } ,
    { 6, 15, 17, 16, 11 } ,
    { 4, 6, 11, 1, 9 } ,
    { 11, 16, 10, 3, 1 } ,
    { 16, 17, 14, 7, 10 } ,
    { 10, 7, 5, 8, 3 } ,
    { 5, 12, 19, 18, 8 } ,
    { 12, 5, 7, 14, 2 } ,
    { 18, 19, 13, 4, 9 } ,
    { 1, 3, 8, 18, 9 } };

  _normal = MFVec3f(12);

  for (int i = 0; i < 12; ++i)
  {
    _normal[i] = SFVec3f(0.0, 0.0, 0.0);
    for (int j = 0; j < 5; ++j)
    {
      _indexes[i][j] = array[i][j];
      _normal[i] += _vertices[_indexes[i][j]];
    }
    _normal[i].normalize();
  }  
}

void Icosahedron::setRadius(const SFFloat &radius)
{
  _radius = radius;
}

void Icosahedron::draw() const
{
  // drawing the polygon.
  // Changing the scale for the radius of the object.
  
  float s = _radius/4.47f;
  
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glScalef(s, s, s);
  
  for (int i = 0; i < 12; ++i)
  {
    // drawing the normals and the faces.
    glNormal3f(_normal[i].x, _normal[i].y, _normal[i].z);
    glBegin(GL_POLYGON);
    for (int j = 0; j < 5; ++j)
      glVertex3f(_vertices[_indexes[i][j]].x, _vertices[_indexes[i][j]].y, _vertices[_indexes[i][j]].z);
    glEnd();
  }  
  
  glPopMatrix();
}

}
}

X3D_MyGLBuilderGeometry3DVisitor.h

#ifndef MYGLBUILDERGEOMETRY3DVISITOR_H
#define MYGLBUILDERGEOMETRY3DVISITOR_H

#include <X3DTK/X3D/glbuilder.h>

namespace X3DTK {
namespace X3D {

class Icosahedron;

// Visitor for the Geometry3D component of the GLBuilder processor.

class MyGLBuilderGeometry3DVisitor : public GLBuilderGeometry3DVisitor
{
public:
  MyGLBuilderGeometry3DVisitor();
  
  static void enterIcosahedron(Icosahedron *C);
};

}
}

#endif

X3D_MyGLBuilderGeometry3DVisitor.cpp

#include "X3D_MyGLBuilderGeometry3DVisitor.h"
#include "X3D_Icosahedron.h"
#include "GL_Icosahedron.h"

#include <iostream>

using namespace std;

namespace X3DTK {
namespace X3D {

MyGLBuilderGeometry3DVisitor::MyGLBuilderGeometry3DVisitor()
: GLBuilderGeometry3DVisitor()
{
  // Define new enter function for Icosahedron. Note that it is the instanciation of a template method
  // with implicit parameters.
  define(Recorder<Icosahedron>::getEnterFunction(&MyGLBuilderGeometry3DVisitor::enterIcosahedron));
}

void MyGLBuilderGeometry3DVisitor::enterIcosahedron(Icosahedron *I)
{
  // StateVariables assignation
  GLBuilderStateVariables *stateVariables = Singleton<GLBuilderStateVariables>::getInstance();
  
  // Checking whether I has been visited or not.
  GL::X3DNode *GI = stateVariables->getNode(I);
  if (GI == 0)
  {
    // Creating a new GL::Icosahedron.
    GI = new GL::Icosahedron();
    GI->setX3DReference(I);
    
    // Recording the couple (I, GI) to avoid to create a new GL::Icosahedron
    // if I is visited a second time.
    stateVariables->addCoupleNode(I, GI);
  }
  // Pushing GI in the GL nodes stack.
  stateVariables->pushNode(GI);
}

}
}

Viewer.h

#ifndef VIEWER_H
#define VIEWER_H

#include <QGLViewer/qglviewer.h>
#include <X3DTK/simplex3dglscene.h>

// Class providing an X3D Viewer by implementing QGLViewer.

class Viewer : public QGLViewer
{
protected :
  void init();
  void draw();
  void keyPressEvent(QKeyEvent *e);
  void loadFile();
  void about();
  QString helpString() const;
  void help() const;
  
private:
  X3DTK::SimpleX3DGLScene scene;
};

#endif

Viewer.cpp

#include "Viewer.h"
#include "X3D_MyGeometry3DCreator.h"
#include "X3D_MyBBoxUpdaterGeometry3DVisitor.h"
#include "X3D_MyGLBuilderGeometry3DVisitor.h"

#include <qfiledialog.h>
#include <qmessagebox.h> 
#include <X3DTK/kernel.h>

using namespace X3DTK;
using namespace std;

void Viewer::init()
{
  // We customize the SimpleX3DGLScene, by modifying its Loader, BBoxUpdater and GLBuilder.
  scene.getLoader()->setComponentCreator(new X3D::MyGeometry3DCreator());
  scene.getBBoxUpdater()->setComponentVisitor(new X3D::MyBBoxUpdaterGeometry3DVisitor());
  scene.getGLBuilder()->setComponentVisitor(new X3D::MyGLBuilderGeometry3DVisitor());

#ifdef GL_RESCALE_NORMAL  
  glEnable(GL_RESCALE_NORMAL);
#endif  
  about();
  loadFile();
}

void Viewer::keyPressEvent(QKeyEvent *e)
{
  switch (e->key())
  {
    case Qt::Key_L : loadFile(); break;
    default:         QGLViewer::keyPressEvent(e);
  }
}

void Viewer::loadFile()
{
  QString name = QFileDialog::getOpenFileName("", "X3D files (*.x3d *.X3D);;All files (*)", this);
  
  // In case of Cancel
  if (name.isEmpty())
    return;

  scene.release();
  // Loads the scene.
  scene.load(name);
  
  // QGLViewer settings.
  setSceneBoundingBox(scene.getBBoxMin().f_data(), scene.getBBoxMax().f_data());
  showEntireScene();
}

void Viewer::draw()
{
  // Draws the scene.
  scene.draw();
}

void Viewer::about()
{
  QMessageBox::about(this, "about the newNodeViewer", "this is an example showing how to create a new node.Type 'h' to display help");
}

QString Viewer::helpString() const
{
  QString message("");
  
  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 Aug 27 13:16:25 2004 for X3DToolKit by doxygen 1.3.6