/*
**************************************************************************
                                 description
                             --------------------
    copyright            : (C) 2000-2002 by Andreas Zehender
    email                : zehender@kde.org
**************************************************************************

**************************************************************************
*                                                                        *
*  This program is free software; you can redistribute it and/or modify  *
*  it under the terms of the GNU General Public License as published by  *
*  the Free Software Foundation; either version 2 of the License, or     *
*  (at your option) any later version.                                   *
*                                                                        *
**************************************************************************/

// conflicting types for INT32 in qt and glx
#ifndef QT_CLEAN_NAMESPACE
#define QT_CLEAN_NAMESPACE
#endif

#include "pmrendermanager.h"
#include "pmviewstructure.h"
#include "pmobject.h"
#include "pmdeclare.h"
#include "pmcamera.h"
#include "pmquickcolor.h"
#include "pmdefaults.h"
#include "pmgraphicalobject.h"

#include <qptrstack.h>
#include <qapplication.h>
#include <qbitmap.h>
#include <qimage.h>
#include <qpainter.h>
#include <kconfig.h>
#include <klocale.h>

#include <time.h>
#include <stdio.h>
#include <math.h>

#include <GL/gl.h>
#include <GL/glx.h>
#include <GL/glut.h> // Only needed for gluPerspective

#include "pmglview.h"

const GLdouble dA = 0.75;
const GLdouble dB = 0.15;

const GLdouble viewVolumeZ = 1e5;

// Size has to be controlPointSize (see pmglview.h)
const GLubyte PointBitmap[7] = { 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE };
const GLubyte CrossBitmap[7] = { 0x10, 0x10, 0x10, 0xFE, 0x10, 0x10, 0x10 };


const int activeTimeAfterRendering = 2;
const int maxEventTime = 0;
const int maxSubdivisions = 32;
const double subdivisionDistance = 0.05;


PMRenderManager* PMRenderManager::s_pManager = 0;
KStaticDeleter<PMRenderManager> PMRenderManager::s_staticDeleter;

bool PMRenderManager::s_hasOpenGL = true;
bool PMRenderManager::s_hasOpenGLChecked = false;

PMRenderManager* PMRenderManager::theManager( )
{
   if( !s_pManager )
      s_staticDeleter.setObject( s_pManager, new PMRenderManager( ) );
   return s_pManager;
}

PMRenderManager::PMRenderManager( )
      : QObject( qApp )
{
   int i;
   
   m_bStartTask = false;
   m_bStopTask = false;
   m_bTaskIsRunning = false;

   m_graphicalObjectColor[0] = c_defaultGraphicalObjectColor0;
   m_graphicalObjectColor[1] = c_defaultGraphicalObjectColor1;
   m_textureColor[0] = c_defaultTextureColor0;
   m_textureColor[1] = c_defaultTextureColor1;
   m_axesColor[0] = c_defaultAxesColorX;
   m_axesColor[1] = c_defaultAxesColorY;
   m_axesColor[2] = c_defaultAxesColorZ;
   m_controlPointColor[0] = c_defaultControlPointColor0;
   m_controlPointColor[1] = c_defaultControlPointColor1;
   m_backgroundColor = c_defaultBackgroundColor;
   m_fieldOfViewColor = c_defaultFieldOfViewColor;
   m_highDetailCameraView = c_defaultHighDetailCameraView;
   m_nMaxRenderedLines = 1000;
   m_gridDistance = c_defaultGridDistance;
   m_gridColor = c_defaultGridColor;
   m_axesViewStructureCreated = false;
   m_currentVisibility = 0;

   m_renderTasks.setAutoDelete( true );
   m_matrixStack.setAutoDelete( true );
   m_quickColors.setAutoDelete( true );
   
   m_nViews = 0;

   m_subdivisionViewStructure = PMViewStructure( maxSubdivisions + 1,
                                                 maxSubdivisions );
   PMLineArray& lines = m_subdivisionViewStructure.lines( );
   for( i = 0; i < maxSubdivisions; i++ )
      lines[i] = PMLine( i, i+1 );
}

PMRenderManager::~PMRenderManager( )
{
   s_pManager = 0;
}

void PMRenderManager::addView( PMGLView* view, PMObject* active, PMObject* top,
                               PMControlPointList* controlPoints,
                               double aspectRatio, int visibilityLevel,
                               bool graphicalChange )
{
   PMRenderTaskListIterator it( m_renderTasks );
   PMRenderTask* task = 0;
   bool restart = false;
   bool first = true;

   for( ; it.current( ) && !task; ++it )
   {
      if( it.current( )->view( ) == view )
         task = it.current( );
      else
         first = false;
   }

   if( task )
   {
      if( first )
         restart = true;
      else if( graphicalChange )
      {
         m_renderTasks.findRef( task );
         m_renderTasks.take( );
         m_renderTasks.prepend( task );
         restart = true;
      }
      task->setActiveObject( active );
      task->setTopLevelObject( top );
      task->setControlPoints( controlPoints );
      task->setAspectRatio( aspectRatio );
      task->setVisibilityLevel( visibilityLevel );
   }
   else
   {
      task = new PMRenderTask( view, active, top, controlPoints, aspectRatio,
                               visibilityLevel );
      if( graphicalChange )
      {
         m_renderTasks.prepend( task );
         restart = true;
      }
      else
      {
         m_renderTasks.append( task );
         if( m_renderTasks.count( ) == 1 )
            restart = true;
      }
   }
   if( restart )
      restartRendering( );
}

void PMRenderManager::removeView( PMGLView* view )
{
   PMRenderTaskListIterator it( m_renderTasks );
   PMRenderTask* task = 0;
   bool restart = false;

   for( ; it.current( ) && !task; ++it )
      if( it.current( )->view( ) == view )
         task = it.current( );

   if( task )
   {
      if( task == m_renderTasks.first( ) )
      {
         restart = true;
         if( m_bTaskIsRunning )
            emit renderingFinished( task->view( ) );
      }
      m_renderTasks.removeRef( task );
   }
   
   if( restart )
      restartRendering( );
}

bool PMRenderManager::containsTask( PMGLView* view ) const
{
   PMRenderTaskListIterator it( m_renderTasks );
   bool contains = false;

   for( ; it.current( ) && !contains; ++it )
      if( it.current( )->view( ) == view )
         contains = true;
   return contains;
}

QColor PMRenderManager::controlPointColor( int i ) const
{
   if( ( i >= 0 ) && ( i <= 1 ) )
      return m_controlPointColor[i];
   return QColor( 0, 0, 0 );
}

