Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

CursorManager.cpp

Go to the documentation of this file.
00001 /*
00002  * CursorManager.cpp
00003  *
00004  * Part of Fly! Legacy project
00005  *
00006  * Copyright 2003 Chris Wallace
00007  *
00008  * Fly! Legacy is free software; you can redistribute it and/or modify
00009  *   it under the terms of the GNU General Public License as published by
00010  *   the Free Software Foundation; either version 2 of the License, or
00011  *   (at your option) any later version.
00012  *
00013  * Fly! Legacy is distributed in the hope that it will be useful,
00014  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *   GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  *   along with Fly! Legacy; if not, write to the Free Software
00020  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  */
00023 
00038 #include "../Include/Globals.h"
00039 #include "../Include/FlyLegacy.h"
00040 #include "../Include/CursorManager.h"
00041 #include "../Include/Utility.h"
00042 #include "../Include/Ui.h"
00043 
00044 using namespace std;
00045 
00046 
00053 /***************************************************
00054 
00055   Fly! II Cursors
00056 
00057   Cursors are specified in a text file with extension .csr, stored in the
00058   \DATA folder.  The following example is \DATA\MPARROW.CSR in STARTUP.POD:
00059 
00060 <bgno> ---- BEGIN CURSOR ----
00061 <csid> ---- cursor id ----
00062 arrw
00063 <imag> ---- image file ----
00064 mparrow.pbm
00065 <hots> ---- cursor x,y hotspot ----
00066 0
00067 0
00068 <endo> ---- END CURSOR ----
00069 
00070  ***************************************************/
00071 
00072 
00073 //
00074 // Constructor
00075 //
00076 CCursor::CCursor (const char* csrfilename)
00077 {
00078   // Initialize fields
00079   strcpy (csr, csrfilename);
00080   strcpy (curs, "");
00081   csid = 0;
00082   strcpy (imag, "");
00083   hots_x = hots_y = 0;
00084 
00085   // Open .CSR file stream and parse tags
00086   SStream s;
00087   strcpy (s.filename, "DATA\\");
00088   strcat (s.filename, csrfilename);
00089   strcpy (s.mode, "r");
00090   if (OpenStream (&s)) {
00091     ReadFrom (this, &s);
00092     CloseStream (&s);
00093   }
00094 }
00095 
00096 //
00097 // Destructor
00098 //
00099 CCursor::~CCursor (void)
00100 {
00101   if (texid != 0) FreeTexture();
00102 }
00103 
00104 
00105 void CCursor::Load (const char* pbmName)
00106 {
00107   int i, j;
00108   
00109   // Construct full names for PBM and ACT files
00110   char pbmFilename[256];
00111   char actFilename[256];
00112   strcpy (pbmFilename, "Art\\");
00113   strcat (pbmFilename, pbmName);
00114 
00115   strcpy (actFilename, pbmFilename);
00116   char *pExt = strrchr (actFilename, '.');
00117   if (pExt != NULL) {
00118     pExt++;
00119     strcpy (pExt, "ACT");
00120   } else {
00121     gtfo ("Invalid cursor PBM filename %s", pbmName);
00122   }
00123 
00124   // Load colormap from the ACT file
00125   PODFILE *act = popen (&globals->pfs, actFilename);
00126   RGB *actdata = new RGB[0x100];
00127   pread (actdata, 3, 0x100, act);
00128   pclose (act);
00129 
00130   // Read PBM header
00131   PODFILE* pbm = popen (&globals->pfs, pbmFilename);
00132   SPBMHeader h;
00133   pread (&h, sizeof(SPBMHeader), 1, pbm);
00134   if (h.magic != 0x1A4D4250) {    // 'PBM<Esc>' in Little-Endian format
00135     gtfo ("Invalid PBM magic value in %s", pbmName);
00136   }
00137   int width = h.width;
00138   int height = h.height;
00139 
00140   // Check that cursor size does not exceed maximum
00141   int texwidth = 32;
00142   int texheight = 32;
00143   if ((width > texwidth) || (height > texheight)) {
00144     gtfo ("Cursor image %s exceeds maximum size of 32x32 pixels", pbmName);
00145   }
00146 
00147   // Read PBM row offset table. This is a list of (height+1) offsets to the
00148   //   start of each row's data.  The last element is the size of the entire
00149   //   data area, which allows for simple calculation of the last row's size
00150   unsigned long *rowOffset = new unsigned long[height+1];
00151   pread (rowOffset, sizeof(unsigned long), height+1, pbm);
00152 
00153   // Allocate an array containing the length in bytes of each row data
00154   unsigned long *rowLength = new unsigned long[height];
00155   for (i=0; i<height; i++) {
00156     // Calculate data length for this row
00157     rowLength[i] = rowOffset[i+1] - rowOffset[i];
00158   }
00159 
00160   // Pre-load all row data into memory for faster processing
00161   int rowDataSize = rowOffset[height];
00162   unsigned char *rowData = new unsigned char[rowDataSize];
00163   pread (rowData, 1, rowDataSize, pbm);
00164   pclose (pbm);
00165 
00166   // Allocate storage for 32x32 RGBA texture image
00167   GLubyte *teximage = new GLubyte[texwidth * texheight * 4];
00168   memset (teximage, 0, texwidth*texheight * 4);
00169 
00170   for (i=0; i<height; i++) {
00171     unsigned char *p = &rowData[rowOffset[i]];
00172 
00173     // Continue reading data chunks until finished this row
00174     unsigned long iRow = 0;
00175     while (iRow < rowLength[i]) {
00176       // Read pixel colum
00177       short column;
00178       column = *((short*)p);
00179       p += sizeof(short);
00180       iRow += sizeof(short);
00181 
00182       // Read pixel count for this chunk
00183       short count;
00184       count = *((short*)p);
00185       p += sizeof(short);
00186       iRow += sizeof(short);
00187 
00188       // Read data (palette index) for each pixel in the chunk
00189       for (j=0; j<count; j++) {
00190         // Get palette index
00191         unsigned char iCmap = *p;
00192         p++;
00193         iRow++;
00194 
00195         // Flip image vertically
00196         unsigned int offset = (((texheight - i - 1) * texwidth) + column + j) * 4;
00197         
00198         // Assign texel RGBA values
00199         if (iCmap == 0) {
00200           // This is a transparent pixel
00201           teximage[offset + 0] = 0;
00202           teximage[offset + 1] = 0;
00203           teximage[offset + 2] = 0;
00204           teximage[offset + 3] = 0;
00205         } else {
00206           // Non-transparent pixel, transfer colour data from palette
00207           teximage[offset + 0] = actdata[iCmap].r;
00208           teximage[offset + 1] = actdata[iCmap].g;
00209           teximage[offset + 2] = actdata[iCmap].b;
00210           teximage[offset + 3] = 0xff;
00211         }
00212       }
00213 
00214       // Pad to next long boundary
00215       while ((iRow % 4) != 0) {
00216         p++;
00217         iRow++;
00218       }
00219     }
00220   }
00221 
00222   // Clean up allocated data
00223   delete actdata;
00224   delete rowOffset;
00225   delete rowLength;
00226   delete rowData;
00227 
00228   // Generate non-mipmapped OpenGL texture
00229   glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
00230   glGenTextures (1, &texid);
00231   glBindTexture (GL_TEXTURE_2D, texid);
00232   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
00233   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
00234   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
00235   glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
00236   GLenum format = GL_RGBA;
00237   glTexImage2D (GL_TEXTURE_2D, 0, format, texwidth, texheight, 0,
00238         format, GL_UNSIGNED_BYTE, teximage);
00239 
00240   // Clean up dynamically allocated data
00241   delete teximage;
00242 }
00243 
00244 void CCursor::FreeTexture (void)
00245 {
00246   glDeleteTextures (1, &texid);
00247   texid = 0;
00248 }
00249 
00250 int CCursor::Read (SStream *stream, Tag tag)
00251 {
00252   int rc = TAG_IGNORED;
00253 
00254   switch (tag) {
00255   case 'csid':
00256     // Read unique ID
00257     ReadTag (&csid, stream);
00258     rc = TAG_READ;
00259     break;
00260 
00261   case 'imag':
00262     // Read .PBM image filename
00263     ReadString (imag, 64, stream);
00264     rc = TAG_READ;
00265     break;
00266 
00267   case 'curs':
00268     // Read OS cursor resource name (not currently used)
00269     ReadString (curs, 64, stream);
00270     rc = TAG_READ;
00271     break;
00272 
00273   case 'hots':
00274     // Read hotspot offsets
00275     ReadInt (&hots_x, stream);
00276     ReadInt (&hots_y, stream);
00277     rc = TAG_READ;
00278     break;
00279 
00280   default:
00281     char s[16];
00282     TagToString (s, tag);
00283     globals->logWarning->Write ("CCursor::Read : Unknown tag <%s>", s);
00284   }
00285 
00286   return rc;
00287 }
00288 
00289 
00290 void CCursor::ReadFinished (void)
00291 {
00292   // Load image bitmap
00293   if (strlen (imag) > 0) {
00294     Load (imag);
00295   }
00296 /*
00297     // Successfully loaded.  Create a surface and draw the bitmap onto it
00298     // The surface will actually be blitted to the screen when this cursor
00299     // is active
00300     int w, h;
00301     GetBitmapSize (&bmImage, &w, &h);
00302     surf = CreateSurface (w, h);
00303     DrawBitmap (surf, &bmImage, 0, 0, 0);
00304 */
00305 }
00306 
00307 
00308 //
00309 // CursorManager
00310 //
00311 CCursorManager::CCursorManager (void)
00312 {
00313   x = y = 0;
00314   crsrCurrent = NULL;
00315 }
00316 
00317 CCursorManager::~CCursorManager (void)
00318 {
00319   map<Tag,CCursor*>::iterator iter;
00320   for (iter=cache.begin(); iter!=cache.end(); iter++) {
00321     delete iter->second;
00322   }
00323 }
00324 
00325 //
00326 // MouseMotion
00327 //
00328 void CCursorManager::MouseMotion (int mouse_x, int mouse_y)
00329 {
00330   if (crsrCurrent != NULL) {
00331 //  int w, h;
00332 //  GetBitmapSize (&crsrCurrent->bmImage, &w, &h);
00333     x = mouse_x - crsrCurrent->hots_x;
00334     y = globals->screenHeight - mouse_y - 1 - 32 + crsrCurrent->hots_y;
00335   }
00336 
00337 /*
00338   char sTag[8];
00339   strcpy (sTag, "----");
00340   if (crsrCurrent != NULL) {
00341     TagToString (sTag, crsrCurrent->csid);
00342   }
00343   char debug[80];
00344   sprintf (debug, "MouseMotion : tag=%s x=%d y=%d", sTag, x, y);
00345   DrawNoticeToUser (debug, 1);
00346 */
00347 }
00348 
00349 
00350 CCursor *CCursorManager::FindCursor (const char *csrfilename)
00351 {
00352   CCursor *rc = NULL;
00353 
00354   map<Tag,CCursor*>::iterator iter;
00355   for (iter=cache.begin(); iter!=cache.end() && (rc == NULL); iter++) {
00356     CCursor *crsr = iter->second;
00357     if (strcmp (crsr->csr, csrfilename) == 0) {
00358       rc = crsr;
00359     }
00360   }
00361 
00362   return rc;
00363 }
00364 
00365 CCursor *CCursorManager::FindCursor (Tag tag)
00366 {
00367   CCursor *rc = NULL;
00368 
00369   map<Tag,CCursor*>::iterator iter = cache.find(tag);
00370   if (iter != cache.end()) {
00371     rc = iter->second;
00372   }
00373 
00374   return rc;
00375 }
00376 
00377 
00378 //
00379 // BindCursor
00380 //
00381 // Applications call this method to load the specified cursor bitmap into cache.
00382 //   The method returns a handle to the cursor that is used in SetCursor().
00383 //
00384 Tag CCursorManager::BindCursor (const char *csrfilename)
00385 {
00386   Tag rc = 0;
00387 
00388   CCursor *crsr = FindCursor (csrfilename);
00389   if (crsr == NULL) {
00390     // Try to load it
00391     crsr = new CCursor (csrfilename);
00392     cache[crsr->csid] = crsr;
00393   }
00394 
00395   // Set return code to cursor unique identifier
00396   if (crsr != NULL) {
00397     rc = crsr->csid;
00398   }
00399 
00400   return rc;
00401 }
00402 
00403 
00404 int CCursorManager::GetNumBoundCursors (void)
00405 {
00406   return cache.size();
00407 }
00408 
00409 
00410 void CCursorManager::SetCursor (Tag tag)
00411 {
00412   crsrCurrent = FindCursor (tag);
00413 }
00414 
00415 Tag CCursorManager::GetCursor (void)
00416 {
00417   Tag rc = 0;
00418 
00419   if (crsrCurrent != NULL) {
00420     rc = crsrCurrent->csid;
00421   }
00422 
00423   return rc;
00424 }
00425 
00426 
00427 void CCursorManager::Draw (void)
00428 {
00429   if (crsrCurrent != NULL) {
00430     // Set projection matrix to 2D screen size
00431     glMatrixMode(GL_PROJECTION);
00432     glPushMatrix();
00433     glLoadIdentity ();
00434     gluOrtho2D (0, globals->screenWidth, 0, globals->screenHeight);
00435 
00436     // Position cursor onscreen
00437     glMatrixMode (GL_MODELVIEW);
00438     glPushMatrix ();
00439     glLoadIdentity ();
00440     glTranslatef (x, y, 0);
00441 
00442     glPushAttrib (GL_ENABLE_BIT);
00443     glDisable (GL_LIGHTING);
00444     glEnable (GL_ALPHA_TEST);
00445     glDisable (GL_DEPTH_TEST);
00446 
00447     glEnable (GL_TEXTURE_2D);
00448     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
00449     glBindTexture (GL_TEXTURE_2D, crsrCurrent->texid);
00450 
00451     // Draw a 32x32 quad, textured with the current cursor texture id
00452     glBegin (GL_QUADS);
00453       glTexCoord2f (0.0, 0.0);
00454       glVertex2f (0,   0);
00455 
00456       glTexCoord2f (1.0, 0.0);
00457       glVertex2f (32,  0);
00458 
00459       glTexCoord2f (1.0, 1.0);
00460       glVertex2f (32, 32);
00461 
00462       glTexCoord2f (0.0, 1.0);
00463       glVertex2f (0,  32);
00464     glEnd ();
00465     glFlush ();
00466 
00467     // Restore original settings
00468     glPopAttrib ();
00469     glMatrixMode (GL_MODELVIEW);
00470     glPopMatrix ();
00471     glMatrixMode (GL_PROJECTION);
00472     glPopMatrix ();
00473 
00474     // Check for an OpenGL error
00475     GLenum e = glGetError ();
00476     if (e != GL_NO_ERROR) {
00477       gtfo ("CCursorManager::Draw : GL Error 0x%04X", e);
00478     }
00479 /*
00480     char sTag[8];
00481     strcpy (sTag, "----");
00482     if (crsrCurrent != NULL) {
00483       TagToString (sTag, crsrCurrent->csid);
00484     }
00485     char debug[80];
00486     sprintf (debug, "Draw: tag=%s x=%d y=%d", sTag, x, y);
00487     DrawNoticeToUser (debug, 1);
00488 */
00489   }
00490 }
00491 
SourceForge.net Logo Documentation generated by doxygen