exlib / sg

The inlib/exlib comes with a set of classes permitting to build "scene graphs" (sg) to do visualization with OpenGL-ES. It is well known that even visualizing a cube with OpenGL is tedious, and something on top of it is needed. You need some kind of "scene manager" with which you can easily specify : here I want a blue cube, here a rotated green cylinder, here I put a light and I want a camera at such position looking in this direction, etc... In the 1980's Silicon Graphics had created Open Inventor to do that. But for the moment there is no free Inventor implementation over OpenGL-ES only and available for iOS and Android. Then to move on with ioda, we have decided to create our own scene manager (exlib/sg) fulfilling these specifications.

Usage

The way to use exlib/sg is similar to Inventor. You create "nodes" that you combine in "scene graphs" by using node containers as the exlib/sg/separator one. When done you create a "exlib::sg::viewer" to which you declare your scene graph. Then you arrange to create a (X11, Cocoa, win32, iOS or Android) window aware of OpenGL-ES and then at each "expose event" you arrange to execute the viewer.render() method.

Visualizing a cube

On a desktop, you can have an idea by building the exlib sg_cube_X11.cpp that visualize a cube by using exlib/sg :

    cd <path to exlib>
    cd examples/cpp
    ./build sg_cube_X11.cpp
    ./exlib_example_sg_cube_X11

You should see :

exlib_exa_sg_0.png

You can have a look to sg_cube_X11.cpp and see that the code is rather simple and easy to customize...

// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file exlib.license for terms.


#include <inlib/mem>

#include <exlib/sg/separator>
#include <exlib/sg/ortho>
#include <exlib/sg/matrix>
#include <exlib/sg/color>
#include <exlib/sg/cube>

#include <exlib/sg/viewer>
#include <exlib/X11/session>

#include <iostream>
#include <cstdlib>

int main(int,char**) {

#ifdef INLIB_MEM
  inlib::mem::set_check_by_class(true);{
#endif

  //////////////////////////////////////////////////////////
  /// create scene graph ///////////////////////////////////
  //////////////////////////////////////////////////////////
  exlib::sg::separator* sep = new exlib::sg::separator;

  exlib::sg::ortho* camera = new exlib::sg::ortho;
  camera->position.value(inlib::vec3f(0,0,4));    
  camera->height.value(2);    
  camera->znear.value(0.1);
  camera->zfar.value(100);
  sep->add(camera);

 {exlib::sg::matrix* m = new exlib::sg::matrix;
  m->mtx.value().set_rotate(0,1,0,inlib::fhalf_pi()/2.0f);
  sep->add(m);}

 {exlib::sg::matrix* m = new exlib::sg::matrix;
  m->mtx.value().set_rotate(1,0,0,inlib::fhalf_pi()/2.0f);
  sep->add(m);} //done first.

  exlib::sg::color* color = new exlib::sg::color();
  color->rgb = inlib::colorf::green();
  sep->add(color); //sg takes ownership of color.

  exlib::sg::cube* node = new exlib::sg::cube();
  node->width.value(1.0f);
  node->height.value(1.0f);
  node->depth.value(1.0f);
  sep->add(node);

  //////////////////////////////////////////////////////////
  /// create the viewer, set the scene graph ///////////////
  //////////////////////////////////////////////////////////
  exlib::sg::viewer viewer(std::cout,400,200);
  viewer.sg().add(sep); //give sep ownership to the viewer.

  //////////////////////////////////////////////////////////
  /// create window, attach to the viewer, steer ///////////
  //////////////////////////////////////////////////////////

  exlib::X11::session x11(std::cout);
  if(!x11.display()) return EXIT_FAILURE;

  Window win = x11.create_window("win 1",0,0,400,200);
  if(win==0L) return EXIT_FAILURE;
  x11.show_window(win);

  Atom atom = ::XInternAtom(x11.display(),"WM_DELETE_WINDOW",False);

  while(true) { 
    XEvent xevent;
    if(::XPending(x11.display())) {
      ::XNextEvent(x11.display(),&xevent);
      //std::cout << xevent.type << std::endl;
      //if((xevent.type==Expose)||(xevent.type==ConfigureNotify)) {
      if(xevent.type==ClientMessage) {
        if(xevent.xclient.data.l[0]==(long)atom) break;        
      } else if(xevent.type==Expose) {
        int width,height;
        x11.window_size(win,width,height);

        viewer.set_size(width,height);

        if(::glXMakeCurrent(x11.display(),win,x11.context())==False){
          std::cout << "glXMakeCurrent failed." << std::endl;
          break;
        }

        viewer.render();

        ::glXSwapBuffers(x11.display(),win);

      } else if(xevent.type==ButtonPress && xevent.xbutton.button==1) {
        break;
      }
    }
  }


  x11.delete_window(win);

#ifdef INLIB_MEM
  }inlib::mem::balance(std::cout);
#endif

  return EXIT_SUCCESS;
}