void PMRenderManager::setControlPointColor( int i, const QColor& c )
{
   if( ( i >= 0 ) && ( i <= 1 ) )
      m_controlPointColor[i] = c;
}

QColor PMRenderManager::graphicalObjectColor( int i ) const
{
   if( ( i >= 0 ) && ( i <= 1 ) )
      return m_graphicalObjectColor[i];
   return QColor( 0, 0, 0 );
}

void PMRenderManager::setGraphicalObjectColor( int i, const QColor& c )
{
   if( ( i >= 0 ) && ( i <= 1 ) )
      m_graphicalObjectColor[i] = c;
}

QColor PMRenderManager::axesColor( int i ) const
{
   if( ( i >= 0 ) && ( i <= 2 ) )
      return m_axesColor[i];
   return QColor( 0, 0, 0 );
}

void PMRenderManager::setAxesColor( int i, const QColor& c )
{
   if( ( i >= 0 ) && ( i <= 2 ) )
      m_axesColor[i] = c;
}

void PMRenderManager::setGridDistance( int d )
{
   if( d >= 20 )
      m_gridDistance = d;
}

void PMRenderManager::restartRendering( )
{
   if( !m_bTaskIsRunning && !m_bStartTask )
      startTimer( 0 );

   m_bStartTask = true;
   m_bStopTask = false;
}

void PMRenderManager::slotStopRendering( )
{
   m_bStopTask = true;
   m_bStartTask = false;

   if( m_bTaskIsRunning )
      if( m_pCurrentTask )
         emit renderingFinished( m_pCurrentTask->view( ) );
   m_renderTasks.clear( );
}

void PMRenderManager::timerEvent( QTimerEvent* )
{
   killTimers( );
   renderTask( );
}

void PMRenderManager::renderTask( )
{
   m_bTaskIsRunning = true;
   emit renderingStarted( );
   
   int r, g, b;
   bool disableView = false;

   while( m_bStartTask && !m_bStopTask )
   {
      m_bStartTask = false;

      // render the views sequential
      while( m_renderTasks.first( ) && !m_bStopTask && !m_bStartTask )
      {
         // reset the member variables for rendering
         m_pCurrentTask = m_renderTasks.first( );
         m_pCurrentGlView = m_pCurrentTask->view( );
         emit renderingStarted( m_pCurrentGlView );
         
         m_renderedLines = 0;
         m_selected = false;
         m_pDeselectObject = 0;
         m_matrixStack.clear( );
         m_quickColorObjects.clear( );
         m_quickColors.clear( );
         m_currentColor = m_graphicalObjectColor[0];
         m_specialCameraMode = false;
         m_currentVisibility = 0;
         m_visibilityStack.clear( );

         if( m_bStopTask || m_bStartTask )
            break;
         m_pCurrentGlView->makeCurrent( );

         m_backgroundColor.rgb( &r, &g, &b );
         glClearColor( r/255.0, g/255.0, b/255.0, 1.0 );
         glPointSize( controlPointSize );
         glEnable( GL_DEPTH_TEST );
         glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
      
         glViewport( 0, 0, ( GLint ) m_pCurrentGlView->width( ),
                     ( GLint ) m_pCurrentGlView->height( ) );
         glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
         
         disableView = false;
         if( m_pCurrentGlView->type( ) == PMGLView::PMViewCamera )
         {
            if( m_pCurrentGlView->camera( ) )
            {
               if( m_pCurrentGlView->camera( )->cameraType( )
                   == PMCamera::Omnimax )
                  disableView = true;
            }
            else
               disableView = true;
         }

         if( !disableView )
            setProjection( );

         glLoadIdentity( );            
               
         glDisable( GL_DEPTH_TEST );
         if( m_pCurrentGlView->type( ) == PMGLView::PMViewCamera )
            renderFieldOfView( );
         else
            renderGrid( );
         renderDescription( );
         glEnable( GL_DEPTH_TEST );

         if( !disableView )
         {
            renderAxes( );
            
            renderObject( m_pCurrentTask->topLevelObject( ) );
            if( !m_bStopTask && ! m_bStartTask )
               renderControlPoints( );
         }
         
         if( !m_bStopTask && ! m_bStartTask )
         {
            glXWaitX( );
            emit aboutToUpdate( m_pCurrentGlView );
            if( m_bStopTask || m_bStartTask )
               break;
            glXWaitX( );
            m_pCurrentGlView->swapBuffers( );
            glFinish( );
            glXWaitGL( );
            emit renderingFinished( m_pCurrentGlView );
            if( m_bStopTask || m_bStartTask )
               break;
            qApp->processEvents( maxEventTime );
            if( m_bStopTask || m_bStartTask )
               break;            
            m_renderTasks.removeFirst( );
         }
      }
   }

   emit renderingFinished( );
   
   m_bStopTask = false;
   m_bStartTask = false;
   m_bTaskIsRunning = false;
}

