]> git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/textures.cpp
Hack around segfault at launch, patch from danfe: https://github.com/TTimo/GtkRadiant...
[xonotic/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 texture_globals_t g_texture_globals( GL_RGBA );
90
91 void SetTexParameters( ETexturesMode mode ){
92         float maxAniso = QGL_maxTextureAnisotropy();
93         if ( maxAniso > 1 ) {
94                 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f );
95         }
96         else
97         if ( mode == eTextures_MAX_ANISOTROPY ) {
98                 mode = eTextures_LINEAR_MIPMAP_LINEAR;
99         }
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         int i,inf;
143         if ( fGamma == 1.0 ) {
144                 for ( i = 0; i < 256; i++ )
145                         g_gammatable[i] = i;
146         }
147         else
148         {
149                 for ( i = 0; i < 256; i++ )
150                 {
151                         inf = (int)( 255 * pow( static_cast<double>( ( i + 0.5 ) / 255.5 ), static_cast<double>( fGamma ) ) + 0.5 );
152                         if ( inf < 0 ) {
153                                 inf = 0;
154                         }
155                         if ( inf > 255 ) {
156                                 inf = 255;
157                         }
158                         g_gammatable[i] = inf;
159                 }
160         }
161 }
162
163 inline const int& min_int( const int& left, const int& right ){
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         static float fGamma = -1;
175         float total[3];
176         byte  *outpixels = 0;
177         int nCount = nWidth * nHeight;
178
179         if ( fGamma != g_texture_globals.fGamma ) {
180                 fGamma = g_texture_globals.fGamma;
181                 ResampleGamma( fGamma );
182         }
183
184         q->width = nWidth;
185         q->height = nHeight;
186
187         total[0] = total[1] = total[2] = 0.0f;
188
189         // resample texture gamma according to user settings
190         for ( int i = 0; i < ( nCount * 4 ); i += 4 )
191         {
192                 for ( int j = 0; j < 3; j++ )
193                 {
194                         total[j] += ( pPixels + i )[j];
195                         byte b = ( pPixels + i )[j];
196                         ( pPixels + i )[j] = g_gammatable[b];
197                 }
198         }
199
200         q->color[0] = total[0] / ( nCount * 255 );
201         q->color[1] = total[1] / ( nCount * 255 );
202         q->color[2] = total[2] / ( nCount * 255 );
203
204         glGenTextures( 1, &q->texture_number );
205
206         glBindTexture( GL_TEXTURE_2D, q->texture_number );
207
208         SetTexParameters( g_texture_mode );
209
210         int gl_width = 1;
211         while ( gl_width < nWidth )
212                 gl_width <<= 1;
213
214         int gl_height = 1;
215         while ( gl_height < nHeight )
216                 gl_height <<= 1;
217
218         bool resampled = false;
219         if ( !( gl_width == nWidth && gl_height == nHeight ) ) {
220                 resampled = true;
221                 outpixels = (byte *)malloc( gl_width * gl_height * 4 );
222                 R_ResampleTexture( pPixels, nWidth, nHeight, outpixels, gl_width, gl_height, 4 );
223         }
224         else
225         {
226                 outpixels = pPixels;
227         }
228
229         int quality_reduction = max_texture_quality - g_Textures_textureQuality.m_value;
230         int target_width = min_int( gl_width >> quality_reduction, max_tex_size );
231         int target_height = min_int( gl_height >> quality_reduction, max_tex_size );
232
233         while ( gl_width > target_width || gl_height > target_height )
234         {
235                 GL_MipReduce( outpixels, outpixels, gl_width, gl_height, target_width, target_height );
236
237                 if ( gl_width > target_width ) {
238                         gl_width >>= 1;
239                 }
240                 if ( gl_height > target_height ) {
241                         gl_height >>= 1;
242                 }
243         }
244
245         int mip = 0;
246         glTexImage2D( GL_TEXTURE_2D, mip++, g_texture_globals.texture_components, gl_width, gl_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, outpixels );
247         while ( gl_width > 1 || gl_height > 1 )
248         {
249                 GL_MipReduce( outpixels, outpixels, gl_width, gl_height, 1, 1 );
250
251                 if ( gl_width > 1 ) {
252                         gl_width >>= 1;
253                 }
254                 if ( gl_height > 1 ) {
255                         gl_height >>= 1;
256                 }
257
258                 glTexImage2D( GL_TEXTURE_2D, mip++, g_texture_globals.texture_components, gl_width, gl_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, outpixels );
259         }
260
261         glBindTexture( GL_TEXTURE_2D, 0 );
262         if ( resampled ) {
263                 free( outpixels );
264         }
265 }
266
267 #if 0
268 /*
269    ==============
270    Texture_InitPalette
271    ==============
272  */
273 void Texture_InitPalette( byte *pal ){
274         int r,g,b;
275         int i;
276         int inf;
277         byte gammatable[256];
278         float gamma;
279
280         gamma = g_texture_globals.fGamma;
281
282         if ( gamma == 1.0 ) {
283                 for ( i = 0 ; i < 256 ; i++ )
284                         gammatable[i] = i;
285         }
286         else
287         {
288                 for ( i = 0 ; i < 256 ; i++ )
289                 {
290                         inf = (int)( 255 * pow( ( i + 0.5 ) / 255.5, gamma ) + 0.5 );
291                         if ( inf < 0 ) {
292                                 inf = 0;
293                         }
294                         if ( inf > 255 ) {
295                                 inf = 255;
296                         }
297                         gammatable[i] = inf;
298                 }
299         }
300
301         for ( i = 0 ; i < 256 ; i++ )
302         {
303                 r = gammatable[pal[0]];
304                 g = gammatable[pal[1]];
305                 b = gammatable[pal[2]];
306                 pal += 3;
307
308                 //v = (r<<24) + (g<<16) + (b<<8) + 255;
309                 //v = BigLong (v);
310
311                 //tex_palette[i] = v;
312                 tex_palette[i * 3 + 0] = r;
313                 tex_palette[i * 3 + 1] = g;
314                 tex_palette[i * 3 + 2] = b;
315         }
316 }
317 #endif
318
319 #if 0
320 class TestHashtable
321 {
322 public:
323 TestHashtable(){
324         HashTable<CopiedString, CopiedString, HashStringNoCase, StringEqualNoCase> strings;
325         strings["Monkey"] = "bleh";
326         strings["MonkeY"] = "blah";
327 }
328 };
329
330 const TestHashtable g_testhashtable;
331
332 #endif
333
334 typedef std::pair<LoadImageCallback, CopiedString> TextureKey;
335
336 void qtexture_realise( qtexture_t& texture, const TextureKey& key ){
337         texture.texture_number = 0;
338         if ( !string_empty( key.second.c_str() ) ) {
339                 Image* image = key.first.loadImage( key.second.c_str() );
340                 if ( image != 0 ) {
341                         LoadTextureRGBA( &texture, image->getRGBAPixels(), image->getWidth(), image->getHeight() );
342                         texture.surfaceFlags = image->getSurfaceFlags();
343                         texture.contentFlags = image->getContentFlags();
344                         texture.value = image->getValue();
345                         image->release();
346                         globalOutputStream() << "Loaded Texture: \"" << key.second.c_str() << "\"\n";
347                         GlobalOpenGL_debugAssertNoErrors();
348                 }
349                 else
350                 {
351                         globalErrorStream() << "Texture load failed: \"" << key.second.c_str() << "\"\n";
352                 }
353         }
354 }
355
356 void qtexture_unrealise( qtexture_t& texture ){
357         if ( GlobalOpenGL().contextValid && texture.texture_number != 0 ) {
358                 glDeleteTextures( 1, &texture.texture_number );
359                 GlobalOpenGL_debugAssertNoErrors();
360         }
361 }
362
363 class TextureKeyEqualNoCase
364 {
365 public:
366 bool operator()( const TextureKey& key, const TextureKey& other ) const {
367         return key.first == other.first && string_equal_nocase( key.second.c_str(), other.second.c_str() );
368 }
369 };
370
371 class TextureKeyHashNoCase
372 {
373 public:
374 typedef hash_t hash_type;
375 hash_t operator()( const TextureKey& key ) const {
376         return hash_combine( string_hash_nocase( key.second.c_str() ), pod_hash( key.first ) );
377 }
378 };
379
380 #define DEBUG_TEXTURES 0
381
382 class TexturesMap : public TexturesCache
383 {
384 class TextureConstructor
385 {
386 TexturesMap* m_cache;
387 public:
388 explicit TextureConstructor( TexturesMap* cache )
389         : m_cache( cache ){
390 }
391 qtexture_t* construct( const TextureKey& key ){
392         qtexture_t* texture = new qtexture_t( key.first, key.second.c_str() );
393         if ( m_cache->realised() ) {
394                 qtexture_realise( *texture, key );
395         }
396         return texture;
397 }
398 void destroy( qtexture_t* texture ){
399         if ( m_cache->realised() ) {
400                 qtexture_unrealise( *texture );
401         }
402         delete texture;
403 }
404 };
405
406 typedef HashedCache<TextureKey, qtexture_t, TextureKeyHashNoCase, TextureKeyEqualNoCase, TextureConstructor> qtextures_t;
407 qtextures_t m_qtextures;
408 TexturesCacheObserver* m_observer;
409 std::size_t m_unrealised;
410
411 public:
412 TexturesMap() : m_qtextures( TextureConstructor( this ) ), m_observer( 0 ), m_unrealised( 1 ){
413 }
414 typedef qtextures_t::iterator iterator;
415
416 iterator begin(){
417         return m_qtextures.begin();
418 }
419 iterator end(){
420         return m_qtextures.end();
421 }
422
423 LoadImageCallback defaultLoader() const {
424         return LoadImageCallback( 0, QERApp_LoadImage );
425 }
426 Image* loadImage( const char* name ){
427         return defaultLoader().loadImage( name );
428 }
429 qtexture_t* capture( const char* name ){
430         return capture( defaultLoader(), name );
431 }
432 qtexture_t* capture( const LoadImageCallback& loader, const char* name ){
433 #if DEBUG_TEXTURES
434         globalOutputStream() << "textures capture: " << makeQuoted( name ) << '\n';
435 #endif
436         return m_qtextures.capture( TextureKey( loader, name ) ).get();
437 }
438 void release( qtexture_t* texture ){
439 #if DEBUG_TEXTURES
440         globalOutputStream() << "textures release: " << makeQuoted( texture->name ) << '\n';
441 #endif
442         m_qtextures.release( TextureKey( texture->load, texture->name ) );
443 }
444 void attach( TexturesCacheObserver& observer ){
445         ASSERT_MESSAGE( m_observer == 0, "TexturesMap::attach: cannot attach observer" );
446         m_observer = &observer;
447 }
448 void detach( TexturesCacheObserver& observer ){
449         ASSERT_MESSAGE( m_observer == &observer, "TexturesMap::detach: cannot detach observer" );
450         m_observer = 0;
451 }
452 void realise(){
453         if ( --m_unrealised == 0 ) {
454                 g_texture_globals.bTextureCompressionSupported = false;
455
456                 if ( GlobalOpenGL().ARB_texture_compression() ) {
457                         g_texture_globals.bTextureCompressionSupported = true;
458                         g_texture_globals.m_bOpenGLCompressionSupported = true;
459                 }
460
461                 if ( GlobalOpenGL().EXT_texture_compression_s3tc() ) {
462                         g_texture_globals.bTextureCompressionSupported = true;
463                         g_texture_globals.m_bS3CompressionSupported = true;
464                 }
465
466                 switch ( g_texture_globals.texture_components )
467                 {
468                 case GL_RGBA:
469                         break;
470                 case GL_COMPRESSED_RGBA_ARB:
471                         if ( !g_texture_globals.m_bOpenGLCompressionSupported ) {
472                                 globalOutputStream() << "OpenGL extension GL_ARB_texture_compression not supported by current graphics drivers\n";
473                                 g_texture_globals.m_nTextureCompressionFormat = TEXTURECOMPRESSION_NONE;
474                                 g_texture_globals.texture_components = GL_RGBA;
475                         }
476                         break;
477                 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
478                 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
479                 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
480                         if ( !g_texture_globals.m_bS3CompressionSupported ) {
481                                 globalOutputStream() << "OpenGL extension GL_EXT_texture_compression_s3tc not supported by current graphics drivers\n";
482                                 if ( g_texture_globals.m_bOpenGLCompressionSupported ) {
483                                         g_texture_globals.m_nTextureCompressionFormat = TEXTURECOMPRESSION_RGBA;
484                                         g_texture_globals.texture_components = GL_COMPRESSED_RGBA_ARB;
485                                 }
486                                 else
487                                 {
488                                         g_texture_globals.m_nTextureCompressionFormat = TEXTURECOMPRESSION_NONE;
489                                         g_texture_globals.texture_components = GL_RGBA;
490                                 }
491                         }
492                         break;
493                 default:
494                         globalOutputStream() << "Unknown texture compression selected, reverting\n";
495                         g_texture_globals.m_nTextureCompressionFormat = TEXTURECOMPRESSION_NONE;
496                         g_texture_globals.texture_components = GL_RGBA;
497                         break;
498                 }
499
500
501                 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &max_tex_size );
502                 if ( max_tex_size == 0 ) {
503                         max_tex_size = 1024;
504                 }
505
506                 for ( qtextures_t::iterator i = m_qtextures.begin(); i != m_qtextures.end(); ++i )
507                 {
508                         if ( !( *i ).value.empty() ) {
509                                 qtexture_realise( *( *i ).value, ( *i ).key );
510                         }
511                 }
512                 if ( m_observer != 0 ) {
513                         m_observer->realise();
514                 }
515         }
516 }
517 void unrealise(){
518         if ( ++m_unrealised == 1 ) {
519                 if ( m_observer != 0 ) {
520                         m_observer->unrealise();
521                 }
522                 for ( qtextures_t::iterator i = m_qtextures.begin(); i != m_qtextures.end(); ++i )
523                 {
524                         if ( !( *i ).value.empty() ) {
525                                 qtexture_unrealise( *( *i ).value );
526                         }
527                 }
528         }
529 }
530 bool realised(){
531         return m_unrealised == 0;
532 }
533 };
534
535 TexturesMap* g_texturesmap;
536
537 TexturesCache& GetTexturesCache(){
538         return *g_texturesmap;
539 }
540
541
542 void Textures_Realise(){
543         g_texturesmap->realise();
544 }
545
546 void Textures_Unrealise(){
547         g_texturesmap->unrealise();
548 }
549
550
551 Callback g_texturesModeChangedNotify;
552
553 void Textures_setModeChangedNotify( const Callback& notify ){
554         g_texturesModeChangedNotify = notify;
555 }
556
557 void Textures_ModeChanged(){
558         if ( g_texturesmap->realised() ) {
559                 SetTexParameters( g_texture_mode );
560
561                 for ( TexturesMap::iterator i = g_texturesmap->begin(); i != g_texturesmap->end(); ++i )
562                 {
563                         glBindTexture( GL_TEXTURE_2D, ( *i ).value->texture_number );
564                         SetTexParameters( g_texture_mode );
565                 }
566
567                 glBindTexture( GL_TEXTURE_2D, 0 );
568         }
569         g_texturesModeChangedNotify();
570 }
571
572 void Textures_SetMode( ETexturesMode mode ){
573         if ( g_texture_mode != mode ) {
574                 g_texture_mode = mode;
575
576                 Textures_ModeChanged();
577         }
578 }
579
580 void Textures_setTextureComponents( GLint texture_components ){
581         if ( g_texture_globals.texture_components != texture_components ) {
582                 Textures_Unrealise();
583                 g_texture_globals.texture_components = texture_components;
584                 Textures_Realise();
585         }
586 }
587
588 void Textures_UpdateTextureCompressionFormat(){
589         GLint texture_components = GL_RGBA;
590
591         switch ( g_texture_globals.m_nTextureCompressionFormat )
592         {
593         case ( TEXTURECOMPRESSION_NONE ):
594         {
595                 texture_components = GL_RGBA;
596                 break;
597         }
598         case ( TEXTURECOMPRESSION_RGBA ):
599         {
600                 texture_components = GL_COMPRESSED_RGBA_ARB;
601                 break;
602         }
603         case ( TEXTURECOMPRESSION_RGBA_S3TC_DXT1 ):
604         {
605                 texture_components = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
606                 break;
607         }
608         case ( TEXTURECOMPRESSION_RGBA_S3TC_DXT3 ):
609         {
610                 texture_components = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
611                 break;
612         }
613         case ( TEXTURECOMPRESSION_RGBA_S3TC_DXT5 ):
614         {
615                 texture_components = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
616                 break;
617         }
618         }
619
620         Textures_setTextureComponents( texture_components );
621 }
622
623 void TextureCompressionImport( TextureCompressionFormat& self, int value ){
624         if ( !g_texture_globals.m_bOpenGLCompressionSupported
625                  && g_texture_globals.m_bS3CompressionSupported
626                  && value >= 1 ) {
627                 ++value;
628         }
629         switch ( value )
630         {
631         case 0:
632                 self = TEXTURECOMPRESSION_NONE;
633                 break;
634         case 1:
635                 self = TEXTURECOMPRESSION_RGBA;
636                 break;
637         case 2:
638                 self = TEXTURECOMPRESSION_RGBA_S3TC_DXT1;
639                 break;
640         case 3:
641                 self = TEXTURECOMPRESSION_RGBA_S3TC_DXT3;
642                 break;
643         case 4:
644                 self = TEXTURECOMPRESSION_RGBA_S3TC_DXT5;
645                 break;
646         }
647         Textures_UpdateTextureCompressionFormat();
648 }
649 typedef ReferenceCaller1<TextureCompressionFormat, int, TextureCompressionImport> TextureCompressionImportCaller;
650
651 void TextureGammaImport( float& self, float value ){
652         if ( self != value ) {
653                 Textures_Unrealise();
654                 self = value;
655                 Textures_Realise();
656         }
657 }
658 typedef ReferenceCaller1<float, float, TextureGammaImport> TextureGammaImportCaller;
659
660 void TextureModeImport( ETexturesMode& self, int value ){
661         switch ( value )
662         {
663         case 0:
664                 Textures_SetMode( eTextures_NEAREST );
665                 break;
666         case 1:
667                 Textures_SetMode( eTextures_NEAREST_MIPMAP_NEAREST );
668                 break;
669         case 2:
670                 Textures_SetMode( eTextures_LINEAR );
671                 break;
672         case 3:
673                 Textures_SetMode( eTextures_NEAREST_MIPMAP_LINEAR );
674                 break;
675         case 4:
676                 Textures_SetMode( eTextures_LINEAR_MIPMAP_NEAREST );
677                 break;
678         case 5:
679                 Textures_SetMode( eTextures_LINEAR_MIPMAP_LINEAR );
680                 break;
681         case 6:
682                 Textures_SetMode( eTextures_MAX_ANISOTROPY );
683         }
684 }
685 typedef ReferenceCaller1<ETexturesMode, int, TextureModeImport> TextureModeImportCaller;
686
687 void TextureModeExport( ETexturesMode& self, const IntImportCallback& importer ){
688         switch ( self )
689         {
690         case eTextures_NEAREST:
691                 importer( 0 );
692                 break;
693         case eTextures_NEAREST_MIPMAP_NEAREST:
694                 importer( 1 );
695                 break;
696         case eTextures_LINEAR:
697                 importer( 2 );
698                 break;
699         case eTextures_NEAREST_MIPMAP_LINEAR:
700                 importer( 3 );
701                 break;
702         case eTextures_LINEAR_MIPMAP_NEAREST:
703                 importer( 4 );
704                 break;
705         case eTextures_LINEAR_MIPMAP_LINEAR:
706                 importer( 5 );
707                 break;
708         case eTextures_MAX_ANISOTROPY:
709                 importer( 6 );
710                 break;
711         default:
712                 importer( 4 );
713         }
714 }
715 typedef ReferenceCaller1<ETexturesMode, const IntImportCallback&, TextureModeExport> TextureModeExportCaller;
716
717 void Textures_constructPreferences( PreferencesPage& page ){
718         {
719                 const char* percentages[] = { "12.5%", "25%", "50%", "100%", };
720                 page.appendRadio(
721                         "Texture Quality",
722                         STRING_ARRAY_RANGE( percentages ),
723                         LatchedIntImportCaller( g_Textures_textureQuality ),
724                         IntExportCaller( g_Textures_textureQuality.m_latched )
725                         );
726         }
727         page.appendSpinner(
728                 "Texture Gamma",
729                 1.0,
730                 0.0,
731                 1.0,
732                 FloatImportCallback( TextureGammaImportCaller( g_texture_globals.fGamma ) ),
733                 FloatExportCallback( FloatExportCaller( g_texture_globals.fGamma ) )
734                 );
735         {
736                 const char* texture_mode[] = { "Nearest", "Nearest Mipmap", "Linear", "Bilinear", "Bilinear Mipmap", "Trilinear", "Anisotropy" };
737                 page.appendCombo(
738                         "Texture Render Mode",
739                         STRING_ARRAY_RANGE( texture_mode ),
740                         IntImportCallback( TextureModeImportCaller( g_texture_mode ) ),
741                         IntExportCallback( TextureModeExportCaller( g_texture_mode ) )
742                         );
743         }
744         {
745                 const char* compression_none[] = { "None" };
746                 const char* compression_opengl[] = { "None", "OpenGL ARB" };
747                 const char* compression_s3tc[] = { "None", "S3TC DXT1", "S3TC DXT3", "S3TC DXT5" };
748                 const char* compression_opengl_s3tc[] = { "None", "OpenGL ARB", "S3TC DXT1", "S3TC DXT3", "S3TC DXT5" };
749                 StringArrayRange compression(
750                         ( g_texture_globals.m_bOpenGLCompressionSupported )
751                         ? ( g_texture_globals.m_bS3CompressionSupported )
752                         ? STRING_ARRAY_RANGE( compression_opengl_s3tc )
753                         : STRING_ARRAY_RANGE( compression_opengl )
754                         : ( g_texture_globals.m_bS3CompressionSupported )
755                         ? STRING_ARRAY_RANGE( compression_s3tc )
756                         : STRING_ARRAY_RANGE( compression_none )
757                         );
758                 page.appendCombo(
759                         "Hardware Texture Compression",
760                         compression,
761                         TextureCompressionImportCaller( g_texture_globals.m_nTextureCompressionFormat ),
762                         IntExportCaller( reinterpret_cast<int&>( g_texture_globals.m_nTextureCompressionFormat ) )
763                         );
764         }
765 }
766 void Textures_constructPage( PreferenceGroup& group ){
767         PreferencesPage page( group.createPage( "Textures", "Texture Settings" ) );
768         Textures_constructPreferences( page );
769 }
770 void Textures_registerPreferencesPage(){
771         PreferencesDialog_addDisplayPage( FreeCaller1<PreferenceGroup&, Textures_constructPage>() );
772 }
773
774 void TextureCompression_importString( const char* string ){
775         g_texture_globals.m_nTextureCompressionFormat = static_cast<TextureCompressionFormat>( atoi( string ) );
776         Textures_UpdateTextureCompressionFormat();
777 }
778 typedef FreeCaller1<const char*, TextureCompression_importString> TextureCompressionImportStringCaller;
779
780
781 void Textures_Construct(){
782         g_texturesmap = new TexturesMap;
783
784         GlobalPreferenceSystem().registerPreference( "TextureCompressionFormat", TextureCompressionImportStringCaller(), IntExportStringCaller( reinterpret_cast<int&>( g_texture_globals.m_nTextureCompressionFormat ) ) );
785         GlobalPreferenceSystem().registerPreference( "TextureFiltering", IntImportStringCaller( reinterpret_cast<int&>( g_texture_mode ) ), IntExportStringCaller( reinterpret_cast<int&>( g_texture_mode ) ) );
786         GlobalPreferenceSystem().registerPreference( "TextureQuality", IntImportStringCaller( g_Textures_textureQuality.m_latched ), IntExportStringCaller( g_Textures_textureQuality.m_latched ) );
787         GlobalPreferenceSystem().registerPreference( "SI_Gamma", FloatImportStringCaller( g_texture_globals.fGamma ), FloatExportStringCaller( g_texture_globals.fGamma ) );
788
789         g_Textures_textureQuality.useLatched();
790
791         Textures_registerPreferencesPage();
792
793         Textures_ModeChanged();
794 }
795 void Textures_Destroy(){
796         delete g_texturesmap;
797 }
798
799
800 #include "modulesystem/modulesmap.h"
801 #include "modulesystem/singletonmodule.h"
802 #include "modulesystem/moduleregistry.h"
803
804 class TexturesDependencies :
805         public GlobalRadiantModuleRef,
806         public GlobalOpenGLModuleRef,
807         public GlobalPreferenceSystemModuleRef
808 {
809 ImageModulesRef m_image_modules;
810 public:
811 TexturesDependencies() :
812         m_image_modules( GlobalRadiant().getRequiredGameDescriptionKeyValue( "texturetypes" ) ){
813 }
814 ImageModules& getImageModules(){
815         return m_image_modules.get();
816 }
817 };
818
819 class TexturesAPI
820 {
821 TexturesCache* m_textures;
822 public:
823 typedef TexturesCache Type;
824 STRING_CONSTANT( Name, "*" );
825
826 TexturesAPI(){
827         Textures_Construct();
828
829         m_textures = &GetTexturesCache();
830 }
831 ~TexturesAPI(){
832         Textures_Destroy();
833 }
834 TexturesCache* getTable(){
835         return m_textures;
836 }
837 };
838
839 typedef SingletonModule<TexturesAPI, TexturesDependencies> TexturesModule;
840 typedef Static<TexturesModule> StaticTexturesModule;
841 StaticRegisterModule staticRegisterTextures( StaticTexturesModule::instance() );
842
843 ImageModules& Textures_getImageModules(){
844         return StaticTexturesModule::instance().getDependencies().getImageModules();
845 }