Sophie

Sophie

distrib > Mandriva > 10.0-com > i586 > by-pkgid > 9347541fe87a5ea3f3b8dbc50f660e8e > files > 170

libQGLViewer-devel-1.3.6-1mdk.i586.rpm

#include "select.h"
#include <math.h>
#include <limits.h> // UINT_MAX

using namespace std;

// The id of the selected object. Should be encapsulated.
// -1 means no object is selected.
static int selected;

static void drawSpiral(const bool specialColor = false)
{
  const float nbSteps = 100.0;
  glBegin(GL_QUAD_STRIP);
  for (float i=0; i<nbSteps; ++i)
    {
      if (specialColor)
	glColor3f((nbSteps-i)/nbSteps, .8 , i/nbSteps/2.0);
      else
	glColor3f((nbSteps-i)/nbSteps, .2 , i/nbSteps);
      float angle = i/4.0;
      float c = cos(angle);
      float s = sin(angle);
      float r1 = 0.5 - i/(3.f*nbSteps);
      float r2 = 0.3 - i/(3.f*nbSteps);
      float alt = i/nbSteps - 0.5;
      const float nor = .5;
      const float up = sqrt(1.0-nor*nor);
      glNormal3f(nor*c, nor*s, up);
      glVertex3f(r1*c, r1*s, alt);
      glVertex3f(r2*c, r2*s, alt+0.05);
    }
  glEnd();
}

static void drawScene(bool pushId = false)
{
  // Draw the scene, with a possible pushName() for selection

  // Consider using several stack levels for different objects, or to separate
  // the triangles, edges and vertices of the same object. Example :
  // glPushName(0)
  //   for all triangles i, glPushName(i), draw triangle, glPopName()
  // glPopName()
  //
  // glPushName(1)
  //   for all edges i, glPushName(i), draw edge, glPopName()
  // glPopName()
  //
  // glPushName(2)
  //   for all vertex i, glPushName(i), raster vertex, glPopName()
  // glPopName()
  // As a result, you have a two level stack, with a type id (0, 1 or 2 here)
  // which indicates the type of the primitive, and then the id of the primitive.
  // See the man page of glSelectBuffer() for details.
  
  const int nb = 10;
  for (int i=0; i<nb; ++i)
    {
      glPushMatrix();
      
      glTranslatef(cos(2.0*i*M_PI/nb), sin(2.0*i*M_PI/nb), 0.);
      
      if (pushId)
	{
	  glPushName(i);
	  drawSpiral();
	  glPopName();
	}
      else
	drawSpiral(i==selected);
	  
      glPopMatrix();
    }
}


void Viewer::select(const QMouseEvent* e)
{
  // You should cut-paste this rather tricky function as your own "select()" function.
  // You need a drawing function that pushes and pops names to tag objects' id (see drawSpiral() above).
  // You have to change the call to the drawWithPush (here a simple drawScene()).
  // "selected" is set to the closest object that was picked (or -1 if no object was selected)
  
  // Make openGL context current
  makeCurrent();
  
  const int SENSITIVITY = 4;
  const int NB_HITS_MAX = 64;
  
  // Prepare the selection mode
  static GLuint hits[NB_HITS_MAX];
  
  glSelectBuffer(NB_HITS_MAX, hits);
  glRenderMode(GL_SELECT);
  glInitNames();

  // Loads the matrices
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  GLint viewport[4];
  camera()->getViewport(viewport);
  gluPickMatrix(static_cast<GLdouble>(e->x()), static_cast<GLdouble>(e->y()), SENSITIVITY, SENSITIVITY, viewport);

  // loadProjectionMatrix() first resets the GL_PROJECTION matrix with a glLoadIdentity.
  // Give false as a parameter in order to prevent this and to combine the matrices.
  camera()->loadProjectionMatrix(false);
  camera()->loadModelViewMatrix();
  
  // Render scene with objects ids.
  // Change here to call your own "drawWithPush()" function.
  drawScene(true);
  glFlush();

  // Get the number of objects were seen through the pick matrix frustum. Reset GL_RENDER mode.
  GLint nb_hits = glRenderMode(GL_RENDER);

  // Compute orig and dir, used to draw a representation of the intersecting line
  camera()->convertClickToLine(e->x(), e->y(), orig, dir);

  // Display of selection results
  cout << "(" << e->x() << "," << e->y() << ") : ";
  if (nb_hits <= 0)
    {
      selected = -1;
      cout << "No object selected" << endl;
      return;
    }
  cout << nb_hits << " spiral" << ((nb_hits>1)?"s":"") << " under the cursor" << endl;

  // Interpret results : each object created 4 values in the "hits" array :
  // hits[4*i+1] is the object minimum depth value, while hits[4*i+3] is the id pushed on the stack.
  // Of all the objects that were projected in the pick region, we select the closest one (zMin comparaison).
  // This code needs to be modified if you use several stack levels. See glSelectBuffer() man page.
  unsigned int zMin = hits[1];
  selected = hits[3];
  for (int i=1; i<nb_hits; ++i)
    if (hits[i*4+1] < zMin)
      {
	zMin = hits[i*4+1];
	selected = hits[i*4+3];
      }

  // Find the selectedPoint coordinates, using camera()->pointUnderPixel().
  bool found;
  selectedPoint = camera()->pointUnderPixel(e->x(), e->y(), found);
  selectedPoint -= 0.01*dir; // Small offset to make point clearly visible.
  // Note that "found" is different from (selected>=0) because of SENSITIVITY.
}

void Viewer::init()
{
  restoreFromFile();

  // Means no object is selected.
  selected = -1;

  glLineWidth(3.0);
  glPointSize(10.0);

  help();
}

void Viewer::draw()
{
  drawScene();

  // Draw the previous intersection line
  glBegin(GL_LINES);
  glVertex3fv(orig.address());
  glVertex3fv((orig + 100.0*dir).address());
  glEnd();

  if (selected >= 0)
    {
      glColor3f(.9, .2, .1);
      glBegin(GL_POINTS);
      glVertex3fv(selectedPoint.address());
      glEnd();
    }
}

QString Viewer::helpString() const
{
  QString text("<h2>S e l e c t</h2>");
  text += "Left click while pressing the <b>Shift</b> key to select an object of the scene.<br>";
  text += "Selection is performed using the OpenGL <i>GL_SELECT</i> render mode. ";
  text += "A line is drawn between the selected point and the camera selection position. ";
  text += "using <i>convertClickToLine()</i>, a useful function for analytical intersections.<br>";
  text += "Feel free to cut and paste this implementation in your own applications.";
  return text;
}