void PMRenderManager::renderObject( PMObject* objectToRender )
{
   bool children = false;
   PMGraphicalObject* go = 0;

   m_objectToRenderStack.append( objectToRender );

   if( objectToRender->isA( PMTGraphicalObject ) )
   {
      go = ( PMGraphicalObject* ) objectToRender;
      m_visibilityStack.push( m_currentVisibility );
      if( go->isVisibilityLevelRelative( ) )
         m_currentVisibility += go->visibilityLevel( );
      else
         m_currentVisibility = go->visibilityLevel( );
   }
   
   if( !m_selected )
   {
      if( objectToRender->isSelected( ) )
      {
         m_selected = true;
         m_pDeselectObject = objectToRender;
         if( objectToRender->hasTransformationMatrix( ) )
            if( objectToRender->parent( ) )
               m_pDeselectObject = objectToRender->parent( );
      }
   }

   if( ( m_pCurrentGlView->type( ) != PMGLView::PMViewCamera )
       || ( objectToRender != ( PMObject* ) ( m_pCurrentGlView->camera( ) ) ) )
   {
      PMObject* obj = 0;
      
      children = objectToRender->lastChild( ) || objectToRender->linkedObject( );
      if( children )
      {
         bool stop;

         PMMatrix* matrix;
         if( m_specialCameraMode )
            matrix = new PMMatrix( m_viewTransformation );
         else
            matrix = new PMMatrix( PMMatrix::modelviewMatrix( ) );
         m_matrixStack.push( matrix );
         
         // render the children and the linked object
         obj = objectToRender->lastChild( );
         while( obj && !m_bStopTask && !m_bStartTask )
         {
            if( !obj->isA( PMTDeclare ) )
               renderObject( obj );
            if( !m_bStopTask && !m_bStartTask )
            {
               do
               {
                  // Do not render declares
                  obj = obj->prevSibling( );
                  if( !obj )
                     stop = true;
                  else
                     stop = !obj->isA( PMTDeclare );
               }
               while( !stop );
            }
         }
         if( !m_bStopTask && !m_bStartTask )
         {
            obj = objectToRender->linkedObject( );
            if( obj )
               renderObject( obj );
         }
      }
   
   
      if( !m_bStopTask && !m_bStartTask )
      {
         // children of the object are rendered
         // render the object

         if( objectToRender == m_pCurrentTask->activeObject( ) )
         {
            if( m_specialCameraMode )
               m_controlPointTransformation = m_viewTransformation;
            else
               m_controlPointTransformation = PMMatrix::modelviewMatrix( );
         }

         if( objectToRender->type( ) == PMTQuickColor )
         {
            PMQuickColor* qc = ( PMQuickColor* ) objectToRender;
            PMObjectListIterator it( m_objectToRenderStack );
            bool pofound = false;
            it.toLast( );
            
            while( it.current( ) && !pofound )
            {
               if( it.current( )->isA( PMTGraphicalObject ) )
                  pofound = true;
               else
                  --it;
            }
            if( pofound )
            {
               if( m_quickColorObjects.top( ) != it.current( ) )
               {
                  m_quickColorObjects.push( it.current( ) );
                  m_quickColors.push( new QColor( m_currentColor ) );
                  m_currentColor = qc->color( ).toQColor( );
               }
            }
         }
         
         PMViewStructure* vs = objectToRender->viewStructure( );
         if( objectToRender->hasTransformationMatrix( ) || vs )
         {
            // object has transformation or view structure
            if( vs )
            {
               if( ( m_currentVisibility <= m_pCurrentTask->visibilityLevel( ) )
                   || ( objectToRender == m_pCurrentTask->activeObject( ) ) )
               {
                  // render the view structure.
                  // call qApp->processEvents( ) each m_nMaxRenderedLines rendered
                  // lines
                  if( m_selected )
                     setGLColor( m_graphicalObjectColor[ 1 ] );
                  else
                     setGLColor( m_currentColor );
                  
                  renderViewStructure( *vs );
               }
            }
            else if( objectToRender->hasTransformationMatrix( ) )
            {
               if( m_specialCameraMode )
                  m_viewTransformation = m_viewTransformation * objectToRender->transformationMatrix( );
               else
                  glMultMatrixd( objectToRender->transformationMatrix( ).data( ) );
            }
         }
         // end rendering
      }
   }
   if( !m_bStopTask && !m_bStartTask )
   {
      if( children )
      {
         PMMatrix* matrix = m_matrixStack.pop( );
         
         if( matrix )
         {
            if( m_specialCameraMode )
               m_viewTransformation = (*matrix);
            else
               glLoadMatrixd( matrix->data( ) );
            delete matrix;
            matrix = 0;
         }
      }
      if( m_selected )
      {
         if( m_pDeselectObject == objectToRender )
         {
            m_selected = false;
            m_pDeselectObject = 0;
         }
      }
      if( m_quickColorObjects.top( ) == objectToRender )
      {
         m_quickColorObjects.pop( );
         QColor* col = m_quickColors.pop( );
         if( col )
         {
            m_currentColor = *col;
            delete col;
            col = 0;
         }
      }
   }

   if( go )
      m_currentVisibility = m_visibilityStack.pop( );

   m_objectToRenderStack.removeLast( );
}

void PMRenderManager::renderViewStructure( PMViewStructure& vs )
{
   if( m_specialCameraMode )
   {
      PMPointArray points = vs.points( );
      points.detach( );
      transformProjection( points.data( ), points.size( ),
                           m_pCurrentGlView->camera( ) );
   
      if( m_highDetailCameraView )
      {
         // subdivide line
         PMLineArray& lines = vs.lines( );
         PMPointArray& utPoints = vs.points( );
         int numLines = lines.size( );
         PMPointArray& sdPoints = m_subdivisionViewStructure.points( );
         PMLineArray& sdLines = m_subdivisionViewStructure.lines( );
         int i, li, sd;
         double distance;
         PMPoint start, end, dir;

         for( i = 0; ( i < numLines ) && !m_bStopTask && !m_bStartTask; i++ )
         {
            // calculate screen distance
            start = points[lines[i].startPoint( )];
            end = points[lines[i].endPoint( )];
            dir[0] = ( end[0] - start[0] ) / m_anglex;
            dir[1] = ( end[1] - start[1] ) / m_angley;
            distance = sqrt( dir[0] * dir[0] + dir[1] * dir[1] );
            
            // calculate number of subdivisions
            sd = ( int ) ( distance / subdivisionDistance );
            if( sd > 1 )
            {
               // calculate subdivision
               if( sd > maxSubdivisions )
                  sd = maxSubdivisions;

               sdPoints[0] = start;
               sdPoints[sd] = end;

               start = utPoints[lines[i].startPoint( )];
               end = utPoints[lines[i].endPoint( )];
               dir[0] = ( end[0] - start[0] ) / sd;
               dir[1] = ( end[1] - start[1] ) / sd;
               dir[2] = ( end[2] - start[2] ) / sd;
               
               for( li = 1; li < sd; li++ )
               {
                  sdPoints[li][0] = start[0] + li * dir[0];
                  sdPoints[li][1] = start[1] + li * dir[1];
                  sdPoints[li][2] = start[2] + li * dir[2];
               }
               // transform points (first and last are already transformed)
               transformProjection( sdPoints.data( ) + 1, sd - 1,
                                    m_pCurrentGlView->camera( ) );
               renderViewStructureSimple( sdPoints, sdLines, sd );
            }
            else
            {
               sdPoints[0] = start;
               sdPoints[1] = end;
               renderViewStructureSimple( sdPoints, sdLines, 1 );
            }
         }
      }
      else
         renderViewStructureSimple( points, vs.lines( ) );
   }
   else
      renderViewStructureSimple( vs.points( ), vs.lines( ) );
}

