]> git.xonotic.org Git - voretournament/voretournament.git/blob - misc/mediasource/netradiant-src/radiant/textures.cpp
Move all other sources in a separate subfolder
[voretournament/voretournament.git] / misc / mediasource / netradiant-src / 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           switch(g_texture_globals.texture_components)
491           {
492                   case GL_RGBA:
493                           break;
494                   case GL_COMPRESSED_RGBA_ARB:
495                           if (!g_texture_globals.m_bOpenGLCompressionSupported)
496                           {
497                                   globalOutputStream() << "OpenGL extension GL_ARB_texture_compression not supported by current graphics drivers\n";
498                                   g_texture_globals.m_nTextureCompressionFormat = TEXTURECOMPRESSION_NONE;
499                                   g_texture_globals.texture_components = GL_RGBA;
500                           }
501                           break;
502                   case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
503                   case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
504                   case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
505                           if(!g_texture_globals.m_bS3CompressionSupported)
506                           {
507                                   globalOutputStream() << "OpenGL extension GL_EXT_texture_compression_s3tc not supported by current graphics drivers\n";
508                                   if(g_texture_globals.m_bOpenGLCompressionSupported)
509                                   {
510                                           g_texture_globals.m_nTextureCompressionFormat = TEXTURECOMPRESSION_RGBA;
511                                           g_texture_globals.texture_components = GL_COMPRESSED_RGBA_ARB;
512                                   }
513                                   else
514                                   {
515                                           g_texture_globals.m_nTextureCompressionFormat = TEXTURECOMPRESSION_NONE;
516                                           g_texture_globals.texture_components = GL_RGBA;
517                                   }
518                           }
519                           break;
520                   default:
521                           globalOutputStream() << "Unknown texture compression selected, reverting\n";
522                           g_texture_globals.m_nTextureCompressionFormat = TEXTURECOMPRESSION_NONE;
523                           g_texture_globals.texture_components = GL_RGBA;
524                           break;
525           }
526
527
528       glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
529       if(max_tex_size == 0)
530       {
531         max_tex_size = 1024;
532       }
533
534       for(qtextures_t::iterator i = m_qtextures.begin(); i != m_qtextures.end(); ++i)
535       {
536         if(!(*i).value.empty())
537         {
538           qtexture_realise(*(*i).value, (*i).key);
539         }
540       }
541       if(m_observer != 0)
542       {
543         m_observer->realise();
544       }
545     }
546   }
547   void unrealise()
548   {
549     if(++m_unrealised == 1)
550     {
551       if(m_observer != 0)
552       {
553         m_observer->unrealise();
554       }
555       for(qtextures_t::iterator i = m_qtextures.begin(); i != m_qtextures.end(); ++i)
556       {
557         if(!(*i).value.empty())
558         {
559           qtexture_unrealise(*(*i).value);
560         }
561       }
562     }
563   }
564   bool realised()
565   {
566     return m_unrealised == 0;
567   }
568 };
569
570 TexturesMap* g_texturesmap;
571
572 TexturesCache& GetTexturesCache()
573 {
574   return *g_texturesmap;
575 }
576
577
578 void Textures_Realise()
579 {
580   g_texturesmap->realise();
581 }
582
583 void Textures_Unrealise()
584 {
585   g_texturesmap->unrealise();
586 }
587
588
589 Callback g_texturesModeChangedNotify;
590
591 void Textures_setModeChangedNotify(const Callback& notify)
592 {
593   g_texturesModeChangedNotify = notify;
594 }
595
596 void Textures_ModeChanged()
597 {
598   if(g_texturesmap->realised())
599   {
600     SetTexParameters(g_texture_mode);
601
602     for(TexturesMap::iterator i = g_texturesmap->begin(); i != g_texturesmap->end(); ++i)
603     {
604       glBindTexture (GL_TEXTURE_2D, (*i).value->texture_number);
605       SetTexParameters(g_texture_mode);
606     }
607
608     glBindTexture( GL_TEXTURE_2D, 0 );
609   }
610   g_texturesModeChangedNotify();
611 }
612
613 void Textures_SetMode(ETexturesMode mode)
614 {
615   if(g_texture_mode != mode)
616   {
617     g_texture_mode = mode;
618
619     Textures_ModeChanged();
620   }
621 }
622
623 void Textures_setTextureComponents(GLint texture_components)
624 {
625   if(g_texture_globals.texture_components != texture_components)
626   {
627     Textures_Unrealise();
628     g_texture_globals.texture_components = texture_components;
629     Textures_Realise();
630   }
631 }
632
633 void Textures_UpdateTextureCompressionFormat()
634 {
635   GLint texture_components = GL_RGBA;
636
637         switch (g_texture_globals.m_nTextureCompressionFormat)
638         {
639         case (TEXTURECOMPRESSION_NONE):
640                 {
641                         texture_components = GL_RGBA;
642                         break;
643                 }
644         case (TEXTURECOMPRESSION_RGBA):
645                 {
646                         texture_components = GL_COMPRESSED_RGBA_ARB;
647                         break;
648                 }
649         case (TEXTURECOMPRESSION_RGBA_S3TC_DXT1):
650                 {
651                         texture_components = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
652                         break;
653                 }
654         case (TEXTURECOMPRESSION_RGBA_S3TC_DXT3):
655                 {
656                         texture_components = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
657                         break;
658                 }
659         case (TEXTURECOMPRESSION_RGBA_S3TC_DXT5):
660                 {
661                         texture_components = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
662                         break;
663                 }
664         }
665
666   Textures_setTextureComponents(texture_components);
667 }
668
669 void TextureCompressionImport(TextureCompressionFormat& self, int value)
670 {
671   if(!g_texture_globals.m_bOpenGLCompressionSupported
672     && g_texture_globals.m_bS3CompressionSupported
673     && value >= 1)
674   {
675     ++value;
676   }
677   switch(value)
678   {
679   case 0:
680     self = TEXTURECOMPRESSION_NONE;
681     break;
682   case 1:
683     self = TEXTURECOMPRESSION_RGBA;
684     break;
685   case 2:
686     self = TEXTURECOMPRESSION_RGBA_S3TC_DXT1;
687     break;
688   case 3:
689     self = TEXTURECOMPRESSION_RGBA_S3TC_DXT3;
690     break;
691   case 4:
692     self = TEXTURECOMPRESSION_RGBA_S3TC_DXT5;
693     break;
694   }
695   Textures_UpdateTextureCompressionFormat();
696 }
697 typedef ReferenceCaller1<TextureCompressionFormat, int, TextureCompressionImport> TextureCompressionImportCaller;
698
699 void TextureGammaImport(float& self, float value)
700 {
701   if(self != value)
702   {
703     Textures_Unrealise();
704     self = value;
705     Textures_Realise();
706   }
707 }
708 typedef ReferenceCaller1<float, float, TextureGammaImport> TextureGammaImportCaller;
709
710 void TextureModeImport(ETexturesMode& self, int value)
711 {
712   switch(value)
713   {
714   case 0:
715     Textures_SetMode(eTextures_NEAREST);
716     break;
717   case 1:
718     Textures_SetMode(eTextures_NEAREST_MIPMAP_NEAREST);
719     break;
720   case 2:
721     Textures_SetMode(eTextures_LINEAR);
722     break;
723   case 3:
724     Textures_SetMode(eTextures_NEAREST_MIPMAP_LINEAR);
725     break;
726   case 4:
727     Textures_SetMode(eTextures_LINEAR_MIPMAP_NEAREST);
728     break;
729   case 5:
730     Textures_SetMode(eTextures_LINEAR_MIPMAP_LINEAR);
731     break;
732   case 6:
733     Textures_SetMode(eTextures_MAX_ANISOTROPY);
734   }
735 }
736 typedef ReferenceCaller1<ETexturesMode, int, TextureModeImport> TextureModeImportCaller;
737
738 void TextureModeExport(ETexturesMode& self, const IntImportCallback& importer)
739 {
740   switch(self)
741   {
742   case eTextures_NEAREST:
743     importer(0);
744     break;
745   case eTextures_NEAREST_MIPMAP_NEAREST:
746     importer(1);
747     break;
748   case eTextures_LINEAR:
749     importer(2);
750     break;
751   case eTextures_NEAREST_MIPMAP_LINEAR:
752     importer(3);
753     break;
754   case eTextures_LINEAR_MIPMAP_NEAREST:
755     importer(4);
756     break;
757   case eTextures_LINEAR_MIPMAP_LINEAR:
758     importer(5);
759     break;
760   case eTextures_MAX_ANISOTROPY:
761     importer(6);
762     break;
763   default:
764     importer(4);
765   }
766 }
767 typedef ReferenceCaller1<ETexturesMode, const IntImportCallback&, TextureModeExport> TextureModeExportCaller;
768
769 void Textures_constructPreferences(PreferencesPage& page)
770 {
771   {
772     const char* percentages[] = { "12.5%", "25%", "50%", "100%", };
773     page.appendRadio(
774       "Texture Quality",
775       STRING_ARRAY_RANGE(percentages),
776       LatchedIntImportCaller(g_Textures_textureQuality),
777       IntExportCaller(g_Textures_textureQuality.m_latched)
778     );
779   }
780   page.appendSpinner(
781     "Texture Gamma",
782     1.0,
783     0.0,
784     1.0,
785     FloatImportCallback(TextureGammaImportCaller(g_texture_globals.fGamma)),
786     FloatExportCallback(FloatExportCaller(g_texture_globals.fGamma))
787   );
788   {
789     const char* texture_mode[] = { "Nearest", "Nearest Mipmap", "Linear", "Bilinear", "Bilinear Mipmap", "Trilinear", "Anisotropy" };
790     page.appendCombo(
791       "Texture Render Mode",
792       STRING_ARRAY_RANGE(texture_mode),
793       IntImportCallback(TextureModeImportCaller(g_texture_mode)),
794       IntExportCallback(TextureModeExportCaller(g_texture_mode))
795     );
796   }
797   {
798     const char* compression_none[] = { "None" };
799     const char* compression_opengl[] = { "None", "OpenGL ARB" };
800     const char* compression_s3tc[] = { "None", "S3TC DXT1", "S3TC DXT3", "S3TC DXT5" };
801     const char* compression_opengl_s3tc[] = { "None", "OpenGL ARB", "S3TC DXT1", "S3TC DXT3", "S3TC DXT5" };
802     StringArrayRange compression(
803       (g_texture_globals.m_bOpenGLCompressionSupported)
804         ? (g_texture_globals.m_bS3CompressionSupported)
805           ? STRING_ARRAY_RANGE(compression_opengl_s3tc)
806           : STRING_ARRAY_RANGE(compression_opengl)
807         : (g_texture_globals.m_bS3CompressionSupported)
808           ? STRING_ARRAY_RANGE(compression_s3tc)
809           : STRING_ARRAY_RANGE(compression_none)
810     );
811     page.appendCombo(
812       "Hardware Texture Compression",
813       compression,
814       TextureCompressionImportCaller(g_texture_globals.m_nTextureCompressionFormat),
815       IntExportCaller(reinterpret_cast<int&>(g_texture_globals.m_nTextureCompressionFormat))
816     );
817   }
818 }
819 void Textures_constructPage(PreferenceGroup& group)
820 {
821   PreferencesPage page(group.createPage("Textures", "Texture Settings"));
822   Textures_constructPreferences(page);
823 }
824 void Textures_registerPreferencesPage()
825 {
826   PreferencesDialog_addDisplayPage(FreeCaller1<PreferenceGroup&, Textures_constructPage>());
827 }
828
829 void TextureCompression_importString(const char* string)
830 {
831   g_texture_globals.m_nTextureCompressionFormat = static_cast<TextureCompressionFormat>(atoi(string));
832   Textures_UpdateTextureCompressionFormat();
833 }
834 typedef FreeCaller1<const char*, TextureCompression_importString> TextureCompressionImportStringCaller;
835
836
837 void Textures_Construct()
838 {
839   g_texturesmap = new TexturesMap;
840
841   GlobalPreferenceSystem().registerPreference("TextureCompressionFormat", TextureCompressionImportStringCaller(), IntExportStringCaller(reinterpret_cast<int&>(g_texture_globals.m_nTextureCompressionFormat)));
842   GlobalPreferenceSystem().registerPreference("TextureFiltering", IntImportStringCaller(reinterpret_cast<int&>(g_texture_mode)), IntExportStringCaller(reinterpret_cast<int&>(g_texture_mode)));
843   GlobalPreferenceSystem().registerPreference("TextureQuality", IntImportStringCaller(g_Textures_textureQuality.m_latched), IntExportStringCaller(g_Textures_textureQuality.m_latched));
844   GlobalPreferenceSystem().registerPreference("SI_Gamma", FloatImportStringCaller(g_texture_globals.fGamma), FloatExportStringCaller(g_texture_globals.fGamma));
845
846   g_Textures_textureQuality.useLatched();
847
848   Textures_registerPreferencesPage();
849
850   Textures_ModeChanged();
851 }
852 void Textures_Destroy()
853 {
854   delete g_texturesmap;
855 }
856
857
858 #include "modulesystem/modulesmap.h"
859 #include "modulesystem/singletonmodule.h"
860 #include "modulesystem/moduleregistry.h"
861
862 class TexturesDependencies :
863   public GlobalRadiantModuleRef,
864   public GlobalOpenGLModuleRef,
865   public GlobalPreferenceSystemModuleRef
866 {
867   ImageModulesRef m_image_modules;
868 public:
869   TexturesDependencies() :
870     m_image_modules(GlobalRadiant().getRequiredGameDescriptionKeyValue("texturetypes"))
871   {
872   }
873   ImageModules& getImageModules()
874   {
875     return m_image_modules.get();
876   }
877 };
878
879 class TexturesAPI
880 {
881   TexturesCache* m_textures;
882 public:
883   typedef TexturesCache Type;
884   STRING_CONSTANT(Name, "*");
885
886   TexturesAPI()
887   {
888     Textures_Construct();
889
890     m_textures = &GetTexturesCache();
891   }
892   ~TexturesAPI()
893   {
894     Textures_Destroy();
895   }
896   TexturesCache* getTable()
897   {
898     return m_textures;
899   }
900 };
901
902 typedef SingletonModule<TexturesAPI, TexturesDependencies> TexturesModule;
903 typedef Static<TexturesModule> StaticTexturesModule;
904 StaticRegisterModule staticRegisterTextures(StaticTexturesModule::instance());
905
906 ImageModules& Textures_getImageModules()
907 {
908   return StaticTexturesModule::instance().getDependencies().getImageModules();
909 }
910
911
912