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