void PMRenderManager::renderViewStructureSimple( PMPointArray& points,
                                                 PMLineArray& lines,
                                                 int numberOfLines )
{
   GLuint* linesData = ( GLuint* ) ( lines.data( ) );
   unsigned int vsLines = 0;
   unsigned int rl;

   if( numberOfLines < 0 )
      vsLines = lines.size( );
   else
      vsLines = ( unsigned ) numberOfLines;
   
   glEnableClientState( GL_VERTEX_ARRAY );
   glVertexPointer( 3, GL_DOUBLE, 0, points.data( ) );
   
   while( ( vsLines > 0 ) && !m_bStopTask && !m_bStartTask )
   {
      rl = m_nMaxRenderedLines - m_renderedLines;
      if( rl > vsLines )
         rl = vsLines;
      
      glDrawElements( GL_LINES, rl * 2,
                      GL_UNSIGNED_INT, linesData );
      
      m_renderedLines += rl;                     
      if( m_renderedLines >=  m_nMaxRenderedLines )
      {
         m_renderedLines = 0;
         qApp->processEvents( maxEventTime );
         if( !m_bStopTask && !m_bStartTask )
            m_pCurrentGlView->makeCurrent( );
      }  
      
      vsLines -= rl;
      linesData = linesData + ( rl * 2 );
   }
   
   glDisableClientState( GL_VERTEX_ARRAY );
}

void PMRenderManager::transformProjection( PMPoint* points, int size,
                                           PMCamera* camera )
{
   int i;
   PMPoint* data = points;
   PMPoint p;
   double cameraAngle = camera->angle( ) * M_PI / 180.0;
   double rad, phi, h;
   
   if( approxZero( cameraAngle ) )
      cameraAngle = M_PI;

   switch( camera->cameraType( ) )
   {
      case PMCamera::Perspective:
      case PMCamera::Orthographic:
         break;
         
      case PMCamera::UltraWideAngle:
         for( i = 0; i < size; i++ )
         {
            p = m_viewTransformation * (*data);
            
            // reverse povray's calculations
            p[0] /= m_rightLength;
            p[1] /= m_upLength;
            p[2] /= m_directionLength;
            h = sqrt( p[0] * p[0] + p[1] * p[1] + p[2] * p[2] );
            if( !approxZero( h ) )
            {
               p[0] /= h;
               p[1] /= h;
            }
            
            (*data)[0] = asin( p[0] );
            (*data)[1] = asin( p[1] );
            if( p[2] > 0 )
            {
               (*data)[0] = M_PI - (*data)[0];
               (*data)[1] = M_PI - (*data)[1];
            }
            (*data)[2] = -h;
            
            data++;
         }
         break;
         
      case PMCamera::FishEye:
         for( i = 0; i < size; i++ )
         {
            p = m_viewTransformation * (*data);

            // reverse povray's calculations
            phi = atan2( p[1], p[0] );
            rad = atan2( sqrt( p[0] * p[0] + p[1] * p[1] ), -p[2] );
            
            (*data)[0] = rad * cos( phi );
            (*data)[1] = rad * sin( phi );
            (*data)[2] = -sqrt( p[0] * p[0] + p[1] * p[1] + p[2] * p[2] );
            
            data++;
         }
         break;
         
      case PMCamera::Panoramic:
         for( i = 0; i < size; i++ )
         {
            p = m_viewTransformation * (*data);

            // reverse povray's calculations
            p[0] /= m_rightLength;
            p[1] /= m_upLength;
            p[2] /= m_directionLength;
            
            (*data)[0] = atan2( p[0], -p[2] );
            (*data)[1] = atan2( p[1], sqrt( p[0] * p[0] + p[2] * p[2] ) );
            (*data)[2] = -sqrt( p[0] * p[0] + p[1] * p[1] + p[2] * p[2] );
            
            data++;
         }
         break;

      case PMCamera::Cylinder:
         switch( camera->cylinderType( ) )
         {
            case 1:
               for( i = 0; i < size; i++ )
               {
                  p = m_viewTransformation * (*data);
                  
                  // reverse povray's calculations
                  p[0] /= m_rightLength;
                  p[1] /= m_upLength;
                  p[2] /= m_directionLength;
                  
                  h = sqrt( p[0] * p[0] + p[2] * p[2] );
                  if( approxZero( h ) )
                     h = 1e-5;
                  
                  (*data)[0] = atan2( p[0], -p[2] ) / cameraAngle;
                  (*data)[1] = p[1] / h;
                  (*data)[2] = -h;

                  data++;
               }
               break;
               
            case 2:
               for( i = 0; i < size; i++ )
               {
                  p = m_viewTransformation * (*data);
                  
                  // reverse povray's calculations
                  p[0] /= m_rightLength;
                  p[1] /= m_upLength;
                  p[2] /= m_directionLength;
                  
                  h = sqrt( p[1] * p[1] + p[2] * p[2] );
                  if( approxZero( h ) )
                     h = 1e-5;
                  
                  (*data)[0] = p[0] / h;
                  (*data)[1] = atan2( p[1], -p[2] ) / cameraAngle;
                  (*data)[2] = -h;

                  data++;
               }
               break;
               
            case 3:
               for( i = 0; i < size; i++ )
               {
                  p = m_viewTransformation * (*data);
                  
                  // reverse povray's calculations
                  p[0] /= m_rightLength;
                  p[1] /= m_upLength;
                  p[2] /= m_directionLength;
                  
                  h = sqrt( p[0] * p[0] + p[2] * p[2] );
                  if( approxZero( h ) )
                     h = 1e-5;
                  
                  (*data)[0] = atan2( p[0], -p[2] ) / cameraAngle;
                  (*data)[1] = p[1];
                  (*data)[2] = -h;

                  data++;
               }
               break;
               
            case 4:
               for( i = 0; i < size; i++ )
               {
                  p = m_viewTransformation * (*data);
                  
                  // reverse povray's calculations
                  p[0] /= m_rightLength;
                  p[1] /= m_upLength;
                  p[2] /= m_directionLength;
                  
                  h = sqrt( p[1] * p[1] + p[2] * p[2] );
                  if( approxZero( h ) )
                     h = 1e-5;
                  
                  (*data)[0] = p[0];
                  (*data)[1] = atan2( p[1], -p[2] ) / cameraAngle;
                  (*data)[2] = -h;

                  data++;
               }
               break;
         }
         break;
         
      case PMCamera::Omnimax:
         break;
         
   }
}

