/* ************************************************************************** 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 TQT_CLEAN_NAMESPACE #define TQT_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 "pmmath.h" #include #include #include #include #include #include #include #include #include #include #include #include #include // 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::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( ) : TQObject( tqApp ) { 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; } TQColor PMRenderManager::controlPointColor( int i ) const { if( ( i >= 0 ) && ( i <= 1 ) ) return m_controlPointColor[i]; return TQColor( 0, 0, 0 ); } void PMRenderManager::setControlPointColor( int i, const TQColor& c ) { if( ( i >= 0 ) && ( i <= 1 ) ) m_controlPointColor[i] = c; } TQColor PMRenderManager::graphicalObjectColor( int i ) const { if( ( i >= 0 ) && ( i <= 1 ) ) return m_graphicalObjectColor[i]; return TQColor( 0, 0, 0 ); } void PMRenderManager::setGraphicalObjectColor( int i, const TQColor& c ) { if( ( i >= 0 ) && ( i <= 1 ) ) m_graphicalObjectColor[i] = c; } TQColor PMRenderManager::axesColor( int i ) const { if( ( i >= 0 ) && ( i <= 2 ) ) return m_axesColor[i]; return TQColor( 0, 0, 0 ); } void PMRenderManager::setAxesColor( int i, const TQColor& 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( TQTimerEvent* ) { 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; tqApp->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( "GraphicalObject" ) ) { 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( "Declare" ) ) renderObject( obj ); if( !m_bStopTask && !m_bStartTask ) { do { // Do not render declares obj = obj->prevSibling( ); if( !obj ) stop = true; else stop = !obj->isA( "Declare" ); } 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( ) == "QuickColor" ) { PMQuickColor* qc = ( PMQuickColor* ) objectToRender; PMObjectListIterator it( m_objectToRenderStack ); bool pofound = false; it.toLast( ); while( it.current( ) && !pofound ) { if( it.current( )->isA( "GraphicalObject" ) ) pofound = true; else --it; } if( pofound ) { if( m_quickColorObjects.top( ) != it.current( ) ) { m_quickColorObjects.push( it.current( ) ); m_quickColors.push( new TQColor( m_currentColor ) ); m_currentColor = qc->color( ).toTQColor( ); } } } 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 tqApp->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( ); TQColor* 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; tqApp->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 - tqApp->fontMetrics( ).height( ) * 2 - 2 ); else if( m_specialCameraMode && !m_highDetailCameraView ) renderString( i18n( "approximated" ), 5.0, height - tqApp->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 = tqApp->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( ); TQString label = TQString( "%1" ).arg( x * signx, 0, 'g', 4 ); if( approxZero( x ) && label.find( "e-" ) ) label = "0"; renderString( label, 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( ); TQString label = TQString( "%1" ).arg( y * signy, 0, 'g', 4 ); if( approxZero( y ) && label.find( "e-" ) ) label = "0"; renderString( label, -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 - tqApp->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 - tqApp->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 = tqApp->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 ) { TQString 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 TQString& str, double x, double y ) { int width = tqApp->fontMetrics( ).boundingRect( str ).width( ); int height = tqApp->fontMetrics( ).height( ); // GL wants word aligned bitmap TQBitmap bm( ( ( width + 32 ) % 32 ) * 32, height, true ); TQPainter p( &bm ); p.setFont( tqApp->font( ) ); p.drawText( bm.rect( ), TQt::AlignLeft | TQt::AlignBottom, str ); p.end(); // Transform to GL bitmap TQImage img = TQImage(bm.convertToImage( )).mirror( ).convertBitOrder( TQImage::BigEndian ); glRasterPos2d( x, y ); glBitmap( img.width( ), img.height( ), 0, 0, 0, 0, img.bits( ) ); } void PMRenderManager::setGLColor( const TQColor& 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( TDEConfig* 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( TDEConfig* 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( tqt_xdisplay( ), 0, 0 ) != 0 ); s_hasOpenGLChecked = true; } return s_hasOpenGL; } void PMRenderManager::disableOpenGL( ) { s_hasOpenGLChecked = true; s_hasOpenGL = false; } #include "pmrendermanager.moc"