Main Page | Class List | File List | Class Members | Related Pages

glcanvas.cpp

00001 /***************************************************************************
00002                            glcanvas.cpp
00003                            -------------------
00004     begin                : 2004-05-24 22:19
00005     copyright            : (C) 2004-2005 by Michael Menne
00006     email                : menne@users.sourceforge.net
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  This program is free software; you can redistribute it and/or
00011  modify it under the terms of the GNU General Public License
00012  as published by the Free Software Foundation; either version 2
00013  of the License, or (at your option) any later version.
00014 
00015  This program is distributed in the hope that it will be useful,
00016  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  GNU General Public License for more details.
00019 
00020  You should have received a copy of the GNU General Public License
00021  along with this program; if not, write to the Free Software
00022  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00023  ***************************************************************************/
00024 
00025 #include "glcanvas.h"
00026 #include "app.h"
00027 #include "texture.h"
00028 #include "map.h"
00029 #include <wx/tooltip.h>
00030 #include <stdio.h>
00031 #include <math.h>
00032 
00033 extern ofstream logFile;
00034 
00035 namespace WDS
00036 {
00037 
00038 // Breiten der jeweiligen Buchstaben
00039 static int g_aGlyphWidths[256] =
00040 {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
00041    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
00042    4,  5,  6,  9,  9, 14, 11,  3,  5,  5,  6,  9,  4,  5,  4,  4,
00043    9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  4,  4,  9,  9,  9,  9,
00044   16, 11, 11, 12, 12, 11, 10, 12, 11,  3,  8, 11,  9, 13, 11, 12,
00045   11, 12, 11, 11,  9, 11, 11, 15, 11,  9,  9,  4,  4,  4,  7,  9,
00046    5,  9,  9,  8,  9,  9,  4,  9,  8,  4,  3,  8,  3, 13,  8,  9,
00047    9,  9,  5,  8,  4,  8,  7, 11,  7,  7,  7,  5,  3,  5,  9, 12,
00048   12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
00049   12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
00050    4,  5,  9,  9,  9,  9,  3,  9,  5, 12,  5,  9,  9,  5, 12,  9,
00051    6,  9,  5,  5,  5,  9,  9,  4,  5,  5,  5,  9, 13, 13, 13, 10,
00052   11, 11, 11, 11, 11, 11, 16, 12, 11, 11, 11, 11,  3,  3,  3,  3,
00053   12, 11, 12, 12, 12, 12, 12,  9, 12, 11, 11, 11, 11,  9, 11,  9,
00054    9,  9,  9,  9,  9,  9, 14,  8,  9,  9,  9,  9,  3,  3,  3,  3,
00055    9,  8,  9,  9,  9,  9,  9,  9,  9,  8,  8,  8,  8,  7,  9,  7
00056 };
00057 
00058 // Message map
00059 BEGIN_EVENT_TABLE(CGLCanvas, wxGLCanvas)
00060     EVT_SIZE(CGLCanvas::OnSize)
00061     EVT_PAINT(CGLCanvas::OnPaint)
00062     EVT_ERASE_BACKGROUND(CGLCanvas::OnEraseBackground)
00063     EVT_KEY_DOWN( CGLCanvas::OnKeyDown )
00064     EVT_MOTION( CGLCanvas::OnMouseMove )
00065     EVT_LEFT_DOWN( CGLCanvas::OnLButtonDown )
00066     EVT_LEFT_UP( CGLCanvas::OnLButtonUp )
00067     EVT_SCROLLWIN( CGLCanvas::OnScrollBar )
00068     EVT_TIMER( 1, CGLCanvas::OnTimer )
00069 END_EVENT_TABLE()
00070 
00071 /*----------------------------------------------------------------------------/
00072     Constructor / Destructor
00073 -----------------------------------------------------------------------------*/
00074 
00075 CGLCanvas::CGLCanvas( wxWindow *parent) : wxGLCanvas(parent, (wxGLCanvas*) NULL ),
00076   m_timer(this, 1), m_pFont(0)
00077 {
00078     m_XPos = 0;
00079     m_YPos = 0;
00080     m_fZoom = 1.0f;
00081 
00082     m_Width = 0;
00083     m_Height = 0;
00084 
00085 #ifdef SHOW_TOOLTIPS
00086     SetToolTip( "Text..." );    // Wichtig!
00087 #endif
00088 }
00089 
00090 CGLCanvas::~CGLCanvas( )
00091 {
00092     // Textur der Font freigeben
00093     DeleteTexture( m_pFont );
00094 
00095     // Alle noch nicht geloeschten Texturen freigeben
00096     TexMap::iterator i = m_aTextures.begin();
00097     for( ; i!=m_aTextures.end(); i++ )
00098     {
00099         CTexture *pTex = i->second;
00100         logFile << "Texture '" << pTex->GetName() << "' still has a RefCount of "
00101                 << pTex->GetRefCount() << "." << endl;
00102         delete pTex;
00103     }
00104     m_aTextures.clear();    // Alle Eintraege loeschen
00105 }
00106 
00107 /*----------------------------------------------------------------------------/
00108     Member Functions
00109 -----------------------------------------------------------------------------*/
00110 
00111 void CGLCanvas::Init()
00112 {
00113     if (!GetContext())
00114     {
00115         cout << "***ERROR: GLCanvas::Init: Kein OpenGL Kontext!" << endl;
00116         return;
00117     }
00118     SetCurrent();
00119 
00120     // Groesse des Fensters abfragen und Viewport setzen
00121     GetClientSize(&m_Width, &m_Height);
00122     glViewport(0, 0, (GLint) m_Width, (GLint) m_Height);
00123 
00124     // Create Ortho View (0,0 At Top Left)
00125     glMatrixMode( GL_PROJECTION );
00126     glLoadIdentity();
00127     glOrtho( 0.0f, m_Width, m_Height, 0.0f, 0.0f, 10.0f);
00128     glMatrixMode( GL_MODELVIEW );
00129 
00130     // Texturing einschalten
00131     glEnable(GL_TEXTURE_2D);
00132 
00133     glDisable( GL_DEPTH_TEST );
00134     glCullFace( GL_BACK );
00135 
00136     // Clear Color
00137     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
00138 
00139     // Alpha testing
00140     glEnable( GL_ALPHA_TEST );
00141     glAlphaFunc(GL_GREATER, 0.7f);
00142 
00143     // Alpha blending
00144     //glEnable(GL_BLEND);
00145     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00146 
00147     // Font Textur laden
00148     m_pFont = LoadTexture( "data/font.png", false );
00149 
00150     // Clear screen
00151     glClear( GL_COLOR_BUFFER_BIT );
00152     SwapBuffers();
00153 }
00154 
00155 void CGLCanvas::UpdateView( )
00156 {
00157     ClampView( );
00158     Refresh( );
00159 }
00160 
00161 void CGLCanvas::OnPaint( wxPaintEvent& event )
00162 {
00163     wxPaintDC dc(this); // Muss vorhanden sein!
00164     if (!GetContext())
00165     {
00166         cout << "***ERROR: GlCanvas::OnPaint: Kein Kontext" << endl;
00167         return;
00168     }
00169     SetCurrent();
00170 
00171     // clear color buffers
00172     glClear( GL_COLOR_BUFFER_BIT );
00173     glLoadIdentity();                  // reset the view before we draw
00174 
00175     // Zeiger auf Karte holen
00176     CMap *pMap = wxGetApp().GetMap();
00177 
00178     if( pMap )
00179     {
00180         glTranslatef( -m_XPos, -m_YPos, 0.0f ); // scroll
00181         glScalef( m_fZoom, m_fZoom, 1.0f );     // zoom
00182 
00183         // Karte zeichnen
00184         pMap->Draw();
00185 
00186         // Sichtbares Rechteck zeichnen
00187         //Rect r2 = GetVisibleRect();
00188         //this->DrawRect( &r2, 1.0f,0.0f, 0.0f );
00189 
00190         // Rechteck um Karte zeichnen
00191         Rect rcMap = pMap->GetBounds();
00192         this->DrawRect( rcMap, 1.0f, 0.0f, 0.0f );
00193     }
00194     else
00195     {
00196         // Keine Karte geladen - Nachricht ausgeben
00197         wxString strNoMap = "No map loaded!";
00198 
00199         wxRect  rcClient = GetClientRect( );
00200         wxPoint ptExtent = GetTextExtent( strNoMap.c_str() );
00201 
00202         int x = rcClient.GetRight()  / 2 - ptExtent.x / 2;
00203         int y = rcClient.GetBottom() / 2 - ptExtent.y / 2;
00204 
00205         // Text zentriert auf dem Bildschirm ausgeben
00206         DrawText( x, y, 1.0f, 0.0f, 0.0f, 1.0f, strNoMap.c_str() );
00207     }
00208 
00209     glFlush();
00210     SwapBuffers();
00211 }
00212 
00213 void CGLCanvas::OnSize( wxSizeEvent &event )
00214 {
00215     if (!GetContext())
00216     {
00217         //cout << "***ERROR: GlCanvas::OnSize: Kein Kontext" << endl;
00218         return;
00219     }
00220     SetCurrent();
00221 
00222     // set GL viewport (not called by wxScrolledGLCanvas::OnSize on all platforms...)
00223     m_Width  = event.GetSize().GetWidth();
00224     m_Height = event.GetSize().GetHeight();
00225 
00226     glViewport(0, 0, (GLint) m_Width, (GLint) m_Height);
00227 
00228     // Create Ortho View (0,0 At Top Left)
00229     glMatrixMode( GL_PROJECTION );
00230     glLoadIdentity();
00231     glOrtho( 0.0f, m_Width, m_Height, 0.0f, 0.0f, 10.0f);
00232     glMatrixMode( GL_MODELVIEW );
00233 
00234     ClampView();
00235 
00236     // this is also necessary to update the context on some platforms
00237     // NOTE: on wxGTK wxGLCanvas::OnSize() is empty!
00238     wxGLCanvas::OnSize(event);
00239 
00240     // HACK: make resizing work for wxGTK.
00241     m_timer.Start( 100, true );
00242 }
00243 
00244 void CGLCanvas::OnTimer( wxTimerEvent &event)
00245 {
00246     // Neuzeichnen um Repaint-Bug in Linux zu umgehen
00247     Refresh();
00248 }
00249 
00250 void CGLCanvas::OnEraseBackground(wxEraseEvent& event)
00251 {
00252     // Do nothing, to avoid flashing.
00253 }
00254 
00255 /*
00256  *  OnKeyDown( )
00257  *  Scrollt die Karte wenn eine Pfeiltaste gedrckt wird
00258  */
00259 void CGLCanvas::OnKeyDown( wxKeyEvent &event )
00260 {
00261     const int nMove = 20;
00262 
00263     // Scrollen wenn Pfeiltasten gedrueckt werden
00264     switch( event.GetKeyCode() )
00265     {
00266     case WXK_LEFT:  ScrollREL( -nMove,      0 ); break;
00267     case WXK_RIGHT: ScrollREL(  nMove,      0 ); break;
00268     case WXK_UP:    ScrollREL(      0, -nMove ); break;
00269     case WXK_DOWN:  ScrollREL(      0,  nMove ); break;
00270 
00271     default:
00272         event.Skip( );  // Weiter an Frame
00273         break;
00274     }
00275 }
00276 
00277 void CGLCanvas::OnLButtonDown( wxMouseEvent &event )
00278 {
00279     CaptureMouse();
00280 
00281     // Verhindert sprunghaftes scrollen, wenn die Canvas eine Zeit lang
00282     // keine Mausevents bekommen hat ( z.B. wegen Kontextmenue )
00283     m_ptMouseOld = event.GetPosition( );
00284 
00285     event.Skip( );  // Event weiterreichen an CFrame
00286 }
00287 
00288 void CGLCanvas::OnLButtonUp( wxMouseEvent &event )
00289 {
00290     // Nur Maus freigeben, wenn wir sie auch haben!
00291     if( GetCapture() == this )
00292         ReleaseMouse();
00293 
00294     event.Skip( );  // Event weiterreichen an CFrame
00295 }
00296 
00297 void CGLCanvas::OnMouseMove( wxMouseEvent &event )
00298 {
00299     // Scrollen wenn die linke Maustaste gedrckt ist
00300     if( event.LeftIsDown() )
00301     {
00302         int dx = m_ptMouseOld.x - event.GetX();
00303         int dy = m_ptMouseOld.y - event.GetY();
00304 
00305         if( dx != 0 || dy != 0 )
00306             ScrollREL( dx, dy );
00307     }
00308 
00309     // Alte Mausposition merken
00310     m_ptMouseOld = event.GetPosition( );
00311 
00312 
00313     event.Skip( );  // Event weiterreichen an CFrame
00314 }
00315 
00316 /*
00317  *  ClampView
00318  *  Stellt sicher, das der sichtbare Bereich immer innerhalb der Karte liegt.
00319  *  Wenn der sichtbare Bereich groesser als die Karte ist, wird die Karte zentriert.
00320  */
00321 void CGLCanvas::ClampView()
00322 {
00323     // Zeiger auf Karte holen
00324     CMap *pMap = wxGetApp().GetMap();
00325     if( pMap == NULL )
00326         return;     // Keine Karte geladen
00327 
00328     Rect rcMap = pMap->GetBounds();
00329 
00330     // Horizontal
00331     if( (rcMap.w * m_fZoom) > m_Width )
00332     {
00333         // Karte kleiner als sichtbarer Bereich
00334         if( m_XPos < rcMap.Left() * m_fZoom )                   // Linker Rand
00335             m_XPos = (int)(rcMap.Left() * m_fZoom);
00336         else if( m_XPos > (rcMap.Right() * m_fZoom - m_Width) ) // Rechter Rand
00337             m_XPos = (int)(rcMap.Right() * m_fZoom - m_Width);
00338     }
00339     else
00340     {
00341         // Karte ist groesser als sichtbarer Bereich, deshalb Karte horizontal zentrieren
00342         m_XPos = (int)(((rcMap.w * m_fZoom) - m_Width) / 2.0f + rcMap.Left() * m_fZoom);
00343     }
00344 
00345     // Vertical
00346     if( (rcMap.h * m_fZoom) > m_Height )
00347     {
00348         // Karte kleiner als sichtbarer Bereich
00349         if( m_YPos < rcMap.Top() * m_fZoom )                            // Oberer Rand
00350             m_YPos = (int)(rcMap.Top() * m_fZoom);
00351         else if( m_YPos > (rcMap.Bottom() * m_fZoom - m_Height) )   // Unterer Rand
00352             m_YPos = (int)(rcMap.Bottom() * m_fZoom - m_Height);
00353     }
00354     else
00355     {
00356         // Karte ist groesser als sichtbarer Bereich, deshalb Karte vertikal zentrieren
00357         m_YPos = (int)(((rcMap.h * m_fZoom) - m_Height) / 2.0f);
00358     }
00359 }
00360 
00361 void CGLCanvas::ClientToOpenGL( int &x, int &y )
00362 {
00363     x = (int)((x + m_XPos) / m_fZoom);
00364     y = (int)((y + m_YPos) / m_fZoom);
00365 }
00366 
00367 void CGLCanvas::OpenGLToClient( int &x, int &y )
00368 {
00369     x = (int)((x * m_fZoom ) - m_XPos);
00370     y = (int)((y * m_fZoom ) - m_YPos);
00371 }
00372 
00373 void CGLCanvas::OnScrollBar( wxScrollWinEvent &event)
00374 {
00375     /*int x, y;
00376     CalcScrolledPosition( 0, 0, &x, &y );
00377 
00378     CMap *pMap = wxGetApp().GetMap();
00379     Rect rcBounds = pMap->GetBounds();
00380 
00381     m_XPos = -x + rcBounds.Left() * m_fZoom;
00382     m_YPos = -y + rcBounds.Top()  * m_fZoom;
00383 
00384     ClampView();*/
00385 
00386     event.Skip( );
00387 }
00388 
00389 
00390 /*
00391  *  ScrollREL( )
00392  *  Scrollt um die gegebene Pixelzahl
00393  */
00394 void CGLCanvas::ScrollREL( int dx, int dy )
00395 {
00396     m_XPos += dx;
00397     m_YPos += dy;
00398 
00399     ClampView();
00400 
00401     Refresh( false );
00402 }
00403 
00404 /*
00405  *  ScrollTo( )
00406  *  Scrollt zu der in Pixeln gegebenen Position
00407  */
00408 void CGLCanvas::ScrollTo( int x, int y )
00409 {
00410     m_XPos = (int)((x*m_fZoom) - (m_Width  / 2.0f));
00411     m_YPos = (int)((y*m_fZoom) - (m_Height / 2.0f));
00412     ClampView( );
00413     Refresh( );
00414 }
00415 
00416 /*
00417  *  ZoomIn( )
00418  *  Zoomt in das Bild
00419  */
00420 void CGLCanvas::ZoomIn()
00421 {
00422     SetZoom( m_fZoom + 0.1f );
00423 }
00424 
00425 /*
00426  *  ZoomOut( )
00427  *  Zoomt aus dem Bild heraus
00428  */
00429 void CGLCanvas::ZoomOut()
00430 {
00431     SetZoom( m_fZoom - 0.1f );
00432 }
00433 
00434 /*
00435  *  SetZoom( )
00436  *  Setzt den aktuellen Zoom auf fZoom und passt den sichtbaren Bereich an
00437  */
00438 void CGLCanvas::SetZoom( float fZoom )
00439 {
00440     // Sicherstellen das der Zoom im gueltigen Bereich liegt
00441     if( fZoom < 0.1f ) fZoom = 0.1f;
00442     if( fZoom > 4.0f ) fZoom = 4.0f;
00443 
00444     // Delta bestimmen
00445     float fDelta = fZoom / m_fZoom;
00446     if( fDelta == 1.0f )    // Bei 1.0f hat sich nichts veraedert
00447         return;
00448 
00449     // Sichtbaren Bereich anpassen
00450     m_XPos += (int)((m_Width  * fDelta - m_Width)  / 2.0f + (m_XPos * fDelta - m_XPos));
00451     m_YPos += (int)((m_Height * fDelta - m_Height) / 2.0f + (m_YPos * fDelta - m_YPos));
00452     m_fZoom = fZoom;
00453 
00454     // Neu zeichnen
00455     ClampView();
00456     Refresh( false );
00457 }
00458 
00459 /*
00460  *  Gibt den zur Zeit sichtbaren Ausschnitt zurueck
00461  */
00462 Rect CGLCanvas::GetVisibleRect()
00463 {
00464     Rect rcScreen( (int)(m_XPos  * (1/m_fZoom)), (int)(m_YPos   * (1/m_fZoom)),
00465                    (int)(m_Width * (1/m_fZoom)), (int)(m_Height * (1/m_fZoom)) );
00466     return rcScreen;
00467 }
00468 
00469 /*-----------------------------------------------------------------------------
00470     Drawing functions
00471 -----------------------------------------------------------------------------*/
00472 
00473 void CGLCanvas::BeginDraw( float r, float g, float b, float a )
00474 {
00475     glPushAttrib( GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT );
00476     glBindTexture( GL_TEXTURE_2D, 0 );  // Textur abwaehlen
00477     glDisable( GL_ALPHA_TEST );         // Alphatesting ausschalten
00478 
00479     // Alphablending anschalten, wenn Alpha < 1
00480     if( a < 1.0f )
00481         glEnable( GL_BLEND );
00482 
00483     glColor4f( r, g, b, a );
00484 }
00485 
00486 void CGLCanvas::EndDraw( )
00487 {
00488     glPopAttrib( );
00489 }
00490 
00491 void CGLCanvas::FillRect( const Rect &rcRect, float r, float g, float b, float a )
00492 {
00493     BeginDraw( r, g, b, a );
00494     glRecti( rcRect.x, rcRect.y, rcRect.Right(), rcRect.Bottom() );
00495     EndDraw( );
00496 }
00497 
00498 void CGLCanvas::DrawRect( const Rect &rcRect, float r, float g, float b, float a )
00499 {
00500     BeginDraw( r, g, b, a );
00501     glBegin( GL_LINE_LOOP );
00502         glVertex2i( rcRect.Left(),  rcRect.Top()    );  // Links oben
00503         glVertex2i( rcRect.Left(),  rcRect.Bottom() );  // Links unten
00504         glVertex2i( rcRect.Right(), rcRect.Bottom() );  // Rechts unten
00505         glVertex2i( rcRect.Right(), rcRect.Top()    );  // Rechts oben
00506     glEnd();
00507     EndDraw( );
00508 }
00509 
00510 void CGLCanvas::DrawCircle( const wxPoint &ptCenter, int n, float radius,
00511                            float r, float g, float b, float a)
00512 {
00513     BeginDraw( r, g, b, a );
00514     glBegin( GL_POLYGON );
00515     {
00516         float step = 2*PI/n;
00517         for( int i=0; i<n; i++ )
00518         {
00519             float r = i * step;
00520             int x = (int)( cos( r ) * radius ) + ptCenter.x;
00521             int y = (int)( sin( r ) * radius ) + ptCenter.y;
00522 
00523             glVertex2i( x, y );
00524         }
00525     }
00526     glEnd();
00527     EndDraw();
00528 }
00529 
00530 void CGLCanvas::DrawLine( const wxPoint &pt1, const wxPoint &pt2, float width,
00531                          float r, float g, float b, float a )
00532 {
00533     BeginDraw( r, g, b, a );
00534     glLineWidth( width );       // Zuruecksetzen?
00535     glBegin( GL_LINES );
00536     {
00537         glVertex2i( pt1.x, pt1.y );
00538         glVertex2i( pt2.x, pt2.y );
00539     }
00540     glEnd();
00541     EndDraw();
00542 }
00543 
00544 /*-----------------------------------------------------------------------------
00545     Font functions
00546 -----------------------------------------------------------------------------*/
00547 
00548 /*
00549  *  DrawTextBox( )
00550  *  Zeichnet einen ganze String in eine graue Box
00551  */
00552 void CGLCanvas::DrawTextBox( int x, int y, float r, float g, float b, float a, const char *szText )
00553 {
00554     if( m_pFont == NULL || szText == NULL )
00555         return;
00556 
00557     wxPoint pt = GetTextExtent( szText );
00558     Rect rcFill( x-2, y-2, pt.x+8, pt.y+2 );
00559 
00560     FillRect( rcFill, 0.0f, 0.0f, 0.0f, 0.75f );
00561     DrawText( x, y, r, g, b, a, szText );
00562 }
00563 
00564 /*
00565  *  DrawText( )
00566  *  Zeichnet einen ganze String and die gegebene Position
00567  */
00568 void CGLCanvas::DrawText( int x, int y, float r, float g, float b, float a, const char *fmt, ...)
00569 {
00570     if( m_pFont == NULL || fmt == NULL )
00571         return;
00572 
00573     char        szText[1024];                               // Holds Our String
00574     va_list     ap;                                         // Pointer To List Of Arguments
00575 
00576     va_start(ap, fmt);                                      // Parses The String For Variables
00577         vsprintf(szText, fmt, ap);                          // And Converts Symbols To Actual Numbers
00578     va_end(ap);                                             // Results Are Stored In Text
00579 
00580     // Font binden
00581     m_pFont->Bind();
00582 
00583     // OpenGL Farbe merken
00584     glPushAttrib( GL_CURRENT_BIT ); // GL_ENABLE_BIT fuehrt zu Fehlern in Linux!
00585     glColor4f( r, g, b, a );
00586 
00587     // Merken, was an/aus war da glPushAttrib in Linux nicht mit GL_ENABLE_BIT
00588     // geht -> Resize bug
00589     GLboolean bAlphaTest  = glIsEnabled( GL_ALPHA_TEST );
00590     GLboolean bAlphaBlend = glIsEnabled( GL_BLEND );
00591 
00592     // Alphatest ausschalten und alphablend einschalten
00593     if(  bAlphaTest ) glDisable( GL_ALPHA_TEST );
00594     if( !bAlphaBlend) glEnable ( GL_BLEND );
00595 
00596     // Text zeichnen
00597     for(int i=0; szText[i]; i++)
00598     {
00599         DrawChar( x, y, szText[i] );
00600         x += g_aGlyphWidths[((unsigned char)szText[i])];
00601     }
00602 
00603     // Ausganszustand wieder herstellen
00604     if(  bAlphaTest  ) glEnable ( GL_ALPHA_TEST );
00605     if( !bAlphaBlend ) glDisable( GL_BLEND );
00606 
00607     glPopAttrib( );
00608 }
00609 
00610 /*
00611  *  DrawChar( )
00612  *  Zeichnet ein einzelnes Zeichen an die gegebene Position
00613  */
00614 void CGLCanvas::DrawChar( int x, int y, unsigned char c)
00615 {
00616     if ( c <= 32 )
00617         return;     // Nicht druckbar ( space usw. )
00618 
00619     // ToDo: Testen ob Zeichen ueberhaupt auf dem Bildschirm d.h. sichtbar
00620 
00621     int row = c >> 4;   // / 16
00622     int col = c & 15;   // letzten 4 Bits
00623 
00624     float fw = 1.0f / 16.0f;
00625     float fh = 1.0f / 16.0f;
00626     float frow = row * fh;
00627     float fcol = col * fw;
00628 
00629     glBegin(GL_QUADS);
00630         glTexCoord2f(fcol,      frow);      glVertex2i( x,      y      );   // links oben
00631         glTexCoord2f(fcol,      frow + fh); glVertex2i( x,      y + 16 );   // links unten
00632         glTexCoord2f(fcol + fw, frow + fh); glVertex2i( x + 16, y + 16 );   // rechts unten
00633         glTexCoord2f(fcol + fw, frow);      glVertex2i( x + 16, y      );   // rechts oben
00634     glEnd();
00635 }
00636 
00637 /*
00638  *  GetTextExtent( )
00639  *  Gibt die Breite und Hoehe des Textes in Pixeln zurueck
00640  */
00641 wxPoint CGLCanvas::GetTextExtent( const char *szText )
00642 {
00643     wxPoint pt(0, 0);
00644     if( m_pFont == NULL || szText == NULL )
00645         return pt;
00646 
00647     // Hoehe, zur Zeit immer fest
00648     pt.y  = 16;
00649 
00650     // Breite aufsummieren
00651     for(int i=0; szText[i]; i++)
00652     {
00653         pt.x += g_aGlyphWidths[((unsigned char)szText[i])];
00654     }
00655 
00656     return pt;
00657 }
00658 
00659 /*----------------------------------------------------------------------------/
00660     Textur Funktionen
00661 -----------------------------------------------------------------------------*/
00662 
00663 /*
00664  *  LoadTexture( )
00665  *  Laed ein .png Bild aus einer Datei und erzeugt davon eine OpenGL Texture
00666  */
00667 CTexture* CGLCanvas::LoadTexture( string strTexture, bool bMipMap )
00668 {
00669     // Pruefen, ob Texture schon geladen:
00670     TexMap::iterator tex = m_aTextures.find( strTexture );
00671     if( tex != m_aTextures.end() )
00672     {
00673         CTexture *pTex = tex->second;
00674         pTex->IncRefCount();
00675         return pTex;            // Breits geladen -> gleich zurueckgeben
00676     }
00677 
00678     // Bilddatei laden. Wenn dies nicht klappt, wird eine Standardtextur erstellt
00679     wxImage image;
00680     if( image.LoadFile( strTexture.c_str() ) == false )
00681     {
00682         logFile << "ERROR: Unable to load " << strTexture << ". Loading default texture." << endl;
00683 
00684         // Default Textur erzeugen
00685         image.Create( 8, 8 );
00686 
00687         // Schwarz/Rotes Muster erzeugen
00688         BYTE *pData = image.GetData();
00689         for( int i=0; i<8*8; i++ )
00690         {
00691             *pData++ = ((i&1) ^ ((i&8)==8)) ? 255 : 0;      // Ohne Worte.
00692             *pData++ = 0;
00693             *pData++ = 0;
00694         }
00695     }
00696 
00697     // OpenGL Texture erzeugen
00698     CTexture *pTexture = CreateTexture( &image, strTexture, bMipMap );
00699     if( pTexture == NULL )
00700     {
00701         logFile << "ERROR: Unable to create Texture '" << strTexture << "'." << endl;
00702         return NULL;
00703     }
00704 
00705     // Geladene Texturen zur map hinzufgen
00706     m_aTextures.insert( TexMap::value_type( strTexture, pTexture ) );
00707 
00708     return pTexture;
00709 }
00710 
00711 /*
00712  *  CreateTexture( )
00713  *
00714  */
00715 CTexture* CGLCanvas::CreateTexture( wxImage *pImage, string strTexture, bool bMipMap )
00716 {
00717     // Pruefen ob ein OpenGL Kontext vorhanden ist
00718     if (!GetContext())
00719     {
00720         cout << "***ERROR: GlCanvas::CreateTexture: Kein Kontext!" << endl;
00721         return false;
00722     }
00723     SetCurrent();
00724 
00725     // Sicherstellen das Breite/Hoehe der Textur eine Zweierpotenz ist
00726     int w = power_of_two( pImage->GetWidth() );
00727     int h = power_of_two( pImage->GetHeight() );
00728     int bpp = 3;
00729 
00730     // Pruefen ob das Bild transparente Bereiche hat
00731     bool bMask = pImage->HasMask();
00732     BYTE mr, mg, mb;
00733     if( bMask )
00734     {
00735         mr = pImage->GetMaskRed();      // Maske abfragen
00736         mg = pImage->GetMaskGreen();
00737         mb = pImage->GetMaskBlue();
00738         bpp = 4;
00739     }
00740 
00741     // Attribute der Textur abfragen
00742     int cols = pImage->GetWidth();
00743     int rows = pImage->GetHeight();
00744     BYTE *dest, *bmp = pImage->GetData();
00745     BYTE *rgba = new BYTE[w * h * bpp];
00746     memset( rgba, 0, w*h*bpp );             // ToDo: In Schleife unten einbauen!
00747 
00748     // Texture skalieren, so das die Breite und Hoehe ein Potenz von 2 ist,
00749     // sonst kann OpenGL die Textuer nicht laden!
00750     /*for( int row = 0; row < h; row++ )
00751     {
00752         dest = rgba  + row * w;                 // Anfang der Zeile berechnen
00753 
00754         if( row < rows)
00755         {
00756             for(int col = 0; col < w; col++)
00757                 *dest++ = col<cols ? *bmp++ : 0;    // Zeile kopieren
00758         }
00759         else
00760         {
00761             for(int col = 0; col < w; col++)        // Leere Zeilen
00762                 *dest++ = 0;
00763         }
00764     }*/
00765 
00766     // Schneller, jedoch werden die Raender nicht bearbeitet
00767     for( int row = 0; row < rows; row++ )
00768     {
00769         dest = rgba  + row * w * bpp;           // Anfang der Zeile berechnen
00770         for(int col = 0; col < cols; col++)     // Zeile kopieren
00771         {
00772             *dest++ = *bmp++;
00773             *dest++ = *bmp++;
00774             *dest++ = *bmp++;
00775 
00776             if( bMask )
00777             {
00778                 if( dest[-3] == mr && dest[-2] == mg && dest[-1] == mb )
00779                     *dest++ = 0;
00780                 else
00781                     *dest++ = 255;
00782             }
00783         }
00784     }
00785 
00786     // OpenGL Texture erzeugen
00787     GLuint nTextureID;
00788     glGenTextures(1, &nTextureID);
00789 
00790     // Neue Textur binden
00791     glBindTexture(GL_TEXTURE_2D, nTextureID);
00792     if(glGetError() != GL_NO_ERROR)
00793     {
00794         logFile << "***ERROR: CGLCanvas::CreateTexture: glBindTexture failed!" << endl;
00795         return NULL;
00796     }
00797 
00798     // Daten vom Bild auf die Textur kopieren
00799     if( bMipMap )   // Mit MipMaps
00800     {
00801         BuildMipmaps( w, h, GL_RGBA, 4, (BYTE*) rgba );
00802         if(glGetError() != GL_NO_ERROR)
00803         {
00804             logFile << "***ERROR: CGLCanvas::CreateTexture: BuildMipmaps failed!" << endl;
00805             return NULL;
00806         }
00807 
00808         // Vergroesserung/Verkleinerungs Attribute setzen
00809         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
00810         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
00811         if(glGetError() != GL_NO_ERROR)
00812             return NULL;
00813     }
00814     else    // Ohne MipMaps
00815     {
00816         glTexImage2D( GL_TEXTURE_2D, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (BYTE*)rgba );
00817         if(glGetError() != GL_NO_ERROR)
00818         {
00819             logFile << "***ERROR: CGLCanvas::CreateTexture: glTexImage2D failed!" << endl;
00820             return NULL;
00821         }
00822 
00823         // Vergroesserung/Verkleinerungs Attribute setzen
00824         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
00825         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
00826         if(glGetError() != GL_NO_ERROR)
00827             return NULL;
00828     }
00829 
00830     // Neue Textur erzeugen und Attribute setzen
00831     CTexture *pTexture = new CTexture( strTexture );
00832     pTexture->m_nWidth  = w;
00833     pTexture->m_nHeigth = h;
00834     pTexture->m_nOriginalWidth  = cols;
00835     pTexture->m_nOriginalHeigth = rows;
00836     pTexture->m_nRefCount = 1;
00837     pTexture->m_nTexureID = nTextureID;
00838 
00839     // Aufraeumen
00840     delete [] rgba;
00841 
00842     return pTexture;
00843 }
00844 
00845 /*
00846  *  BuildMipmaps( )
00847  *  Erzeugt eine Bildpyramide indem es die Breite und Hoehe des Bildes so lange halbiert, bis
00848  *  das Bild nur noch eine Groesse von 1x1 Pixel hat. Dadurch kann OpenGL schneller zeichnen
00849  *  wenn die Textur verkleinert werden muss, z.B. beim Rauszoomen
00850  */
00851 void CGLCanvas::BuildMipmaps(int nWidth, int nHeigth, GLenum nFormat, int nBpp, BYTE *pData)
00852 {
00853     if( nWidth < 1 || nHeigth < 1 )
00854         return;
00855 
00856     BYTE *d = pData;
00857     int   w = nWidth;
00858     int   h = nHeigth;
00859 
00860     int nLevel = 0;
00861     while( true )
00862     {
00863         // Textur zur Bildpyramide hinzufgen
00864         glTexImage2D( GL_TEXTURE_2D, nLevel, nBpp, w, h, 0, nFormat, GL_UNSIGNED_BYTE, d );
00865 
00866         if( w == 1 && h == 1)                       // Bei 1x1 Pixel ist schluss
00867             break;
00868 
00869         int   neww = (w<2) ? 1 : w/2;               // Neue H�e und Breite berechnen
00870         int   newh = (h<2) ? 1 : h/2;
00871         BYTE *newd = new BYTE[neww * newh * nBpp];  // Speicher reservieren
00872 
00873         ScaleForWDS( w, h, d, neww, newh, newd );   // Bild verkleinern
00874 
00875         if( d != pData )
00876             delete [] d;
00877 
00878         nLevel++;
00879         w = neww;
00880         h = newh;
00881         d = newd;
00882     }
00883 
00884     if( d != pData )
00885         delete [] d;
00886 }
00887 
00888 /*
00889  *  ScaleForWDS( )
00890  *  Verkleinert das gegebene Bild, wobei nur nichttransparente Pixel beruecksichtigt werden.
00891  *  Dadurch entstehen an den Raendern der Kacheln keine Artefakte ( wie schwarzer Rand ).
00892  *  Weiterhin wird das Bild geglaettet.
00893  */
00894 void CGLCanvas::ScaleForWDS( int inw, int inh, BYTE *in, int outw, int outh, BYTE *out )
00895 {
00896     BYTE *dest, *src;
00897     int sy = (inh == 1 ? 1 : 2);
00898     int sx = (inw == 1 ? 4 : 8);
00899 
00900     int r, g, b;
00901     int n;
00902 
00903     for( int y=0; y<outh; y++ )
00904     {
00905         dest = out + outw * y * 4;
00906         src  = in  + inw  * y * 4 * sy;
00907         for ( int x=0; x<outw ; x++ )
00908         {
00909             r = g = b = 0;
00910             n = 0;
00911 
00912             if( src[3] == 255 )
00913             {
00914                 r += src[0]; g += src[1]; b += src[2]; n++;
00915             }
00916 
00917             if( inw > 1 && src[7] == 255  )
00918             {
00919                 r += src[4]; g += src[5]; b += src[6]; n++;
00920             }
00921 
00922             if( inh > 1 && src[inw*4+3] == 255 )
00923             {
00924                 r += src[inw*4]; g += src[inw*4+1]; b += src[inw*4+2]; n++;
00925             }
00926 
00927             if( inh > 1 && inw > 1 && src[inw*4+7] == 255)
00928             {
00929                 r += src[inw*4+4]; g += src[inw*4+5]; b += src[inw*4+6]; n++;
00930             }
00931 
00932             if( n > 0 )
00933             {
00934                 dest[0] = r / n; dest[1] = g / n; dest[2] = b / n; dest[3] = 255;   // Gewichtetes Mittel -> Glaettung
00935             }
00936             else
00937             {
00938                 dest[3] = 0;    // Transparenter Pixel
00939             }
00940 
00941             dest += 4;
00942             src  += sx;
00943         }
00944     }
00945 }
00946 
00947 void CGLCanvas::DeleteTexture( CTexture *pTexture )
00948 {
00949     if( !pTexture )
00950         return;
00951 
00952     pTexture->DecRefCount();
00953     if( pTexture->m_nRefCount == 0 )    // Textur wird nicht mehr benutzt
00954     {
00955         TexMap::iterator tex = m_aTextures.find( pTexture->GetName() );
00956         if( tex != m_aTextures.end() )
00957             m_aTextures.erase( tex );
00958         delete pTexture;
00959     }
00960 }
00961 
00962 /*
00963  *  Gibt die naechste 2er Potenz zurueck
00964  */
00965 int CGLCanvas::power_of_two(int input)
00966 {
00967     int value = 1;
00968     while ( value < input )
00969         value <<= 1;
00970     return value;
00971 }
00972 
00973 }
00974 // kate: space-indent off; tab-width 4; replace-tabs off;
00975 

Generated on Sun Jan 16 18:20:26 2005 for WDSMap by  doxygen 1.3.9.1