void PMRenderManager::renderControlPoints( )
{
   if( ( m_pCurrentGlView->type( ) == PMGLView::PMViewCamera )
       && ( m_pCurrentGlView->camera( ) == m_pCurrentTask->activeObject( ) ) )
      return;

   if( m_specialCameraMode )
      m_viewTransformation = m_controlPointTransformation;
   else
      glLoadMatrixd( m_controlPointTransformation.data( ) );
   
   PMControlPointList* cplist = m_pCurrentTask->controlPoints( );
   if( cplist->count( ) > 0 )
   {
      PMControlPointListIterator it( *cplist );
      PMControlPoint* cp;
      PMPoint v;
      const GLubyte* bitmap = 0;
      
      // draw extra control point lines
      setGLColor( m_graphicalObjectColor[1] );
      for( it.toFirst( ); it.current( ); ++it )
      {
         cp = it.current( );
         if( cp->hasExtraLine( ) )
         {
            PMPoint s = PMPoint( cp->extraLineStart( ) );
            PMPoint e = PMPoint( cp->extraLineEnd( ) );

            if( m_specialCameraMode )
            {
               transformProjection( &s, 1, m_pCurrentGlView->camera( ) );
               transformProjection( &e, 1, m_pCurrentGlView->camera( ) );
            }

            glBegin( GL_LINES );
            glVertex3d( s[0], s[1], s[2] );
            glVertex3d( e[0], e[1], e[2] );
            glEnd( );
         }
      }
      
      glDisable( GL_DEPTH_TEST );
      // draw not selected control points
      setGLColor( m_controlPointColor[0] );
      for( it.toFirst( ); it.current( ); ++it )
      {
         cp = it.current( );
         
         if( cp->display( ) )
         {
            v = PMPoint( cp->position( ) );
            if( m_specialCameraMode )
               transformProjection( &v, 1, m_pCurrentGlView->camera( ) );
            switch( cp->displayType( ) )
            {
               case PMControlPoint::CPPoint:
                  if( !cp->selected( ) )
                     bitmap = PointBitmap;
                  break;
               case PMControlPoint::CPCross:
                  bitmap = CrossBitmap;
                  break;
            }
            
            glRasterPos3d( v[0], v[1], v[2] );
            if( bitmap )
               glBitmap( controlPointSize, controlPointSize,
                         controlPointSize/2, controlPointSize/2,
                         0, 0, bitmap );
         }
      }
      // draw selected control points
      setGLColor( m_controlPointColor[1] );
      for( it.toFirst( ); it.current( ); ++it )
      {
         cp = it.current( );
                  
         if( cp->selected( ) && cp->display( ) )
         {
            v = PMPoint( cp->position( ) );
            if( m_specialCameraMode )
               transformProjection( &v, 1, m_pCurrentGlView->camera( ) );
            if( cp->displayType( ) == PMControlPoint::CPPoint )
               bitmap = PointBitmap;
                  
            glRasterPos3d( v[0], v[1], v[2] );
            if( bitmap )
               glBitmap( controlPointSize, controlPointSize,
                         controlPointSize/2, controlPointSize/2,
                         0, 0, bitmap );
         }
      }
   }
}

void PMRenderManager::renderAxes( )
{
   int i;
   
   if( !m_axesViewStructureCreated )
   {
      m_axesViewStructure[0] = PMViewStructure( 6, 9 );
      PMPointArray& points = m_axesViewStructure[0].points( );
      PMLineArray& lines = m_axesViewStructure[0].lines( );
      
      lines[0] = PMLine( 0, 1 );
      lines[1] = PMLine( 1, 2 );
      lines[2] = PMLine( 1, 3 );
      lines[3] = PMLine( 1, 4 );
      lines[4] = PMLine( 1, 5 );
      lines[5] = PMLine( 2, 3 );
      lines[6] = PMLine( 3, 4 );
      lines[7] = PMLine( 4, 5 );
      lines[8] = PMLine( 5, 2 );

      points[0] = PMPoint( 0.0, 0.0, 0.0 );
      points[1] = PMPoint( 1.0, 0.0, 0.0 );
      points[2] = PMPoint(  dA,  dB,  dB );
      points[3] = PMPoint(  dA, -dB,  dB );
      points[4] = PMPoint(  dA, -dB, -dB );
      points[5] = PMPoint(  dA,  dB, -dB );

      m_axesViewStructure[1] = m_axesViewStructure[0];
      PMPointArray& points1 = m_axesViewStructure[1].points( );
      points1.detach( );

      points1[0] = PMPoint( 0.0, 0.0, 0.0 );
      points1[1] = PMPoint( 0.0, 1.0, 0.0 );
      points1[2] = PMPoint(  dB,  dA,  dB );
      points1[3] = PMPoint( -dB,  dA,  dB );
      points1[4] = PMPoint( -dB,  dA, -dB );
      points1[5] = PMPoint(  dB,  dA, -dB );

      m_axesViewStructure[2] = m_axesViewStructure[0];
      PMPointArray& points2 = m_axesViewStructure[2].points( );
      points2.detach( );
      
      points2[0] = PMPoint( 0.0, 0.0, 0.0 );
      points2[1] = PMPoint( 0.0, 0.0, 1.0 );
      points2[2] = PMPoint(  dB,  dB,  dA );
      points2[3] = PMPoint( -dB,  dB,  dA );
      points2[4] = PMPoint( -dB, -dB,  dA );
      points2[5] = PMPoint(  dB, -dB,  dA );
      
      m_axesViewStructureCreated = true;
   }

   glEnable( GL_DEPTH_TEST );
   for( i = 0; i < 3; i++ )
   {
      setGLColor( m_axesColor[i] );
      renderViewStructure( m_axesViewStructure[i] );
   }
}

PMMatrix PMRenderManager::viewTransformation( PMCamera* c ) const
{
   PMVector location, lookAt, sky;
   PMMatrix m;
   
   sky = c->sky( );
   location = c->location( );
   lookAt = c->lookAt( );
   
   if( approxZero( sky.abs( ) ) )
      sky = PMVector( 0.0, 1.0, 0.0 );
   if( approxZero( ( location - lookAt ).abs( ) ) )
      lookAt = location + PMVector( 0.0, 0.0, 1.0 );
   
   m = c->transformedWith( );
   if( m.canBuildInverse( ) )
      return PMMatrix::viewTransformation( location, lookAt, sky ) * m.inverse( );
   return PMMatrix::viewTransformation( location, lookAt, sky );
}

