//
//    File: glmesh.cc
//
//    (C) 2000-2008 Helmut Cantzler
//
//    Licensed under the terms of the Lesser General Public License.
//

#include "timeval.h"
#ifndef WIN32
    #include <unistd.h>
#endif

#include <QMouseEvent>
#include <QWheelEvent>
#include <QCursor>
#include <QAction>

#include "glmesh.h"

#include "mview.h"
#include "coordinates.h"

// for FPS
unsigned long timeCounter;
unsigned int frameCounter;

GLMeshSettings::GLMeshSettings()
{
  init();
}

void GLMeshSettings::init()
{
  mesh = features = NULL;
  meshDisplayMode = featuresDisplayMode = NOTHING;
  pointSize = 1.1;
  lineSize = lightBrightness = 1.0;
  red = green = blue = 0.0;
  displayNormals = displayShapeColors = displayBoundingBox = false;
  displayFeatures = displayPolygons = false;
  enableTextureFilter = enableCutBackFaces = enableLighting = false;
  enablePicking = enableAspectRatio = false;

  pickRendering = false;
  pickOutputWindow = NULL;

  resetViewpoint();
}

void GLMeshSettings::clear()
{
  delete mesh;
  delete features;

  init();
}

void GLMeshSettings::clearFeatures()
{
  delete features;

  features = NULL;
  featuresDisplayMode = NOTHING;
}

void GLMeshSettings::resetViewpoint()
{
  xShift = yShift = zShift = 0.0;
  clipping = 0.0;

  // create identity matrix
  for (int i=0; i < 4; i++)
    for (int j=0; j < 4; j++)
      tbTransform[i][j]= i == j ? 1.0 : 0.0;
}

void GLMeshSettings::setViewpoint(GLMeshSettings &s)
{
  xShift = s.xShift;
  yShift = s.yShift;
  zShift = s.zShift;
  clipping = s.clipping;

  for (int i=0; i < 4; i++)
    for (int j=0; j < 4; j++)
      tbTransform[i][j] = s.tbTransform[i][j];
}

GLMesh::GLMesh(QWidget *parent, GLMeshSettings *s, bool p, int f)
  : QGLWidget(parent)
{
  GLMesh::parent=parent;
  settings = s;
  perspective = p;
  face = f;

  // Initialise variables
  initMesh();
  initFeatures();
}

GLMesh::~GLMesh()
{
  // delete texture
  glDeleteTextures(numberOfShapes, textureList);
  delete textureList;
}

float GLMesh::getFps(void)
{
  static float fps = 0.0;

  if (timeCounter != 0)
    {
      fps = frameCounter * 1000000.0 / (float) timeCounter;
      timeCounter = frameCounter = 0;
    }

  return fps;
}

void GLMesh::resetViewpoint(void)
{
  settings->resetViewpoint();
  updateGL();
}

void GLMesh::setViewpoint(GLMeshSettings &vp)
{
  settings->setViewpoint(vp);
  updateGL();
}

void GLMesh::displayPickObjects(void)
{
  Mesh *mesh = settings->mesh;

  if (mesh != NULL)
    {
      list<Vertex*>::iterator iv;
      list<Edge*>::iterator ie;
      list<Triangle*>::iterator it;

      // Purple, Green, Yellow, White
      //  const int colors[4] = {5, 2, 4, 7};
      const int colors[4] = {19, 16, 18, 21};
      int nr;

      glPointSize(8.0);
      for (nr=-1, iv=mesh->selectedVertices.begin(); 
	   iv != mesh->selectedVertices.end(); iv++)
	{
	  // Set color
	  nr = nr+1 <= 3 ? nr+1 : 0;
	  setMaterialColor(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE,
			   colors[nr]);
	  
	  glBegin(GL_POINTS);
	   glVertex3fv( (GLfloat*) (*iv)->floatData() );
	  glEnd();
	}
      glPointSize(settings->pointSize);

      glLineWidth(10);
      for (nr=-1, ie=mesh->selectedEdges.begin(); 
	   ie != mesh->selectedEdges.end(); ie++)
	{
	  // Set color
	  nr = nr+1 <= 3 ? nr+1 : 0;
	  setMaterialColor(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, 
			   colors[nr]);
	  
	  glBegin(GL_LINES);
	   glVertex3fv((GLfloat*) (*ie)->vertices[0]->floatData() );
	   glVertex3fv((GLfloat*) (*ie)->vertices[1]->floatData() );
	  glEnd();
	}
      glLineWidth(settings->lineSize);

      for (nr=-1, it=mesh->selectedTriangles.begin(); 
	   it != mesh->selectedTriangles.end(); it++)
	{
	  // Set color
	  nr = nr+1 <= 3 ? nr+1 : 0;
	  setMaterialColor(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, 
			   colors[nr]);
	  
	  glBegin(GL_TRIANGLES);
	   glNormal3fv( (GLfloat*) (*it)->floatNormal() );
	   glVertex3fv( (GLfloat*) (*it)->vertices[0]->floatData() );
	   glVertex3fv( (GLfloat*) (*it)->vertices[1]->floatData() );
	   glVertex3fv( (GLfloat*) (*it)->vertices[2]->floatData() );
	  glEnd();
	}
    }
}

