texture.cpp

00001 /*
00002 * "PS2" Application Framework
00003 *
00004 * University of Abertay Dundee
00005 * May be used for educational purposed only
00006 *
00007 * Author - Dr Henry S Fortuna
00008 *
00009 * $Revision: 1.2 $
00010 * $Date: 2007/08/19 12:45:13 $
00011 *
00012 */
00013 
00014 #include "dma.h"
00015 #include "texture.h"
00016 
00017 // Returns the base 2 logarithm of the texture size
00018 // (also a handy way to check for illegal texture sizes)
00019 uint32 TextureLog2(uint32 n)
00020 {
00021         switch(n)
00022         {
00023         case 256:
00024                 return 8;
00025         case 128:
00026                 return 7;
00027         case 64:
00028                 return 6;
00029         case 32:
00030                 return 5;
00031         default:
00032                 {
00033                         printf("Unsupported texture size\n");
00034                         exit(-1);
00035                 }
00036         }
00037 }
00038 
00039 uint32 TSMFromColourDepth(uint32 depth)
00040 {
00041         switch(depth)
00042         {
00043         case 32:
00044                 return PSMCT32;
00045         case 8:
00046                 return PSMT8;
00047         default:
00048                 printf("Unsupported colour depth.\n");
00049                 exit(-1);
00050         }
00051 }
00052 
00053 // Set the valid flag to false until texture loaded
00054 CTexture::CTexture()
00055 {
00056         m_bValid = false;
00057 }
00058 
00059 CTexture::~CTexture()
00060 {
00061 }
00062 
00063 // This method will load 24 or 8 bit windows bmp files
00064 bool CTexture::LoadBitmap(const char * strFilename, bool bBlackTransparent, bool bRedAsAlpha)
00065 {
00066         // Open the file
00067         FILE * fp = NULL;
00068         fp = fopen(strFilename, "rb");
00069 
00070         if(fp == NULL)
00071         {
00072                 printf("Failed to load %s\n", strFilename);
00073                 return false;
00074         }
00075 
00076         // This struct contains the headers that we will read in.
00077         BitmapFile bmp;
00078 
00079         // Read in the bitmap header
00080         fread(&bmp.bitmapfileheader,sizeof(BitmapFileHeader),1,fp);
00081 
00082         // Check that this is a bitmap file and someone isn't trying to read
00083         // some weird file in.
00084         if(bmp.bitmapfileheader.bfType != BITMAP_ID)
00085         {
00086                 printf("%s is not a valid bitmap file\n",strFilename);
00087                 
00088                 fclose(fp);
00089                 return false;
00090         }
00091 
00092         // Read in the position of the bitmap header, then set the file
00093         // pointer to it
00094         fread(&bmp.bitmapinfoheader,sizeof(BitmapInfoHeader),1,fp);
00095 
00096         m_iWidth = bmp.bitmapinfoheader.biWidth;
00097         m_iHeight = bmp.bitmapinfoheader.biHeight;
00098 
00099         // Make sure that the user isn't trying to load an unsupported colour
00100         // depth
00101         assert(bmp.bitmapinfoheader.biBitCount == 8 ||
00102                    bmp.bitmapinfoheader.biBitCount == 24);
00103 
00104         // We will use PSMT8 for 8 bit images, and PSMCT32 for 24 bit images
00105         m_iImageDepth = (bmp.bitmapinfoheader.biBitCount == 8) ? 8 : 32;
00106 
00107         // The log2 function has an assert in it that will be fired off if
00108         // the texture is not a power of 2 or is an unsupported size
00109         m_l2Width = TextureLog2(m_iWidth);
00110         m_l2Height = TextureLog2(m_iHeight);
00111 
00112         // The image size in bytes
00113         int iImageSize;
00114 
00115         // Some image utilities don't set this field, so check first
00116         if(bmp.bitmapinfoheader.biSizeImage!=0)
00117                 iImageSize = bmp.bitmapinfoheader.biSizeImage;
00118         else
00119                 iImageSize = bmp.bitmapfileheader.bfSize - bmp.bitmapfileheader.bfOffBits;
00120 
00121         // Next, if we are loading an 8 bit texture we must load
00122         // a CLUT (Colour look-up table). 
00123         int clut[256], swizzledclut[256];
00124 
00125         if(bmp.bitmapinfoheader.biBitCount == 8)
00126         {
00127                 fread(&clut, sizeof(int), 256, fp);
00128 
00129                 // Loop through each CLUT entry and convert it from
00130                 // BGR to RGB, and "swizzle" it
00131                 for(int p=0; p<256; p++)
00132                 {       
00133                         if(bRedAsAlpha)
00134                         {
00135                                 uint8 * pPtr = (uint8*)&clut[p];
00136                                 pPtr[3]= pPtr[2] / 2;
00137                                 pPtr[0] = 0xFF;
00138                                 pPtr[1] = 0xFF;
00139                                 pPtr[2] = 0xFF;
00140                         }
00141                         else
00142                         {
00143                                 unsigned char * pPtr = (unsigned char *)&clut[p];
00144                                 unsigned char red = pPtr[2];
00145                                 pPtr[2] = pPtr[0];
00146                                 pPtr[0] = red;
00147 
00148                                 // Set the alpha to 0 if the colour is black, 0x80 otherwise
00149                                 if(clut[p] == 0 && bBlackTransparent)
00150                                         pPtr[3] = 0x00;
00151                                 else
00152                                         pPtr[3] = 0x80;
00153                         }
00154 
00155                         // If you look at page 32 of GSUSER_E.pdf you will see
00156                         // a table called "Arrangement of CLUT in IDTEX8" if you
00157                         // look closely you will see that the CLUT is not laid out
00158                         // contiguously. Therefore we must "swizzle" the position so that
00159                         // the clut is placed into memory correctly.
00160                         // (basically what this does is swap bits 3 & 4, see the tutorial
00161                         // doc for more info)
00162                         ((uint32 *) swizzledclut)[(p & 231) + ((p & 8) << 1) + ((p & 16) >> 1)] = clut[p];
00163                 }
00164         }
00165 
00166         // The texture data buffer
00167         m_iTexAddr = VIFStaticDMA.GetPointer();
00168         VIFStaticDMA.StartDirect();
00169 
00170         // Get the total size of all of the pixel data
00171         m_iDataSize = m_iWidth * m_iHeight * (m_iImageDepth / 8);
00172 
00173         assert((m_iDataSize & 0xF) == 0);       // Make sure the texture data size is a quadword multiple.
00174 
00175         // Start on the last line and work our way up the image as
00176         // bitmaps are stored upside down
00177         unsigned int uLine = m_iHeight - 1;
00178         int iColumn = 0;
00179         int i = 0;
00180 
00181         // Seek the file to the image data
00182         fseek(fp,(int)(bmp.bitmapfileheader.bfOffBits) + (uLine * m_iWidth * (bmp.bitmapinfoheader.biBitCount / 8)) ,SEEK_SET);
00183 
00184         for(i = 0; i < (m_iWidth * m_iHeight); i++, iColumn++)
00185         {
00186                 // We have finished a whole row move up one line
00187                 // (we know we have finished a row if i is a multiple
00188                 // of the image width
00189                 if(i > 0 && i % m_iWidth == 0)
00190                 {
00191                         --uLine;
00192                         iColumn = 0;
00193 
00194                         fseek(fp,(int)(bmp.bitmapfileheader.bfOffBits) + (uLine * m_iWidth * (bmp.bitmapinfoheader.biBitCount / 8)) ,SEEK_SET);
00195                 }
00196 
00197                 if(bmp.bitmapinfoheader.biBitCount == 8)
00198                 {
00199                         // We have to wait until we have 4 pixels to add them to the DMA chain, because 32 bits are the smallest size
00200                         // the DMA class accepts. (we only accept images that have widths that are a multiple of 4 so we don't need to
00201                         // check that)
00202                         if( i % 4 == 0 )
00203                         {
00204                                 // Read the pixel in
00205                                 int pixel = 0;
00206                                 fread(&pixel,4,1,fp);
00207 
00208                                 VIFStaticDMA.Add32(pixel);
00209                         }
00210                 }
00211                 else    // Must be 24 bit colour
00212                 {
00213                         // Read the pixel in
00214                         uint32 pixel = 0;
00215                         fread(&pixel,1,3,fp);
00216 
00217                         if(bRedAsAlpha)
00218                         {
00219                                 uint8 * pPtr = (uint8*)&pixel;
00220                                 pPtr[3]= pPtr[2] / 2;
00221                                 pPtr[0] = 0xFF;
00222                                 pPtr[1] = 0xFF;
00223                                 pPtr[2] = 0xFF;
00224                         }
00225                         else
00226                         {
00227                                 // Swap the red and blue channels
00228                                 uint8 * pPtr = (uint8*)&pixel;
00229                                 uint8 red = pPtr[2];
00230                                 pPtr[2] = pPtr[0];
00231                                 pPtr[0] = red;
00232 
00233                                 // Set the alpha to 0 if the colour is black
00234                                 if(pixel == 0 && bBlackTransparent)
00235                                         pPtr[3] = 0;
00236                                 else
00237                                         pPtr[3] = 0x80;
00238                         }
00239 
00240                         VIFStaticDMA.Add32(pixel);
00241                 }
00242         }
00243 
00244         // End the texture data buffer
00245         VIFStaticDMA.EndDirect();
00246         VIFStaticDMA.DMARet();
00247 
00248         // And if we have a clut...
00249         if( m_iImageDepth == 8 )
00250         {
00251                 // Start a clut buffer
00252                 m_iClutAddr = VIFStaticDMA.GetPointer();
00253 
00254                 VIFStaticDMA.StartDirect();
00255 
00256                 for(int i = 0; i < 256; i++)
00257                 {
00258                         VIFStaticDMA.Add32(swizzledclut[i]);
00259                 }
00260 
00261                 VIFStaticDMA.EndDirect();
00262 
00263                 VIFStaticDMA.DMARet();
00264         }
00265 
00266         // Close the file
00267         fclose(fp);
00268         
00269         // Flag the texture as valid
00270         m_bValid = true;
00271         
00272         return true;
00273 }
00274 
00275 // Select the texture for use - it needs to be uploaded first
00276 void CTexture::Select(uint64 Tex0 = 0)
00277 {
00278         assert(m_bValid);
00279         
00280         // Wait for idle state
00281         VIFDynamicDMA.Add32(FLUSH);
00282                 
00283         VIFDynamicDMA.StartDirect();
00284         VIFDynamicDMA.StartAD();
00285         VIFDynamicDMA.AddAD(0, TEXFLUSH);
00286         VIFDynamicDMA.AddAD(ALPHA_SET(0, 1, 0, 1, 128), ALPHA_1);
00287         VIFDynamicDMA.AddAD(TEX1_SET(1, 0, 1, 1, 0, 0, 0), TEX1_1);
00288 
00289         // If the user passed their own TEX0 value, use it.
00290         // Otherwise just use the default.
00291         if(Tex0 != 0)
00292                 VIFDynamicDMA.AddAD(Tex0, TEX0_1);
00293         else
00294                 VIFDynamicDMA.AddAD(m_iTex0, TEX0_1);
00295 
00296         VIFDynamicDMA.EndAD();
00297         VIFDynamicDMA.EndDirect();
00298 }
00299 
00300 uint64 CTexture::Upload(uint32 uGSPage)
00301 {
00302         assert(m_bValid);
00303         
00304         VIFDynamicDMA.Add32(FLUSH);
00305 
00306         uint32 iDstPtr = (uGSPage << 5);
00307         uint32 iClutPtr = ((uGSPage + 15) << 5);
00308         uint32 uQSizeClut = (256 >> 2);
00309 
00310         if( m_iImageDepth == 8 )
00311         {
00312                 // Add the clut
00313                 VIFDynamicDMA.StartDirect();
00314 
00315                 VIFDynamicDMA.Add128(GS_GIFTAG_BATCH(4, 0, 0, 0, 
00316                                                          GIF_FLG_PACKED, GS_BATCH_1(GIF_REG_A_D)));
00317 
00318                 VIFDynamicDMA.Add64(BITBLTBUF_SET(0, 0, 0, iClutPtr, 1, 0));
00319                 VIFDynamicDMA.Add64(BITBLTBUF);
00320 
00321                 VIFDynamicDMA.Add64(TRXPOS_SET(0, 0, 0, 0, 0));
00322                 VIFDynamicDMA.Add64(TRXPOS);
00323 
00324                 VIFDynamicDMA.Add64(TRXREG_SET(16, 16));
00325                 VIFDynamicDMA.Add64(TRXREG);
00326 
00327                 VIFDynamicDMA.Add64(TRXDIR_SET(0));
00328                 VIFDynamicDMA.Add64(TRXDIR);
00329 
00330                 VIFDynamicDMA.Add128(GS_GIFTAG_BATCH(uQSizeClut, 0, 0, 0, 
00331                                                          GIF_FLG_IMAGE, 0));
00332 
00333                 VIFDynamicDMA.EndDirect();
00334 
00335                 VIFDynamicDMA.DMACall(m_iClutAddr);
00336         }
00337 
00338         // Add the texture data itself.
00339         VIFDynamicDMA.StartDirect();
00340         VIFDynamicDMA.Add128(GS_GIFTAG_BATCH(4, 0, 0, 0,
00341                                                  GIF_FLG_PACKED, GS_BATCH_1(GIF_REG_A_D)));
00342 
00343         // TODO - This bit doesn't seem to work so just set TBW to 4 (256 wide) for now.
00344         // Problems arise with the texture displayed when in 8 bit mode with a texture width less than 256?
00345         // Can anybody tell me why? (i'd love to know!)
00346         int TBW = 4;
00347         //int TBW = m_iWidth / 64;
00348 
00349         VIFDynamicDMA.Add64(BITBLTBUF_SET(0, 0, 0, iDstPtr, (TBW == 0) ? 1 : TBW, m_iImageDepth == 8 ? PSMT8 : PSMCT32));
00350         VIFDynamicDMA.Add64(BITBLTBUF);
00351 
00352         VIFDynamicDMA.Add64(TRXPOS_SET(0, 0, 0, 0, 0));
00353         VIFDynamicDMA.Add64(TRXPOS);
00354 
00355         VIFDynamicDMA.Add64(TRXREG_SET(m_iWidth, m_iHeight));
00356         VIFDynamicDMA.Add64(TRXREG);
00357 
00358         VIFDynamicDMA.Add64(TRXDIR_SET(0));
00359         VIFDynamicDMA.Add64(TRXDIR);
00360 
00361         uint32 iQWC = (m_iDataSize) >> 4;
00362 
00363         VIFDynamicDMA.Add128(GS_GIFTAG_BATCH(iQWC, 1, 0, 0, 
00364                                                  GIF_FLG_IMAGE, 0));
00365 
00366         VIFDynamicDMA.EndDirect();
00367 
00368         VIFDynamicDMA.DMACall(m_iTexAddr);
00369 
00370         // Generate a default TEX0.
00371         m_iTex0 = TEX0_SET(iDstPtr, (TBW == 0) ? 1 : TBW, m_iImageDepth == 8 ? PSMT8 : PSMCT32, m_l2Width, m_l2Height, 1, 0, iClutPtr, 0, 0, 0, 1);
00372 
00373         return m_iTex0;
00374 }

Generated on Sun May 18 21:45:09 2008 for PS2X by  doxygen 1.5.4