28.4. Applying a Render Action Inside a GLX Window

Example 28.2, “ Using a GLX Window creates a GLX window, makes Inventor and OpenGL calls, and then applies a GL render action. It uses OpenGL to render a checked “floor” and Inventor to render a red cube and a blue sphere, in the same window.

Example 28.2.  Using a GLX Window

#include <stdio.h>
#include <unistd.h>

#include <Inventor/SoDB.h>
#include <Inventor/SoSceneManager.h>
#include <Inventor/devices/SoGLContext.h>
#include <Inventor/nodes/SoCube.h>
#include <Inventor/nodes/SoDirectionalLight.h>
#include <Inventor/nodes/SoLightModel.h>
#include <Inventor/nodes/SoMaterial.h>
#include <Inventor/nodes/SoTransform.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoSphere.h>

// This is related to <GL/GL.h> and <GL/GLX.h> but
// it is handled by GLEW
#include <Inventor/sys/SoGL.h>
#include <Inventor/sys/SoGLX.h>

#define    WINWIDTH     400     
#define    WINHEIGHT    400     

float   floorObj[81][3];
SoGLContext* oivContext = NULL;

// Build an Inventor scene with two objects and some light
void
buildScene(SoGroup *root)
{
   // Some light
   root->addChild(new SoLightModel);
   root->addChild(new SoDirectionalLight);

   // A red cube translated to the left and down
   SoTransform *myTrans = new SoTransform;
   myTrans->translation.setValue(-2.0, -2.0, 0.0);
   root->addChild(myTrans);

   SoMaterial *myMtl = new SoMaterial;
   myMtl->diffuseColor.setValue(1.0, 0.0, 0.0);
   root->addChild(myMtl);
   root->addChild(new SoCube);

   // A blue sphere translated right
   myTrans = new SoTransform;
   myTrans->translation.setValue(4.0, 0.0, 0.0);
   root->addChild(myTrans);

   myMtl = new SoMaterial;
   myMtl->diffuseColor.setValue(0.0, 0.0, 1.0);
   root->addChild(myMtl);

   root->addChild(new SoSphere);
}


// Build a floor that will be rendered using OpenGL.
void
buildFloor()
{
   int a = 0;

   for (float i = -5.0; i <= 5.0; i += 1.25) {
      for (float j = -5.0; j <= 5.0; j += 1.25, a++) {
         floorObj[a][0] = j;
         floorObj[a][1] = 0.0;
         floorObj[a][2] = i;
      }
   }
}

// Callback used by GLX window
static Bool
waitForNotify(Display *, XEvent *e, char *arg)
{
   return (e->type == MapNotify) &&
            (e->xmap.window == (Window) arg);
}

// Create and initialize GLX window.
void
openWindow(Display *&display, Window &window)
{
   XVisualInfo  *vi;
   Colormap cmap;
   XSetWindowAttributes swa;
   GLXContext cx;
   XEvent event;
   static int attributeList[] = {
            GLX_RGBA,
            GLX_RED_SIZE, 1,
            GLX_GREEN_SIZE, 1,
            GLX_BLUE_SIZE, 1,
            GLX_DEPTH_SIZE, 1,
            GLX_DOUBLEBUFFER,
            None,
   };

   display = XOpenDisplay(0);
   vi = glXChooseVisual(display,
            DefaultScreen(display), attributeList);
   cx = glXCreateContext(display, vi, 0, GL_TRUE);
   cmap = XCreateColormap(display,
            RootWindow(display, vi->screen),
            vi->visual, AllocNone);
   swa.colormap = cmap;
   swa.border_pixel = 0;
   swa.event_mask = StructureNotifyMask;
   window = XCreateWindow(display,
            RootWindow(display, vi->screen), 100, 100,
            WINWIDTH, WINHEIGHT, 0, vi->depth, InputOutput,
            vi->visual,
            (CWBorderPixel | CWColormap | CWEventMask), &swa);

   XMapWindow(display, window);
   XIfEvent(display, &event, waitForNotify, (char *) window);

   // OIV needs an SoGLContext to perform OpenGL operations
   oivContext = new SoGLContext(display, vi, window, cx);
}


// Draw the lines that make up the floor, using OpenGL
void
drawFloor()
{
   int i;

   glBegin(GL_LINES);
   for (i=0; i<4; i++) {
      glVertex3fv(floorObj[i*18]);
      glVertex3fv(floorObj[(i*18)+8]);
      glVertex3fv(floorObj[(i*18)+17]);
      glVertex3fv(floorObj[(i*18)+9]);
   }

   glVertex3fv(floorObj[i*18]);
   glVertex3fv(floorObj[(i*18)+8]);
   glEnd();

   glBegin(GL_LINES);
   for (i=0; i<4; i++) {
      glVertex3fv(floorObj[i*2]);
      glVertex3fv(floorObj[(i*2)+72]);
      glVertex3fv(floorObj[(i*2)+73]);
      glVertex3fv(floorObj[(i*2)+1]);
   }
   glVertex3fv(floorObj[i*2]);
   glVertex3fv(floorObj[(i*2)+72]);
   glEnd();
}


int main(int, char **)
{
   // Initialize Inventor
   SoDB::init();

   // Build a simple scene graph
   SoSeparator *root = new SoSeparator;
   root->ref();
   buildScene(root);

   // It's better to use an SoSceneManager for some basic operations
   // - clear the buffers
   // - Handles the SoGLRenderAction
   // - Event handling (it is possible to provide the events to the
   // scene graph, useful for draggers...)
   SoSceneManager* sceneManager = new SoSceneManager();
   sceneManager->setSceneGraph(root);
   sceneManager->setBackgroundColor(SbColor(0.8, 0.8, 0.8));
   sceneManager->setWindowSize(SbVec2s(WINWIDTH, WINHEIGHT));

   // Build the floor geometry
   buildFloor();

   // Create and initialize window
   Display *display;
   Window window;
   openWindow(display, window);

   // We need a context here for rendering
   // OIV will bind the GL context.
   oivContext->bind();

   // By default OIV does not enable the depth test.
   glEnable(GL_DEPTH_TEST);

   // Set up the camera using OpenGL.
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluPerspective(90.0, 1.0, 2.0, 12.0);

   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   glTranslatef(0.0, 0.0, -5.0);

   // Render the OIV scene. The render method will clear
   // the OpenGL buffers.
   sceneManager->render();

   // Render the floor using OpenGL
   // - We push the attributes since OIV consider that
   // non-OIV code won't modify the OpenGL state.
   glPushAttrib(GL_ALL_ATTRIB_BITS);

   glPushMatrix();
   glTranslatef(0.0, -3.0, 0.0);
   glColor3f(0.0, 0.7, 0.0);
   glLineWidth(2.0);
   glDisable(GL_LIGHTING);
   drawFloor();
   glEnable(GL_LIGHTING);
   glPopMatrix();

   glPopAttrib();

   // Swap the buffers, the OIV context can handle this.
   oivContext->swapBuffers();

   oivContext->unbind();
   oivContext->unref();

   sleep (10);
   root->unref();

   delete sceneManager;

   return 0;
}