Boolean operations on solids

As a curiosity you can have a look to the sg_polyhedron_X11.cpp example that shows a boolean operation over solids.

    cd <path to exlib>
    cd examples/cpp
    ./build sg_polyhedron_X11.cpp
    ./exlib_example_sg_polyhedron_X11

You should see :

exlib_exa_sg_1.png

The code of sg_polyhedron_X11.cpp is similar to the sg_cube_X11 one. We show here only the boolean operation over solids part :

  #include <exlib/sg/polyhedron>
  ...
  // A Tube with a transvers hole :
  inlib::hep::polyhedronTubs tubs_1(0.7,1.5,2,0,inlib::two_pi());
  inlib::hep::polyhedronTubs tubs_2(  0,0.5,4,0,inlib::two_pi());
  tubs_2.Transform(inlib::rotd(inlib::vec3d(0,1,0),inlib::half_pi()),
                   inlib::vec3d(0,0,0));
  inlib::hep::polyhedron op = tubs_1.subtract(tubs_2);

  exlib::sg::polyhedron* node = new exlib::sg::polyhedron();
  //node->ph = 
  //  inlib::hep::polyhedronSphere(0.9,1,0,inlib::two_pi(),0,inlib::pi());
  node->ph = op;
  //node->solid = false;
  //node->reduced_wire_frame = false;
  sep->add(node);
  ...

GUI done with scene graphs !

In ioda, in order to have the same look and feel on desktops, phones and tablets, we have decided to handle the GUI by using also OpenGL-ES for the rendering. Then exlib/sg contains nodes as "button, arrowb, list" that permit to build a graphical user interface by using the same tools used for the visualization of data.

Obviously, being able to handle a GUI in this way simplifies a lot the situation for Android and iOS since we don't have to coope too much with the problem of mixing java or Obj-C with C++. (The only things that we ask to java on Android is to give us an OpenGL-ES window, and the same with Obj-C on iOS). The drawback is that for the moment the ioda GUI does not look so great, but at least the functionality is here and it is workable.

As a "button example" you can build sg_button_X11.cpp :

    <install coin3d>
    cd <path to exlib>
    cd examples/cpp
    ./build sg_button_X11.cpp
    ./exlib_example_sg_button_X11

You should see :

exlib_exa_sg_button.png

Clicking inside the button should display the message :

    hello exlib/sg/button callback.

You exit the application by clicking the window close button.

// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file exlib.license for terms.


#include <inlib/mem>

#include <exlib/sg/separator>
#include <exlib/sg/ortho>
#include <exlib/sg/matrix>
#include <exlib/sg/color>
#include <exlib/sg/button>

#include <exlib/sg/viewer>
#include <exlib/X11/session>

#include <iostream>
#include <cstdlib>