void GLMesh::pickObject(int x, int y)
{
  Mesh *mesh = settings->mesh;
  GLuint *records, name, selectBuffer[1000];
  GLint hits;

  // set the coordinates & size for the pick window
  settings->pickX = x;   settings->pickY = y;

  // enable pick rendering
  settings->pickRendering = true;

  // vertices & lines are harder to pick => they get a bigger pick window
  switch (settings->meshDisplayMode)
    {
    case EDGES:
    case VERTICES:
      settings->pickSizeX = settings->pickSizeY = 20.0;
      break;

    default:
      settings->pickSizeX = settings->pickSizeY = 10.0;
      break;
    }

  glSelectBuffer(1000, selectBuffer);

  // start with a big window size
  // gradually decrease the size until we only get one hit
  do
    {
      // "select" rendering
      glRenderMode(GL_SELECT);

      glInitNames();
      glPushName(0);
      updateGL();

      // back to normal rendering
      hits = glRenderMode(GL_RENDER);

      settings->pickSizeX--;
      settings->pickSizeY--;
    }
  while (hits > 1 && settings->pickSizeX > 0);

  // we might have made the window to small???
  if (hits < 1)
    {
      settings->pickSizeX+=2;
      settings->pickSizeY+=2;

      // "select" rendering
      glRenderMode(GL_SELECT);

      glInitNames();
      glPushName(0);
      updateGL();

      // back to normal rendering
      hits = glRenderMode(GL_RENDER);
    }

  // disable pick rendering
  settings->pickRendering = false;

  settings->mesh->clearSelection();

  records = (GLuint*) selectBuffer;

  // process results
  // we are only interested in one selection, only take the 1st one
  if (hits >= 1)
    {
      Vertex *v = NULL;
      Edge *e = NULL;
      Triangle *t = NULL;

      // number of names in the record stack
      records++;
      // min & max z values
      records++;   records++;

      // names (well the 1st one) are:
      name = *records;   records++;

      switch (settings->meshDisplayMode)
	{
	case VERTICES:
	  v = mesh->selectVertex(name);
	  break;

	case EDGES:
	  e = mesh->selectEdge(name);
	  break;

	case SOLID:
	case TEXTURE:
	case FRONTLINES:
	case WIRE:
	  t = mesh->selectTriangle(name);
	  break;

	case FEATURES:
	  switch (settings->featuresDisplayMode)
	    {
	    case VERTICES:
	      v = mesh->selectVertex(name);
	      break;

	    case EDGES:
	      e = mesh->selectEdge(name);
	      break;
	    }

	  break;
	  }

      if (settings->pickOutputWindow != NULL)
	settings->pickOutputWindow->printResult(v, e, t);
    }
}

void GLMesh::clearSelection(void)
{
  if (settings->mesh != NULL)
    {
      settings->mesh->clearSelection();
      updateGL();
    }
}

// Mouse Events

