We want to display and select the different entities of a MESH scene graph. We define a processor called X3DTK::MESH::Drawer
that displays the content of the Mesh structure in order to display the 3D model and to enable selection. The different entities are the vertices, the edges and the faces.
The example is mainly based upon the creation of a X3DTK::X3DProcessor
so you can see glNormalViewer before reading this example.
Some important functions and classes:
X3DTK::MESH::Drawer
traversal, we store the variables necessary to the display for the GL selection mechanism.X3DTK::MESH::Drawer
processor, we draw the different mesh entities and execute the GL commands relative to selection.X3DTK::MESH::Drawer
and the X3DTK::Viewer
, the events being emitted by the X3DTK::Viewer
and treated by the X3DTK::MESH::Drawer
.QGLViewer
, redefining particularly the draw and select methods.#ifndef MESH_DRAWER_GLOBALVARIABLES_H #define MESH_DRAWER_GLOBALVARIABLES_H #include <X3DTK/MESH/scenegraph.h> #include <list> #include <vector> namespace X3DTK { namespace MESH { // State variables for the MESH::Drawer processor. class DrawerStateVariables : public StateVariables { public: DrawerStateVariables(); void initTraversal(); void finishTraversal(); void pushMatrix(const SFMatrix34f &transformation); void popMatrix(); int getCurrentMeshId(); void storeMesh(Mesh *mesh); inline const Mesh *getMesh(int i) const {return _meshVector[i];}; inline SFMatrix34f getMatrix(int i) const {return _matrixVector[i];}; inline void setSelectionMode(bool selectionMode) {_selectionMode = selectionMode;}; inline bool getSelectionMode() const {return _selectionMode;}; void setPrimitiveType(int primitiveType) {_primitiveType = primitiveType;}; inline int getPrimitiveType() const {return _primitiveType;}; void changeOneColorPerMesh() {_oneColorPerMesh = !_oneColorPerMesh;}; inline bool getOneColorPerMesh() const {return _oneColorPerMesh;}; private: std::list<SFMatrix34f> _matrixStack; std::vector<SFMatrix34f> _matrixVector; std::vector<Mesh *> _meshVector; bool _selectionMode; bool _oneColorPerMesh; int _primitiveType; }; } } #endif
#include "MESH_DrawerStateVariables.h" #include <qglobal.h> namespace X3DTK { namespace MESH { DrawerStateVariables::DrawerStateVariables() : StateVariables(), _selectionMode(false), _oneColorPerMesh(true), _primitiveType(1) {} void DrawerStateVariables::initTraversal() { _matrixStack.push_front(SFMatrix34f::identity); _matrixVector.clear(); _meshVector.clear(); } void DrawerStateVariables::finishTraversal() { _matrixStack.pop_front(); if (_matrixStack.size() != 0) { qWarning("Non empty matrix stack at the end of Drawer traversal"); _matrixStack.clear(); } } void DrawerStateVariables::pushMatrix(const SFMatrix34f &transformation) { _matrixStack.push_front(_matrixStack.front()*transformation); } void DrawerStateVariables::popMatrix() { _matrixStack.pop_front(); } void DrawerStateVariables::storeMesh(Mesh* mesh) { _matrixVector.push_back(_matrixStack.front()); _meshVector.push_back(mesh); } int DrawerStateVariables::getCurrentMeshId() { return _matrixVector.size() - 1; } } }
#ifndef MESH_DRAWER_COREVISITOR_H #define MESH_DRAWER_COREVISITOR_H #include <X3DTK/MESH/scenegraph.h> #include "MESH_DrawerStateVariables.h" namespace X3DTK { namespace MESH { class Transform; // Visitor for the Core component of the Drawer processor. class DrawerCoreVisitor : public CoreVisitor { public: DrawerCoreVisitor(); static void enterMesh(Mesh *M); static void enterTransform(Transform *T); static void leaveTransform(Transform *T); }; } } #endif
#include "MESH_DrawerCoreVisitor.h" #include <GL/gl.h> #include <iostream> using namespace std; namespace X3DTK { namespace MESH { DrawerCoreVisitor::DrawerCoreVisitor() { // Enter and leave functions. define(Recorder<Transform>::getEnterFunction(&DrawerCoreVisitor::enterTransform)); define(Recorder<Mesh>::getEnterFunction(&DrawerCoreVisitor::enterMesh)); define(Recorder<Transform>::getLeaveFunction(&DrawerCoreVisitor::leaveTransform)); } void DrawerCoreVisitor::enterTransform(Transform *T) { glMatrixMode(GL_MODELVIEW); Singleton<DrawerStateVariables>::getInstance()->pushMatrix(T->getTransform()); glPushMatrix(); glMultMatrixf(T->getTransform().toFloat16()); } void DrawerCoreVisitor::leaveTransform(Transform *) { glMatrixMode(GL_MODELVIEW); Singleton<DrawerStateVariables>::getInstance()->popMatrix(); glPopMatrix(); } void DrawerCoreVisitor::enterMesh(Mesh *M) { // StateVariables assignation. DrawerStateVariables *stateVariables = Singleton<DrawerStateVariables>::getInstance(); stateVariables->storeMesh(M); int meshId = stateVariables->getCurrentMeshId(); if (stateVariables->getSelectionMode()) glPushName(meshId); bool colorPerVertex = M->data().getColorPerVertex(); bool normalPerVertex = M->data().getNormalPerVertex(); switch (stateVariables->getPrimitiveType()) { case 1 : // MeshViewer::VERTICES { glColor3f(1.0, 0.9, 0.8); glPointSize(6.0); const Mesh::MFVertex &vertices = M->getVertices(); if (stateVariables->getSelectionMode()) { glPushName(1); for (Mesh::MFVertex::const_iterator v = vertices.begin(); v != vertices.end(); ++v) { glPushName((*v)->getIndex()); SFPoint3f point = (*v)->data().getPoint(); glRasterPos3fv(point); glPopName(); } glPopName(); } else { glBegin(GL_POINTS); for (Mesh::MFVertex::const_iterator v = vertices.begin(); v != vertices.end(); ++v) { SFPoint3f point = (*v)->data().getPoint(); glVertex3fv(point); } glEnd(); } break; } case 2 : // MeshViewer::EDGES { glLineWidth(2.0); const Mesh::MFEdge &edges = M->getEdges(); if (stateVariables->getSelectionMode()) glPushName(2); int index = 0; for (Mesh::MFEdge::const_iterator e = edges.begin(); e != edges.end(); ++e) { if (stateVariables->getSelectionMode()) glPushName(index++); if ((*e)->isBoundary()) glColor3f(0.3, 0.3, 0.9); else glColor3f(0.7, 0.7, 0.7); // Normal SFFace *firstFace = (*((*e)->getLeftFaces().begin())); if (!firstFace) firstFace = (*((*e)->getRightFaces().begin())); SFVec3f normal; if (normalPerVertex) normal = (*e)->getFromVertex()->data().getNormalOfFace(firstFace); else normal = firstFace->data().getNormal(); glNormal3fv(normal); glBegin(GL_LINES); SFPoint3f point = (*e)->getFromVertex()->data().getPoint(); glVertex3fv(point); point = (*e)->getToVertex()->data().getPoint(); glVertex3fv(point); glEnd(); if (stateVariables->getSelectionMode()) glPopName(); } if (stateVariables->getSelectionMode()) glPopName(); break; } case 3 : // MeshViewer::FACES { const Mesh::MFFace& faces = M->getFaces(); if (stateVariables->getSelectionMode()) glPushName(3); if (stateVariables->getOneColorPerMesh()) { SFColorRGBA color = SFColorRGBA(0.1*((3+meshId*3)%10), 0.1*((5+meshId*4)%10), 0.1*((7+meshId*5)%10), 1.0); glColor4f(color.r, color.g, color.b, color.a); } for (Mesh::MFFace::const_iterator f = faces.begin(); f != faces.end(); ++f) { if (!normalPerVertex) { SFVec3f normal = (*f)->data().getNormal(); glNormal3fv(normal); } if ((!colorPerVertex) && (!stateVariables->getOneColorPerMesh())) { SFColorRGBA color = (*f)->data().getColor(); glColor4f(color.r, color.g, color.b, color.a); } if (stateVariables->getSelectionMode()) glPushName((*f)->getIndex()); glBegin(GL_POLYGON); const SFFace::MFEdge &edges = (*f)->getEdges(); for (SFFace::MFEdge::const_iterator e = edges.begin(); e != edges.end(); ++e) { if (normalPerVertex) { SFVec3f normal = (*e)->getFromVertex()->data().getNormalOfFace(*f); glNormal3fv(normal); } if ((colorPerVertex) && (!stateVariables->getOneColorPerMesh())) { SFColorRGBA color = (*e)->getFromVertex()->data().getColorOfFace(*f); glColor4f(color.r, color.g, color.b, color.a); } SFPoint3f point = (*e)->getFromVertex()->data().getPoint(); glVertex3fv(point); } glEnd(); if (stateVariables->getSelectionMode()) glPopName(); } if (stateVariables->getSelectionMode()) glPopName(); break; } } if (stateVariables->getSelectionMode()) glPopName(); } } }
#ifndef MESH_DRAWER_H #define MESH_DRAWER_H #include "MESH_DrawerStateVariables.h" #include <X3DTK/kernel.h> #include <X3DTK/MESH/scenegraph.h> namespace X3DTK { enum SelectedType {NOTHING, VERTICES, EDGES, FACES}; namespace MESH { // Processor drawing the mesh from the Mesh scene graph. class Drawer : public X3DOnePassProcessor { public: Drawer(); virtual ~Drawer(); void changeDrawPoints(); void changeDrawEdges(); void changeDrawFaces(); void changeOneColorPerMesh(); void setSelectedType(SelectedType selectedType); void setSelectedMesh(int selectedMesh); void setSelectedId(int selectedId); inline SelectedType getSelectedType() const {return selectedType;}; inline int getSelectedMesh() const {return selectedMesh;}; inline int getSelectedId() const {return selectedId;}; void drawMesh(X3DNode *N, bool selection = false); void drawSelected(); private: bool drawPoints, drawEdges, drawFaces; SelectedType selectedType; int selectedMesh; int selectedId; // Draw methods. void drawVertex(const SFVertex *vertex); void drawEdge(SFEdge *edge); void drawFace(const SFFace *face); }; } } #endif
#include "MESH_Drawer.h" #include "MESH_DrawerCoreVisitor.h" #include <iostream> using namespace std; namespace X3DTK { namespace MESH { Drawer::Drawer() : X3DOnePassProcessor(), drawPoints(false), drawEdges(true), drawFaces(true), selectedType(NOTHING) { setGraphTraversal(new DFSGraphTraversal()); setComponentVisitor(new DrawerCoreVisitor()); } Drawer::~Drawer() { Singleton<DrawerStateVariables>::removeInstance(); } void Drawer::changeDrawPoints() { drawPoints = !drawPoints; } void Drawer::changeDrawEdges() { drawEdges = !drawEdges; } void Drawer::changeDrawFaces() { drawFaces = !drawFaces; } void Drawer::changeOneColorPerMesh() { Singleton<DrawerStateVariables>::getInstance()->changeOneColorPerMesh(); } void Drawer::setSelectedType(SelectedType selectedType) { this->selectedType = selectedType; } void Drawer::setSelectedMesh(int selectedMesh) { this->selectedMesh = selectedMesh; } void Drawer::setSelectedId(int selectedId) { this->selectedId = selectedId; } void Drawer::drawMesh(X3DNode *N, bool selection) { Singleton<DrawerStateVariables>::getInstance()->setSelectionMode(selection); if (selection || drawPoints) { Singleton<DrawerStateVariables>::getInstance()->setPrimitiveType(VERTICES); Singleton<DrawerStateVariables>::getInstance()->initTraversal(); traverse(N); Singleton<DrawerStateVariables>::getInstance()->finishTraversal(); } if (selection || drawEdges) { Singleton<DrawerStateVariables>::getInstance()->setPrimitiveType(EDGES); Singleton<DrawerStateVariables>::getInstance()->initTraversal(); traverse(N); Singleton<DrawerStateVariables>::getInstance()->finishTraversal(); } if (selection || drawFaces) { Singleton<DrawerStateVariables>::getInstance()->setPrimitiveType(FACES); Singleton<DrawerStateVariables>::getInstance()->initTraversal(); traverse(N); Singleton<DrawerStateVariables>::getInstance()->finishTraversal(); } } void Drawer::drawSelected() { if (selectedType == NOTHING) return; glPushMatrix(); glMultMatrixf(Singleton<DrawerStateVariables>::getInstance()->getMatrix(selectedMesh).toFloat16()); const Mesh *mesh = Singleton<DrawerStateVariables>::getInstance()->getMesh(selectedMesh); bool normalPerVertex = mesh->data().getNormalPerVertex(); glEnable(GL_POLYGON_OFFSET_FILL); #ifdef GL_POLYGON_OFFSET_LINES glEnable(GL_POLYGON_OFFSET_LINES); #endif glPointSize(15.0); glLineWidth(8.0); switch (selectedType) { case NOTHING : break; case VERTICES : { glPointSize(25.0); SFVertex *vertex = (mesh->getVertices())[selectedId]; const SFVertex::MFFace &faces = vertex->getFaces(); // Normal SFFace *firstFace = (*(faces.begin())); SFVec3f normal; if (normalPerVertex) normal = vertex->data().getNormalOfFace(firstFace); else normal = firstFace->data().getNormal(); glNormal3fv(normal.f_data()); glColor3f(1.0, 1.0, 0.0); drawVertex(vertex); glColor3f(0.0, 0.8, 0.0); const SFVertex::MFEdge &edges = vertex->getEdges(); for (SFVertex::MFEdge::const_iterator e = edges.begin(); e != edges.end(); ++e) drawEdge(*e); glColor3f(0.7, 0.9, 0.7); for (SFVertex::MFFace::const_iterator f = faces.begin(); f != faces.end(); ++f) drawFace(*f); break; } case EDGES : { glLineWidth(15.0); SFEdge *edge = (mesh->getEdges())[selectedId]; // Normal SFFace *firstFace = (*(edge->getLeftFaces().begin())); if (!firstFace) firstFace = (*(edge->getRightFaces().begin())); SFVec3f normal; if (normalPerVertex) normal = edge->getFromVertex()->data().getNormalOfFace(firstFace); else normal = firstFace->data().getNormal(); glNormal3fv(normal.f_data()); glColor3f(1.0, 1.0, 0.0); drawEdge(edge); glColor3f(0.0, 0.8, 0.0); drawVertex(edge->getFromVertex()); drawVertex(edge->getToVertex()); glColor3f(0.9, 0.7, 0.7); const SFEdge::MFFace &lfaces = edge->getLeftFaces(); for (SFEdge::MFFace::const_iterator lf = lfaces.begin(); lf != lfaces.end(); ++lf) drawFace(*lf); glColor3f(0.7, 0.9, 0.7); const SFEdge::MFFace &rfaces = edge->getRightFaces(); for (SFEdge::MFFace::const_iterator rf = rfaces.begin(); rf != rfaces.end(); ++rf) drawFace(*rf); break; } case FACES : { SFFace *face = (mesh->getFaces())[selectedId]; const SFFace::MFEdge &edges = face->getEdges(); SFFace::MFVertex vertices = face->getVertices(); // Normal SFEdge *firstEdge = (*(edges.begin())); SFVec3f normal; if (normalPerVertex) normal = firstEdge->getFromVertex()->data().getNormalOfFace(face); else normal = face->data().getNormal(); glNormal3fv(normal.f_data()); glColor3f(1.0, 1.0, 0.0); drawFace(face); for (SFFace::MFEdge::const_iterator e = edges.begin(); e != edges.end(); ++e) { glColor3f(0.0, 0.8, 0.0); drawEdge(*e); } for (SFFace::MFVertex::const_iterator v = vertices.begin(); v != vertices.end(); ++v) { glColor3f(0.7, 0.7, 0.7); drawVertex(*v); } break; } } glDisable(GL_POLYGON_OFFSET_FILL); #ifdef GL_POLYGON_OFFSET_LINES glDisable(GL_POLYGON_OFFSET_LINES); #endif glPopMatrix(); } void Drawer::drawVertex(const SFVertex *vertex) { glBegin(GL_POINTS); SFPoint3f point = vertex->data().getPoint(); glVertex3fv(point.f_data()); glEnd(); } void Drawer::drawEdge(SFEdge *edge) { glPolygonOffset(-100, -100); glDisable(GL_LIGHTING); glBegin(GL_LINES); SFPoint3f point = edge->getFromVertex()->data().getPoint(); glVertex3fv(point.f_data()); point = edge->getToVertex()->data().getPoint(); glVertex3fv(point.f_data()); glEnd(); glEnable(GL_LIGHTING); glPolygonOffset(-2, -2); } void Drawer::drawFace(const SFFace *face) { const SFFace::MFEdge &edges = face->getEdges(); glBegin(GL_POLYGON); for (SFFace::MFEdge::const_iterator e = edges.begin(); e != edges.end(); ++e) { SFPoint3f point = (*e)->getFromVertex()->data().getPoint(); glVertex3fv(point.f_data()); } glEnd(); } } }
#ifndef SIMPLEMESHGLSCENE_H #define SIMPLEMESHGLSCENE_H #include "MESH_Drawer.h" #include <X3DTK/kernel.h> #include <X3DTK/X3D/meshbuilder.h> #include <X3DTK/MESH/normalsupdater.h> namespace X3DTK { class MemReleaser; namespace X3D { class Scene; class Loader; class BBoxUpdater; } namespace MESH { class Scene; class Drawer; } // Class defining a facade to allow an easy load and display of an X3D scene. class SimpleMeshGLScene { public: SimpleMeshGLScene(); virtual ~SimpleMeshGLScene(); virtual void load(const char *file); virtual void drawMesh(bool selection = false); virtual void drawSelected(); inline SFVec3f getBBoxMin() const {return min;}; inline SFVec3f getBBoxMax() const {return max;}; void release(); inline void changeDrawPoints() {_drawer->changeDrawPoints();}; inline void changeDrawEdges() {_drawer->changeDrawEdges();}; inline void changeDrawFaces() {_drawer->changeDrawFaces();}; inline void changeOneColorPerMesh() {_drawer->changeOneColorPerMesh();}; inline void setSelectedType(SelectedType selectedType) {_drawer->setSelectedType(selectedType);}; inline void setSelectedMesh(int selectedMesh) {_drawer->setSelectedMesh(selectedMesh);}; inline void setSelectedId(int selectedId) {_drawer->setSelectedId(selectedId);}; inline SelectedType getSelectedType() const {return _drawer->getSelectedType();}; inline int getSelectedMesh() const {return _drawer->getSelectedMesh();}; inline int getSelectedId() const {return _drawer->getSelectedId();}; protected: X3D::Scene *scene; MESH::Scene *meshScene; SFVec3f min; SFVec3f max; void loadFile(const char *file); void computeBBox(); void buildMeshScene(); private: X3D::Loader *_loader; X3D::BBoxUpdater *_bboxupdater; X3D::MeshBuilder *_builder; MESH::NormalsUpdater *_normalUpdater; MESH::Drawer *_drawer; MemReleaser *_releaser; }; } #endif
#include "SimpleMeshGLScene.h" #include <X3DTK/X3D/scenegraph.h> #include <X3DTK/X3D/bboxupdater.h> #include <X3DTK/memreleaser.h> #include <X3DTK/MESH/scenegraph.h> #include <iostream> #include <time.h> using namespace std; namespace X3DTK { SimpleMeshGLScene::SimpleMeshGLScene() : scene(0), meshScene(0) { _loader = Singleton<X3D::Loader>::getInstance(); _bboxupdater = Singleton<X3D::BBoxUpdater>::getInstance(); _builder = Singleton<X3D::MeshBuilder>::getInstance(); _drawer = Singleton<MESH::Drawer>::getInstance(); _normalUpdater = Singleton<MESH::NormalsUpdater>::getInstance(); _releaser = Singleton<MemReleaser>::getInstance(); } SimpleMeshGLScene::~SimpleMeshGLScene() { _releaser->release(scene); _releaser->release(meshScene); scene = 0; meshScene = 0; } void SimpleMeshGLScene::load(const char *file) { loadFile(file); computeBBox(); buildMeshScene(); } void SimpleMeshGLScene::drawMesh(bool selection) { _drawer->drawMesh(meshScene, selection); } void SimpleMeshGLScene::drawSelected() { _drawer->drawSelected(); } void SimpleMeshGLScene::release() { _releaser->release(scene); _releaser->release(meshScene); scene = 0; meshScene = 0; } void SimpleMeshGLScene::loadFile(const char *file) { _releaser->release(scene); _releaser->release(meshScene); scene = 0; meshScene = 0; scene = _loader->load(file, true); } void SimpleMeshGLScene::computeBBox() { _bboxupdater->update(scene, true); if (scene != 0) { SFVec3f center = scene->getBBoxCenter(); SFVec3f size = scene->getBBoxSize(); SFVec3f A = center + 0.5f*size; SFVec3f B = center - 0.5f*size; if (A.x < B.x) { min.x = A.x; max.x = B.x; } else { min.x = B.x; max.x = A.x; } if (A.y < B.y) { min.y = A.y; max.y = B.y; } else { min.y = B.y; max.y = A.y; } if (A.z < B.z) { min.z = A.z; max.z = B.z; } else { min.z = B.z; max.z = A.z; } } } void SimpleMeshGLScene::buildMeshScene() { meshScene = _builder->build(scene); _normalUpdater->updateNormals(meshScene); } }
#ifndef VIEWER_H #define VIEWER_H #include <QGLViewer/qglviewer.h> #include <X3DTK/simplex3dglscene.h> // Class providing an X3D Viewer by using X3DTK::SimpleX3DGLScene 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
#include "Viewer.h" #include <qfiledialog.h> #include <qmessagebox.h> #include <X3DTK/kernel.h> using namespace X3DTK; using namespace std; void Viewer::init() { #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; // Loads the file name 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 X3DViewer", "this is an example showing how to load and display a simple X3D scene.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(); }
#include "Viewer.h" #include <qapplication.h> using namespace X3DTK; using namespace std; int main(int argc, char *argv[]) { // Read command lines arguments. QApplication application(argc, argv); // Instantiate the viewer. Viewer v; // Make the viewer window visible on screen. v.show(); // Set the viewer as the application main widget. application.setMainWidget(&v); // Run main loop. return application.exec(); }