X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FGLViewer%2FGLViewer_Drawer.cxx;h=d8bd48cd3459cf5a2835a4f413bd10bf6412af00;hb=1739d7cf27a6bb98f91aa32f3adeec8b909a0c55;hp=9231804f03fd336952700b1d581bd9dc2f993715;hpb=da961490341b86208c0b7ec9e4c8dc37bea1075b;p=modules%2Fgui.git diff --git a/src/GLViewer/GLViewer_Drawer.cxx b/src/GLViewer/GLViewer_Drawer.cxx index 9231804f0..d8bd48cd3 100644 --- a/src/GLViewer/GLViewer_Drawer.cxx +++ b/src/GLViewer/GLViewer_Drawer.cxx @@ -1,37 +1,58 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +// Author : OPEN CASCADE // File: GLViewer_Drawer.cxx // Created: November, 2004 -// Author: OCC team -// Copyright (C) CEA 2004 - //#include +// #include "GLViewer_Drawer.h" #include "GLViewer_Object.h" #include "GLViewer_Text.h" #include "GLViewer_ViewFrame.h" #include "GLViewer_ViewPort2d.h" +#include +#include +#include +#include + #ifndef WIN32 #include #endif -#include -#include +#include #define TEXT_GAP 5 +// Two texture components for texmapped fonts: luminance and alpha +#define NB_TEX_COMP 2 +// A font is split into rows each containing 32 characters +#define TEX_ROW_LEN 32 +// Gap in pixels between two character rows in a font texture +#define TEX_ROW_GAP 2 -GLboolean TFLoaded = GL_FALSE; +GLfloat modelMatrix[16]; -GLdouble modelMatrix[16], projMatrix[16]; -GLint viewport[4]; -GLdouble winx, winy, winz; -GLint status; -GLViewer_TexFont* staticGlFont; - -//================================================================ -// Class : GLViewer_TexFont -// Description : -//================================================================ //! code of first font symbol static int FirstSymbolNumber = 32; //! code of last font symbol @@ -40,10 +61,9 @@ static int LastSymbolNumber = 127; QMap GLViewer_TexFont::TexFontBase; QMap GLViewer_TexFont::BitmapFontCache; -//======================================================================= -// Function: clearTextBases -// Purpose : -//======================================================================= +/*! + Clears all generated fonts +*/ void GLViewer_TexFont::clearTextBases() { //cout << "Clear font map" << endl; @@ -51,72 +71,95 @@ void GLViewer_TexFont::clearTextBases() BitmapFontCache.clear(); } -//====================================================================== -// Function: GLViewer_TexFont -// Purpose : -//======================================================================= +/*! + Default constructor +*/ GLViewer_TexFont::GLViewer_TexFont() +: myMaxRowWidth( 0 ), myFontHeight( 0 ) { - myQFont = QFont::defaultFont(); - QFontMetrics aFM( myQFont ); - myWidths = new int[LastSymbolNumber - FirstSymbolNumber+1]; - myPositions = new int[LastSymbolNumber - FirstSymbolNumber+1]; + myQFont = QApplication::font();//QFont::defaultFont(); mySeparator = 2; - for( int k = FirstSymbolNumber, aWidth = 0; k <= LastSymbolNumber; k++ ) - { - myWidths[ k - FirstSymbolNumber ] = aFM.width( k ); - myPositions[ k - FirstSymbolNumber ] = aWidth; - aWidth += myWidths[ k - FirstSymbolNumber ] + 2;//mySeparator; - } + myIsResizeable = false; + myMinMagFilter = GL_LINEAR; - myTexFontWidth = 0; - myTexFontHeight = 0; + init(); } -//====================================================================== -// Function: GLViewer_TexFont -// Purpose : -//======================================================================= -GLViewer_TexFont::GLViewer_TexFont( QFont* theFont, int theSeparator ) +/*! + Constructor + \param theFont - a base font + \param theSeparator - separator between letters + \param theIsResizeable - specifies whether text drawn by this object can be scaled along with the scene + \param theMinMagFilter - min/mag filter, affects text sharpness +*/ +GLViewer_TexFont::GLViewer_TexFont( QFont* theFont, int theSeparator, bool theIsResizeable, GLuint theMinMagFilter ) +: myMaxRowWidth( 0 ), myFontHeight( 0 ) { myQFont = *theFont; - QFontMetrics aFM( myQFont ); - myWidths = new int[LastSymbolNumber - FirstSymbolNumber+1]; - myPositions = new int[LastSymbolNumber - FirstSymbolNumber+1]; mySeparator = theSeparator; - for( int k = FirstSymbolNumber, aWidth = 0; k <= LastSymbolNumber; k++ ) - { - myWidths[ k - FirstSymbolNumber ] = aFM.width( k ); - myPositions[ k - FirstSymbolNumber ] = aWidth; - aWidth += myWidths[ k - FirstSymbolNumber ] + 2;//mySeparator; - } + myIsResizeable = theIsResizeable; + myMinMagFilter = theMinMagFilter; - myTexFontWidth = 0; - myTexFontHeight = 0; - + init(); } -//====================================================================== -// Function: ~GLViewer_TexFont -// Purpose : -//======================================================================= +/*! + Destructor +*/ GLViewer_TexFont::~GLViewer_TexFont() { delete[] myWidths; delete[] myPositions; } - -//====================================================================== -// Function: generateTexture -// Purpose : -//======================================================================= -void GLViewer_TexFont::generateTexture() + +/*! + Initializes font parameters +*/ +void GLViewer_TexFont::init() { - QFontMetrics aFM( myQFont ); + myNbSymbols = LastSymbolNumber - FirstSymbolNumber + 1; + + // It is unsafe to draw all characters in a single row - + // this leads to problems on some graphic cards with small GL_MAX_TEXTURE_SIZE. + // So splitting the characters into rows each containing 32 characters (or less). + // Assuming contant height of each row (64 pixels) to simplify texture mapping. + // However, this can be improved if necessary. + QFontMetrics aFM( myQFont ); + myFontHeight = aFM.height(); + + myWidths = new int[myNbSymbols]; + myPositions = new int[myNbSymbols]; + for( int i = 0, k = FirstSymbolNumber, aWidth = 0; i < myNbSymbols; i++, k++ ) + { + // is it time to start a new row? + if ( !( i % TEX_ROW_LEN ) ) + { + if( aWidth > myMaxRowWidth ) + myMaxRowWidth = aWidth; + aWidth = 0; + } + myWidths[i] = aFM.width( k ); + myPositions[i] = aWidth; + aWidth += myWidths[i] + 2; + } + + myTexFontWidth = 0; + myTexFontHeight = 0; +} + +/*! + Generating font texture +*/ +bool GLViewer_TexFont::generateTexture() +{ GLViewer_TexFindId aFindFont; - aFindFont.myFontString = myQFont.toString(); - aFindFont.myViewPortId = (int)QGLContext::currentContext(); + aFindFont.myFontFamily = myQFont.family();//myQFont.toString(); + aFindFont.myIsBold = myQFont.bold(); + aFindFont.myIsItal = myQFont.italic(); + aFindFont.myIsUndl = myQFont.underline(); + aFindFont.myPointSize = myQFont.pointSize(); + aFindFont.myViewPortId = size_t(QGLContext::currentContext()); if( TexFontBase.contains( aFindFont ) ) { @@ -127,34 +170,51 @@ void GLViewer_TexFont::generateTexture() } else { - QString aStr; - int pixelsWidth = 0; - int pixelsHight = aFM.height(); - myTexFontWidth = 64; + // Adding some pixels to have a gap between rows + int aRowPixelHeight = myFontHeight + TEX_ROW_GAP; + int aDescent = QFontMetrics( myQFont ).descent(); + + int aNumRows = myNbSymbols / TEX_ROW_LEN; + if ( myNbSymbols % TEX_ROW_LEN ) + aNumRows++; + int pixelsHight = aNumRows * aRowPixelHeight; + + myTexFontWidth = 64; myTexFontHeight = 64; - - pixelsWidth = myWidths[LastSymbolNumber - FirstSymbolNumber] + - myPositions[LastSymbolNumber - FirstSymbolNumber]; - while( myTexFontWidth < pixelsWidth ) - myTexFontWidth = myTexFontWidth * 2; + while( myTexFontWidth < myMaxRowWidth ) + myTexFontWidth <<= 1; while( myTexFontHeight < pixelsHight ) - myTexFontHeight = myTexFontHeight * 2; + myTexFontHeight <<= 1; + + // Checking whether the texture dimensions for the requested font + // do not exceed the maximum size supported by the OpenGL implementation + int maxSize; + glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxSize ); + if ( myTexFontWidth > maxSize || myTexFontHeight > maxSize ) + return false; QPixmap aPixmap( myTexFontWidth, myTexFontHeight ); aPixmap.fill( QColor( 0, 0, 0) ); QPainter aPainter( &aPixmap ); aPainter.setFont( myQFont ); - for( int l = 0/*, gap = 0*/; l < LastSymbolNumber - FirstSymbolNumber; l++ ) + int row; + for( int l = 0; l < myNbSymbols; l++ ) { + row = l / TEX_ROW_LEN; QString aLetter; aLetter += (char)(FirstSymbolNumber + l); aPainter.setPen( QColor( 255,255,255) ); - aPainter.drawText ( myPositions[l], pixelsHight, aLetter ); + aPainter.drawText( myPositions[l], ( row + 1 ) * aRowPixelHeight - aDescent, aLetter ); } - QImage aImage = aPixmap.convertToImage(); - char* pixels = new char[myTexFontWidth * myTexFontHeight * 2]; + QImage aImage = aPixmap.toImage(); + + //int qqq = 0; + //if (qqq) + // aImage.save("w:\\work\\CATHARE\\texture.png", "PNG"); + + char* pixels = new char[myTexFontWidth * myTexFontHeight * NB_TEX_COMP]; for( int i = 0; i < myTexFontHeight; i++ ) { @@ -166,13 +226,13 @@ void GLViewer_TexFont::generateTexture() if( aRed != 0 || aGreen != 0 || aBlue != 0 ) { - pixels[i * myTexFontWidth * 2 + j * 2] = (GLubyte)( (aRed + aGreen + aBlue)/3 ); - pixels[i * myTexFontWidth * 2 + j * 2 + 1]= (GLubyte) 255; + pixels[i * myTexFontWidth * NB_TEX_COMP + j * NB_TEX_COMP] = (GLubyte)( (aRed + aGreen + aBlue)/3 ); + pixels[i * myTexFontWidth * NB_TEX_COMP + j * NB_TEX_COMP + 1]= (GLubyte) 255; } else { - pixels[i * myTexFontWidth * 2 + j * 2] = (GLubyte) 0; - pixels[i * myTexFontWidth * 2 + j * 2 + 1]= (GLubyte) 0; + pixels[i * myTexFontWidth * NB_TEX_COMP + j * NB_TEX_COMP] = (GLubyte) 0; + pixels[i * myTexFontWidth * NB_TEX_COMP + j * NB_TEX_COMP + 1]= (GLubyte) 0; } } } @@ -180,13 +240,17 @@ void GLViewer_TexFont::generateTexture() glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glGenTextures(1, &myTexFont); glBindTexture(GL_TEXTURE_2D, myTexFont); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glTexImage2D(GL_TEXTURE_2D, 0, 2, myTexFontWidth, - myTexFontHeight, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, pixels); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myMinMagFilter); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myMinMagFilter); + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_INTENSITY, + myTexFontWidth, + myTexFontHeight, + 0, + GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE, + pixels); delete[] pixels; @@ -197,61 +261,90 @@ void GLViewer_TexFont::generateTexture() TexFontBase.insert( aFindFont, aTexture ); } + return true; } -//====================================================================== -// Function: drawString -// Purpose : -//======================================================================= -void GLViewer_TexFont::drawString( QString theStr, GLdouble theX , GLdouble theY ) +/*! + Drawing string in viewer + \param theStr - string to be drawn + \param theX - X position + \param theY - Y position + \param theScale - scale coefficient +*/ +void GLViewer_TexFont::drawString( QString theStr, GLdouble theX , GLdouble theY, GLfloat theScale ) { + // Adding some pixels to have a gap between rows + int aRowPixelHeight = myFontHeight + TEX_ROW_GAP; + + float aXScale = 1.f, aYScale = 1.f; + if ( !myIsResizeable ) + { + glGetFloatv (GL_MODELVIEW_MATRIX, modelMatrix); + aXScale = modelMatrix[0]; + aYScale = modelMatrix[5]; + } + else if ( theScale > 0.f ) + { + aXScale = aXScale / theScale; + aYScale = aYScale / theScale; + } + + // store attributes + glPushAttrib( GL_ENABLE_BIT | GL_TEXTURE_BIT ); + glEnable(GL_TEXTURE_2D); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glPixelTransferi(GL_MAP_COLOR, 0); - glAlphaFunc(GL_GEQUAL, 0.5F); + + glAlphaFunc(GL_GEQUAL, 0.05F); glEnable(GL_ALPHA_TEST); - glBindTexture(GL_TEXTURE_2D, myTexFont); - glBegin(GL_QUADS); - QFontMetrics aFM( myQFont ); - int pixelsHeight = aFM.height(); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); - theY = theY - myTexFontHeight + pixelsHeight; + glBindTexture(GL_TEXTURE_2D, myTexFont); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - for( int i = 0, aGap = 0; i < theStr.length(); i++ ) + glBegin(GL_QUADS); + + float aLettBeginS, aLettEndS, aLettBeginT, aLettEndT; + float aDY = ( aRowPixelHeight - 1 ) / aYScale, aDX; + char aLetter; + int aLettIndex, row; + for ( int i = 0; i < (int)theStr.length(); i++ ) { - char aLetter = theStr.data()[i]; - int aLettIndex = (int)aLetter - FirstSymbolNumber; + aLetter = theStr.data()[i].toLatin1(); + aLettIndex = (int)aLetter - FirstSymbolNumber; + row = aLettIndex / TEX_ROW_LEN; - float aLettBegin = (float)myPositions[aLettIndex]; - float aLettEnd = aLettBegin + myWidths[aLettIndex]-1; + aLettBeginS = (float)myPositions[aLettIndex] / ( (float)myTexFontWidth - 1.f ); + aLettEndS = aLettBeginS + ( (float)myWidths[aLettIndex] - 1.f ) / ( (float)myTexFontWidth - 1.f ); + aLettBeginT = ( myTexFontHeight - ( row + 1 ) * aRowPixelHeight ) / ( (float)myTexFontHeight - 1.f ); + aLettEndT = aLettBeginT + ( (float)aRowPixelHeight - 1.f ) / ( (float)myTexFontHeight - 1.f ); - aLettBegin = aLettBegin / myTexFontWidth; - aLettEnd = aLettEnd / myTexFontWidth; + aDX = ( (float)myWidths[aLettIndex] - 1.f ) / aXScale; - glTexCoord2f( aLettBegin, 0.0 ); glVertex3f( theX + aGap, theY, 1.0 ); - glTexCoord2f( aLettBegin, 1.0 ); glVertex3f( theX + aGap, theY + myTexFontHeight, 1.0 ); - glTexCoord2f( aLettEnd, 1.0 ); glVertex3f( theX + aGap + myWidths[aLettIndex]-1, theY + myTexFontHeight, 1.0 ); - glTexCoord2f( aLettEnd, 0.0 ); glVertex3f( theX + aGap + myWidths[aLettIndex]-1, theY, 1.0 ); + glTexCoord2f( aLettBeginS, aLettBeginT ); glVertex3f( theX, theY, 1.f ); + glTexCoord2f( aLettBeginS, aLettEndT ); glVertex3f( theX, theY + aDY, 1.f ); + glTexCoord2f( aLettEndS, aLettEndT ); glVertex3f( theX + aDX, theY + aDY, 1.f ); + glTexCoord2f( aLettEndS, aLettBeginT ); glVertex3f( theX + aDX, theY, 1.f ); - aGap += myWidths[aLettIndex]-1 + mySeparator; + theX += aDX + mySeparator / aXScale; } glEnd(); - glDisable(GL_ALPHA_TEST); - glDisable(GL_TEXTURE_2D); + // restore attributes + glPopAttrib(); } -//====================================================================== -// Function: getStringWidth -// Purpose : -//======================================================================= +/*! + \return width of string in pixels +*/ int GLViewer_TexFont::getStringWidth( QString theStr ) { int aWidth = 0; - for( int i = 0; i < theStr.length(); i ++ ) + for ( int i = 0; i < (int)theStr.length(); i ++ ) { - char aLetter = theStr.data()[i]; + char aLetter = theStr.data()[i].toLatin1(); int aLettIndex = (int)aLetter - FirstSymbolNumber; aWidth += myWidths[aLettIndex] + mySeparator; } @@ -259,23 +352,30 @@ int GLViewer_TexFont::getStringWidth( QString theStr ) return aWidth; } -//====================================================================== -// Function: getStringHeight -// Purpose : -//======================================================================= +/*! + \return height of string in pixels +*/ int GLViewer_TexFont::getStringHeight() { QFontMetrics aFM( myQFont ); return aFM.height(); } -//! function for generation list base for bitmap fonts +/*! + Generates list base for bitmap fonts +*/ static GLuint displayListBase( QFont* theFont ) { + if ( !theFont ) + return 0; GLuint aList = 0; //static QMap fontCache; GLViewer_TexFindId aFindFont; - aFindFont.myFontString = theFont->toString(); + aFindFont.myFontFamily = theFont->family();//theFont->toString(); + aFindFont.myIsBold = theFont->bold(); + aFindFont.myIsItal = theFont->italic(); + aFindFont.myIsUndl = theFont->underline(); + aFindFont.myPointSize = theFont->pointSize(); #ifdef WIN32 HGLRC ctx = ::wglGetCurrentContext(); @@ -292,8 +392,8 @@ static GLuint displayListBase( QFont* theFont ) QMap::iterator it = GLViewer_TexFont::BitmapFontCache.begin(); for ( ; it != GLViewer_TexFont::BitmapFontCache.end(); ++it ) { - if ( it.key().myViewPortId == (int)ctx && it.data() > listBase ) - listBase = it.data(); + if ( it.key().myViewPortId == (int)ctx && it.value() > listBase ) + listBase = it.value(); } listBase += 256; @@ -323,7 +423,7 @@ static GLuint displayListBase( QFont* theFont ) return aList; } - aFindFont.myViewPortId = (int)aCont; + aFindFont.myViewPortId = size_t(aCont); if ( GLViewer_TexFont::BitmapFontCache.contains( aFindFont ) ) aList = GLViewer_TexFont::BitmapFontCache[aFindFont]; @@ -333,38 +433,43 @@ static GLuint displayListBase( QFont* theFont ) QMap::iterator it = GLViewer_TexFont::BitmapFontCache.begin(); for ( ; it != GLViewer_TexFont::BitmapFontCache.end(); ++it ) { - if ( it.key().myViewPortId == (int)aCont && it.data() > listBase ) - listBase = it.data(); + if ( it.key().myViewPortId == size_t(aCont) && it.value() > listBase ) + listBase = it.value(); } listBase += 256; //glXUseXFont( (Font)(theFont->handle()), 0, 256, listBase ); int aFontCont = 0; - char** xFontList = XListFonts( aDisp, aFindFont.myFontString.data(), 1, &aFontCont ); + QString aFontDef = theFont->toString(); + char** xFontList = XListFonts( aDisp, aFontDef.toLatin1()/*aFindFont.myFontString.data()*/, 1, &aFontCont ); +// TODO (QT5 PORTING) Below is a temporary solution, to allow compiling with Qt 5 +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) if( !theFont->handle() ) - { + { +#endif #ifdef _DEBUG_ - printf( "Can't load font %s. loading default font....\n", aFindFont.myFontString.data() ); + printf( "Can't load font %s. loading default font....\n", aFontDef.toLatin1().data()/*aFindFont.myFontString.data()*/ ); #endif QString aFontMask ("-*-*-*-r-*-*-"); - aFontMask += aFindFont.myFontString.section( ',', 1, 1 ); + aFontMask += aFontDef/*aFindFont.myFontString*/.section( ',', 1, 1 ); #ifdef _DEBUG_ - printf( "Height of Default font: %s\n", aFindFont.myFontString.section( ',', 1, 1 ).data() ); + printf( "Height of Default font: %s\n", aFontDef/*aFindFont.myFontString*/.section( ',', 1, 1 ).data() ); #endif aFontMask += "-*-*-*-m-*-*-*"; - xFontList = XListFonts( aDisp, aFontMask.data()/*"-*-*-*-r-*-*-12-*-*-*-m-*-*-*"*/, 1, &aFontCont ); + xFontList = XListFonts( aDisp, aFontMask.toLatin1().constData()/*"-*-*-*-r-*-*-12-*-*-*-m-*-*-*"*/, 1, &aFontCont ); if( aFontCont == 0 ) - { + { #ifdef _DEBUG_ printf( "Can't load default font\n" ); #endif return 0; } glXUseXFont( (Font)(XLoadFont( aDisp,xFontList[0] )), 0, 256, listBase ); +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) } else glXUseXFont( (Font)(theFont->handle()), 0, 256, listBase ); - +#endif aList = listBase; GLViewer_TexFont::BitmapFontCache[aFindFont] = aList; } @@ -374,80 +479,79 @@ static GLuint displayListBase( QFont* theFont ) return aList; } -/*************************************************************************** -** Class: GLViewer_Drawer -** Descr: Drawer for GLViewer_Object -** Module: GLViewer -** Created: UI team, 01.10.01 -****************************************************************************/ -//====================================================================== -// Function: GLViewer_Drawer -// Purpose : -//======================================================================= +/*! + Default constructor +*/ GLViewer_Drawer::GLViewer_Drawer() +: myFont( "Helvetica", 10, QFont::Bold ) { myXScale = myYScale = 0.0; myObjects.clear(); myTextList = 0/*-1*/; myObjectType = "GLViewer_Object"; myPriority = 0; + myTextFormat = DTF_BITMAP; + myTextScale = 0.125; } -//====================================================================== -// Function: ~GLViewer_Drawer -// Purpose : -//======================================================================= +/*! + Destructor +*/ GLViewer_Drawer::~GLViewer_Drawer() { myObjects.clear(); glDeleteLists( myTextList, 1 ); } -//====================================================================== -// Function: destroyAllTextures -// Purpose : -//======================================================================= +/*! + Clears all generated textures +*/ void GLViewer_Drawer::destroyAllTextures() { QMap::Iterator anIt= GLViewer_TexFont::TexFontBase.begin(); QMap::Iterator anEndIt= GLViewer_TexFont::TexFontBase.end(); for( ; anIt != anEndIt; anIt++ ) - glDeleteTextures( 1, &(anIt.data().myTexFontId) ); + glDeleteTextures( 1, &(anIt.value().myTexFontId) ); } -//======================================================================= -// Function: setAntialiasing -// Purpose : The function enables and disables antialiasing in Open GL (for points, lines and polygons). -//======================================================================= +/*! + Enables and disables antialiasing in Open GL (for points, lines and polygons). + \param on - if it is true, antialiasing is enabled +*/ void GLViewer_Drawer::setAntialiasing(const bool on) { - if (on) - { + if (on) + { glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); - glEnable(GL_POINT_SMOOTH); - glEnable(GL_LINE_SMOOTH); - glEnable(GL_POLYGON_SMOOTH); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable (GL_BLEND); - } - else - { - glDisable(GL_POINT_SMOOTH); - glDisable(GL_LINE_SMOOTH); - glDisable(GL_POLYGON_SMOOTH); - glBlendFunc (GL_ONE, GL_ZERO); - glDisable (GL_BLEND); - } -} - -//====================================================================== -// Function: loadTexture -// Purpose : -//======================================================================= -GLuint GLViewer_Drawer::loadTexture( const QString& fileName ) + glEnable(GL_POINT_SMOOTH); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_POLYGON_SMOOTH); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable (GL_BLEND); + } + else + { + glDisable(GL_POINT_SMOOTH); + glDisable(GL_LINE_SMOOTH); + glDisable(GL_POLYGON_SMOOTH); + glBlendFunc (GL_ONE, GL_ZERO); + glDisable (GL_BLEND); + } +} + +/*! Loads texture from file + \param fileName - the name of texture file + \param x_size - the horizontal size of picture ( less or equal texture horizontal size ) + \param y_size - the vertical size of picture ( less or equal texture vertical size ) + \param t_size - the size of texture ( texture vertical size equals texture horizontal size ) +*/ +GLuint GLViewer_Drawer::loadTexture( const QString& fileName, + GLint* x_size, + GLint* y_size, + GLint* t_size ) { QImage buf; if ( fileName.isEmpty() || !buf.load( fileName ) ) @@ -496,24 +600,78 @@ GLuint GLViewer_Drawer::loadTexture( const QString& fileName ) glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); glGenTextures( 1, &texture ); glBindTexture( GL_TEXTURE_2D, texture ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels ); delete[] pixels; + if ( x_size ) + *(x_size) = w; + + if ( y_size ) + *(y_size) = h; + + if ( t_size ) + *(t_size) = size; + return texture; } -//====================================================================== -// Function: drawTexture -// Purpose : -//======================================================================= +/*! Draw square texture + \param texture - the texture ID + \param size - the size of square texture + \param x - x coord + \param y - y coord +*/ void GLViewer_Drawer::drawTexture( GLuint texture, GLint size, GLfloat x, GLfloat y ) { - if( !texture ) - return; + /*float xScale = myXScale; + float yScale = myYScale; + + glColor4f( 1.0, 1.0, 1.0, 1.0 ); + + glEnable( GL_TEXTURE_2D ); + glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + glAlphaFunc( GL_GREATER, 0.95F ); + glEnable( GL_ALPHA_TEST ); + + glBindTexture( GL_TEXTURE_2D, texture ); + glBegin( GL_QUADS ); + + glTexCoord2f( 0.0, 0.0 ); + glVertex3f( x-size/2./xScale, y-size/2./yScale, 0.0 ); + + glTexCoord2f( 0.0, 1.0 ); + glVertex3f( x-size/2./xScale, y+size/2./yScale, 0.0 ); + + glTexCoord2f( 1.0, 1.0 ); + glVertex3f( x+size/2./xScale, y+size/2./yScale, 0.0 ); + + glTexCoord2f( 1.0, 0.0 ); + glVertex3f( x+size/2./xScale, y-size/2./yScale, 0.0 ); + + glEnd(); + glFlush(); + + glDisable( GL_ALPHA_TEST ); + glDisable( GL_TEXTURE_2D );*/ + + drawTexture( texture, size, size, x, y ); +} + +/*! Draw texture + \param texture - the texture ID + \param x_size - the horizontal size of texture + \param y_size - the vertical size of texture + \param x - x coord + \param y - y coord +*/ +void GLViewer_Drawer::drawTexture( GLuint texture, GLint x_size, GLint y_size, GLfloat x, GLfloat y ) +{ + /*float xScale = myXScale; + float yScale = myYScale; glColor4f( 1.0, 1.0, 1.0, 1.0 ); @@ -526,76 +684,122 @@ void GLViewer_Drawer::drawTexture( GLuint texture, GLint size, GLfloat x, GLfloa glBegin( GL_QUADS ); glTexCoord2f( 0.0, 0.0 ); - glVertex3f( x-size/2., y-size/2., 0.0 ); + glVertex3f( x-x_size/2./xScale, y-y_size/2./yScale, 0.0 ); glTexCoord2f( 0.0, 1.0 ); - glVertex3f( x-size/2., y+size/2., 0.0 ); + glVertex3f( x-x_size/2./xScale, y+y_size/2./yScale, 0.0 ); glTexCoord2f( 1.0, 1.0 ); - glVertex3f( x+size/2., y+size/2., 0.0 ); + glVertex3f( x+x_size/2./xScale, y+y_size/2./yScale, 0.0 ); glTexCoord2f( 1.0, 0.0 ); - glVertex3f( x+size/2., y-size/2., 0.0 ); + glVertex3f( x+x_size/2./xScale, y-y_size/2./yScale, 0.0 ); glEnd(); glFlush(); glDisable( GL_ALPHA_TEST ); - glDisable( GL_TEXTURE_2D ); + glDisable( GL_TEXTURE_2D );*/ + drawTexturePart( texture, 1.0, 1.0, x_size, y_size, x, y ); } -//====================================================================== -// Function: drawText -// Purpose : -//======================================================================= +/*! Draw texture part + \param texture - the texture ID + \param x_ratio - the horizontal ratio of texture part + \param y_ratio - the vertical ratio of texture part + \param x_size - the horizontal size of texture + \param y_size - the vertical size of texture + \param x - x coord + \param y - y coord + \param scale - common scale factor ( if = 0, use drawer scales ) +*/ +void GLViewer_Drawer::drawTexturePart( GLuint texture, + GLfloat x_ratio, + GLfloat y_ratio, + GLfloat x_size, + GLfloat y_size, + GLfloat x, + GLfloat y, + GLfloat scale ) +{ + if( !texture ) + return; + + float xScale = scale > 0. ? 1./scale : myXScale; + float yScale = scale > 0. ? 1./scale : myYScale; + + glColor4f( 1.0, 1.0, 1.0, 1.0 ); + + + glEnable( GL_TEXTURE_2D ); + glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + bool hasAlpha = glIsEnabled( GL_ALPHA_TEST ); + glDisable( GL_ALPHA_TEST ); + + glBindTexture( GL_TEXTURE_2D, texture ); + glBegin( GL_QUADS ); + + glTexCoord2f( 0.0, 0.0 ); + glVertex3f( x-x_size/2./xScale, y-y_size/2./yScale, 0.0 ); + + glTexCoord2f( 0.0, y_ratio ); + glVertex3f( x-x_size/2./xScale, y+y_size/2./yScale, 0.0 ); + + glTexCoord2f( x_ratio, y_ratio ); + glVertex3f( x+x_size/2./xScale, y+y_size/2./yScale, 0.0 ); + + glTexCoord2f( x_ratio, 0.0 ); + glVertex3f( x+x_size/2./xScale, y-y_size/2./yScale, 0.0 ); + + glEnd(); + glFlush(); + + if ( hasAlpha ) + glEnable( GL_ALPHA_TEST ); + + glDisable( GL_TEXTURE_2D ); +} + +/*! + Draw text + \param text - text to be drawn + \param xPos - x position + \param yPos - y position + \param color - color of text + \param theFont - font of text + \param theSeparator - letter separator + \param theFormat - text format (by default DTF_BITMAP) +*/ void GLViewer_Drawer::drawText( const QString& text, GLfloat xPos, GLfloat yPos, const QColor& color, QFont* theFont, int theSeparator, DisplayTextFormat theFormat ) { - if( theFormat == DTF_TEXTURE ) + glColor3f( ( GLfloat )color.red() / 255, + ( GLfloat )color.green() / 255, + ( GLfloat )color.blue() / 255 ); + + if( theFormat != DTF_BITMAP ) { - GLViewer_TexFont aTexFont( theFont, theSeparator ); - aTexFont.generateTexture(); - - glGetIntegerv (GL_VIEWPORT, viewport); - glGetDoublev (GL_MODELVIEW_MATRIX, modelMatrix); - glGetDoublev (GL_PROJECTION_MATRIX, projMatrix); - status = gluProject (xPos, yPos, 0, modelMatrix, projMatrix, viewport, &winx, &winy, &winz); - - glPushAttrib( GL_TRANSFORM_BIT | GL_VIEWPORT_BIT | GL_LIST_BIT ); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0,viewport[2],0,viewport[3],-100,100); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - glColor3f( ( GLfloat )color.red() / 255, - ( GLfloat )color.green() / 255, - ( GLfloat )color.blue() / 255 ); - - aTexFont.drawString( text, winx, winy ); - - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glPopAttrib(); + GLViewer_TexFont aTexFont( theFont, theSeparator, theFormat == DTF_TEXTURE_SCALABLE, GL_LINEAR ); + // Font texture was not found or generated --> cannot draw text + if ( !aTexFont.generateTexture() ) + return; + + if ( theFormat == DTF_TEXTURE_SCALABLE ) + aTexFont.drawString( text, xPos, yPos, textScale() ); + else + aTexFont.drawString( text, xPos, yPos ); } - else if( theFormat == DTF_BITMAP ) + else { - glColor3f( ( GLfloat )color.red() / 255, - ( GLfloat )color.green() / 255, - ( GLfloat )color.blue() / 255 ); glRasterPos2f( xPos, yPos ); glListBase( displayListBase( theFont ) ); - glCallLists( text.length(), GL_UNSIGNED_BYTE, text.local8Bit().data() ); + glCallLists( text.length(), GL_UNSIGNED_BYTE, text.toLocal8Bit().data() ); } } -//====================================================================== -// Function: drawText -// Purpose : -//======================================================================= +/*! + Draws object-text +*/ void GLViewer_Drawer::drawText( GLViewer_Object* theObject ) { if( !theObject ) @@ -612,20 +816,27 @@ void GLViewer_Drawer::drawText( GLViewer_Object* theObject ) drawText( aText->getText(), aPosX, aPosY, aText->getColor(), &aTmpVarFont, aText->getSeparator(), aText->getDisplayTextFormat() ); } -//====================================================================== -// Function: drawGLText -// Purpose : -//======================================================================= +/*! Draw text + \param text - the text string + \param x - x coord + \param y - y coord + \param hPosition - horizontal alignment + \param vPosition - vertical alignment + \param color - text color + \param smallFont - font format +*/ void GLViewer_Drawer::drawGLText( QString text, float x, float y, int hPosition, int vPosition, QColor color, bool smallFont ) { - QFont aFont( "Arial", 10, QFont::Bold ); + QFont aFont( myFont ); if( smallFont ) - aFont.setPointSize( 8 ); + aFont.setPointSize( int(aFont.pointSize() * 0.8) ); + + GLfloat scale = textScale() > 0. ? textScale() : 1.; QFontMetrics aFontMetrics( aFont ); - float width = aFontMetrics.width( text ) / myXScale; - float height = aFontMetrics.height() / myXScale; + float width = myTextFormat == DTF_TEXTURE_SCALABLE ? aFontMetrics.width( text ) * scale : aFontMetrics.width( text ) / myXScale; + float height = myTextFormat == DTF_TEXTURE_SCALABLE ? aFontMetrics.height() * scale : aFontMetrics.height() / myYScale; float gap = 5 / myXScale; switch( hPosition ) @@ -644,13 +855,28 @@ void GLViewer_Drawer::drawGLText( QString text, float x, float y, default : break; } - drawText( text, x, y, color, &aFont, 2, DTF_BITMAP ); // DTF_BITMAP or DTF_TEXTURE + drawText( text, x, y, color, &aFont, 2, myTextFormat ); } -//====================================================================== -// Function: drawRectangle -// Purpose : -//======================================================================= +/*! + \return a rectangle of text (without viewer scale) +*/ +GLViewer_Rect GLViewer_Drawer::textRect( const QString& text ) const +{ + GLfloat scale = textScale() > 0. ? textScale() : 1.; + + QFontMetrics aFontMetrics( myFont ); + float width = myTextFormat == DTF_TEXTURE_SCALABLE ? aFontMetrics.width( text ) * scale : aFontMetrics.width( text ); + float height = myTextFormat == DTF_TEXTURE_SCALABLE ? aFontMetrics.height() * scale : aFontMetrics.height(); + + return GLViewer_Rect( 0, width, height, 0 ); +} + +/*! + Draws rectangle + \param rect - instance of primitive + \param color - color of primitive +*/ void GLViewer_Drawer::drawRectangle( GLViewer_Rect* rect, QColor color ) { if( !rect ) @@ -674,10 +900,12 @@ void GLViewer_Drawer::drawRectangle( GLViewer_Rect* rect, QColor color ) glEnd(); } -//====================================================================== -// Function: translateToHPGL -// Purpose : -//======================================================================= +/*! + Saves object to file with format of HPGL + \param hFile - file + \param aViewerCS - viewer co-ordinate system + \param aHPGLCS - paper co-ordinate system +*/ bool GLViewer_Drawer::translateToHPGL( QFile& hFile, GLViewer_CoordSystem* aViewerCS, GLViewer_CoordSystem* aHPGLCS ) { bool result = true; @@ -686,10 +914,12 @@ bool GLViewer_Drawer::translateToHPGL( QFile& hFile, GLViewer_CoordSystem* aView return result; } -//====================================================================== -// Function: translateToPS -// Purpose : -//======================================================================= +/*! + Saves object to file with format of PostScript + \param hFile - file + \param aViewerCS - viewer co-ordinate system + \param aPSCS - paper co-ordinate system +*/ bool GLViewer_Drawer::translateToPS( QFile& hFile, GLViewer_CoordSystem* aViewerCS, GLViewer_CoordSystem* aPSCS ) { bool result = true; @@ -699,10 +929,12 @@ bool GLViewer_Drawer::translateToPS( QFile& hFile, GLViewer_CoordSystem* aViewer } #ifdef WIN32 -//====================================================================== -// Function: translateToEMF -// Purpose : -//======================================================================= +/*! + Saves object to file with format of EMF + \param hFile - file + \param aViewerCS - viewer co-ordinate system + \param aEMFCS - paper co-ordinate system +*/ bool GLViewer_Drawer::translateToEMF( HDC hDC, GLViewer_CoordSystem* aViewerCS, GLViewer_CoordSystem* aEMFCS ) { bool result = true; @@ -711,3 +943,253 @@ bool GLViewer_Drawer::translateToEMF( HDC hDC, GLViewer_CoordSystem* aViewerCS, return result; } #endif + +/*! + Draws rectangle + \param rect - instance of primitive + \param lineWidth - width of line + \param gap - gap of rectangle + \param color - color of primitive + \param filled - if it is true, then rectangle will be drawn filled with color "fillingColor" + \param fillingColor - color of filling +*/ +void GLViewer_Drawer::drawRectangle( GLViewer_Rect* rect, GLfloat lineWidth, GLfloat gap, + QColor color, bool filled, QColor fillingColor ) +{ + if( !rect ) + return; + + float x1 = rect->left() - gap; + float x2 = rect->right() + gap; + float y1 = rect->bottom() - gap; + float y2 = rect->top() + gap; + + if( filled ) + { + glColor3f( ( GLfloat )fillingColor.red() / 255, + ( GLfloat )fillingColor.green() / 255, + ( GLfloat )fillingColor.blue() / 255 ); + glBegin( GL_POLYGON ); + glVertex2f( x1, y1 ); + glVertex2f( x1, y2 ); + glVertex2f( x2, y2 ); + glVertex2f( x2, y1 ); + glEnd(); + } + + glColor3f( ( GLfloat )color.red() / 255, + ( GLfloat )color.green() / 255, + ( GLfloat )color.blue() / 255 ); + glLineWidth( lineWidth ); + + glBegin( GL_LINE_LOOP ); + glVertex2f( x1, y1 ); + glVertex2f( x1, y2 ); + glVertex2f( x2, y2 ); + glVertex2f( x2, y1 ); + glEnd(); +} + +/*! + Draws contour + \param pntList - list of points + \param color - color of contour + \param lineWidth - width of line +*/ +void GLViewer_Drawer::drawContour( const GLViewer_PntList& pntList, QColor color, GLfloat lineWidth ) +{ + glColor3f( ( GLfloat )color.red() / 255, + ( GLfloat )color.green() / 255, + ( GLfloat )color.blue() / 255 ); + glLineWidth( lineWidth ); + + glBegin( GL_LINES ); + QList::const_iterator it = pntList.begin(); + for( ; it != pntList.end(); ++it ) + glVertex2f( (*it).x(), (*it).y() ); + glEnd(); +} + +/*! + Draws rectangular contour + \param rect - instance of rectangle + \param color - color of primitive + \param lineWidth - width of line + \param pattern - pattern of line + \param isStripe - enables line stipple +*/ +void GLViewer_Drawer::drawContour( GLViewer_Rect* rect, QColor color, GLfloat lineWidth, + GLushort pattern, bool isStripe ) +{ + float x1 = rect->left(); + float x2 = rect->right(); + float y1 = rect->bottom(); + float y2 = rect->top(); + + glColor3f( ( GLfloat )color.red() / 255, + ( GLfloat )color.green() / 255, + ( GLfloat )color.blue() / 255 ); + glLineWidth( lineWidth ); + + if ( isStripe ) + { + glEnable( GL_LINE_STIPPLE ); + glLineStipple( 1, pattern ); + } + + glBegin( GL_LINE_LOOP ); + + glVertex2f( x1, y1 ); + glVertex2f( x1, y2 ); + glVertex2f( x2, y2 ); + glVertex2f( x2, y1 ); + + glEnd(); + glDisable( GL_LINE_STIPPLE ); +} + +/*! + Draws polygon + \param pntList - list of points + \param color - color of polygon +*/ +void GLViewer_Drawer::drawPolygon( const GLViewer_PntList& pntList, QColor color ) +{ + glColor3f( ( GLfloat )color.red() / 255, + ( GLfloat )color.green() / 255, + ( GLfloat )color.blue() / 255 ); + glBegin( GL_POLYGON ); + QList::const_iterator it = pntList.begin(); + for( ; it != pntList.end(); ++it ) + glVertex2f( (*it).x(), (*it).y() ); + glEnd(); +} + +/*! + Draws rectangle + \param rect - instance of rectangle + \param color - color of polygon + \param pattern - pattern of line + \param isStripe - enables line stipple +*/ +void GLViewer_Drawer::drawPolygon( GLViewer_Rect* rect, QColor color, + GLushort pattern, bool isStripe ) +{ + float x1 = rect->left(); + float x2 = rect->right(); + float y1 = rect->bottom(); + float y2 = rect->top(); + glColor3f( ( GLfloat )color.red() / 255, + ( GLfloat )color.green() / 255, + ( GLfloat )color.blue() / 255 ); + + if ( isStripe ) + { + glEnable( GL_LINE_STIPPLE ); + glLineStipple( 1, pattern ); + } + glBegin( GL_POLYGON ); + + glVertex2f( x1, y1 ); + glVertex2f( x1, y2 ); + glVertex2f( x2, y2 ); + glVertex2f( x2, y1 ); + + glEnd(); + glDisable( GL_LINE_STIPPLE ); +} + +GLubyte rasterVertex[5] = { 0x70, 0xf8, 0xf8, 0xf8, 0x70 }; + +/*! + Draws vertex + \param x - x position + \param y - y position + \param color - color of vertex +*/ +void GLViewer_Drawer::drawVertex( GLfloat x, GLfloat y, QColor color ) +{ + glColor3f( ( GLfloat )color.red() / 255, ( GLfloat )color.green() / 255, ( GLfloat )color.blue() / 255 ); + glRasterPos2f( x, y ); + glBitmap( 5, 5, 2, 2, 0, 0, rasterVertex ); +} + +GLubyte rasterCross[7] = { 0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82 }; + +/*! + Draws cross + \param x - x position + \param y - y position + \param color - color of cross +*/ +void GLViewer_Drawer::drawCross( GLfloat x, GLfloat y, QColor color ) +{ + glColor3f( ( GLfloat )color.red() / 255, ( GLfloat )color.green() / 255, ( GLfloat )color.blue() / 255 ); + glRasterPos2f( x, y ); + glBitmap( 7, 7, 3, 3, 0, 0, rasterCross ); +} + +/*! + Draws arrow + \param red, green, blue - components of color + \param lineWidth - width of line + \param staff - + \param length - length of arrow + \param width - width of arrow + \param x - x position + \param y - y position + \param angle - angle of arrow + \param filled - drawn as filled +*/ +void GLViewer_Drawer::drawArrow( const GLfloat red, const GLfloat green, const GLfloat blue, + GLfloat lineWidth, + GLfloat staff, GLfloat length, GLfloat width, + GLfloat x, GLfloat y, GLfloat angle, GLboolean filled ) +{ + GLfloat vx1 = x; + GLfloat vy1 = y + staff + length; + GLfloat vx2 = vx1 - width / 2; + GLfloat vy2 = vy1 - length; + GLfloat vx3 = vx1 + width / 2; + GLfloat vy3 = vy1 - length; + + gp_Pnt2d p0( x, y ); + gp_Pnt2d p1( vx1, vy1 ); + gp_Pnt2d p2( vx2, vy2 ); + gp_Pnt2d p3( vx3, vy3 ); + + p1.Rotate( p0, angle ); + p2.Rotate( p0, angle ); + p3.Rotate( p0, angle ); + + vx1 = p1.X(); vy1 = p1.Y(); + vx2 = p2.X(); vy2 = p2.Y(); + vx3 = p3.X(); vy3 = p3.Y(); + + glColor3f( red, green, blue ); + glLineWidth( lineWidth ); + + glBegin( GL_LINES ); + glVertex2f( x, y ); + glVertex2f( vx1, vy1 ); + glEnd(); + + filled = true; + if( !filled ) + { + glBegin( GL_LINES ); + glVertex2f( vx1, vy1 ); + glVertex2f( vx2, vy2 ); + glVertex2f( vx1, vy1 ); + glVertex2f( vx3, vy3 ); + glEnd(); + } + else + { + glBegin( GL_POLYGON ); + glVertex2f( vx1, vy1 ); + glVertex2f( vx2, vy2 ); + glVertex2f( vx3, vy3 ); + glEnd(); + } +}