void GLMesh::mousePressEvent(QMouseEvent *e)
{
  int x = e->x();
  int y = e->y();

  if ( face != -1 )
    return;

  setFocus();

  switch (e->button())
    {

    case Qt::LeftButton:

      if (settings->enablePicking)
	{
	  if (settings->mesh != NULL)
	    {
	      pickObject(x, y);
	      updateGL();
	    }
	}
      else
	{
	  actionMode=ROTATE;
	  setCursor( QCursor(Qt::CrossCursor) );
	  tbPointToVector(x, y, tbLastPosition);
	}
      break;

    case Qt::MidButton:

      actionMode=TRANSLATE;
      setCursor( QCursor(Qt::SizeAllCursor) );
      lastMouseX = x;
      lastMouseY = y;
      break;

    case Qt::RightButton:

      actionMode=ZOOM;
      setCursor( QCursor(Qt::SizeVerCursor) );
      lastMouseX = x;
      lastMouseY = y;
      break;

    }
}

void GLMesh::mouseMoveEvent(QMouseEvent *e)
{
  if (actionMode == NONE)
    return;

//  if ( face != -1 )
  //  return;

  int x = e->x();
  int y = e->y();

  switch (actionMode)
    {
    case ZOOM:
      settings->zShift += (y-lastMouseY)/(double)height()*3;
      lastMouseX = x;
      lastMouseY = y;
      break;
 
    case TRANSLATE:
      settings->xShift += (x-lastMouseX)/(double)width()*3;
      settings->yShift -= (y-lastMouseY)/(double)height()*3;
      lastMouseX = x;
      lastMouseY = y;
      break;
 
    case ROTATE:
      float currentPosition[3], angle, dx, dy, dz;
 
      tbPointToVector(x, y, currentPosition);
 
      dx = currentPosition[0] - tbLastPosition[0];
      dy = currentPosition[1] - tbLastPosition[1];
      dz = currentPosition[2] - tbLastPosition[2];

      angle = 180.0 * sqrt(dx * dx + dy * dy + dz * dz);
 
      tbAxis[0] = tbLastPosition[1] * currentPosition[2] -
                  tbLastPosition[2] * currentPosition[1];
      tbAxis[1] = tbLastPosition[2] * currentPosition[0] -
                  tbLastPosition[0] * currentPosition[2];
      tbAxis[2] = tbLastPosition[0] * currentPosition[1] -
                  tbLastPosition[1] * currentPosition[0];
 
      tbLastPosition[0] = currentPosition[0];
      tbLastPosition[1] = currentPosition[1];
      tbLastPosition[2] = currentPosition[2];

      glPushMatrix();
      glLoadIdentity();
      glRotatef(angle, tbAxis[0], tbAxis[1], tbAxis[2]);
      glMultMatrixf((GLfloat*) settings->tbTransform);
      glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)settings->tbTransform);
      glPopMatrix();

      break;
    }

  updateGL();
}

void GLMesh::wheelEvent(QWheelEvent *e)   
{
  settings->zShift -= (e->delta())/(double)height();

  updateGL();
}

void GLMesh::mouseReleaseEvent(QMouseEvent *e)
{
  actionMode = NONE;
  setCursor( QCursor(Qt::ArrowCursor) );
}

// public slots

void GLMesh::newMesh()
{
  if (settings->enableLighting)
    glEnable(GL_LIGHTING);
  else
    glDisable(GL_LIGHTING);
 
  if (settings->enableCutBackFaces)
    glEnable(GL_CULL_FACE);
  else
    glDisable(GL_CULL_FACE);

  // delete old texture
  glDeleteTextures(numberOfShapes, textureList);
  delete textureList;

  // initialise new mesh
  initMesh();
  initFeatures();
  initTexture();

  updateGL();
}

void GLMesh::newFeatures(void)
{
  initFeatures();
  updateGL();
}

