2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
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.
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.
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
24 #include "debugging/debugging.h"
27 #include "itextures.h"
29 #include "preferencesystem.h"
32 #include "texturelib.h"
33 #include "container/hashfunc.h"
34 #include "container/cache.h"
35 #include "generic/callback.h"
40 #include "preferences.h"
46 eTextures_NEAREST = 0,
47 eTextures_NEAREST_MIPMAP_NEAREST = 1,
48 eTextures_NEAREST_MIPMAP_LINEAR = 2,
50 eTextures_LINEAR_MIPMAP_NEAREST = 4,
51 eTextures_LINEAR_MIPMAP_LINEAR = 5,
52 eTextures_MAX_ANISOTROPY = 6,
55 enum TextureCompressionFormat
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,
64 struct texture_globals_t
67 // texture compression format
68 TextureCompressionFormat m_nTextureCompressionFormat;
72 bool bTextureCompressionSupported; // is texture compression supported by hardware?
73 GLint texture_components;
75 // temporary values that should be initialised only once at run-time
76 bool m_bOpenGLCompressionSupported;
77 bool m_bS3CompressionSupported;
79 texture_globals_t(GLint components) :
80 m_nTextureCompressionFormat(TEXTURECOMPRESSION_NONE),
82 bTextureCompressionSupported(false),
83 texture_components(components),
84 m_bOpenGLCompressionSupported(false),
85 m_bS3CompressionSupported(false)
90 texture_globals_t g_texture_globals(GL_RGBA);
92 void SetTexParameters(ETexturesMode mode)
94 float maxAniso = QGL_maxTextureAnisotropy();
96 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
98 if(mode == eTextures_MAX_ANISOTROPY)
99 mode = eTextures_LINEAR_MIPMAP_LINEAR;
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 );
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 );
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 );
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 );
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 );
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 );
127 case eTextures_MAX_ANISOTROPY:
128 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAniso);
131 globalOutputStream() << "invalid texture mode\n";
135 ETexturesMode g_texture_mode = eTextures_LINEAR_MIPMAP_LINEAR;
140 byte g_gammatable[256];
141 void ResampleGamma(float fGamma)
146 for (i = 0; i < 256; i++)
150 for (i = 0; i < 256; i++)
152 inf = (int)(255 * pow ( static_cast<double>((i + 0.5) / 255.5) , static_cast<double>(fGamma) ) + 0.5);
157 g_gammatable[i] = inf;
162 inline const int& min_int(const int& left, const int& right)
164 return std::min(left, right);
167 int max_tex_size = 0;
168 const int max_texture_quality = 3;
169 LatchedInt g_Textures_textureQuality(3, "Texture Quality");
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)
175 static float fGamma = -1;
178 int nCount = nWidth * nHeight;
180 if (fGamma != g_texture_globals.fGamma)
182 fGamma = g_texture_globals.fGamma;
183 ResampleGamma(fGamma);
189 total[0] = total[1] = total[2] = 0.0f;
191 // resample texture gamma according to user settings
192 for (int i = 0; i < (nCount * 4); i += 4)
194 for (int j = 0; j < 3; j++)
196 total[j] += (pPixels + i)[j];
197 byte b = (pPixels + i)[j];
198 (pPixels + i)[j] = g_gammatable[b];
202 q->color[0] = total[0] / (nCount * 255);
203 q->color[1] = total[1] / (nCount * 255);
204 q->color[2] = total[2] / (nCount * 255);
206 glGenTextures (1, &q->texture_number);
208 glBindTexture( GL_TEXTURE_2D, q->texture_number );
210 SetTexParameters(g_texture_mode);
213 while(gl_width < nWidth)
217 while(gl_height < nHeight)
220 bool resampled = false;
221 if (!(gl_width == nWidth && gl_height == nHeight))
224 outpixels = (byte *)malloc(gl_width * gl_height * 4);
225 R_ResampleTexture(pPixels, nWidth, nHeight, outpixels, gl_width, gl_height, 4);
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);
236 while (gl_width > target_width || gl_height > target_height)
238 GL_MipReduce(outpixels, outpixels, gl_width, gl_height, target_width, target_height);
240 if (gl_width > target_width)
242 if (gl_height > target_height)
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)
250 GL_MipReduce(outpixels, outpixels, gl_width, gl_height, 1, 1);
257 glTexImage2D(GL_TEXTURE_2D, mip++, g_texture_globals.texture_components, gl_width, gl_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, outpixels);
260 glBindTexture(GL_TEXTURE_2D, 0);
271 void Texture_InitPalette (byte *pal)
276 byte gammatable[256];
279 gamma = g_texture_globals.fGamma;
283 for (i=0 ; i<256 ; i++)
287 for (i=0 ; i<256 ; i++)
289 inf = (int)(255 * pow ( (i+0.5)/255.5 , gamma ) + 0.5);
298 for (i=0 ; i<256 ; i++)
300 r = gammatable[pal[0]];
301 g = gammatable[pal[1]];
302 b = gammatable[pal[2]];
305 //v = (r<<24) + (g<<16) + (b<<8) + 255;
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;
322 HashTable<CopiedString, CopiedString, HashStringNoCase, StringEqualNoCase> strings;
323 strings["Monkey"] = "bleh";
324 strings["MonkeY"] = "blah";
328 const TestHashtable g_testhashtable;
332 typedef std::pair<LoadImageCallback, CopiedString> TextureKey;
334 void qtexture_realise(qtexture_t& texture, const TextureKey& key)
336 texture.texture_number = 0;
337 if(!string_empty(key.second.c_str()))
339 Image* image = key.first.loadImage(key.second.c_str());
342 LoadTextureRGBA(&texture, image->getRGBAPixels(), image->getWidth(), image->getHeight());
343 texture.surfaceFlags = image->getSurfaceFlags();
344 texture.contentFlags = image->getContentFlags();
345 texture.value = image->getValue();
347 globalOutputStream() << "Loaded Texture: \"" << key.second.c_str() << "\"\n";
348 GlobalOpenGL_debugAssertNoErrors();
352 globalErrorStream() << "Texture load failed: \"" << key.second.c_str() << "\"\n";
357 void qtexture_unrealise(qtexture_t& texture)
359 if(GlobalOpenGL().contextValid && texture.texture_number != 0)
361 glDeleteTextures(1, &texture.texture_number);
362 GlobalOpenGL_debugAssertNoErrors();
366 class TextureKeyEqualNoCase
369 bool operator()(const TextureKey& key, const TextureKey& other) const
371 return key.first == other.first && string_equal_nocase(key.second.c_str(), other.second.c_str());
375 class TextureKeyHashNoCase
378 typedef hash_t hash_type;
379 hash_t operator()(const TextureKey& key) const
381 return hash_combine(string_hash_nocase(key.second.c_str()), pod_hash(key.first));
385 #define DEBUG_TEXTURES 0
387 class TexturesMap : public TexturesCache
389 class TextureConstructor
391 TexturesMap* m_cache;
393 explicit TextureConstructor(TexturesMap* cache)
397 qtexture_t* construct(const TextureKey& key)
399 qtexture_t* texture = new qtexture_t(key.first, key.second.c_str());
400 if(m_cache->realised())
402 qtexture_realise(*texture, key);
406 void destroy(qtexture_t* texture)
408 if(m_cache->realised())
410 qtexture_unrealise(*texture);
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;
422 TexturesMap() : m_qtextures(TextureConstructor(this)), m_observer(0), m_unrealised(1)
425 typedef qtextures_t::iterator iterator;
429 return m_qtextures.begin();
433 return m_qtextures.end();
436 LoadImageCallback defaultLoader() const
438 return LoadImageCallback(0, QERApp_LoadImage);
440 Image* loadImage(const char* name)
442 return defaultLoader().loadImage(name);
444 qtexture_t* capture(const char* name)
446 return capture(defaultLoader(), name);
448 qtexture_t* capture(const LoadImageCallback& loader, const char* name)
451 globalOutputStream() << "textures capture: " << makeQuoted(name) << '\n';
453 return m_qtextures.capture(TextureKey(loader, name)).get();
455 void release(qtexture_t* texture)
458 globalOutputStream() << "textures release: " << makeQuoted(texture->name) << '\n';
460 m_qtextures.release(TextureKey(texture->load, texture->name));
462 void attach(TexturesCacheObserver& observer)
464 ASSERT_MESSAGE(m_observer == 0, "TexturesMap::attach: cannot attach observer");
465 m_observer = &observer;
467 void detach(TexturesCacheObserver& observer)
469 ASSERT_MESSAGE(m_observer == &observer, "TexturesMap::detach: cannot detach observer");
474 if(--m_unrealised == 0)
476 g_texture_globals.bTextureCompressionSupported = false;
478 if(GlobalOpenGL().ARB_texture_compression())
480 g_texture_globals.bTextureCompressionSupported = true;
481 g_texture_globals.m_bOpenGLCompressionSupported = true;
484 if(GlobalOpenGL().EXT_texture_compression_s3tc())
486 g_texture_globals.bTextureCompressionSupported = true;
487 g_texture_globals.m_bS3CompressionSupported = true;
490 switch(g_texture_globals.texture_components)
494 case GL_COMPRESSED_RGBA_ARB:
495 if (!g_texture_globals.m_bOpenGLCompressionSupported)
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;
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)
507 globalOutputStream() << "OpenGL extension GL_EXT_texture_compression_s3tc not supported by current graphics drivers\n";
508 if(g_texture_globals.m_bOpenGLCompressionSupported)
510 g_texture_globals.m_nTextureCompressionFormat = TEXTURECOMPRESSION_RGBA;
511 g_texture_globals.texture_components = GL_COMPRESSED_RGBA_ARB;
515 g_texture_globals.m_nTextureCompressionFormat = TEXTURECOMPRESSION_NONE;
516 g_texture_globals.texture_components = GL_RGBA;
521 globalOutputStream() << "Unknown texture compression selected, reverting\n";
522 g_texture_globals.m_nTextureCompressionFormat = TEXTURECOMPRESSION_NONE;
523 g_texture_globals.texture_components = GL_RGBA;
528 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
529 if(max_tex_size == 0)
534 for(qtextures_t::iterator i = m_qtextures.begin(); i != m_qtextures.end(); ++i)
536 if(!(*i).value.empty())
538 qtexture_realise(*(*i).value, (*i).key);
543 m_observer->realise();
549 if(++m_unrealised == 1)
553 m_observer->unrealise();
555 for(qtextures_t::iterator i = m_qtextures.begin(); i != m_qtextures.end(); ++i)
557 if(!(*i).value.empty())
559 qtexture_unrealise(*(*i).value);
566 return m_unrealised == 0;
570 TexturesMap* g_texturesmap;
572 TexturesCache& GetTexturesCache()
574 return *g_texturesmap;
578 void Textures_Realise()
580 g_texturesmap->realise();
583 void Textures_Unrealise()
585 g_texturesmap->unrealise();
589 Callback g_texturesModeChangedNotify;
591 void Textures_setModeChangedNotify(const Callback& notify)
593 g_texturesModeChangedNotify = notify;
596 void Textures_ModeChanged()
598 if(g_texturesmap->realised())
600 SetTexParameters(g_texture_mode);
602 for(TexturesMap::iterator i = g_texturesmap->begin(); i != g_texturesmap->end(); ++i)
604 glBindTexture (GL_TEXTURE_2D, (*i).value->texture_number);
605 SetTexParameters(g_texture_mode);
608 glBindTexture( GL_TEXTURE_2D, 0 );
610 g_texturesModeChangedNotify();
613 void Textures_SetMode(ETexturesMode mode)
615 if(g_texture_mode != mode)
617 g_texture_mode = mode;
619 Textures_ModeChanged();
623 void Textures_setTextureComponents(GLint texture_components)
625 if(g_texture_globals.texture_components != texture_components)
627 Textures_Unrealise();
628 g_texture_globals.texture_components = texture_components;
633 void Textures_UpdateTextureCompressionFormat()
635 GLint texture_components = GL_RGBA;
637 switch (g_texture_globals.m_nTextureCompressionFormat)
639 case (TEXTURECOMPRESSION_NONE):
641 texture_components = GL_RGBA;
644 case (TEXTURECOMPRESSION_RGBA):
646 texture_components = GL_COMPRESSED_RGBA_ARB;
649 case (TEXTURECOMPRESSION_RGBA_S3TC_DXT1):
651 texture_components = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
654 case (TEXTURECOMPRESSION_RGBA_S3TC_DXT3):
656 texture_components = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
659 case (TEXTURECOMPRESSION_RGBA_S3TC_DXT5):
661 texture_components = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
666 Textures_setTextureComponents(texture_components);
669 void TextureCompressionImport(TextureCompressionFormat& self, int value)
671 if(!g_texture_globals.m_bOpenGLCompressionSupported
672 && g_texture_globals.m_bS3CompressionSupported
680 self = TEXTURECOMPRESSION_NONE;
683 self = TEXTURECOMPRESSION_RGBA;
686 self = TEXTURECOMPRESSION_RGBA_S3TC_DXT1;
689 self = TEXTURECOMPRESSION_RGBA_S3TC_DXT3;
692 self = TEXTURECOMPRESSION_RGBA_S3TC_DXT5;
695 Textures_UpdateTextureCompressionFormat();
697 typedef ReferenceCaller1<TextureCompressionFormat, int, TextureCompressionImport> TextureCompressionImportCaller;
699 void TextureGammaImport(float& self, float value)
703 Textures_Unrealise();
708 typedef ReferenceCaller1<float, float, TextureGammaImport> TextureGammaImportCaller;
710 void TextureModeImport(ETexturesMode& self, int value)
715 Textures_SetMode(eTextures_NEAREST);
718 Textures_SetMode(eTextures_NEAREST_MIPMAP_NEAREST);
721 Textures_SetMode(eTextures_LINEAR);
724 Textures_SetMode(eTextures_NEAREST_MIPMAP_LINEAR);
727 Textures_SetMode(eTextures_LINEAR_MIPMAP_NEAREST);
730 Textures_SetMode(eTextures_LINEAR_MIPMAP_LINEAR);
733 Textures_SetMode(eTextures_MAX_ANISOTROPY);
736 typedef ReferenceCaller1<ETexturesMode, int, TextureModeImport> TextureModeImportCaller;
738 void TextureModeExport(ETexturesMode& self, const IntImportCallback& importer)
742 case eTextures_NEAREST:
745 case eTextures_NEAREST_MIPMAP_NEAREST:
748 case eTextures_LINEAR:
751 case eTextures_NEAREST_MIPMAP_LINEAR:
754 case eTextures_LINEAR_MIPMAP_NEAREST:
757 case eTextures_LINEAR_MIPMAP_LINEAR:
760 case eTextures_MAX_ANISOTROPY:
767 typedef ReferenceCaller1<ETexturesMode, const IntImportCallback&, TextureModeExport> TextureModeExportCaller;
769 void Textures_constructPreferences(PreferencesPage& page)
772 const char* percentages[] = { "12.5%", "25%", "50%", "100%", };
775 STRING_ARRAY_RANGE(percentages),
776 LatchedIntImportCaller(g_Textures_textureQuality),
777 IntExportCaller(g_Textures_textureQuality.m_latched)
785 FloatImportCallback(TextureGammaImportCaller(g_texture_globals.fGamma)),
786 FloatExportCallback(FloatExportCaller(g_texture_globals.fGamma))
789 const char* texture_mode[] = { "Nearest", "Nearest Mipmap", "Linear", "Bilinear", "Bilinear Mipmap", "Trilinear", "Anisotropy" };
791 "Texture Render Mode",
792 STRING_ARRAY_RANGE(texture_mode),
793 IntImportCallback(TextureModeImportCaller(g_texture_mode)),
794 IntExportCallback(TextureModeExportCaller(g_texture_mode))
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)
812 "Hardware Texture Compression",
814 TextureCompressionImportCaller(g_texture_globals.m_nTextureCompressionFormat),
815 IntExportCaller(reinterpret_cast<int&>(g_texture_globals.m_nTextureCompressionFormat))
819 void Textures_constructPage(PreferenceGroup& group)
821 PreferencesPage page(group.createPage("Textures", "Texture Settings"));
822 Textures_constructPreferences(page);
824 void Textures_registerPreferencesPage()
826 PreferencesDialog_addDisplayPage(FreeCaller1<PreferenceGroup&, Textures_constructPage>());
829 void TextureCompression_importString(const char* string)
831 g_texture_globals.m_nTextureCompressionFormat = static_cast<TextureCompressionFormat>(atoi(string));
832 Textures_UpdateTextureCompressionFormat();
834 typedef FreeCaller1<const char*, TextureCompression_importString> TextureCompressionImportStringCaller;
837 void Textures_Construct()
839 g_texturesmap = new TexturesMap;
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));
846 g_Textures_textureQuality.useLatched();
848 Textures_registerPreferencesPage();
850 Textures_ModeChanged();
852 void Textures_Destroy()
854 delete g_texturesmap;
858 #include "modulesystem/modulesmap.h"
859 #include "modulesystem/singletonmodule.h"
860 #include "modulesystem/moduleregistry.h"
862 class TexturesDependencies :
863 public GlobalRadiantModuleRef,
864 public GlobalOpenGLModuleRef,
865 public GlobalPreferenceSystemModuleRef
867 ImageModulesRef m_image_modules;
869 TexturesDependencies() :
870 m_image_modules(GlobalRadiant().getRequiredGameDescriptionKeyValue("texturetypes"))
873 ImageModules& getImageModules()
875 return m_image_modules.get();
881 TexturesCache* m_textures;
883 typedef TexturesCache Type;
884 STRING_CONSTANT(Name, "*");
888 Textures_Construct();
890 m_textures = &GetTexturesCache();
896 TexturesCache* getTable()
902 typedef SingletonModule<TexturesAPI, TexturesDependencies> TexturesModule;
903 typedef Static<TexturesModule> StaticTexturesModule;
904 StaticRegisterModule staticRegisterTextures(StaticTexturesModule::instance());
906 ImageModules& Textures_getImageModules()
908 return StaticTexturesModule::instance().getDependencies().getImageModules();