void PMRenderManager::setProjection( )
{
   PMGLView::PMViewType type = m_pCurrentGlView->type( );
   PMCamera* camera = m_pCurrentGlView->camera( );
   int width = m_pCurrentGlView->width( );
   int height = m_pCurrentGlView->height( );

   if( type == PMGLView::PMViewCamera )
   {
      if( camera )
         setCameraProjection( );
   }
   else
   {
      glMatrixMode( GL_PROJECTION );
      glLoadIdentity( );

      double d = m_pCurrentGlView->scale( );

      // TODO calculating the z clipping plane
      glOrtho( -width/2, width/2, -height/2, height/2,
               -viewVolumeZ, viewVolumeZ );
      glScaled( d, d, d );
      glTranslated( m_pCurrentGlView->translationX( ),
                    m_pCurrentGlView->translationY( ), 0 );
      
      switch( type )
      {
         case PMGLView::PMViewPosZ:
            break;
         case PMGLView::PMViewNegZ:
            glRotated( 180.0, 0.0, 1.0, 0.0 );
            break;
         case PMGLView::PMViewNegY:
            glRotated( 90.0, 1.0, 0.0, 0.0 );
            break;
         case PMGLView::PMViewPosY:
            glRotated( -90.0, 1.0, 0.0, 0.0 );
            break;
         case PMGLView::PMViewPosX:
            glRotated( 90.0, 0.0, 1.0, 0.0 );
            break;
         case PMGLView::PMViewNegX:
            glRotated( -90.0, 0.0, 1.0, 0.0 );
            break;
         default:
            break;
      }

      glScaled( 1.0, 1.0, -1.0 );

      glMatrixMode( GL_MODELVIEW );
      m_pCurrentGlView->setProjectionUpToDate( true );
   }
}

void PMRenderManager::setCameraProjection( )
{
   PMCamera* camera = m_pCurrentGlView->camera( );
   int width = m_pCurrentGlView->width( );
   int height = m_pCurrentGlView->height( );
   double angle = M_PI / 2.0;
   double modeAspect, viewAspect, cameraAspect;

   m_viewTransformation = viewTransformation( camera );
   m_upLength = camera->up( ).abs( );
   if( approxZero( m_upLength ) )
      m_upLength = 1.0;
   m_rightLength = camera->right( ).abs( );
   if( approxZero( m_rightLength ) )
      m_rightLength = 1.0;
   m_directionLength = camera->direction( ).abs( );
   if( approxZero( m_directionLength ) )
      m_directionLength = 1.0;

   if( camera->isAngleEnabled( ) )
      angle = camera->angle( ) * M_PI / 180.0;
   
   m_anglex = 0.5;
   m_angley = 0.5;
   if( ( angle <= 0.0 ) || ( angle > 2 * M_PI ) )
      angle = M_PI;
   
   switch( camera->cameraType( ) )
   {
      case PMCamera::Perspective:
         //  If angle wasn't specified determine one from right and direction
         if( !camera->isAngleEnabled( ) )
            angle = 2 * atan2( 0.5 * m_rightLength, m_directionLength );
         break;
      case PMCamera::UltraWideAngle:
         m_anglex = angle / ( 2.0 * M_PI );
         m_angley = angle / ( 2.0 * M_PI );
         m_specialCameraMode = true;
         break;
      case PMCamera::FishEye:
         m_anglex = angle / 2.0;
         m_angley = angle / 2.0;
         m_specialCameraMode = true;
         break;
      case PMCamera::Panoramic:
         m_anglex = M_PI / 2.0;
         m_angley = M_PI / 2.0;
         m_specialCameraMode = true;
         break;
      case PMCamera::Cylinder:
         m_anglex = 0.5;
         m_angley = 0.5;
         m_specialCameraMode = true;
         break;
      case PMCamera::Omnimax:
         m_specialCameraMode = true;
         break;
      default:
         break;
   }
      
   modeAspect = m_pCurrentTask->aspectRatio( );
   if( approxZero( modeAspect ) )
      modeAspect = 1.0;
   cameraAspect = camera->aspect( );
   if( approxZero( cameraAspect ) )
      cameraAspect = 1.0;
   viewAspect = ( double ) width / ( double ) height;
   if( approxZero( viewAspect ) )
      viewAspect = 1.0;

   if( viewAspect > modeAspect )
      m_anglex *= viewAspect / modeAspect;
   else
      m_angley *= modeAspect / viewAspect;

   
   glMatrixMode( GL_PROJECTION );
   glLoadIdentity( );

   
   PMVector up, right, direction;
   double handedness;
   PMMatrix m;

   up = camera->up( );
   right = camera->right( );
   direction = camera->direction( );

   if( approxZero( m_upLength ) )
      up = PMVector( 0.0, 1.0, 0.0 );
   if( approxZero( m_rightLength ) )
      right = PMVector( 1.0, 0.0, 0.0 );
   if( approxZero( m_directionLength ) )
      direction = PMVector( 0.0, 0.0, 1.0 );
   
   handedness = PMVector::dot( PMVector::cross( up, direction ), right );
   
   switch( camera->cameraType( ) )
   {
      case PMCamera::Perspective:
         if( ( angle <= 0.0 ) || ( angle >= M_PI ) )
            angle = M_PI / 2.0;
         
         // opengl needs the vertical angle
         if( viewAspect < modeAspect )
            angle = atan( tan( angle / 2.0 ) / cameraAspect * modeAspect
                          / viewAspect ) * 360.0 / M_PI;
         else
            angle = atan( tan( angle / 2.0 ) / cameraAspect )
                    * 360.0 / M_PI;
         
         gluPerspective( angle, cameraAspect * viewAspect / modeAspect,
                         0.001, viewVolumeZ );
         
         if( handedness > 0 )
            glScaled( -1.0, 1.0, 1.0 );
         
         glMultMatrixd( m_viewTransformation.data( ) );
         break;
         
      case PMCamera::Orthographic:
         m_anglex = m_rightLength / 2.0;
         m_angley = m_upLength / 2.0;
         
         if( viewAspect > modeAspect )
            m_anglex *= viewAspect / modeAspect;
         else
            m_angley *= modeAspect / viewAspect;
         
         glOrtho( -m_anglex, m_anglex, -m_angley, m_angley,
                  0, viewVolumeZ );
         
         if( handedness > 0 )
            glScaled( -1.0, 1.0, 1.0 );
         
         glMultMatrixd( m_viewTransformation.data( ) );
         break;
         
      case PMCamera::UltraWideAngle:
      case PMCamera::FishEye:
      case PMCamera::Panoramic:
      case PMCamera::Cylinder:
      case PMCamera::Omnimax:
         glOrtho( -m_anglex, m_anglex, -m_angley, m_angley,
                  -viewVolumeZ, viewVolumeZ );
         
         if( handedness > 0 )
            glScaled( -1.0, 1.0, 1.0 );
         break;
   }

   glMatrixMode( GL_MODELVIEW );
   m_pCurrentGlView->setProjectionUpToDate( true );
}