void GLMesh::setDisplayMode(QAction *a)
{ 
  if (settings->mesh != NULL)
    {
      QString name = a->toolTip();

      if (name == "Texture") {
	settings->meshDisplayMode = TEXTURE;
      }

      if (name == "Solid") {
	settings->meshDisplayMode = SOLID;
      }

      if (name == "Frontlines") {
	settings->meshDisplayMode = FRONTLINES;
      }

      if (name == "Wireframe") {
	if (settings->mesh->numberOfTriangles() == 0)
	{ settings->meshDisplayMode = EDGES; }
	else
	{ settings->meshDisplayMode = WIRE; }
      }

      if (name == "Points") {
	settings->meshDisplayMode = VERTICES;
      }

      if (name == "Feature") {
	settings->meshDisplayMode = FEATURES;
      }

      clearSelection();
    }
}

void GLMesh::setClipping(int c)
{
  settings->clipping = -c/10.0;
  updateGL();
}

void GLMesh::setPick(bool p, CoordinatesWindow *cw)
{
  clearSelection();
  settings->enablePicking = p;
  settings->pickOutputWindow = cw;
}

void GLMesh::setLightIntensity(int l) // l = 1..20
{
  settings->lightBrightness = 0.4 + (l-10) * 0.02;
  GLfloat source[] = {settings->lightBrightness, settings->lightBrightness,
		      settings->lightBrightness, settings->lightBrightness};

  glLightfv(GL_LIGHT0, GL_DIFFUSE, source);
  glLightfv(GL_LIGHT1, GL_DIFFUSE, source);
  glLightfv(GL_LIGHT2, GL_DIFFUSE, source);
  glLightfv(GL_LIGHT3, GL_DIFFUSE, source);

  updateGL();
}

void GLMesh::setPointSize(int l) // 1.0 1.2 1.4 ...
{
  settings->pointSize = 1.0 + l * 0.2;
  glPointSize(settings->pointSize);
  updateGL();
}

void GLMesh::setLineSize(int l) // 1.0 2.0 3.0 ...
{
  settings->lineSize = l;
  glLineWidth(settings->lineSize);
  updateGL();
}

void GLMesh::setLight(bool l)
{
  settings->enableLighting = l;
  if (settings->enableLighting)
    glEnable(GL_LIGHTING);
  else
    glDisable(GL_LIGHTING);
  updateGL();
}

void GLMesh::setPolygons(bool p)
{
  settings->displayPolygons = p;
  updateGL();
}

void GLMesh::setShapeColors(bool sc)
{
  settings->displayShapeColors = sc;
  updateGL();
}

void GLMesh::setNormals(bool n)
{
  settings->displayNormals = n;
  updateGL();
}

void GLMesh::setBoundingBox(bool b)
{
  settings->displayBoundingBox = b;
  updateGL();
}

void GLMesh::setBackFaces(bool bf)
{
  settings->enableCutBackFaces = bf;
  if (settings->enableCutBackFaces)
    glEnable(GL_CULL_FACE);
  else
    glDisable(GL_CULL_FACE);
  updateGL();
}

void GLMesh::setTextureFiltering(bool tf)
{
  GLint filter;
  int id;
 
  settings->enableTextureFilter = tf;
  filter = settings->enableTextureFilter ? GL_LINEAR : GL_NEAREST;
 
  for (int i=0; i < numberOfShapes; i++)
    {
      id=settings->mesh->getShape(i)->getTextureId();
 
      if (id != -1)
        {
          glBindTexture(GL_TEXTURE_2D, id);
 
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
        }
    }
  updateGL();
}

void GLMesh::setFeatures(bool f)
{
  settings->displayFeatures = f;
  updateGL();
}

void GLMesh::setAspectRatio(bool a)
{
  settings->enableAspectRatio = a;
  resizeGL(width(), height());
  updateGL();
}

void GLMesh::setBackgroundColor(QColor c)
{
  settings->red = c.red()/255.0;
  settings->green = c.green()/255.0;
  settings->blue = c.blue()/255.0;

  qglClearColor(c);
  updateGL();
}

// Initialise OpenGL

void GLMesh::initFeatures(void)
{
  if (settings->features == NULL)
    settings->featuresDisplayMode=NOTHING;
  else
    if (settings->features->numberOfEdges() > 0)
      settings->featuresDisplayMode=EDGES;
    else if (settings->features->numberOfVertices() > 0)
      settings->featuresDisplayMode=VERTICES;
}

