]> icculus.org git repositories - divverent/netradiant.git/blob - radiant/textures.cpp
add a "todo item" directory containing VorteX's patches
[divverent/netradiant.git] / radiant / textures.cpp
1 /*
2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #include "textures.h"
23
24 #include "debugging/debugging.h"
25 #include "warnings.h"
26
27 #include "itextures.h"
28 #include "igl.h"
29 #include "preferencesystem.h"
30 #include "qgl.h"
31
32 #include "texturelib.h"
33 #include "container/hashfunc.h"
34 #include "container/cache.h"
35 #include "generic/callback.h"
36 #include "stringio.h"
37
38 #include "image.h"
39 #include "texmanip.h"
40 #include "preferences.h"
41
42
43
44 enum ETexturesMode
45 {
46   eTextures_NEAREST = 0,
47   eTextures_NEAREST_MIPMAP_NEAREST = 1,
48   eTextures_NEAREST_MIPMAP_LINEAR = 2,
49   eTextures_LINEAR = 3,
50   eTextures_LINEAR_MIPMAP_NEAREST = 4,
51   eTextures_LINEAR_MIPMAP_LINEAR = 5,
52   eTextures_MAX_ANISOTROPY = 6,
53 };
54
55 enum TextureCompressionFormat
56 {
57   TEXTURECOMPRESSION_NONE = 0,
58   TEXTURECOMPRESSION_RGBA = 1,
59   TEXTURECOMPRESSION_RGBA_S3TC_DXT1 = 2,
60   TEXTURECOMPRESSION_RGBA_S3TC_DXT3 = 3,
61   TEXTURECOMPRESSION_RGBA_S3TC_DXT5 = 4,
62 };
63
64 struct texture_globals_t
65 {
66   // RIANT
67   // texture compression format
68   TextureCompressionFormat m_nTextureCompressionFormat;
69
70   float fGamma;
71
72   bool bTextureCompressionSupported; // is texture compression supported by hardware?
73   GLint texture_components;
74
75   // temporary values that should be initialised only once at run-time
76   bool m_bOpenGLCompressionSupported;
77   bool m_bS3CompressionSupported;
78
79   texture_globals_t(GLint components) :
80     m_nTextureCompressionFormat(TEXTURECOMPRESSION_NONE),
81     fGamma(1.0f),
82     bTextureCompressionSupported(false),
83     texture_components(components),
84     m_bOpenGLCompressionSupported(false),
85     m_bS3CompressionSupported(false)
86   {
87   }
88 };
89
90 texture_globals_t g_texture_globals(GL_RGBA);
91
92 void SetTexParameters(ETexturesMode mode)
93 {
94         float maxAniso = QGL_maxTextureAnisotropy();
95         if(maxAniso > 1)
96                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
97         else
98                 if(mode == eTextures_MAX_ANISOTROPY)
99                         mode = eTextures_LINEAR_MIPMAP_LINEAR;
100
101   switch (mode)
102   {
103   case eTextures_NEAREST:
104     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
105     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
106     break;
107   case eTextures_NEAREST_MIPMAP_NEAREST:
108     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
109     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
110     break;
111   case eTextures_NEAREST_MIPMAP_LINEAR:
112     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR );
113     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
114     break;
115   case eTextures_LINEAR:
116     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
117     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
118     break;
119   case eTextures_LINEAR_MIPMAP_NEAREST:
120     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
121     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
122     break;
123   case eTextures_LINEAR_MIPMAP_LINEAR:
124     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
125     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
126     break;
127   case eTextures_MAX_ANISOTROPY:
128     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAniso);
129     break;
130   default:
131     globalOutputStream() << "invalid texture mode\n";
132   }
133 }
134
135 ETexturesMode g_texture_mode = eTextures_LINEAR_MIPMAP_LINEAR;
136
137
138
139
140 byte g_gammatable[256];
141 void ResampleGamma(float fGamma)
142 {
143   int i,inf;
144   if (fGamma == 1.0)
145   {
146     for (i = 0; i < 256; i++)
147       g_gammatable[i] = i;
148   } else
149   {
150     for (i = 0; i < 256; i++)
151     {
152       inf = (int)(255 * pow ( static_cast<double>((i + 0.5) / 255.5) , static_cast<double>(fGamma) ) + 0.5);
153       if (inf < 0)
154         inf = 0;
155       if (inf > 255)
156         inf = 255;
157       g_gammatable[i] = inf;
158     }
159   }
160 }
161
162 inline const int& min_int(const int& left, const int& right)
163 {
164   return std::min(left, right);
165 }
166
167 int max_tex_size = 0;
168 const int max_texture_quality = 3;
169 LatchedInt g_Textures_textureQuality(3, "Texture Quality");
170
171 /// \brief This function does the actual processing of raw RGBA data into a GL texture.
172 /// It will also resample to power-of-two dimensions, generate the mipmaps and adjust gamma.
173 void LoadTextureRGBA(qtexture_t* q, unsigned char* pPixels, int nWidth, int nHeight)
174 {
175   static float fGamma = -1;
176   float total[3];
177   byte  *outpixels = 0;
178   int   nCount = nWidth * nHeight;
179
180   if (fGamma != g_texture_globals.fGamma)
181   {
182     fGamma = g_texture_globals.fGamma;
183     ResampleGamma(fGamma);
184   }
185
186   q->width = nWidth;
187   q->height = nHeight;
188
189   total[0] = total[1] = total[2] = 0.0f;
190
191   // resample texture gamma according to user settings
192   for (int i = 0; i < (nCount * 4); i += 4)
193   {
194     for (int j = 0; j < 3; j++)
195     {
196       total[j] += (pPixels + i)[j];
197       byte b = (pPixels + i)[j];
198       (pPixels + i)[j] = g_gammatable[b];
199     }
200   }
201
202   q->color[0] = total[0] / (nCount * 255);
203   q->color[1] = total[1] / (nCount * 255);
204   q->color[2] = total[2] / (nCount * 255);
205
206   glGenTextures (1, &q->texture_number);
207
208   glBindTexture( GL_TEXTURE_2D, q->texture_number );
209
210   SetTexParameters(g_texture_mode);
211
212   int gl_width = 1;
213   while(gl_width < nWidth)
214     gl_width <<= 1;
215
216   int gl_height = 1;
217   while(gl_height < nHeight)
218     gl_height <<= 1;
219
220   bool resampled = false;
221   if (!(gl_width == nWidth && gl_height == nHeight))
222   {
223     resampled = true;
224     outpixels = (byte *)malloc(gl_width * gl_height * 4);
225     R_ResampleTexture(pPixels, nWidth, nHeight, outpixels, gl_width, gl_height, 4);
226   }
227   else
228   {
229     outpixels = pPixels;
230   }
231
232   int quality_reduction = max_texture_quality - g_Textures_textureQuality.m_value;
233   int target_width = min_int(gl_width >> quality_reduction, max_tex_size);
234   int target_height = min_int(gl_height >> quality_reduction, max_tex_size);
235
236   while (gl_width > target_width || gl_height > target_height)
237   {
238     GL_MipReduce(outpixels, outpixels, gl_width, gl_height, target_width, target_height);
239
240     if (gl_width > target_width)
241       gl_width >>= 1;
242     if (gl_height > target_height)
243       gl_height >>= 1;
244   }
245
246   int mip = 0;
247   glTexImage2D(GL_TEXTURE_2D, mip++, g_texture_globals.texture_components, gl_width, gl_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, outpixels);
248   while (gl_width > 1 || gl_height > 1)
249   {
250     GL_MipReduce(outpixels, outpixels, gl_width, gl_height, 1, 1);
251
252     if (gl_width > 1)
253       gl_width >>= 1;
254     if (gl_height > 1)
255       gl_height >>= 1;
256
257     glTexImage2D(GL_TEXTURE_2D, mip++, g_texture_globals.texture_components, gl_width, gl_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, outpixels);
258   }
259
260   glBindTexture(GL_TEXTURE_2D, 0);
261   if (resampled)
262     free(outpixels);
263 }
264
265 #if 0
266 /*
267 ==============
268 Texture_InitPalette
269 ==============
270 */
271 void Texture_InitPalette (byte *pal)
272 {
273   int   r,g,b;
274   int   i;
275   int   inf;
276   byte  gammatable[256];
277   float gamma;
278
279   gamma = g_texture_globals.fGamma;
280
281   if (gamma == 1.0)
282   {
283     for (i=0 ; i<256 ; i++)
284       gammatable[i] = i;
285   } else
286   {
287     for (i=0 ; i<256 ; i++)
288     {
289       inf = (int)(255 * pow ( (i+0.5)/255.5 , gamma ) + 0.5);
290       if (inf < 0)
291         inf = 0;
292       if (inf > 255)
293         inf = 255;
294       gammatable[i] = inf;
295     }
296   }
297
298   for (i=0 ; i<256 ; i++)
299   {
300     r = gammatable[pal[0]];
301     g = gammatable[pal[1]];
302     b = gammatable[pal[2]];
303     pal += 3;
304
305     //v = (r<<24) + (g<<16) + (b<<8) + 255;
306     //v = BigLong (v);
307
308     //tex_palette[i] = v;
309     tex_palette[i*3+0] = r;
310     tex_palette[i*3+1] = g;
311     tex_palette[i*3+2] = b;
312   }
313 }
314 #endif
315
316 #if 0
317 class TestHashtable
318 {
319 public:
320   TestHashtable()
321   {
322     HashTable<CopiedString, CopiedString, HashStringNoCase, StringEqualNoCase> strings;
323     strings["Monkey"] = "bleh";
324     strings["MonkeY"] = "blah";
325   }
326 };
327
328 const TestHashtable g_testhashtable;
329
330 #endif
331
332 typedef std::pair<LoadImageCallback, CopiedString> TextureKey;
333
334 void qtexture_realise(qtexture_t& texture, const TextureKey& key)
335 {
336   texture.texture_number = 0;
337   if(!string_empty(key.second.c_str()))
338   {
339     Image* image = key.first.loadImage(key.second.c_str());
340     if(image != 0)
341     {
342       LoadTextureRGBA(&texture, image->getRGBAPixels(), image->getWidth(), image->getHeight());
343       texture.surfaceFlags = image->getSurfaceFlags();
344       texture.contentFlags = image->getContentFlags();
345       texture.value = image->getValue();
346       image->release();
347       globalOutputStream() << "Loaded Texture: \"" << key.second.c_str() << "\"\n";
348       GlobalOpenGL_debugAssertNoErrors();
349     }
350     else
351     {
352       globalErrorStream() << "Texture load failed: \"" << key.second.c_str() << "\"\n";
353     }
354   }
355 }
356
357 void qtexture_unrealise(qtexture_t& texture)
358 {
359   if(GlobalOpenGL().contextValid && texture.texture_number != 0)
360   {
361     glDeleteTextures(1, &texture.texture_number);
362     GlobalOpenGL_debugAssertNoErrors();
363   }
364 }
365
366 class TextureKeyEqualNoCase
367 {
368 public:
369   bool operator()(const TextureKey& key, const TextureKey& other) const
370   {
371     return key.first == other.first && string_equal_nocase(key.second.c_str(), other.second.c_str());
372   }
373 };
374
375 class TextureKeyHashNoCase
376 {
377 public:
378   typedef hash_t hash_type;
379   hash_t operator()(const TextureKey& key) const
380   {
381     return hash_combine(string_hash_nocase(key.second.c_str()), pod_hash(key.first));
382   }
383 };
384
385 #define DEBUG_TEXTURES 0
386
387 class TexturesMap : public TexturesCache
388 {
389   class TextureConstructor
390   {
391     TexturesMap* m_cache;
392   public:
393     explicit TextureConstructor(TexturesMap* cache)
394       : m_cache(cache)
395     {
396     }
397     qtexture_t* construct(const TextureKey& key)
398     {
399       qtexture_t* texture = new qtexture_t(key.first, key.second.c_str());
400       if(m_cache->realised())
401       {
402         qtexture_realise(*texture, key);
403       }
404       return texture;
405     }
406     void destroy(qtexture_t* texture)
407     {
408       if(m_cache->realised())
409       {
410         qtexture_unrealise(*texture);
411       }
412       delete texture;
413     }
414   };
415
416   typedef HashedCache<TextureKey, qtexture_t, TextureKeyHashNoCase, TextureKeyEqualNoCase, TextureConstructor> qtextures_t;
417   qtextures_t m_qtextures;
418   TexturesCacheObserver* m_observer;
419   std::size_t m_unrealised;
420
421 public:
422   TexturesMap() : m_qtextures(TextureConstructor(this)), m_observer(0), m_unrealised(1)
423   {
424   }
425   typedef qtextures_t::iterator iterator;
426
427   iterator begin()
428   {
429     return m_qtextures.begin();
430   }
431   iterator end()
432   {
433     return m_qtextures.end();
434   }
435
436   LoadImageCallback defaultLoader() const
437   {
438     return LoadImageCallback(0, QERApp_LoadImage);
439   }
440   Image* loadImage(const char* name)
441   {
442     return defaultLoader().loadImage(name);
443   }
444   qtexture_t* capture(const char* name)
445   {
446     return capture(defaultLoader(), name);
447   }
448   qtexture_t* capture(const LoadImageCallback& loader, const char* name)
449   {
450 #if DEBUG_TEXTURES
451     globalOutputStream() << "textures capture: " << makeQuoted(name) << '\n';
452 #endif
453     return m_qtextures.capture(TextureKey(loader, name)).get();
454   }
455   void release(qtexture_t* texture)
456   {
457 #if DEBUG_TEXTURES
458     globalOutputStream() << "textures release: " << makeQuoted(texture->name) << '\n';
459 #endif
460     m_qtextures.release(TextureKey(texture->load, texture->name));
461   }
462   void attach(TexturesCacheObserver& observer)
463   {
464     ASSERT_MESSAGE(m_observer == 0, "TexturesMap::attach: cannot attach observer");
465     m_observer = &observer;
466   }
467   void detach(TexturesCacheObserver& observer)
468   {
469     ASSERT_MESSAGE(m_observer == &observer, "TexturesMap::detach: cannot detach observer");
470     m_observer = 0;
471   }
472   void realise()
473   {
474     if(--m_unrealised == 0)
475     {
476       g_texture_globals.bTextureCompressionSupported = false;
477
478       if(GlobalOpenGL().ARB_texture_compression())
479       {
480         g_texture_globals.bTextureCompressionSupported = true;
481         g_texture_globals.m_bOpenGLCompressionSupported = true;
482       }
483
484       if(GlobalOpenGL().EXT_texture_compression_s3tc())
485       {
486         g_texture_globals.bTextureCompressionSupported = true;
487         g_texture_globals.m_bS3CompressionSupported = true;
488       }
489
490       glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
491       if(max_tex_size == 0)
492       {
493         max_tex_size = 1024;
494       }
495
496       for(qtextures_t::iterator i = m_qtextures.begin(); i != m_qtextures.end(); ++i)
497       {
498         if(!(*i).value.empty())
499         {
500           qtexture_realise(*(*i).value, (*i).key);
501         }
502       }
503       if(m_observer != 0)
504       {
505         m_observer->realise();
506       }
507     }
508   }
509   void unrealise()
510   {
511     if(++m_unrealised == 1)
512     {
513       if(m_observer != 0)
514       {
515         m_observer->unrealise();
516       }
517       for(qtextures_t::iterator i = m_qtextures.begin(); i != m_qtextures.end(); ++i)
518       {
519         if(!(*i).value.empty())
520         {
521           qtexture_unrealise(*(*i).value);
522         }
523       }
524     }
525   }
526   bool realised()
527   {
528     return m_unrealised == 0;
529   }
530 };
531
532 TexturesMap* g_texturesmap;
533
534 TexturesCache& GetTexturesCache()
535 {
536   return *g_texturesmap;
537 }
538
539
540 void Textures_Realise()
541 {
542   g_texturesmap->realise();
543 }
544
545 void Textures_Unrealise()
546 {
547   g_texturesmap->unrealise();
548 }
549
550
551 Callback g_texturesModeChangedNotify;
552
553 void Textures_setModeChangedNotify(const Callback& notify)
554 {
555   g_texturesModeChangedNotify = notify;
556 }
557
558 void Textures_ModeChanged()
559 {
560   if(g_texturesmap->realised())
561   {
562     SetTexParameters(g_texture_mode);
563
564     for(TexturesMap::iterator i = g_texturesmap->begin(); i != g_texturesmap->end(); ++i)
565     {
566       glBindTexture (GL_TEXTURE_2D, (*i).value->texture_number);
567       SetTexParameters(g_texture_mode);
568     }
569
570     glBindTexture( GL_TEXTURE_2D, 0 );
571   }
572   g_texturesModeChangedNotify();
573 }
574
575 void Textures_SetMode(ETexturesMode mode)
576 {
577   if(g_texture_mode != mode)
578   {
579     g_texture_mode = mode;
580
581     Textures_ModeChanged();
582   }
583 }
584
585 void Textures_setTextureComponents(GLint texture_components)
586 {
587   if(g_texture_globals.texture_components != texture_components)
588   {
589     Textures_Unrealise();
590     g_texture_globals.texture_components = texture_components;
591     Textures_Realise();
592   }
593 }
594
595 void Textures_UpdateTextureCompressionFormat()
596 {
597   GLint texture_components = GL_RGBA;
598
599         if(!g_texturesmap->realised())
600         {
601                 texture_components = g_texture_globals.m_nTextureCompressionFormat;
602                 if(texture_components == TEXTURECOMPRESSION_NONE)
603                         texture_components = GL_RGBA;
604         }
605         else
606         {
607                 if (g_texture_globals.bTextureCompressionSupported)
608                 {
609                         if(g_texture_globals.m_nTextureCompressionFormat != TEXTURECOMPRESSION_NONE
610                                 && g_texture_globals.m_nTextureCompressionFormat != TEXTURECOMPRESSION_RGBA
611                                 && !g_texture_globals.m_bS3CompressionSupported)
612                         {
613                                 globalOutputStream() << "OpenGL extension GL_EXT_texture_compression_s3tc not supported by current graphics drivers\n";
614                                 g_texture_globals.m_nTextureCompressionFormat = TEXTURECOMPRESSION_RGBA; // if this is not supported either, see below
615                         }
616                         if (g_texture_globals.m_nTextureCompressionFormat == TEXTURECOMPRESSION_RGBA && !g_texture_globals.m_bOpenGLCompressionSupported)
617                         {
618                                 globalOutputStream() << "OpenGL extension GL_ARB_texture_compression not supported by current graphics drivers\n";
619                                 g_texture_globals.m_nTextureCompressionFormat = TEXTURECOMPRESSION_NONE;
620                         }
621
622                         switch (g_texture_globals.m_nTextureCompressionFormat)
623                         {
624                         case (TEXTURECOMPRESSION_NONE):
625                                 {
626                                         texture_components = GL_RGBA;
627                                         break;
628                                 }
629                         case (TEXTURECOMPRESSION_RGBA):
630                                 {
631                                         texture_components = GL_COMPRESSED_RGBA_ARB;
632                                         break;
633                                 }
634                         case (TEXTURECOMPRESSION_RGBA_S3TC_DXT1):
635                                 {
636                                         texture_components = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
637                                         break;
638                                 }
639                         case (TEXTURECOMPRESSION_RGBA_S3TC_DXT3):
640                                 {
641                                         texture_components = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
642                                         break;
643                                 }
644                         case (TEXTURECOMPRESSION_RGBA_S3TC_DXT5):
645                                 {
646                                         texture_components = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
647                                         break;
648                                 }
649                         }
650                 }
651                 else
652                 {
653                         texture_components = GL_RGBA;
654                         g_texture_globals.m_nTextureCompressionFormat = TEXTURECOMPRESSION_NONE;
655                 }
656         }
657
658   Textures_setTextureComponents(texture_components);
659 }
660
661 void TextureCompressionImport(TextureCompressionFormat& self, int value)
662 {
663   if(!g_texture_globals.m_bOpenGLCompressionSupported
664     && g_texture_globals.m_bS3CompressionSupported
665     && value >= 1)
666   {
667     ++value;
668   }
669   switch(value)
670   {
671   case 0:
672     self = TEXTURECOMPRESSION_NONE;
673     break;
674   case 1:
675     self = TEXTURECOMPRESSION_RGBA;
676     break;
677   case 2:
678     self = TEXTURECOMPRESSION_RGBA_S3TC_DXT1;
679     break;
680   case 3:
681     self = TEXTURECOMPRESSION_RGBA_S3TC_DXT3;
682     break;
683   case 4:
684     self = TEXTURECOMPRESSION_RGBA_S3TC_DXT5;
685     break;
686   }
687   Textures_UpdateTextureCompressionFormat();
688 }
689 typedef ReferenceCaller1<TextureCompressionFormat, int, TextureCompressionImport> TextureCompressionImportCaller;
690
691 void TextureGammaImport(float& self, float value)
692 {
693   if(self != value)
694   {
695     Textures_Unrealise();
696     self = value;
697     Textures_Realise();
698   }
699 }
700 typedef ReferenceCaller1<float, float, TextureGammaImport> TextureGammaImportCaller;
701
702 void TextureModeImport(ETexturesMode& self, int value)
703 {
704   switch(value)
705   {
706   case 0:
707     Textures_SetMode(eTextures_NEAREST);
708     break;
709   case 1:
710     Textures_SetMode(eTextures_NEAREST_MIPMAP_NEAREST);
711     break;
712   case 2:
713     Textures_SetMode(eTextures_LINEAR);
714     break;
715   case 3:
716     Textures_SetMode(eTextures_NEAREST_MIPMAP_LINEAR);
717     break;
718   case 4:
719     Textures_SetMode(eTextures_LINEAR_MIPMAP_NEAREST);
720     break;
721   case 5:
722     Textures_SetMode(eTextures_LINEAR_MIPMAP_LINEAR);
723     break;
724   case 6:
725     Textures_SetMode(eTextures_MAX_ANISOTROPY);
726   }
727 }
728 typedef ReferenceCaller1<ETexturesMode, int, TextureModeImport> TextureModeImportCaller;
729
730 void TextureModeExport(ETexturesMode& self, const IntImportCallback& importer)
731 {
732   switch(self)
733   {
734   case eTextures_NEAREST:
735     importer(0);
736     break;
737   case eTextures_NEAREST_MIPMAP_NEAREST:
738     importer(1);
739     break;
740   case eTextures_LINEAR:
741     importer(2);
742     break;
743   case eTextures_NEAREST_MIPMAP_LINEAR:
744     importer(3);
745     break;
746   case eTextures_LINEAR_MIPMAP_NEAREST:
747     importer(4);
748     break;
749   case eTextures_LINEAR_MIPMAP_LINEAR:
750     importer(5);
751     break;
752   case eTextures_MAX_ANISOTROPY:
753     importer(6);
754     break;
755   default:
756     importer(4);
757   }
758 }
759 typedef ReferenceCaller1<ETexturesMode, const IntImportCallback&, TextureModeExport> TextureModeExportCaller;
760
761 void Textures_constructPreferences(PreferencesPage& page)
762 {
763   {
764     const char* percentages[] = { "12.5%", "25%", "50%", "100%", };
765     page.appendRadio(
766       "Texture Quality",
767       STRING_ARRAY_RANGE(percentages),
768       LatchedIntImportCaller(g_Textures_textureQuality),
769       IntExportCaller(g_Textures_textureQuality.m_latched)
770     );
771   }
772   page.appendSpinner(
773     "Texture Gamma",
774     1.0,
775     0.0,
776     1.0,
777     FloatImportCallback(TextureGammaImportCaller(g_texture_globals.fGamma)),
778     FloatExportCallback(FloatExportCaller(g_texture_globals.fGamma))
779   );
780   {
781     const char* texture_mode[] = { "Nearest", "Nearest Mipmap", "Linear", "Bilinear", "Bilinear Mipmap", "Trilinear", "Anisotropy" };
782     page.appendCombo(
783       "Texture Render Mode",
784       STRING_ARRAY_RANGE(texture_mode),
785       IntImportCallback(TextureModeImportCaller(g_texture_mode)),
786       IntExportCallback(TextureModeExportCaller(g_texture_mode))
787     );
788   }
789   {
790     const char* compression_none[] = { "None" };
791     const char* compression_opengl[] = { "None", "OpenGL ARB" };
792     const char* compression_s3tc[] = { "None", "S3TC DXT1", "S3TC DXT3", "S3TC DXT5" };
793     const char* compression_opengl_s3tc[] = { "None", "OpenGL ARB", "S3TC DXT1", "S3TC DXT3", "S3TC DXT5" };
794     StringArrayRange compression(
795       (g_texture_globals.m_bOpenGLCompressionSupported)
796         ? (g_texture_globals.m_bS3CompressionSupported)
797           ? STRING_ARRAY_RANGE(compression_opengl_s3tc)
798           : STRING_ARRAY_RANGE(compression_opengl)
799         : (g_texture_globals.m_bS3CompressionSupported)
800           ? STRING_ARRAY_RANGE(compression_s3tc)
801           : STRING_ARRAY_RANGE(compression_none)
802     );
803     page.appendCombo(
804       "Hardware Texture Compression",
805       compression,
806       TextureCompressionImportCaller(g_texture_globals.m_nTextureCompressionFormat),
807       IntExportCaller(reinterpret_cast<int&>(g_texture_globals.m_nTextureCompressionFormat))
808     );
809   }
810 }
811 void Textures_constructPage(PreferenceGroup& group)
812 {
813   PreferencesPage page(group.createPage("Textures", "Texture Settings"));
814   Textures_constructPreferences(page);
815 }
816 void Textures_registerPreferencesPage()
817 {
818   PreferencesDialog_addDisplayPage(FreeCaller1<PreferenceGroup&, Textures_constructPage>());
819 }
820
821 void TextureCompression_importString(const char* string)
822 {
823   g_texture_globals.m_nTextureCompressionFormat = static_cast<TextureCompressionFormat>(atoi(string));
824   Textures_UpdateTextureCompressionFormat();
825 }
826 typedef FreeCaller1<const char*, TextureCompression_importString> TextureCompressionImportStringCaller;
827
828
829 void Textures_Construct()
830 {
831   g_texturesmap = new TexturesMap;
832
833   GlobalPreferenceSystem().registerPreference("TextureCompressionFormat", TextureCompressionImportStringCaller(), IntExportStringCaller(reinterpret_cast<int&>(g_texture_globals.m_nTextureCompressionFormat)));
834   GlobalPreferenceSystem().registerPreference("TextureFiltering", IntImportStringCaller(reinterpret_cast<int&>(g_texture_mode)), IntExportStringCaller(reinterpret_cast<int&>(g_texture_mode)));
835   GlobalPreferenceSystem().registerPreference("TextureQuality", IntImportStringCaller(g_Textures_textureQuality.m_latched), IntExportStringCaller(g_Textures_textureQuality.m_latched));
836   GlobalPreferenceSystem().registerPreference("SI_Gamma", FloatImportStringCaller(g_texture_globals.fGamma), FloatExportStringCaller(g_texture_globals.fGamma));
837
838   g_Textures_textureQuality.useLatched();
839
840   Textures_registerPreferencesPage();
841
842   Textures_ModeChanged();
843 }
844 void Textures_Destroy()
845 {
846   delete g_texturesmap;
847 }
848
849
850 #include "modulesystem/modulesmap.h"
851 #include "modulesystem/singletonmodule.h"
852 #include "modulesystem/moduleregistry.h"
853
854 class TexturesDependencies :
855   public GlobalRadiantModuleRef,
856   public GlobalOpenGLModuleRef,
857   public GlobalPreferenceSystemModuleRef
858 {
859   ImageModulesRef m_image_modules;
860 public:
861   TexturesDependencies() :
862     m_image_modules(GlobalRadiant().getRequiredGameDescriptionKeyValue("texturetypes"))
863   {
864   }
865   ImageModules& getImageModules()
866   {
867     return m_image_modules.get();
868   }
869 };
870
871 class TexturesAPI
872 {
873   TexturesCache* m_textures;
874 public:
875   typedef TexturesCache Type;
876   STRING_CONSTANT(Name, "*");
877
878   TexturesAPI()
879   {
880     Textures_Construct();
881
882     m_textures = &GetTexturesCache();
883   }
884   ~TexturesAPI()
885   {
886     Textures_Destroy();
887   }
888   TexturesCache* getTable()
889   {
890     return m_textures;
891   }
892 };
893
894 typedef SingletonModule<TexturesAPI, TexturesDependencies> TexturesModule;
895 typedef Static<TexturesModule> StaticTexturesModule;
896 StaticRegisterModule staticRegisterTextures(StaticTexturesModule::instance());
897
898 ImageModules& Textures_getImageModules()
899 {
900   return StaticTexturesModule::instance().getDependencies().getImageModules();
901 }
902
903
904