void PMRenderManager::renderFieldOfView( )
{
   if( m_pCurrentGlView->type( ) == PMGLView::PMViewCamera )
   {
      PMCamera* camera = m_pCurrentGlView->camera( );
      
      if( camera )
      {
         int width = m_pCurrentGlView->width( );
         int height = m_pCurrentGlView->height( );
         double modeAspect, viewAspect;
         int d, vx1, vx2, vy1, vy2;
         
         modeAspect = m_pCurrentTask->aspectRatio( ); //camera->aspect( );
         if( approxZero( modeAspect ) )
            modeAspect = 1.0;
         viewAspect = ( double ) width / ( double ) height;
         
         if( viewAspect < modeAspect )
         {
            vx1 = 0;
            vx2 = width - 1;
            
            d = ( int ) ( height - width / modeAspect + 0.5 ) / 2;
            
            vy1 = d;
            vy2 = height - d - 1;
         }
         else
         {
            vy1 = 0;
            vy2 = height - 1;
            
            d = ( int ) ( height * modeAspect );
            
            d = ( width - d ) / 2;
            vx1 = d;
            vx2 = width - d - 1;
         }

         glMatrixMode( GL_PROJECTION );
         glPushMatrix( );
         glLoadIdentity( );
         glOrtho( 0, width, 0, height, -2, 2 );
         glMatrixMode( GL_MODELVIEW );
         glPushMatrix( );
         glLoadIdentity( );

         setGLColor( m_fieldOfViewColor );
         glDisable( GL_DEPTH_TEST );

         if( camera->cameraType( ) == PMCamera::Omnimax )
            renderString( i18n( "not supported" ), 5.0,
                          height - qApp->fontMetrics( ).height( ) * 2 - 2 );
         else if( m_specialCameraMode && !m_highDetailCameraView )
            renderString( i18n( "approximated" ), 5.0,
                          height - qApp->fontMetrics( ).height( ) * 2 - 2 );
            
         glBegin( GL_LINE_LOOP );
         
         glVertex2d( vx1, vy1 );
         glVertex2d( vx2, vy1 );
         glVertex2d( vx2, vy2 );
         glVertex2d( vx1, vy2 );
         
         glEnd( );
         glEnable( GL_DEPTH_TEST );
         
         glMatrixMode( GL_PROJECTION );
         glPopMatrix( );
         glMatrixMode( GL_MODELVIEW );
         glPopMatrix( );
      }
   }
}

void PMRenderManager::renderGrid( )
{
   double scale = m_pCurrentGlView->scale( );
   
   if( scale >= 0 )
   {
      // calculate the views grid distance
      double viewGridDistance = pow( 10.0, ceil( log10( ( double ) m_gridDistance / scale ) ) );
      int sd = ( int ) ( viewGridDistance * scale + 0.5 );
      if( ( sd * 0.2 ) > m_gridDistance )
         viewGridDistance *= 0.2;
      else if( ( sd * 0.5 ) > m_gridDistance )
         viewGridDistance *= 0.5;
      
      // draw the grid
      double x1, x2, y1, y2, sx, sy;
      double x, y;
      int gi;
      double screenx, screeny;
      double signx = 1.0, signy = 1.0;

      int height = m_pCurrentGlView->height( );
      int width = m_pCurrentGlView->width( );
      double transX = m_pCurrentGlView->translationX( );
      double transY = m_pCurrentGlView->translationY( );
      int fontHeight = qApp->fontMetrics( ).height( );
      
      glMatrixMode( GL_PROJECTION );
      glPushMatrix( );
      glLoadIdentity( );
      glOrtho( -width/2, width/2, -height/2, height/2, -2, 2 );
      
      glMatrixMode( GL_MODELVIEW );
      glPushMatrix( );
      glLoadIdentity( );
      
      setGLColor( m_gridColor );
      glDisable( GL_DEPTH_TEST );
      
      switch( m_pCurrentGlView->type( ) )
      {
         case PMGLView::PMViewPosX:
            signx = -1.0;
            break;
         case PMGLView::PMViewPosY:
            signy = -1.0;
            break;
         case PMGLView::PMViewNegZ:
            signx = -1.0;
            break;
         default:
            break;
      }
      
      sx = width / scale;
      sy = height / scale;
      x1 = -transX - sx / 2;
      x2 = -transX + sx / 2;
      y1 = -transY - sy / 2;
      y2 = -transY + sy / 2;

      sx = ceil( x1 / viewGridDistance ) * viewGridDistance;
      gi = 0;
      x = sx;
      while( x < x2 )
      {
         screenx = ( x + transX ) * scale;   
         glBegin( GL_LINES );
         glVertex2d( screenx, -height/2 );
         glVertex2d( screenx, height/2 );
         glEnd( );

         renderString( QString( "%1" ).arg( x * signx, 0, 'g', 4 ),
                       screenx + 3, height / 2 - fontHeight - 2 );
         
         gi++;
         x = sx + viewGridDistance * gi;
      }
      
      sy = ceil( y1 / viewGridDistance ) * viewGridDistance;
      gi = 0;
      y = sy;
      while( y < y2 )
      {
         screeny = ( y + transY ) * scale;
         glBegin( GL_LINES );
         glVertex2d( -width/2, screeny );
         glVertex2d( width/2, screeny );
         glEnd( );

         renderString( QString( "%1" ).arg( y * signy, 0, 'g', 4 ),
                       -width / 2 + 3, screeny + 2 );
         
         gi++;
         y = sy + viewGridDistance * gi;
      }
      

      setGLColor( axesColor( 0 ) );
      switch( m_pCurrentGlView->type( ) )
      {
         case PMGLView::PMViewPosY:
         case PMGLView::PMViewPosZ:
         case PMGLView::PMViewNegY:
            renderString( "x", width / 2 - qApp->fontMetrics( ).boundingRect( "x" ).width( ) - 4, -3 );
            break;
         case PMGLView::PMViewNegZ:
            renderString( "x", -width / 2 + 3, -3 );
            break;
         default:
            break;
      }
      setGLColor( axesColor( 1 ) );
      switch( m_pCurrentGlView->type( ) )
      {
         case PMGLView::PMViewPosX:
         case PMGLView::PMViewNegX:
         case PMGLView::PMViewPosZ:
         case PMGLView::PMViewNegZ:
            renderString( "y", -3, height / 2 - fontHeight );
            break;
         default:
            break;
      }
      setGLColor( axesColor( 2 ) );
      switch( m_pCurrentGlView->type( ) )
      {
         case PMGLView::PMViewPosX:
            renderString( "z", -width / 2 + 3, -3 );
            break;
         case PMGLView::PMViewNegX:
            renderString( "z", width / 2 - qApp->fontMetrics( ).boundingRect( "z" ).width( ) - 4, -3 );
            break;
         case PMGLView::PMViewNegY:
            renderString( "z", -3, height / 2 - fontHeight );
            break;
         case PMGLView::PMViewPosY:
            renderString( "z", -3, -height / 2 );
            break;
         default:
            break;
      }
      
      glEnable( GL_DEPTH_TEST );
      glMatrixMode( GL_PROJECTION );
      glPopMatrix( );
      glMatrixMode( GL_MODELVIEW );
      glPopMatrix( );
   }
}

