1 // texture.cpp
2 //
3 // Copyright (C) 2001-2003, Chris Laurel
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 
10 #ifndef TARGET_OS_MAC
11 #define JPEG_SUPPORT
12 #define PNG_SUPPORT
13 #endif
14 
15 #ifdef TARGET_OS_MAC
16 #include <unistd.h>
17 #include "CGBuffer.h"
18 #ifndef PNG_SUPPORT
19 #include <Quicktime/ImageCompression.h>
20 #include <QuickTime/QuickTimeComponents.h>
21 #endif
22 #endif
23 
24 #include <cmath>
25 #include <algorithm>
26 #include <iostream>
27 #include <fstream>
28 #include <cstdlib>
29 #include <cstdio>
30 #include <cstring>
31 #include <cassert>
32 
33 #ifndef _WIN32
34 #ifndef TARGET_OS_MAC
35 #include <config.h>
36 #endif /* ! TARGET_OS_MAC */
37 #endif /* ! _WIN32 */
38 
39 #include <celmath/vecmath.h>
40 #include <celutil/filetype.h>
41 #include <celutil/debug.h>
42 #include <celutil/util.h>
43 
44 #include "gl.h"
45 #include "glext.h"
46 #include "celestia.h"
47 
48 // OpenGL 1.2 stuff missing from Windows headers . . . probably should be
49 // moved into glext.h
50 #ifndef GL_TEXTURE_MAX_LEVEL
51 #define GL_TEXTURE_MAX_LEVEL 0x813D
52 #endif
53 
54 #ifdef JPEG_SUPPORT
55 
56 #ifndef PNG_SUPPORT
57 #include "setjmp.h"
58 #endif // PNG_SUPPORT
59 
60 extern "C" {
61 #ifdef _WIN32
62 #include "jpeglib.h"
63 #else
64 #include <jpeglib.h>
65 #endif
66 }
67 
68 #endif // JPEG_SUPPORT
69 
70 #ifdef PNG_SUPPORT // PNG_SUPPORT
71 #ifdef TARGET_OS_MAC
72 #include "../../macosx/png.h"
73 #else
74 #include "png.h"
75 #endif // TARGET_OS_MAC
76 
77 // Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng
78 #ifndef png_jmpbuf
79 #define png_jmpbuf(png_ptr) png_ptr->jmpbuf
80 #endif // PNG_SUPPORT
81 
82 // Define various expansion transformations for old versions of libpng
83 #if PNG_LIBPNG_VER < 10004
84 #define png_set_palette_to_rgb(p)  png_set_expand(p)
85 #define png_set_gray_1_2_4_to_8(p) png_set_expand(p)
86 #define png_set_tRNS_to_alpha(p)   png_set_expand(p)
87 #endif // PNG_LIBPNG_VER < 10004
88 
89 #endif // PNG_SUPPORT
90 
91 #include "texture.h"
92 #include "virtualtex.h"
93 
94 using namespace std;
95 
96 static bool texCapsInitialized = false;
97 
98 struct TextureCaps
99 {
100     bool compressionSupported;
101     bool clampToEdgeSupported;
102     bool clampToBorderSupported;
103     bool autoMipMapSupported;
104     bool maxLevelSupported;
105     GLint maxTextureSize;
106     bool nonPow2Supported;
107 };
108 
109 static TextureCaps texCaps;
110 
111 
testMaxLevel()112 static bool testMaxLevel()
113 {
114     unsigned char texels[64];
115 
116     glEnable(GL_TEXTURE_2D);
117     // Test whether GL_TEXTURE_MAX_LEVEL is supported . . .
118     glTexImage2D(GL_TEXTURE_2D,
119                  0,
120                  GL_LUMINANCE,
121                  8, 8,
122                  0,
123                  GL_LUMINANCE,
124                  GL_UNSIGNED_BYTE,
125                  texels);
126     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
127     float maxLev = -1.0f;
128     glGetTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, &maxLev);
129     glDisable(GL_TEXTURE_2D);
130 
131     return maxLev == 2;
132 }
133 
134 
GetTextureCaps()135 static const TextureCaps& GetTextureCaps()
136 {
137     if (!texCapsInitialized)
138     {
139         texCapsInitialized = true;
140         texCaps.compressionSupported = ExtensionSupported("GL_ARB_texture_compression");
141         if (texCaps.compressionSupported)
142             InitExtension("GL_ARB_texture_compression");
143 
144 #ifdef GL_VERSION_1_2
145         texCaps.clampToEdgeSupported = true;
146 #else
147         texCaps.clampToEdgeSupported = ExtensionSupported("GL_EXT_texture_edge_clamp");
148 #endif // GL_VERSION_1_2
149         texCaps.clampToBorderSupported = ExtensionSupported("GL_ARB_texture_border_clamp");
150         texCaps.autoMipMapSupported = ExtensionSupported("GL_SGIS_generate_mipmap");
151         texCaps.maxLevelSupported = testMaxLevel();
152         glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texCaps.maxTextureSize);
153         texCaps.nonPow2Supported = ExtensionSupported("GL_ARB_texture_non_power_of_two");
154     }
155 
156     return texCaps;
157 }
158 
159 
160 
getInternalFormat(int format)161 static int getInternalFormat(int format)
162 {
163     switch (format)
164     {
165     case GL_RGBA:
166     case GL_BGRA_EXT:
167         return 4;
168     case GL_RGB:
169     case GL_BGR_EXT:
170         return 3;
171     case GL_LUMINANCE_ALPHA:
172         return 2;
173     case GL_ALPHA:
174     case GL_INTENSITY:
175     case GL_LUMINANCE:
176         return 1;
177     case GL_DSDT_NV:
178         return format;
179     case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
180     case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
181     case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
182         return format;
183     default:
184         return 0;
185     }
186 }
187 
188 
189 #if 0
190 // Required in order to support on-the-fly compression; currently, this
191 // feature is disabled.
192 static int getCompressedInternalFormat(int format)
193 {
194     switch (format)
195     {
196     case GL_RGB:
197     case GL_BGR_EXT:
198         return GL_COMPRESSED_RGB_ARB;
199     case GL_RGBA:
200     case GL_BGRA_EXT:
201         return GL_COMPRESSED_RGBA_ARB;
202     case GL_ALPHA:
203         return GL_COMPRESSED_ALPHA_ARB;
204     case GL_LUMINANCE:
205         return GL_COMPRESSED_LUMINANCE_ARB;
206     case GL_LUMINANCE_ALPHA:
207         return GL_COMPRESSED_LUMINANCE_ALPHA_ARB;
208     case GL_INTENSITY:
209         return GL_COMPRESSED_INTENSITY_ARB;
210     case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
211     case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
212     case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
213         return format;
214     default:
215         return 0;
216     }
217 }
218 #endif
219 
220 
getCompressedBlockSize(int format)221 static int getCompressedBlockSize(int format)
222 {
223     if (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
224         return 8;
225     else
226         return 16;
227 }
228 
229 
GetGLTexAddressMode(Texture::AddressMode addressMode)230 static GLenum GetGLTexAddressMode(Texture::AddressMode addressMode)
231 {
232     const TextureCaps& caps = GetTextureCaps();
233 
234     switch (addressMode)
235     {
236     case Texture::Wrap:
237         return GL_REPEAT;
238 
239     case Texture::EdgeClamp:
240         return caps.clampToEdgeSupported ? GL_CLAMP_TO_EDGE : GL_CLAMP;
241 
242     case Texture::BorderClamp:
243         if (caps.clampToBorderSupported)
244             return GL_CLAMP_TO_BORDER_ARB;
245         else
246             return caps.clampToEdgeSupported ? GL_CLAMP_TO_EDGE : GL_CLAMP;
247     }
248 
249     return 0;
250 }
251 
252 
SetBorderColor(Color borderColor,GLenum target)253 static void SetBorderColor(Color borderColor, GLenum target)
254 {
255     float bc[4] = { borderColor.red(), borderColor.green(),
256                     borderColor.blue(), borderColor.alpha() };
257     glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, bc);
258 }
259 
260 
261 // Load a prebuilt set of mipmaps; assumes that the image contains
262 // a complete set of mipmap levels.
LoadMipmapSet(Image & img,GLenum target)263 static void LoadMipmapSet(Image& img, GLenum target)
264 {
265     int internalFormat = getInternalFormat(img.getFormat());
266 
267     for (int mip = 0; mip < img.getMipLevelCount(); mip++)
268     {
269         uint mipWidth  = max((uint) img.getWidth() >> mip, 1u);
270         uint mipHeight = max((uint) img.getHeight() >> mip, 1u);
271 
272         if (img.isCompressed())
273         {
274             glx::glCompressedTexImage2DARB(target,
275                                            mip,
276                                            internalFormat,
277                                            mipWidth, mipHeight,
278                                            0,
279                                            img.getMipLevelSize(mip),
280                                            img.getMipLevel(mip));
281         }
282         else
283         {
284             glTexImage2D(target,
285                          mip,
286                          internalFormat,
287                          mipWidth, mipHeight,
288                          0,
289                          (GLenum) img.getFormat(),
290                          GL_UNSIGNED_BYTE,
291                          img.getMipLevel(mip));
292         }
293     }
294 }
295 
296 
297 // Load a texture without any mipmaps
LoadMiplessTexture(Image & img,GLenum target)298 static void LoadMiplessTexture(Image& img, GLenum target)
299 {
300     int internalFormat = getInternalFormat(img.getFormat());
301 
302     if (img.isCompressed())
303     {
304         glx::glCompressedTexImage2DARB(target,
305                                        0,
306                                        internalFormat,
307                                        img.getWidth(), img.getHeight(),
308                                        0,
309                                        img.getMipLevelSize(0),
310                                        img.getMipLevel(0));
311     }
312     else
313     {
314         glTexImage2D(target,
315                      0,
316                      internalFormat,
317                      img.getWidth(), img.getHeight(),
318                      0,
319                      (GLenum) img.getFormat(),
320                      GL_UNSIGNED_BYTE,
321                      img.getMipLevel(0));
322     }
323 }
324 
325 
ilog2(unsigned int x)326 static int ilog2(unsigned int x)
327 {
328     int n = -1;
329 
330     while (x != 0)
331     {
332         x >>= 1;
333         n++;
334     }
335 
336     return n;
337 }
338 
339 
CalcMipLevelCount(int w,int h)340 static int CalcMipLevelCount(int w, int h)
341 {
342     return max(ilog2(w), ilog2(h)) + 1;
343 }
344 
345 
Texture(int w,int h,int d)346 Texture::Texture(int w, int h, int d) :
347     alpha(false),
348     compressed(false),
349     width(w),
350     height(h),
351     depth(d),
352     formatOptions(0)
353 {
354 }
355 
356 
~Texture()357 Texture::~Texture()
358 {
359 }
360 
361 
getLODCount() const362 int Texture::getLODCount() const
363 {
364     return 1;
365 }
366 
367 
getUTileCount(int) const368 int Texture::getUTileCount(int) const
369 {
370     return 1;
371 }
372 
373 
getVTileCount(int) const374 int Texture::getVTileCount(int) const
375 {
376     return 1;
377 }
378 
379 
getWTileCount(int) const380 int Texture::getWTileCount(int) const
381 {
382     return 1;
383 }
384 
385 
setBorderColor(Color)386 void Texture::setBorderColor(Color)
387 {
388 }
389 
390 
getWidth() const391 int Texture::getWidth() const
392 {
393     return width;
394 }
395 
396 
getHeight() const397 int Texture::getHeight() const
398 {
399     return height;
400 }
401 
402 
getDepth() const403 int Texture::getDepth() const
404 {
405     return depth;
406 }
407 
408 
getFormatOptions() const409 unsigned int Texture::getFormatOptions() const
410 {
411     return formatOptions;
412 }
413 
414 
setFormatOptions(unsigned int opts)415 void Texture::setFormatOptions(unsigned int opts)
416 {
417     formatOptions = opts;
418 }
419 
420 
ImageTexture(Image & img,AddressMode addressMode,MipMapMode mipMapMode)421 ImageTexture::ImageTexture(Image& img,
422                            AddressMode addressMode,
423                            MipMapMode mipMapMode) :
424     Texture(img.getWidth(), img.getHeight()),
425     glName(0)
426 {
427     glGenTextures(1, (GLuint*) &glName);
428     glBindTexture(GL_TEXTURE_2D, glName);
429 
430 
431     bool mipmap = mipMapMode != NoMipMaps;
432     bool precomputedMipMaps = false;
433 
434     // Use precomputed mipmaps only if a complete set is supplied
435     int mipLevelCount = img.getMipLevelCount();
436     if (mipmap && mipLevelCount == CalcMipLevelCount(img.getWidth(), img.getHeight()))
437     {
438         precomputedMipMaps = true;
439     }
440 
441     // We can't automatically generate mipmaps for compressed textures.
442     // If a precomputed mipmap set isn't provided, turn off mipmapping entirely.
443     if (!precomputedMipMaps && img.isCompressed())
444     {
445         mipmap = false;
446     }
447 
448     GLenum texAddress = GetGLTexAddressMode(addressMode);
449     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texAddress);
450     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texAddress);
451     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
452     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
453                     mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
454 
455     if (mipMapMode == AutoMipMaps)
456         glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
457 
458     int internalFormat = getInternalFormat(img.getFormat());
459 
460     if (mipmap)
461     {
462         if (precomputedMipMaps)
463         {
464             LoadMipmapSet(img, GL_TEXTURE_2D);
465         }
466         else if (mipMapMode == DefaultMipMaps)
467         {
468             gluBuild2DMipmaps(GL_TEXTURE_2D,
469                               internalFormat,
470                               getWidth(), getHeight(),
471                               (GLenum) img.getFormat(),
472                               GL_UNSIGNED_BYTE,
473                               img.getPixels());
474         }
475         else
476         {
477             assert(mipMapMode == AutoMipMaps);
478             LoadMiplessTexture(img, GL_TEXTURE_2D);
479         }
480     }
481     else
482     {
483         LoadMiplessTexture(img, GL_TEXTURE_2D);
484     }
485 
486     alpha = img.hasAlpha();
487     compressed = img.isCompressed();
488 }
489 
490 
~ImageTexture()491 ImageTexture::~ImageTexture()
492 {
493     if (glName != 0)
494         glDeleteTextures(1, (const GLuint*) &glName);
495 }
496 
497 
bind()498 void ImageTexture::bind()
499 {
500     glBindTexture(GL_TEXTURE_2D, glName);
501 }
502 
503 
getTile(int lod,int u,int v)504 const TextureTile ImageTexture::getTile(int lod, int u, int v)
505 {
506     if (lod != 0 || u != 0 || v != 0)
507         return TextureTile(0);
508     else
509         return TextureTile(glName);
510 }
511 
512 
getName() const513 unsigned int ImageTexture::getName() const
514 {
515     return glName;
516 }
517 
518 
setBorderColor(Color borderColor)519 void ImageTexture::setBorderColor(Color borderColor)
520 {
521     bind();
522     SetBorderColor(borderColor, GL_TEXTURE_2D);
523 }
524 
525 
TiledTexture(Image & img,int _uSplit,int _vSplit,MipMapMode mipMapMode)526 TiledTexture::TiledTexture(Image& img,
527                            int _uSplit, int _vSplit,
528                            MipMapMode mipMapMode) :
529     Texture(img.getWidth(), img.getHeight()),
530     uSplit(_uSplit),
531     vSplit(_vSplit),
532     glNames(NULL)
533 {
534     glNames = new uint[uSplit * vSplit];
535     {
536         for (int i = 0; i < uSplit * vSplit; i++)
537             glNames[i] = 0;
538     }
539 
540     alpha = img.hasAlpha();
541     compressed = img.isCompressed();
542 
543     bool mipmap = mipMapMode != NoMipMaps;
544     bool precomputedMipMaps = false;
545 
546     // Require a complete set of mipmaps
547     int mipLevelCount = img.getMipLevelCount();
548     int completeMipCount = CalcMipLevelCount(img.getWidth(), img.getHeight());
549     // Allow a bit of slack here--it turns out that some tools don't want to
550     // calculate the 1x1 mip level.  Rather than turn off mipmaps, we'll just
551     // point the 1x1 mip to the 2x1.
552     if (mipmap && mipLevelCount >= completeMipCount - 1)
553         precomputedMipMaps = true;
554 
555     // We can't automatically generate mipmaps for compressed textures.
556     // If a precomputed mipmap set isn't provided, turn of mipmapping entirely.
557     if (!precomputedMipMaps && img.isCompressed())
558         mipmap = false;
559 
560     GLenum texAddress = GetGLTexAddressMode(EdgeClamp);
561     int internalFormat = getInternalFormat(img.getFormat());
562     int components = img.getComponents();
563 
564     // Create a temporary image which we'll use for the tile texels
565     int tileWidth = img.getWidth() / uSplit;
566     int tileHeight = img.getHeight() / vSplit;
567     int tileMipLevelCount = CalcMipLevelCount(tileWidth, tileHeight);
568     Image* tile = new Image(img.getFormat(),
569                             tileWidth, tileHeight,
570                             tileMipLevelCount);
571     if (tile == NULL)
572         return;
573 
574     for (int v = 0; v < vSplit; v++)
575     {
576         for (int u = 0; u < uSplit; u++)
577         {
578             // Create the texture and set up sampling and addressing
579             glGenTextures(1, (GLuint*)&glNames[v * uSplit + u]);
580             glBindTexture(GL_TEXTURE_2D, glNames[v * uSplit + u]);
581 
582             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texAddress);
583             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texAddress);
584             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
585             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
586                             mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
587 
588             // Copy texels from the subtexture area to the pixel buffer.  This
589             // is straightforward for normal textures, but an immense headache
590             // for compressed textures with prebuilt mipmaps.
591             if (precomputedMipMaps)
592             {
593                 if (img.isCompressed())
594                 {
595                     for (int mip = 0; mip < tileMipLevelCount; mip++)
596                     {
597                         int blockSize = getCompressedBlockSize(img.getFormat());
598                         unsigned char* imgMip =
599                             img.getMipLevel(min(mip, mipLevelCount));
600                         uint mipWidth  = max((uint) img.getWidth() >> mip, 1u);
601                         unsigned char* tileMip = tile->getMipLevel(mip);
602                         uint tileMipWidth  = max((uint) tile->getWidth() >> mip, 1u);
603                         uint tileMipHeight = max((uint) tile->getHeight() >> mip, 1u);
604                         int uBlocks = max(tileMipWidth / 4, 1u);
605                         int vBlocks = max(tileMipHeight / 4, 1u);
606                         int destBytesPerRow = uBlocks * blockSize;
607                         int srcBytesPerRow = max(mipWidth / 4, 1u) * blockSize;
608                         int srcU = u * tileMipWidth / 4;
609                         int srcV = v * tileMipHeight / 4;
610                         int tileOffset = srcV * srcBytesPerRow +
611                             srcU * blockSize;
612 
613                         for (int y = 0; y < vBlocks; y++)
614                         {
615                             memcpy(tileMip + y * destBytesPerRow,
616                                    imgMip + tileOffset + y * srcBytesPerRow,
617                                    destBytesPerRow);
618                         }
619                     }
620                 }
621                 else
622                 {
623                     // TODO: Handle uncompressed textures with prebuilt mipmaps
624                 }
625 
626                 LoadMipmapSet(*tile, GL_TEXTURE_2D);
627             }
628             else
629             {
630                 if (img.isCompressed())
631                 {
632                     int blockSize = getCompressedBlockSize(img.getFormat());
633                     int uBlocks = max(tileWidth / 4, 1);
634                     int vBlocks = max(tileHeight / 4, 1);
635                     int destBytesPerRow = uBlocks * blockSize;
636                     int srcBytesPerRow = max(img.getWidth() / 4, 1) * blockSize;
637                     int srcU = u * tileWidth / 4;
638                     int srcV = v * tileHeight / 4;
639                     int tileOffset = srcV * srcBytesPerRow +
640                             srcU * blockSize;
641 
642                     for (int y = 0; y < vBlocks; y++)
643                     {
644                         memcpy(tile->getPixels() + y * destBytesPerRow,
645                                img.getPixels() + tileOffset + y * srcBytesPerRow,
646                                destBytesPerRow);
647                     }
648                 }
649                 else
650                 {
651                     unsigned char* tilePixels = img.getPixels() +
652                         (v * tileHeight * img.getWidth() + u * tileWidth) * components;
653                     for (int y = 0; y < tileHeight; y++)
654                     {
655                         memcpy(tile->getPixels() + y * tileWidth * components,
656                                tilePixels + y * img.getWidth() * components,
657                                tileWidth * components);
658                     }
659                 }
660 
661                 if (mipmap)
662                 {
663                     gluBuild2DMipmaps(GL_TEXTURE_2D,
664                                       internalFormat,
665                                       tileWidth, tileHeight,
666                                       (GLenum) tile->getFormat(),
667                                       GL_UNSIGNED_BYTE,
668                                       tile->getPixels());
669                 }
670                 else
671                 {
672                     LoadMiplessTexture(*tile, GL_TEXTURE_2D);
673                 }
674             }
675         }
676     }
677 
678     delete tile;
679 }
680 
681 
~TiledTexture()682 TiledTexture::~TiledTexture()
683 {
684     if (glNames != NULL)
685     {
686         for (int i = 0; i < uSplit * vSplit; i++)
687         {
688             if (glNames[i] != 0)
689                 glDeleteTextures(1, (const GLuint*) &glNames[i]);
690         }
691         delete[] glNames;
692     }
693 }
694 
695 
bind()696 void TiledTexture::bind()
697 {
698 }
699 
700 
setBorderColor(Color borderColor)701 void TiledTexture::setBorderColor(Color borderColor)
702 {
703     for (int i = 0; i < vSplit; i++)
704     {
705         for (int j = 0; j < uSplit; j++)
706         {
707             glBindTexture(GL_TEXTURE_2D, glNames[i * uSplit + j]);
708             SetBorderColor(borderColor, GL_TEXTURE_2D);
709         }
710     }
711 }
712 
713 
getUTileCount(int) const714 int TiledTexture::getUTileCount(int) const
715 {
716     return uSplit;
717 }
718 
719 
getVTileCount(int) const720 int TiledTexture::getVTileCount(int) const
721 {
722     return vSplit;
723 }
724 
725 
getTile(int lod,int u,int v)726 const TextureTile TiledTexture::getTile(int lod, int u, int v)
727 {
728     if (lod != 0 || u >= uSplit || u < 0 || v >= vSplit || v < 0)
729         return TextureTile(0);
730     else
731     {
732         return TextureTile(glNames[v * uSplit + u]);
733     }
734 }
735 
736 
737 
CubeMap(Image * faces[])738 CubeMap::CubeMap(Image* faces[]) :
739     Texture(faces[0]->getWidth(), faces[0]->getHeight()),
740     glName(0)
741 {
742     // Verify that all the faces are square and have the same size
743     int width = faces[0]->getWidth();
744     int format = faces[0]->getFormat();
745     int i = 0;
746     for (i = 0; i < 6; i++)
747     {
748         if (faces[i]->getWidth() != width ||
749             faces[i]->getHeight() != width ||
750             faces[i]->getFormat() != format)
751             return;
752     }
753 
754     // For now, always enable mipmaps; in the future, it should be possible to
755     // override this.
756     bool mipmap = true;
757     bool precomputedMipMaps = false;
758 
759     // Require a complete set of mipmaps
760     int mipLevelCount = faces[0]->getMipLevelCount();
761     if (mipmap && mipLevelCount == CalcMipLevelCount(width, width))
762         precomputedMipMaps = true;
763 
764     // We can't automatically generate mipmaps for compressed textures.
765     // If a precomputed mipmap set isn't provided, turn of mipmapping entirely.
766     if (!precomputedMipMaps && faces[0]->isCompressed())
767         mipmap = false;
768 
769     glGenTextures(1, (GLuint*) &glName);
770     glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, glName);
771 
772     glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
773     glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
774     glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
775     glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER,
776                     mipmap ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
777 
778     int internalFormat = getInternalFormat(format);
779 
780     for (i = 0; i < 6; i++)
781     {
782         GLenum targetFace = (GLenum) ((int) GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + i);
783         Image* face = faces[i];
784 
785         if (mipmap)
786         {
787             if (precomputedMipMaps)
788             {
789                 LoadMipmapSet(*face, targetFace);
790             }
791             else
792             {
793                 gluBuild2DMipmaps(targetFace,
794                                   internalFormat,
795                                   getWidth(), getHeight(),
796                                   (GLenum) face->getFormat(),
797                                   GL_UNSIGNED_BYTE,
798                                   face->getPixels());
799             }
800         }
801         else
802         {
803             LoadMiplessTexture(*face, targetFace);
804         }
805     }
806 }
807 
808 
~CubeMap()809 CubeMap::~CubeMap()
810 {
811     if (glName != 0)
812         glDeleteTextures(1, (const GLuint*) &glName);
813 }
814 
815 
bind()816 void CubeMap::bind()
817 {
818     glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, glName);
819 }
820 
821 
getTile(int lod,int u,int v)822 const TextureTile CubeMap::getTile(int lod, int u, int v)
823 {
824     if (lod != 0 || u != 0 || v != 0)
825         return TextureTile(0);
826     else
827         return TextureTile(glName);
828 }
829 
830 
setBorderColor(Color borderColor)831 void CubeMap::setBorderColor(Color borderColor)
832 {
833     bind();
834     SetBorderColor(borderColor, GL_TEXTURE_CUBE_MAP_ARB);
835 }
836 
837 
838 
CreateProceduralTexture(int width,int height,int format,ProceduralTexEval func,Texture::AddressMode addressMode,Texture::MipMapMode mipMode)839 Texture* CreateProceduralTexture(int width, int height,
840                                  int format,
841                                  ProceduralTexEval func,
842                                  Texture::AddressMode addressMode,
843                                  Texture::MipMapMode mipMode)
844 {
845     Image* img = new Image(format, width, height);
846     if (img == NULL)
847         return NULL;
848 
849     for (int y = 0; y < height; y++)
850     {
851         for (int x = 0; x < width; x++)
852         {
853             float u = ((float) x + 0.5f) / (float) width * 2 - 1;
854             float v = ((float) y + 0.5f) / (float) height * 2 - 1;
855             func(u, v, 0, img->getPixelRow(y) + x * img->getComponents());
856         }
857     }
858 
859     Texture* tex = new ImageTexture(*img, addressMode, mipMode);
860     delete img;
861 
862     return tex;
863 }
864 
865 
CreateProceduralTexture(int width,int height,int format,TexelFunctionObject & func,Texture::AddressMode addressMode,Texture::MipMapMode mipMode)866 Texture* CreateProceduralTexture(int width, int height,
867                                  int format,
868                                  TexelFunctionObject& func,
869                                  Texture::AddressMode addressMode,
870                                  Texture::MipMapMode mipMode)
871 {
872     Image* img = new Image(format, width, height);
873     if (img == NULL)
874         return NULL;
875 
876     for (int y = 0; y < height; y++)
877     {
878         for (int x = 0; x < width; x++)
879         {
880             float u = ((float) x + 0.5f) / (float) width * 2 - 1;
881             float v = ((float) y + 0.5f) / (float) height * 2 - 1;
882             func(u, v, 0, img->getPixelRow(y) + x * img->getComponents());
883         }
884     }
885 
886     Texture* tex = new ImageTexture(*img, addressMode, mipMode);
887     delete img;
888 
889     return tex;
890 }
891 
892 
893 // Helper function for CreateProceduralCubeMap; return the normalized
894 // vector pointing to (s, t) on the specified face.
cubeVector(int face,float s,float t)895 static Vec3f cubeVector(int face, float s, float t)
896 {
897     Vec3f v;
898     switch (face)
899     {
900     case 0:
901         v = Vec3f(1.0f, -t, -s);
902         break;
903     case 1:
904         v = Vec3f(-1.0f, -t, s);
905         break;
906     case 2:
907         v = Vec3f(s, 1.0f, t);
908         break;
909     case 3:
910         v = Vec3f(s, -1.0f, -t);
911         break;
912     case 4:
913         v = Vec3f(s, -t, 1.0f);
914         break;
915     case 5:
916         v = Vec3f(-s, -t, -1.0f);
917         break;
918     default:
919         // assert(false);
920         break;
921     }
922 
923     v.normalize();
924 
925     return v;
926 }
927 
928 
CreateProceduralCubeMap(int size,int format,ProceduralTexEval func)929 extern Texture* CreateProceduralCubeMap(int size, int format,
930                                         ProceduralTexEval func)
931 {
932     Image* faces[6];
933     bool failed = false;
934 
935     int i = 0;
936     for (i = 0; i < 6; i++)
937     {
938         faces[i] = NULL;
939         faces[i] = new Image(format, size, size);
940         if (faces == NULL)
941             failed = true;
942     }
943 
944     if (!failed)
945     {
946         for (int i = 0; i < 6; i++)
947         {
948             Image* face = faces[i];
949             for (int y = 0; y < size; y++)
950             {
951                 for (int x = 0; x < size; x++)
952                 {
953                     float s = ((float) x + 0.5f) / (float) size * 2 - 1;
954                     float t = ((float) y + 0.5f) / (float) size * 2 - 1;
955                     Vec3f v = cubeVector(i, s, t);
956                     func(v.x, v.y, v.z,
957                          face->getPixelRow(y) + x * face->getComponents());
958                 }
959             }
960         }
961     }
962 
963     Texture* tex = new CubeMap(faces);
964 
965     // Clean up the images
966     for (i = 0; i < 6; i++)
967     {
968         if (faces[i] != NULL)
969             delete faces[i];
970     }
971 
972     return tex;
973 }
974 
975 #if 0
976 static bool isPow2(int x)
977 {
978     return ((x & (x - 1)) == 0);
979 }
980 #endif
981 
CreateTextureFromImage(Image & img,Texture::AddressMode addressMode,Texture::MipMapMode mipMode)982 static Texture* CreateTextureFromImage(Image& img,
983                                        Texture::AddressMode addressMode,
984                                        Texture::MipMapMode mipMode)
985 {
986 #if 0
987     // Require texture dimensions to be powers of two.  Even though the
988     // OpenGL driver will automatically rescale textures with non-power of
989     // two sizes, some quality loss may result.  The power of two requirement
990     // forces texture creators to resize their textures in an image editing
991     // program, hopefully resulting in  textures that look as good as possible
992     // when rendered on current hardware.
993     if (!isPow2(img.getWidth()) || !isPow2(img.getHeight()))
994     {
995         clog << "Texture has non-power of two dimensions.\n";
996         return NULL;
997     }
998 #endif
999 
1000     // If non power of two textures are supported switch mipmap generation to
1001     // automatic. gluBuildMipMaps may rescale the texture to a power of two
1002     // on some drivers even when the hardware supports non power of two textures,
1003     // whereas auto mipmap generation will properly deal with the dimensions.
1004     if (GetTextureCaps().nonPow2Supported)
1005     {
1006         if (mipMode == Texture::DefaultMipMaps)
1007             mipMode = Texture::AutoMipMaps;
1008     }
1009 
1010     bool splittingAllowed = true;
1011     Texture* tex = NULL;
1012 
1013     int maxDim = GetTextureCaps().maxTextureSize;
1014     if ((img.getWidth() > maxDim || img.getHeight() > maxDim) &&
1015         splittingAllowed)
1016     {
1017         // The texture is too large; we need to split it.
1018         int uSplit = max(1, img.getWidth() / maxDim);
1019         int vSplit = max(1, img.getHeight() / maxDim);
1020         clog << _("Creating tiled texture. Width=") << img.getWidth() << _(", max=") << maxDim << "\n";
1021         tex = new TiledTexture(img, uSplit, vSplit, mipMode);
1022     }
1023     else
1024     {
1025         clog << _("Creating ordinary texture: ") << img.getWidth() << "x" << img.getHeight() << "\n";
1026         // The image is small enough to fit in a single texture; or, splitting
1027         // was disallowed so we'll scale the large image down to fit in
1028         // an ordinary texture.
1029         tex = new ImageTexture(img, addressMode, mipMode);
1030     }
1031 
1032     return tex;
1033 }
1034 
1035 
LoadTextureFromFile(const string & filename,Texture::AddressMode addressMode,Texture::MipMapMode mipMode)1036 Texture* LoadTextureFromFile(const string& filename,
1037                              Texture::AddressMode addressMode,
1038                              Texture::MipMapMode mipMode)
1039 {
1040     // Check for a Celestia texture--these need to be handled specially.
1041     ContentType contentType = DetermineFileType(filename);
1042 
1043     if (contentType == Content_CelestiaTexture)
1044         return LoadVirtualTexture(filename);
1045 
1046     // All other texture types are handled by first loading an image, then
1047     // creating a texture from that image.
1048     Image* img = LoadImageFromFile(filename);
1049     if (img == NULL)
1050         return NULL;
1051 
1052     Texture* tex = CreateTextureFromImage(*img, addressMode, mipMode);
1053 
1054     if (contentType == Content_DXT5NormalMap)
1055     {
1056         // If the texture came from a .dxt5nm file then mark it as a dxt5
1057         // compressed normal map. There's no separate OpenGL format for dxt5
1058         // normal maps, so the file extension is the only thing that
1059         // distinguishes it from a plain old dxt5 texture.
1060         if (img->getFormat() == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
1061         {
1062             tex->setFormatOptions(Texture::DXT5NormalMap);
1063         }
1064     }
1065 
1066     delete img;
1067 
1068     return tex;
1069 }
1070 
1071 
1072 // Load a height map texture from a file and convert it to a normal map.
LoadHeightMapFromFile(const string & filename,float height,Texture::AddressMode addressMode)1073 Texture* LoadHeightMapFromFile(const string& filename,
1074                                float height,
1075                                Texture::AddressMode addressMode)
1076 {
1077     Image* img = LoadImageFromFile(filename);
1078     if (img == NULL)
1079         return NULL;
1080     Image* normalMap = img->computeNormalMap(height,
1081                                              addressMode == Texture::Wrap);
1082     delete img;
1083     if (normalMap == NULL)
1084         return NULL;
1085 
1086     Texture* tex = CreateTextureFromImage(*normalMap, addressMode,
1087                                           Texture::DefaultMipMaps);
1088     delete normalMap;
1089 
1090     return tex;
1091 }
1092