void GLMesh::initMesh(void)
{
  if (settings->mesh == NULL)
    {
      numberOfShapes = 0;
      settings->meshDisplayMode=NOTHING;
    }
  else
    {
      numberOfShapes = settings->mesh->numberOfShapes();
      surfaceNormalLength = 0.02 + settings->mesh->averageTriangleSize()/10;
    }
    
  timeCounter = frameCounter = 0;

  actionMode=NONE;
}

void GLMesh::initializeGL(void)
{
  // Set background colour and clear
  glClearColor(settings->red, settings->green, settings->blue, 1.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
  // Initialise light source
  initLighting();
 
  // Other initialisations
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);
  glLineWidth(1.0);
  glPointSize(1.0);
  glEnable(GL_POINT_SMOOTH);

  if (settings->enableCutBackFaces)
    glEnable(GL_CULL_FACE);
  else
    glDisable(GL_CULL_FACE);

  // Enable Smooth Shading
  //glShadeModel(GL_SMOOTH);
  // Really Nice Perspective Calculations
  //glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
  
  // Initialise the modelview transformation
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  // Texture stuff
  initTexture();
}

void GLMesh::paintGL()
{
    if ( face != -1 )
    {
        const GLcharARB* my_fragment_shader_source;
        const GLcharARB* my_vertex_shader_source;

        // Get Vertex And Fragment Shader Sources
        //my_fragment_shader_source = GetFragmentShaderSource();
        //my_vertex_shader_source = GetVertexShaderSource();

        my_fragment_shader_source = "\n"
            "varying vec4 viewspace;\n"
            "\n"
            "void main()\n"
            "{\n"
            "    // Setting Each Pixel To Red\n"
            "    float col = viewspace.z + 0.5;\n"
            "    gl_FragColor = vec4( col, col, col, 1.0 );// vec4(1.0, 0.0, 0.0, 1.0);\n"
            "}\n";

        my_vertex_shader_source = "\n"
            "varying vec4 viewspace;\n"
            "\n"
            "void main()\n"
            "{\n"
            "    // Transforming The Vertex\n"
            "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
            "\n"
            "    // Transforming The Vertex Position To ModelView-Space\n"
            "    viewspace = gl_Position; // gl_Vertex; // gl_ModelViewMatrx * gl_Vertex;\n"
            "}\n";

        // Create Shader And Program Objects
        void *my_program         = glCreateProgramObjectARB();
        void *my_vertex_shader   = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
        void *my_fragment_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);

        // Load Shader Sources
        glShaderSourceARB(my_vertex_shader, 1, &my_vertex_shader_source, NULL);
        glShaderSourceARB(my_fragment_shader, 1, &my_fragment_shader_source, NULL);

        // Compile The Shaders
        glCompileShaderARB(my_vertex_shader);
        glCompileShaderARB(my_fragment_shader);

        // Attach The Shader Objects To The Program Object
        glAttachObjectARB(my_program, my_vertex_shader);
        glAttachObjectARB(my_program, my_fragment_shader);

        // Link The Program Object
        glLinkProgramARB(my_program);

        // Use The Program Object Instead Of Fixed Function OpenGL
        glUseProgramObjectARB(my_program);
    }


  static struct timeval start, end;

  gettimeofday(&start, NULL);

  // Clear the current display
  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

  // save matrix
  glPushMatrix();
 
  // Change the modelview transformation
  glLoadIdentity();

  //glTranslatef( settings->xShift, settings->yShift, settings->zShift );
  glTranslatef( 0, 0, settings->zShift );

  GLfloat matrix[6][16] = 
  {
   { // Left Side
     0, 0, -1, 0,
     0, 1, 0, 0,
     1, 0, 0, 0,
     0, 0, 0, 1,
   },
   { // Top
     1, 0, 0, 0,
     0, 0, 1, 0,
     0, -1, 0, 0,
     0, 0, 0, 1,
   },
   { // Front
     1, 0, 0, 0,
     0, 1, 0, 0,
     0, 0, 1, 0,
     0, 0, 0, 1,
   },
   { // Bottom
     1, 0, 0, 0,
     0, 0, -1, 0,
     0, 1, 0, 0,
     0, 0, 0, 1,
   },
   { // Right Side
     0, 0, 1, 0,
     0, 1, 0, 0,
     -1, 0, 0, 0,
     0, 0, 0, 1,
   },
   { // Back
     -1, 0, 0, 0,
     0, 1, 0, 0,
     0, 0, -1, 0,
     0, 0, 0, 1,
   },
  };

  if ( face >= 0 && face < 6 )
  {
      GLfloat mat[16];
      for (int i = 0; i < 16; i++)
        mat[i] = matrix[face][i] * 0.99f; 
      mat[16] = 1;
      glLoadMatrixf( mat );
  }
  else 
  {
      glMultMatrixf((GLfloat*) settings->tbTransform);
  }

  // Change the projection transformation
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  // for object picking
  if (settings->pickRendering)
      {
        int viewport[4];
	glGetIntegerv(GL_VIEWPORT, viewport);
        gluPickMatrix(settings->pickX,
		      viewport[3] + 2*viewport[1] - settings->pickY,
		      settings->pickSizeX, settings->pickSizeY, viewport);
      }

  // use orthogonal 2D rendering
  //if ( perspective )
  //gluPerspective(30, 1.0f, -3.1 - settings->zShift - settings->clipping, 3.1 );
  //gluOrtho2D(1, 1, -3.1 - settings->zShift - settings->clipping, 3.1 );

  if ( perspective )
  {
    //glFrustum( -10.0f, 10.0f, -10.0f, 10.0f, 0.0001f, 100000.0f);
    gluPerspective(90, 1.0f, 0.01f, 100000.0f);
  }
  else
  {
    glOrtho(-2.1 - settings->xShift - settings->zShift,
	   2.1 - settings->xShift + settings->zShift,
          -2.1 - settings->yShift - settings->zShift,
	   2.1 - settings->yShift + settings->zShift,
          -3.1 - settings->zShift - settings->clipping, 3.1);
  }

  glMatrixMode(GL_MODELVIEW);

  // display main object (typically a mesh)
  switch (settings->meshDisplayMode)
    {
    case VERTICES:
      setMaterialColor(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, CYAN);
      displayShapePoints();
      break;

    case EDGES:
      setMaterialColor(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, CYAN*4+3);
      //setMaterialColor(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, BLUE);
      displayEdges(settings->mesh->getEdges());
      break;

    case SOLID:
    case TEXTURE:
      // Set polygon mode
      glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
      displayMesh();
      break;
 
    case FRONTLINES:
      displayMeshWithStencil();
      break;

    case WIRE:
      // Set polygon mode
      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      displayMesh();
      break;

    case FEATURES:
      displayFeatures();
      break;
      
      //    case FRONTLINES2:
      // draw the solid
      //      glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
      //      displayMesh();
      // draw the the wireframe on top of the solid
      //      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      //      glDepthFunc(GL_LEQUAL);
      //      displayMesh();
      //      glDepthFunc(GL_LESS);
      //      break;
    }

  checkForTraps("Draw Polygons", 1);

  // Draw features like points and edges additionally
  if (settings->displayFeatures && settings->meshDisplayMode != FEATURES)
    displayFeatures();

  checkForTraps("Draw Features", 1);

  // Draw the surface normals
  if (settings->displayNormals)
    displayNormals();

  checkForTraps("Draw Surface Normals",1);

  // Draw the bounding box
  if (settings->displayBoundingBox)
    displayBoundingBox();

  checkForTraps("Draw Bounding Box", 1);

  displayPickObjects();
  
  glPopMatrix();
  glFlush();

  gettimeofday(&end, NULL);
  timeCounter += (end.tv_sec - start.tv_sec)*1000000 +
                  end.tv_usec - start.tv_usec;
  frameCounter++;
}

void GLMesh::resizeGL(int width, int height)
{
  int max = width > height ? width : height;

  if (settings->enableAspectRatio)
    glViewport((width-max)/2, (height-max)/2, max, max);
  else
    glViewport(0, 0, width, height);
}