void PMRenderManager::renderDescription( )
{
   int height = m_pCurrentGlView->height( );
   int width = m_pCurrentGlView->width( );
   int fontHeight = qApp->fontMetrics( ).height( );

   glMatrixMode( GL_PROJECTION );
   glPushMatrix( );
   glLoadIdentity( );
   glOrtho( 0, width, 0, height, -2, 2 );
   glMatrixMode( GL_MODELVIEW );
   glPushMatrix( );
   glLoadIdentity( );
   
   setGLColor( m_fieldOfViewColor );
   
   switch( m_pCurrentGlView->type( ) )
   {
      case PMGLView::PMViewPosX:
         renderString( i18n( "left" ), 5.0, height - fontHeight - 2 );
         break;
      case PMGLView::PMViewNegX:
         renderString( i18n( "right" ), 5.0, height - fontHeight - 2 );
         break;
      case PMGLView::PMViewPosY:
         renderString( i18n( "bottom" ), 5.0, height - fontHeight - 2 );
         break;
      case PMGLView::PMViewNegY:
         renderString( i18n( "top" ), 5.0, height - fontHeight - 2 );
         break;
      case PMGLView::PMViewPosZ:
         renderString( i18n( "front" ), 5.0, height - fontHeight - 2 );
         break;
      case PMGLView::PMViewNegZ:
         renderString( i18n( "back" ), 5.0, height - fontHeight - 2 );
         break;
      case PMGLView::PMViewCamera:
      {
         PMCamera* c = m_pCurrentGlView->camera( );
         if( c )
         {
            QString name( "-" );
            if( !c->name( ).isEmpty( ) )
               name = c->name( );
            else
               name = i18n( "(unnamed)" );
            
            renderString( i18n( "camera" ) + ": " + name,
                          5.0, height - fontHeight - 2 );
         }
         else
            renderString( i18n( "camera" ), 5.0, height - fontHeight - 2 );
         break;
      }
   }
   
   glEnable( GL_DEPTH_TEST );
   
   glMatrixMode( GL_PROJECTION );
   glPopMatrix( );
   glMatrixMode( GL_MODELVIEW );
   glPopMatrix( );
}

void PMRenderManager::renderString( const QString& str, double x, double y )
{
   int width  = qApp->fontMetrics( ).boundingRect( str ).width( );
   int height = qApp->fontMetrics( ).height( );

   // GL wants word aligned bitmap
   QBitmap bm( ( ( width + 32 ) % 32 ) * 32, height, true );

   QPainter p( &bm );
   p.setFont( qApp->font( ) );
   p.drawText( bm.rect( ), Qt::AlignLeft | Qt::AlignBottom, str );
   p.end();

   // Transform to GL bitmap
   QImage img = bm.convertToImage( ).mirror( ).convertBitOrder( QImage::BigEndian );

   glRasterPos2d( x, y );
   glBitmap( img.width( ), img.height( ), 0, 0, 0, 0, img.bits( ) );
}

void PMRenderManager::setGLColor( const QColor& c )
{
   int r, g, b;

   c.rgb( &r, &g, &b );
   glColor3ub( ( GLubyte ) r, ( GLubyte ) g, ( GLubyte ) b );
}

void PMRenderManager::slotRenderingSettingsChanged( )
{
   emit renderingSettingsChanged( );
}

void PMRenderManager::saveConfig( KConfig* cfg )
{
   cfg->setGroup( "Rendering" );
   cfg->writeEntry( "BackgroundColor", m_backgroundColor );
   cfg->writeEntry( "GraphicalObjectColor0", m_graphicalObjectColor[0] );
   cfg->writeEntry( "GraphicalObjectColor1", m_graphicalObjectColor[1] );
   cfg->writeEntry( "ControlPointColor0", m_controlPointColor[0] );
   cfg->writeEntry( "ControlPointColor1", m_controlPointColor[1] );
   cfg->writeEntry( "AxesColorX", m_axesColor[0] );
   cfg->writeEntry( "AxesColorY", m_axesColor[1] );
   cfg->writeEntry( "AxesColorZ", m_axesColor[2] );
   cfg->writeEntry( "GridColor", m_gridColor );
   cfg->writeEntry( "GridDistance", m_gridDistance );
   cfg->writeEntry( "FieldOfViewColor", m_fieldOfViewColor );
   cfg->writeEntry( "HighDetailCameraViews", m_highDetailCameraView );
}

void PMRenderManager::restoreConfig( KConfig* cfg )
{
   cfg->setGroup( "Rendering" );

   m_backgroundColor = cfg->readColorEntry( "BackgroundColor", &m_backgroundColor );
   m_graphicalObjectColor[0] = cfg->readColorEntry( "GraphicalObjectColor0", &( m_graphicalObjectColor[0] ) );
   m_graphicalObjectColor[1] = cfg->readColorEntry( "GraphicalObjectColor1", &( m_graphicalObjectColor[1] ) );
   m_controlPointColor[0] = cfg->readColorEntry( "ControlPointColor0", &( m_controlPointColor[0] ) );
   m_controlPointColor[1] = cfg->readColorEntry( "ControlPointColor1", &( m_controlPointColor[1] ) );
   m_axesColor[0] = cfg->readColorEntry( "AxesColorX", &( m_axesColor[0] ) );
   m_axesColor[1] = cfg->readColorEntry( "AxesColorY", &( m_axesColor[1] ) );
   m_axesColor[2] = cfg->readColorEntry( "AxesColorZ", &( m_axesColor[2] ) );
   m_gridColor = cfg->readColorEntry( "GridColor", &m_gridColor );
   m_gridDistance = cfg->readNumEntry( "GridDistance", m_gridDistance );
   m_fieldOfViewColor = cfg->readColorEntry( "FieldOfViewColor", &m_fieldOfViewColor );
   m_highDetailCameraView = cfg->readBoolEntry( "HighDetailCameraViews", m_highDetailCameraView );
}

bool PMRenderManager::hasOpenGL( )
{
   if( !s_hasOpenGLChecked )
   {
      s_hasOpenGL = ( glXQueryExtension( qt_xdisplay( ), 0, 0 ) != 0 );
      s_hasOpenGLChecked = true;
   }

   return s_hasOpenGL;
}

void PMRenderManager::disableOpenGL( )
{
   s_hasOpenGLChecked = true;
   s_hasOpenGL = false;
}

#include "pmrendermanager.moc"
