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