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

texture.cpp

Go to the documentation of this file.
00001 // texture.cpp
00002 //
00003 // Copyright (C) 2001-2003, Chris Laurel
00004 //
00005 // This program is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU General Public License
00007 // as published by the Free Software Foundation; either version 2
00008 // of the License, or (at your option) any later version.
00009 
00010 #ifndef MACOSX
00011 #define JPEG_SUPPORT
00012 #define PNG_SUPPORT
00013 #endif
00014 
00015 #ifdef MACOSX
00016 #include <unistd.h>
00017 #include "CGBuffer.h"
00018 #ifndef PNG_SUPPORT
00019 #include <Quicktime/ImageCompression.h>
00020 #include <QuickTime/QuickTimeComponents.h>
00021 #endif
00022 #endif
00023 
00024 #include <cmath>
00025 #include <algorithm>
00026 #include <iostream>
00027 #include <fstream>
00028 #include <cstdlib>
00029 #include <cstdio>
00030 #include <cassert>
00031 
00032 #ifndef _WIN32
00033 #ifndef MACOSX
00034 #include <config.h>
00035 #endif /* ! MACOSX */
00036 #endif /* ! _WIN32 */
00037 
00038 #include <celmath/vecmath.h>
00039 #include <celutil/filetype.h>
00040 #include <celutil/debug.h>
00041 
00042 #include "gl.h"
00043 #include "glext.h"
00044 #include "celestia.h"
00045 
00046 // OpenGL 1.2 stuff missing from Windows headers . . . probably should be
00047 // moved into glext.h
00048 #ifndef GL_TEXTURE_MAX_LEVEL
00049 #define GL_TEXTURE_MAX_LEVEL 0x813D
00050 #endif
00051 
00052 #ifdef JPEG_SUPPORT
00053 
00054 #ifndef PNG_SUPPORT
00055 #include "setjmp.h"
00056 #endif // PNG_SUPPORT
00057 
00058 extern "C" {
00059 #ifdef _WIN32
00060 #include "jpeglib.h"
00061 #else
00062 #include <jpeglib.h>
00063 #endif
00064 }
00065 
00066 #endif // JPEG_SUPPORT
00067 
00068 #ifdef PNG_SUPPORT // PNG_SUPPORT
00069 #ifdef MACOSX
00070 #include "../../macosx/png.h"
00071 #else
00072 #include "png.h"
00073 #endif // MACOSX
00074 
00075 // Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng
00076 #ifndef png_jmpbuf
00077 #define png_jmpbuf(png_ptr) png_ptr->jmpbuf
00078 #endif // PNG_SUPPORT
00079 
00080 // Define various expansion transformations for old versions of libpng
00081 #if PNG_LIBPNG_VER < 10004
00082 #define png_set_palette_to_rgb(p)  png_set_expand(p)
00083 #define png_set_gray_1_2_4_to_8(p) png_set_expand(p)
00084 #define png_set_tRNS_to_alpha(p)   png_set_expand(p)
00085 #endif // PNG_LIBPNG_VER < 10004
00086 
00087 #endif // PNG_SUPPORT
00088 
00089 #include "texture.h"
00090 #include "virtualtex.h"
00091 
00092 using namespace std;
00093 
00094 static bool texCapsInitialized = false;
00095 
00096 struct TextureCaps
00097 {
00098     bool compressionSupported;
00099     bool clampToEdgeSupported;
00100     bool clampToBorderSupported;
00101     bool autoMipMapSupported;
00102     bool maxLevelSupported;
00103     GLint maxTextureSize;
00104 };
00105 
00106 static TextureCaps texCaps;
00107 
00108 
00109 static bool testMaxLevel()
00110 {
00111     unsigned char texels[64];
00112 
00113     glEnable(GL_TEXTURE_2D);
00114     // Test whether GL_TEXTURE_MAX_LEVEL is supported . . .
00115     glTexImage2D(GL_TEXTURE_2D,
00116                  0,
00117                  GL_LUMINANCE,
00118                  8, 8,
00119                  0,
00120                  GL_LUMINANCE,
00121                  GL_UNSIGNED_BYTE,
00122                  texels);
00123     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
00124     float maxLev = -1.0f;
00125     glGetTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, &maxLev);
00126     glDisable(GL_TEXTURE_2D);
00127 
00128     return maxLev == 2;
00129 }
00130 
00131 
00132 static const TextureCaps& GetTextureCaps()
00133 {
00134     if (!texCapsInitialized)
00135     {
00136         texCapsInitialized = true;
00137         texCaps.compressionSupported = ExtensionSupported("GL_ARB_texture_compression");
00138         if (texCaps.compressionSupported)
00139             InitExtension("GL_ARB_texture_compression");
00140 
00141 #ifdef GL_VERSION_1_2
00142         texCaps.clampToEdgeSupported = true;
00143 #else
00144         texCaps.clampToEdgeSupported = ExtensionSupported("GL_EXT_texture_edge_clamp");
00145 #endif // GL_VERSION_1_2
00146         texCaps.clampToBorderSupported = ExtensionSupported("GL_ARB_texture_border_clamp");
00147         texCaps.autoMipMapSupported = ExtensionSupported("GL_SGIS_generate_mipmap");
00148         texCaps.maxLevelSupported = testMaxLevel();
00149         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texCaps.maxTextureSize);
00150     }
00151 
00152     return texCaps;
00153 }
00154 
00155 
00156 
00157 static int getInternalFormat(int format)
00158 {
00159     switch (format)
00160     {
00161     case GL_RGBA:
00162     case GL_BGRA_EXT:
00163         return 4;
00164     case GL_RGB:
00165     case GL_BGR_EXT:
00166         return 3;
00167     case GL_LUMINANCE_ALPHA:
00168         return 2;
00169     case GL_ALPHA:
00170     case GL_INTENSITY:
00171     case GL_LUMINANCE:
00172         return 1;
00173     case GL_DSDT_NV:
00174         return format;
00175     case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
00176     case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
00177     case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
00178         return format;
00179     default:
00180         return 0;
00181     }
00182 }
00183 
00184 
00185 #if 0
00186 // Required in order to support on-the-fly compression; currently, this
00187 // feature is disabled.
00188 static int getCompressedInternalFormat(int format)
00189 {
00190     switch (format)
00191     {
00192     case GL_RGB:
00193     case GL_BGR_EXT:
00194         return GL_COMPRESSED_RGB_ARB;
00195     case GL_RGBA:
00196     case GL_BGRA_EXT:
00197         return GL_COMPRESSED_RGBA_ARB;
00198     case GL_ALPHA:
00199         return GL_COMPRESSED_ALPHA_ARB;
00200     case GL_LUMINANCE:
00201         return GL_COMPRESSED_LUMINANCE_ARB;
00202     case GL_LUMINANCE_ALPHA:
00203         return GL_COMPRESSED_LUMINANCE_ALPHA_ARB;
00204     case GL_INTENSITY:
00205         return GL_COMPRESSED_INTENSITY_ARB;
00206     case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
00207     case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
00208     case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
00209         return format;
00210     default:
00211         return 0;
00212     }
00213 }
00214 #endif
00215 
00216 
00217 static int getCompressedBlockSize(int format)
00218 {
00219     if (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
00220         return 8;
00221     else
00222         return 16;
00223 }
00224 
00225 
00226 static GLenum GetGLTexAddressMode(Texture::AddressMode addressMode)
00227 {
00228     const TextureCaps& caps = GetTextureCaps();
00229 
00230     switch (addressMode)
00231     {
00232     case Texture::Wrap:
00233         return GL_REPEAT;
00234 
00235     case Texture::EdgeClamp:
00236         return caps.clampToEdgeSupported ? GL_CLAMP_TO_EDGE : GL_CLAMP;
00237 
00238     case Texture::BorderClamp:
00239         if (caps.clampToBorderSupported)
00240             return GL_CLAMP_TO_BORDER_ARB;
00241         else
00242             return caps.clampToEdgeSupported ? GL_CLAMP_TO_EDGE : GL_CLAMP;
00243     }
00244 
00245     return 0;
00246 }
00247 
00248 
00249 static void SetBorderColor(Color borderColor, GLenum target)
00250 {
00251     float bc[4] = { borderColor.red(), borderColor.green(),
00252                     borderColor.blue(), borderColor.alpha() };
00253     glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, bc);
00254 }
00255 
00256 
00257 // Load a prebuilt set of mipmaps; assumes that the image contains
00258 // a complete set of mipmap levels.
00259 static void LoadMipmapSet(Image& img, GLenum target)
00260 {
00261     int internalFormat = getInternalFormat(img.getFormat());
00262 
00263     for (int mip = 0; mip < img.getMipLevelCount(); mip++)
00264     {
00265         uint mipWidth  = max((uint) img.getWidth() >> mip, 1u);
00266         uint mipHeight = max((uint) img.getHeight() >> mip, 1u);
00267             
00268         if (img.isCompressed())
00269         {
00270             glx::glCompressedTexImage2DARB(target,
00271                                            mip,
00272                                            internalFormat,
00273                                            mipWidth, mipHeight,
00274                                            0,
00275                                            img.getMipLevelSize(mip),
00276                                            img.getMipLevel(mip));
00277         }
00278         else
00279         {
00280             glTexImage2D(target,
00281                          mip,
00282                          internalFormat,
00283                          mipWidth, mipHeight,
00284                          0,
00285                          (GLenum) img.getFormat(),
00286                          GL_UNSIGNED_BYTE,
00287                          img.getMipLevel(mip));
00288         }
00289     }
00290 }
00291 
00292 
00293 // Load a texture without any mipmaps
00294 static void LoadMiplessTexture(Image& img, GLenum target)
00295 {
00296     int internalFormat = getInternalFormat(img.getFormat());
00297 
00298     if (img.isCompressed())
00299     {
00300         glx::glCompressedTexImage2DARB(target,
00301                                        0,
00302                                        internalFormat,
00303                                        img.getWidth(), img.getHeight(),
00304                                        0,
00305                                        img.getMipLevelSize(0),
00306                                        img.getMipLevel(0));
00307     }
00308     else
00309     {
00310         glTexImage2D(target,
00311                      0,
00312                      internalFormat,
00313                      img.getWidth(), img.getHeight(),
00314                      0,
00315                      (GLenum) img.getFormat(),
00316                      GL_UNSIGNED_BYTE,
00317                      img.getMipLevel(0));
00318     }
00319 }
00320 
00321 
00322 static int ilog2(unsigned int x)
00323 {
00324     int n = -1;
00325 
00326     while (x != 0)
00327     {
00328         x >>= 1;
00329         n++;
00330     }
00331 
00332     return n;
00333 }
00334 
00335 
00336 static int CalcMipLevelCount(int w, int h)
00337 {
00338     return max(ilog2(w), ilog2(h)) + 1;
00339 }
00340 
00341 
00342 Texture::Texture(int w, int h, int d) :
00343     width(w), height(h), depth(d), alpha(false)
00344 {
00345 }
00346 
00347 
00348 Texture::~Texture()
00349 {
00350 }
00351 
00352 
00353 int Texture::getLODCount() const
00354 {
00355     return 1;
00356 }
00357 
00358 
00359 int Texture::getUTileCount(int) const
00360 {
00361     return 1;
00362 }
00363 
00364 
00365 int Texture::getVTileCount(int) const
00366 {
00367     return 1;
00368 }
00369 
00370 
00371 int Texture::getWTileCount(int) const
00372 {
00373     return 1;
00374 }
00375 
00376 
00377 void Texture::setBorderColor(Color)
00378 {
00379 }
00380 
00381 
00382 int Texture::getWidth() const
00383 {
00384     return width;
00385 }
00386 
00387 
00388 int Texture::getHeight() const
00389 {
00390     return height;
00391 }
00392 
00393 
00394 int Texture::getDepth() const
00395 {
00396     return depth;
00397 }
00398 
00399 
00400 
00401 ImageTexture::ImageTexture(Image& img,
00402                            AddressMode addressMode,
00403                            MipMapMode mipMapMode) :
00404     Texture(img.getWidth(), img.getHeight()),
00405     glName(0)
00406 {
00407     glGenTextures(1, (GLuint*)&glName);
00408     glBindTexture(GL_TEXTURE_2D, glName);
00409 
00410     bool mipmap = mipMapMode == DefaultMipMaps;
00411     bool precomputedMipMaps = false;
00412 
00413     // Require a complete set of mipmaps
00414     int mipLevelCount = img.getMipLevelCount();
00415     if (mipmap &&
00416         mipLevelCount == CalcMipLevelCount(img.getWidth(), img.getHeight()))
00417         precomputedMipMaps = true;
00418 
00419     // We can't automatically generate mipmaps for compressed textures.
00420     // If a precomputed mipmap set isn't provided, turn of mipmapping entirely.
00421     if (!precomputedMipMaps && img.isCompressed())
00422         mipmap = false;
00423 
00424     GLenum texAddress = GetGLTexAddressMode(addressMode);
00425     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texAddress);
00426     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texAddress);
00427     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00428     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
00429                     mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
00430             
00431     if (mipMapMode == AutoMipMaps)
00432         glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
00433 
00434     int internalFormat = getInternalFormat(img.getFormat());
00435 
00436     if (mipmap)
00437     {
00438 #if 0
00439         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL,
00440                         maxMipMapLevel);
00441 #endif
00442         if (precomputedMipMaps)
00443         {
00444             LoadMipmapSet(img, GL_TEXTURE_2D);
00445         }
00446         else
00447         {
00448             gluBuild2DMipmaps(GL_TEXTURE_2D,
00449                               internalFormat,
00450                               getWidth(), getHeight(),
00451                               (GLenum) img.getFormat(),
00452                               GL_UNSIGNED_BYTE,
00453                               img.getPixels());
00454         }
00455     }
00456     else
00457     {
00458         LoadMiplessTexture(img, GL_TEXTURE_2D);
00459     }
00460 
00461     alpha = img.hasAlpha();
00462 }
00463 
00464 
00465 ImageTexture::~ImageTexture()
00466 {
00467     if (glName != 0)
00468         glDeleteTextures(1, (const GLuint*) &glName);
00469 }
00470 
00471 
00472 void ImageTexture::bind()
00473 {
00474     glBindTexture(GL_TEXTURE_2D, glName);
00475 }
00476 
00477 
00478 const TextureTile ImageTexture::getTile(int lod, int u, int v)
00479 {
00480     if (lod != 0 || u != 0 || v != 0)
00481         return TextureTile(0);
00482     else
00483         return TextureTile(glName);
00484 }
00485 
00486 
00487 unsigned int ImageTexture::getName() const
00488 {
00489     return glName;
00490 }
00491 
00492 
00493 void ImageTexture::setBorderColor(Color borderColor)
00494 {
00495     bind();
00496     SetBorderColor(borderColor, GL_TEXTURE_2D);
00497 }
00498 
00499 
00500 TiledTexture::TiledTexture(Image& img, 
00501                            int _uSplit, int _vSplit,
00502                            MipMapMode mipMapMode) :
00503     Texture(img.getWidth(), img.getHeight()),
00504     uSplit(_uSplit),
00505     vSplit(_vSplit),
00506     glNames(NULL)
00507 {
00508     glNames = new uint[uSplit * vSplit];
00509     {
00510         for (int i = 0; i < uSplit * vSplit; i++)
00511             glNames[i] = 0;
00512     }
00513 
00514     alpha = img.hasAlpha();
00515 
00516     bool mipmap = mipMapMode == DefaultMipMaps;
00517     bool precomputedMipMaps = false;
00518 
00519     // Require a complete set of mipmaps
00520     int mipLevelCount = img.getMipLevelCount();
00521     int completeMipCount = CalcMipLevelCount(img.getWidth(), img.getHeight());
00522     // Allow a bit of slack here--it turns out that some tools don't want to
00523     // calculate the 1x1 mip level.  Rather than turn off mipmaps, we'll just
00524     // point the 1x1 mip to the 2x1.
00525     if (mipmap && mipLevelCount >= completeMipCount - 1)
00526         precomputedMipMaps = true;
00527 
00528     // We can't automatically generate mipmaps for compressed textures.
00529     // If a precomputed mipmap set isn't provided, turn of mipmapping entirely.
00530     if (!precomputedMipMaps && img.isCompressed())
00531         mipmap = false;
00532 
00533     GLenum texAddress = GetGLTexAddressMode(EdgeClamp);
00534     int internalFormat = getInternalFormat(img.getFormat());
00535     int components = img.getComponents();
00536 
00537     // Create a temporary image which we'll use for the tile texels
00538     int tileWidth = img.getWidth() / uSplit;
00539     int tileHeight = img.getHeight() / vSplit;
00540     int tileMipLevelCount = CalcMipLevelCount(tileWidth, tileHeight);
00541     Image* tile = new Image(img.getFormat(),
00542                             tileWidth, tileHeight,
00543                             tileMipLevelCount);
00544     if (tile == NULL)
00545         return;
00546 
00547     for (int v = 0; v < vSplit; v++)
00548     {
00549         for (int u = 0; u < uSplit; u++)
00550         {
00551             // Create the texture and set up sampling and addressing
00552             glGenTextures(1, (GLuint*)&glNames[v * uSplit + u]);
00553             glBindTexture(GL_TEXTURE_2D, glNames[v * uSplit + u]);
00554 
00555             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texAddress);
00556             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texAddress);
00557             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00558             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
00559                             mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
00560 
00561             // Copy texels from the subtexture area to the pixel buffer.  This
00562             // is straightforward for normal textures, but an immense headache
00563             // for compressed textures with prebuilt mipmaps.
00564             if (precomputedMipMaps)
00565             {
00566                 if (img.isCompressed())
00567                 {
00568                     for (int mip = 0; mip < tileMipLevelCount; mip++)
00569                     {
00570                         int blockSize = getCompressedBlockSize(img.getFormat());
00571                         unsigned char* imgMip =
00572                             img.getMipLevel(min(mip, mipLevelCount));
00573                         uint mipWidth  = max((uint) img.getWidth() >> mip, 1u);
00574                         unsigned char* tileMip = tile->getMipLevel(mip);
00575                         uint tileMipWidth  = max((uint) tile->getWidth() >> mip, 1u);
00576                         uint tileMipHeight = max((uint) tile->getHeight() >> mip, 1u);
00577                         int uBlocks = max(tileMipWidth / 4, 1u);
00578                         int vBlocks = max(tileMipHeight / 4, 1u);
00579                         int destBytesPerRow = uBlocks * blockSize;
00580                         int srcBytesPerRow = max(mipWidth / 4, 1u) * blockSize;
00581                         int srcU = u * tileMipWidth / 4;
00582                         int srcV = v * tileMipHeight / 4;
00583                         int tileOffset = srcV * srcBytesPerRow +
00584                             srcU * blockSize;
00585 
00586                         for (int y = 0; y < vBlocks; y++)
00587                         {
00588                             memcpy(tileMip + y * destBytesPerRow,
00589                                    imgMip + tileOffset + y * srcBytesPerRow,
00590                                    destBytesPerRow);
00591                         }
00592                     }
00593                 }
00594                 else
00595                 {
00596                     // TODO: Handle uncompressed textures with prebuilt mipmaps
00597                 }
00598 
00599                 LoadMipmapSet(*tile, GL_TEXTURE_2D);
00600             }
00601             else
00602             {
00603                 if (img.isCompressed())
00604                 {
00605                     int blockSize = getCompressedBlockSize(img.getFormat());
00606                     int uBlocks = max(tileWidth / 4, 1);
00607                     int vBlocks = max(tileHeight / 4, 1);
00608                     int destBytesPerRow = uBlocks * blockSize;
00609                     int srcBytesPerRow = max(img.getWidth() / 4, 1) * blockSize;
00610                     int srcU = u * tileWidth / 4;
00611                     int srcV = v * tileHeight / 4;
00612                     int tileOffset = srcV * srcBytesPerRow +
00613                             srcU * blockSize;
00614 
00615                     for (int y = 0; y < vBlocks; y++)
00616                     {
00617                         memcpy(tile->getPixels() + y * destBytesPerRow,
00618                                img.getPixels() + tileOffset + y * srcBytesPerRow,
00619                                destBytesPerRow);
00620                     }
00621                 }
00622                 else
00623                 {
00624                     unsigned char* tilePixels = img.getPixels() +
00625                         (v * tileHeight * img.getWidth() + u * tileWidth) * components;
00626                     for (int y = 0; y < tileHeight; y++)
00627                     {
00628                         memcpy(tile->getPixels() + y * tileWidth * components,
00629                                tilePixels + y * img.getWidth() * components,
00630                                tileWidth * components);
00631                     }
00632                 }
00633 
00634                 if (mipmap)
00635                 {
00636                     gluBuild2DMipmaps(GL_TEXTURE_2D,
00637                                       internalFormat,
00638                                       tileWidth, tileHeight,
00639                                       (GLenum) tile->getFormat(),
00640                                       GL_UNSIGNED_BYTE,
00641                                       tile->getPixels());
00642                 }
00643                 else
00644                 {
00645                     LoadMiplessTexture(*tile, GL_TEXTURE_2D);
00646                 }
00647             }
00648         }
00649     }
00650             
00651     delete tile;
00652 }
00653 
00654 
00655 TiledTexture::~TiledTexture()
00656 {
00657     if (glNames != NULL)
00658     {
00659         for (int i = 0; i < uSplit * vSplit; i++)
00660         {
00661             if (glNames[i] != 0)
00662                 glDeleteTextures(1, (const GLuint*) &glNames[i]);
00663         }
00664         delete[] glNames;
00665     }
00666 }
00667 
00668 
00669 void TiledTexture::bind()
00670 {
00671 }
00672 
00673 
00674 void TiledTexture::setBorderColor(Color borderColor)
00675 {
00676     for (int i = 0; i < vSplit; i++)
00677     {
00678         for (int j = 0; j < uSplit; j++)
00679         {
00680             glBindTexture(GL_TEXTURE_2D, glNames[i * uSplit + j]);
00681             SetBorderColor(borderColor, GL_TEXTURE_2D);
00682         }
00683     }
00684 }
00685 
00686 
00687 int TiledTexture::getUTileCount(int) const
00688 {
00689     return uSplit;
00690 }
00691 
00692 
00693 int TiledTexture::getVTileCount(int) const
00694 {
00695     return vSplit;
00696 }
00697 
00698 
00699 const TextureTile TiledTexture::getTile(int lod, int u, int v)
00700 {
00701     if (lod != 0 || u >= uSplit || u < 0 || v >= vSplit || v < 0)
00702         return TextureTile(0); 
00703     else
00704     {
00705         return TextureTile(glNames[v * uSplit + u]);
00706     }
00707 }
00708 
00709 
00710 
00711 CubeMap::CubeMap(Image* faces[]) :
00712     Texture(faces[0]->getWidth(), faces[0]->getHeight()),
00713     glName(0)
00714 {
00715     // Verify that all the faces are square and have the same size
00716     int width = faces[0]->getWidth();
00717     int format = faces[0]->getFormat();
00718     int i = 0;
00719     for (i = 0; i < 6; i++)
00720     {
00721         if (faces[i]->getWidth() != width ||
00722             faces[i]->getHeight() != width ||
00723             faces[i]->getFormat() != format)
00724             return;
00725     }
00726 
00727     // For now, always enable mipmaps; in the future, it should be possible to
00728     // override this.
00729     bool mipmap = true;
00730     bool precomputedMipMaps = false;
00731 
00732     // Require a complete set of mipmaps
00733     int mipLevelCount = faces[0]->getMipLevelCount();
00734     if (mipmap && mipLevelCount == CalcMipLevelCount(width, width))
00735         precomputedMipMaps = true;
00736 
00737     // We can't automatically generate mipmaps for compressed textures.
00738     // If a precomputed mipmap set isn't provided, turn of mipmapping entirely.
00739     if (!precomputedMipMaps && faces[0]->isCompressed())
00740         mipmap = false;
00741 
00742     glGenTextures(1, (GLuint*)&glName);
00743     glBindTexture(GL_TEXTURE_CUBE_MAP_EXT, glName);
00744 
00745     glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
00746     glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
00747     glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00748     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
00749                     mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
00750 
00751     int internalFormat = getInternalFormat(format);
00752 
00753     for (i = 0; i < 6; i++)
00754     {
00755         GLenum targetFace = (GLenum) ((int) GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT + i);
00756         Image* face = faces[i];
00757 
00758         if (mipmap)
00759         {
00760             if (precomputedMipMaps)
00761             {
00762                 LoadMipmapSet(*face, targetFace);
00763             }
00764             else
00765             {
00766                 gluBuild2DMipmaps(targetFace,
00767                                   internalFormat,
00768                                   getWidth(), getHeight(),
00769                                   (GLenum) face->getFormat(),
00770                                   GL_UNSIGNED_BYTE,
00771                                   face->getPixels());
00772             }
00773         }
00774         else
00775         {
00776             LoadMiplessTexture(*face, targetFace);
00777         }
00778     }
00779 }
00780 
00781 
00782 CubeMap::~CubeMap()
00783 {
00784     if (glName != 0)
00785         glDeleteTextures(1, (const GLuint*) &glName);
00786 }
00787 
00788 
00789 void CubeMap::bind()
00790 {
00791     glBindTexture(GL_TEXTURE_CUBE_MAP_EXT, glName);
00792 }
00793 
00794 
00795 const TextureTile CubeMap::getTile(int lod, int u, int v)
00796 {
00797     if (lod != 0 || u != 0 || v != 0)
00798         return TextureTile(0);
00799     else
00800         return TextureTile(glName);
00801 }
00802 
00803 
00804 void CubeMap::setBorderColor(Color borderColor)
00805 {
00806     bind();
00807     SetBorderColor(borderColor, GL_TEXTURE_CUBE_MAP_EXT);
00808 }
00809 
00810 
00811 
00812 Texture* CreateProceduralTexture(int width, int height,
00813                                  int format,
00814                                  ProceduralTexEval func,
00815                                  Texture::AddressMode addressMode,
00816                                  Texture::MipMapMode mipMode)
00817 {
00818     Image* img = new Image(format, width, height);
00819     if (img == NULL)
00820         return NULL;
00821 
00822     for (int y = 0; y < height; y++)
00823     {
00824         for (int x = 0; x < width; x++)
00825         {
00826             float u = (float) x / (float) width * 2 - 1;
00827             float v = (float) y / (float) height * 2 - 1;
00828             func(u, v, 0, img->getPixelRow(y) + x * img->getComponents());
00829         }
00830     }
00831 
00832     Texture* tex = new ImageTexture(*img, addressMode, mipMode);
00833     delete img;
00834 
00835     return tex;
00836 }
00837 
00838 
00839 Texture* CreateProceduralTexture(int width, int height,
00840                                  int format,
00841                                  TexelFunctionObject& func,
00842                                  Texture::AddressMode addressMode,
00843                                  Texture::MipMapMode mipMode)
00844 {
00845     Image* img = new Image(format, width, height);
00846     if (img == NULL)
00847         return NULL;
00848 
00849     for (int y = 0; y < height; y++)
00850     {
00851         for (int x = 0; x < width; x++)
00852         {
00853             float u = (float) x / (float) width * 2 - 1;
00854             float v = (float) y / (float) height * 2 - 1;
00855             func(u, v, 0, img->getPixelRow(y) + x * img->getComponents());
00856         }
00857     }
00858 
00859     Texture* tex = new ImageTexture(*img, addressMode, mipMode);
00860     delete img;
00861 
00862     return tex;
00863 }
00864 
00865 
00866 // Helper function for CreateNormalizationCubeMap
00867 static Vec3f cubeVector(int face, float s, float t)
00868 {
00869     Vec3f v;
00870     switch (face)
00871     {
00872     case 0:
00873         v = Vec3f(1.0f, -t, -s);
00874         break;
00875     case 1:
00876         v = Vec3f(-1.0f, -t, s);
00877         break;
00878     case 2:
00879         v = Vec3f(s, 1.0f, t);
00880         break;
00881     case 3:
00882         v = Vec3f(s, -1.0f, -t);
00883         break;
00884     case 4:
00885         v = Vec3f(s, -t, 1.0f);
00886         break;
00887     case 5:
00888         v = Vec3f(-s, -t, -1.0f);
00889         break;
00890     default:
00891         // assert(false);
00892         break;
00893     }
00894 
00895     v.normalize();
00896 
00897     return v;
00898 }
00899 
00900 
00901 extern Texture* CreateProceduralCubeMap(int size, int format,
00902                                         ProceduralTexEval func)
00903 {
00904     Image* faces[6];
00905     bool failed = false;
00906 
00907     int i = 0;
00908     for (i = 0; i < 6; i++)
00909     {
00910         faces[i] = NULL;
00911         faces[i] = new Image(format, size, size);
00912         if (faces == NULL)
00913             failed = true;
00914     }
00915 
00916     if (!failed)
00917     {
00918         for (int i = 0; i < 6; i++)
00919         {
00920             Image* face = faces[i];
00921             for (int y = 0; y < size; y++)
00922             {
00923                 for (int x = 0; x < size; x++)
00924                 {
00925                     float s = ((float) x + 0.5f) / (float) size * 2 - 1;
00926                     float t = ((float) y + 0.5f) / (float) size * 2 - 1;
00927                     Vec3f v = cubeVector(i, s, t);
00928                     func(v.x, v.y, v.z,
00929                          face->getPixelRow(y) + x * face->getComponents());
00930                 }
00931             }
00932         }
00933     }
00934 
00935     Texture* tex = new CubeMap(faces);
00936 
00937     // Clean up the images
00938     for (i = 0; i < 6; i++)
00939     {
00940         if (faces[i] != NULL)
00941             delete faces[i];
00942     }
00943 
00944     return tex;
00945 }
00946 
00947 
00948 static bool isPow2(int x)
00949 {
00950     return ((x & (x - 1)) == 0);
00951 }
00952 
00953 
00954 static Texture* CreateTextureFromImage(Image& img,
00955                                        Texture::AddressMode addressMode,
00956                                        Texture::MipMapMode mipMode)
00957 {
00958     // Require texture dimensions to be powers of two.  Even though the
00959     // OpenGL driver will automatically rescale textures with non-power of
00960     // two sizes, some quality loss may result.  The power of two requirement
00961     // forces texture creators to resize their textures in an image editing
00962     // program, hopefully resulting in  textures that look as good as possible
00963     // when rendered on current hardware.
00964     if (!isPow2(img.getWidth()) || !isPow2(img.getHeight()))
00965     {
00966         clog << "Texture has non-power of two dimensions.\n";
00967         return NULL;
00968     }
00969 
00970     bool splittingAllowed = true;
00971     Texture* tex = NULL;
00972 
00973     int maxDim = GetTextureCaps().maxTextureSize;
00974     //int maxDim = 256;
00975     if ((img.getWidth() > maxDim || img.getHeight() > maxDim) &&
00976         splittingAllowed)
00977     {
00978         // The texture is too large; we need to split it.
00979         int uSplit = max(1, img.getWidth() / maxDim);
00980         int vSplit = max(1, img.getHeight() / maxDim);
00981         
00982         tex = new TiledTexture(img, uSplit, vSplit, mipMode);
00983     }
00984     else
00985     {
00986         // The image is small enough to fit in a single texture; or, splitting
00987         // was disallowed so we'll scale the large image down to fit in
00988         // an ordinary texture.
00989         tex = new ImageTexture(img, addressMode, mipMode);
00990     }
00991 
00992     return tex;
00993 }
00994 
00995 
00996 Texture* LoadTextureFromFile(const string& filename,
00997                              Texture::AddressMode addressMode,
00998                              Texture::MipMapMode mipMode)
00999 {
01000     // Check for a Celestia texture--these need to be handled specially.
01001     if (DetermineFileType(filename) == Content_CelestiaTexture)
01002         return LoadVirtualTexture(filename);
01003 
01004     // All other texture types are handled by first loading an image, then
01005     // creating a texture from that image.
01006     Image* img = LoadImageFromFile(filename);
01007     if (img == NULL)
01008         return NULL;
01009 
01010     Texture* tex = CreateTextureFromImage(*img, addressMode, mipMode);
01011     delete img;
01012 
01013     return tex;
01014 }
01015 
01016 
01017 // Load a height map texture from a file and convert it to a normal map.
01018 Texture* LoadHeightMapFromFile(const string& filename,
01019                                float height,
01020                                Texture::AddressMode addressMode)
01021 {
01022     Image* img = LoadImageFromFile(filename);
01023     if (img == NULL)
01024         return NULL;
01025     Image* normalMap = img->computeNormalMap(height,
01026                                              addressMode == Texture::Wrap);
01027     delete img;
01028     if (normalMap == NULL)
01029         return NULL;
01030 
01031     Texture* tex = CreateTextureFromImage(*normalMap, addressMode,
01032                                           Texture::DefaultMipMaps);
01033     delete normalMap;
01034 
01035     return tex;
01036 }

Generated on Sat Jan 14 22:30:29 2006 for Celestia by  doxygen 1.4.1