int main(int,char**) {

#ifdef INLIB_MEM
  inlib::mem::set_check_by_class(true);{
#endif

  //////////////////////////////////////////////////////////
  /// create scene graph ///////////////////////////////////
  //////////////////////////////////////////////////////////
  exlib::sg::separator* sep = new exlib::sg::separator;

  exlib::sg::ortho* camera = new exlib::sg::ortho;
  camera->position.value(inlib::vec3f(0,0,4));    
  camera->height.value(2);    
  camera->znear.value(0.1);
  camera->zfar.value(100);
  sep->add(camera);

  /*
 {exlib::sg::matrix* m = new exlib::sg::matrix;
  m->mtx.value().set_rotate(0,1,0,inlib::fhalf_pi()/2.0f);
  sep->add(m);}

 {exlib::sg::matrix* m = new exlib::sg::matrix;
  m->mtx.value().set_rotate(1,0,0,inlib::fhalf_pi()/2.0f);
  sep->add(m);} //done first.
  */

  exlib::sg::color* color = new exlib::sg::color();
  color->rgb = inlib::colorf::green();
  sep->add(color); //sg takes ownership of color.

  exlib::sg::button* b = new exlib::sg::button();
  b->width = 3;
  b->height = 1;
  b->font = inlib::sg::font_arialbd_ttf();
  b->front_face = inlib::sg::winding_cw;

  b->back_area::color = inlib::colorf::orange();
  b->color = inlib::colorf::black();
  b->arm_color = inlib::colorf::yellow();

  b->strings.add("click me !");
  b->confine = true;

  class cbk : public inlib::sg::cbk {
  public:
    virtual return_action action() {
      std::cout << "hello exlib/sg/button callback." << std::endl;
      return return_none;     
    }
    virtual inlib::sg::cbk* copy() const {return new cbk(*this);}
  public:
    cbk(): inlib::sg::cbk(){}
    virtual ~cbk(){}
  private:
    cbk(const cbk& a_from)
    :inlib::sg::cbk(a_from)/*,m_gv(a_from.m_gv)*/{}
    cbk& operator=(const cbk& a_from){
      inlib::sg::cbk::operator=(a_from);
      return *this;
    }
  private:
    //gui_viewer& m_gv;
  };

  b->add_callback(new cbk());
  sep->add(b);

  //////////////////////////////////////////////////////////
  /// create the viewer, set the scene graph ///////////////
  //////////////////////////////////////////////////////////
  exlib::sg::viewer viewer(std::cout,400,200);
  viewer.sg().add(sep); //give sep ownership to the viewer.

  //////////////////////////////////////////////////////////
  /// create window, attach to the viewer, steer ///////////
  //////////////////////////////////////////////////////////

  exlib::X11::session x11(std::cout);
  if(!x11.display()) return EXIT_FAILURE;

  Window win = x11.create_window("win 1",0,0,400,200);
  if(win==0L) return EXIT_FAILURE;
  x11.show_window(win);

  Atom atom = ::XInternAtom(x11.display(),"WM_DELETE_WINDOW",False);

  while(true) { 
    XEvent xevent;
    if(::XPending(x11.display())) {
      ::XNextEvent(x11.display(),&xevent);
      //std::cout << xevent.type << std::endl;
      //if((xevent.type==Expose)||(xevent.type==ConfigureNotify)) {
      if(xevent.type==ClientMessage) {
        if(xevent.xclient.data.l[0]==(long)atom) break;        
      } else if(xevent.type==Expose) {
        int width,height;
        x11.window_size(win,width,height);

        viewer.set_size(width,height);

        if(::glXMakeCurrent(x11.display(),win,x11.context())==False){
          std::cout << "glXMakeCurrent failed." << std::endl;
          break;
        }

        viewer.render();

        ::glXSwapBuffers(x11.display(),win);

      } else if(xevent.type==ButtonPress && xevent.xbutton.button==1) {

        int width,height;
        x11.window_size(win,width,height);

        int x = xevent.xbutton.x;
        int y = height-xevent.xbutton.y;

        float hsize = 2;
        float l = x-hsize; //could be negative.
        float r = x+hsize;
        float b = y-hsize; //could be negative.
        float t = y+hsize;
        inlib::sg::pick_action action(std::cout,width,height,l,r,b,t);

        sep->pick(action); //can trigger button callback.
        if(!action.end()) {
	  std::cout << "main :" 
                    << " bad pick_action end."
                    << std::endl;
          return EXIT_FAILURE;
        }

        //execute callbacks of picked button :
        inlib::sg::node* node = action.node();
        if(node) {
          if(inlib::sg::base_button* btn = 
               inlib::cast<inlib::sg::node,inlib::sg::base_button>(*node)){
            const std::vector<inlib::sg::cbk*>& cbks = btn->callbacks();
            std::vector<inlib::sg::cbk*>::const_iterator it;
            for(it=cbks.begin();it!=cbks.end();++it) {
              (*it)->action();
            }
          }
        }

      }
    }
  }


  x11.delete_window(win);

#ifdef INLIB_MEM
  }inlib::mem::balance(std::cout);
#endif

  return EXIT_SUCCESS;
}

Incorporating an Inventor scene (desktop only)

The example sg_iv_X11.cpp shows how to incorporate an Inventor scene graph read from an Inventor file within an exlib/sg environment. Note that you need first to install an Inventor implementation, for example coin3d.

    <install coin3d>
    cd <path to exlib>
    cd examples/cpp
    ./build sg_iv_X11.cpp
    ./exlib_example_sg_iv_X11 -rot bird.iv

You should see :

exlib_exa_sg_bird.png

By bringing the file :

   http://ioda.lal.in2p3.fr/download/data/iv/LHCb_artist.hiv

and doing :

    ./exlib_example_sg_iv_X11 LHCb_artist.hiv

You should see :

exlib_exa_sg_LHCb.png