2 Copyright (c) 2001, Loki software, inc.
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
8 Redistributions of source code must retain the above copyright notice, this list
9 of conditions and the following disclaimer.
11 Redistributions in binary form must reproduce the above copyright notice, this
12 list of conditions and the following disclaimer in the documentation and/or
13 other materials provided with the distribution.
15 Neither the name of Loki software nor the names of its contributors may be used
16 to endorse or promote products derived from this software without specific prior
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
23 DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 // Shaders Manager Plugin
34 // Leonardo Zide (leo@lokigames.com)
44 #include "ifilesystem.h"
46 #include "iscriplib.h"
47 #include "itextures.h"
48 #include "qerplugin.h"
51 #include <glib/gslist.h>
53 #include "debugging/debugging.h"
54 #include "string/pooledstring.h"
55 #include "math/vector.h"
56 #include "generic/callback.h"
57 #include "generic/referencecounted.h"
58 #include "stream/memstream.h"
59 #include "stream/stringstream.h"
60 #include "stream/textfilestream.h"
65 #include "shaderlib.h"
66 #include "texturelib.h"
68 #include "moduleobservers.h"
69 #include "archivelib.h"
72 const char* g_shadersExtension = "";
73 const char* g_shadersDirectory = "";
74 bool g_enableDefaultShaders = true;
75 ShaderLanguage g_shaderLanguage = SHADERLANGUAGE_QUAKE3;
76 bool g_useShaderList = true;
77 _QERPlugImageTable* g_bitmapModule = 0;
78 const char* g_texturePrefix = "textures/";
80 void ActiveShaders_IteratorBegin();
81 bool ActiveShaders_IteratorAtEnd();
82 IShader *ActiveShaders_IteratorCurrent();
83 void ActiveShaders_IteratorIncrement();
84 Callback g_ActiveShadersChangedNotify;
87 void LoadShaderFile (const char *filename);
88 qtexture_t *Texture_ForName (const char *filename);
92 NOTE TTimo: there is an important distinction between SHADER_NOT_FOUND and SHADER_NOTEX:
93 SHADER_NOT_FOUND means we didn't find the raw texture or the shader for this
94 SHADER_NOTEX means we recognize this as a shader script, but we are missing the texture to represent it
95 this was in the initial design of the shader code since early GtkRadiant alpha, and got sort of foxed in 1.2 and put back in
98 Image* loadBitmap(void* environment, const char* name)
100 DirectoryArchiveFile file(name, name);
103 return g_bitmapModule->loadImage(file);
108 inline byte* getPixel(byte* pixels, int width, int height, int x, int y)
110 return pixels + (((((y + height) % height) * width) + ((x + width) % width)) * 4);
120 Image& convertHeightmapToNormalmap(Image& heightmap, float scale)
122 int w = heightmap.getWidth();
123 int h = heightmap.getHeight();
125 Image& normalmap = *(new RGBAImage(heightmap.getWidth(), heightmap.getHeight()));
127 byte* in = heightmap.getRGBAPixels();
128 byte* out = normalmap.getRGBAPixels();
132 const int kernelSize = 2;
133 KernelElement kernel_du[kernelSize] = {
137 KernelElement kernel_dv[kernelSize] = {
143 const int kernelSize = 6;
144 KernelElement kernel_du[kernelSize] = {
152 KernelElement kernel_dv[kernelSize] = {
169 for(KernelElement* i = kernel_du; i != kernel_du + kernelSize; ++i)
171 du += (getPixel(in, w, h, x + (*i).x, y + (*i).y)[0] / 255.0) * (*i).w;
174 for(KernelElement* i = kernel_dv; i != kernel_dv + kernelSize; ++i)
176 dv += (getPixel(in, w, h, x + (*i).x, y + (*i).y)[0] / 255.0) * (*i).w;
179 float nx = -du * scale;
180 float ny = -dv * scale;
184 float norm = 1.0/sqrt(nx*nx + ny*ny + nz*nz);
185 out[0] = float_to_integer(((nx * norm) + 1) * 127.5);
186 out[1] = float_to_integer(((ny * norm) + 1) * 127.5);
187 out[2] = float_to_integer(((nz * norm) + 1) * 127.5);
200 Image* loadHeightmap(void* environment, const char* name)
202 Image* heightmap = GlobalTexturesCache().loadImage(name);
205 Image& normalmap = convertHeightmapToNormalmap(*heightmap, *reinterpret_cast<float*>(environment));
206 heightmap->release();
213 Image* loadSpecial(void* environment, const char* name)
215 if(*name == '_') // special image
217 StringOutputStream bitmapName(256);
218 bitmapName << GlobalRadiant().getAppPath() << "bitmaps/" << name + 1 << ".bmp";
219 Image* image = loadBitmap(environment, bitmapName.c_str());
225 return GlobalTexturesCache().loadImage(name);
228 class ShaderPoolContext
231 typedef Static<StringPool, ShaderPoolContext> ShaderPool;
232 typedef PooledString<ShaderPool> ShaderString;
233 typedef ShaderString ShaderVariable;
234 typedef ShaderString ShaderValue;
235 typedef CopiedString TextureExpression;
237 // clean a texture name to the qtexture_t name format we use internally
238 // NOTE: case sensitivity: the engine is case sensitive. we store the shader name with case information and save with case
239 // information as well. but we assume there won't be any case conflict and so when doing lookups based on shader name,
240 // we compare as case insensitive. That is Radiant is case insensitive, but knows that the engine is case sensitive.
241 //++timo FIXME: we need to put code somewhere to detect when two shaders that are case insensitive equal are present
242 template<typename StringType>
243 void parseTextureName(StringType& name, const char* token)
245 StringOutputStream cleaned(256);
246 cleaned << PathCleaned(token);
247 name = CopiedString(StringRange(cleaned.c_str(), path_get_filename_base_end(cleaned.c_str()))).c_str(); // remove extension
250 bool Tokeniser_parseTextureName(Tokeniser& tokeniser, TextureExpression& name)
252 const char* token = tokeniser.getToken();
255 Tokeniser_unexpectedError(tokeniser, token, "#texture-name");
258 parseTextureName(name, token);
262 bool Tokeniser_parseShaderName(Tokeniser& tokeniser, CopiedString& name)
264 const char* token = tokeniser.getToken();
267 Tokeniser_unexpectedError(tokeniser, token, "#shader-name");
270 parseTextureName(name, token);
274 bool Tokeniser_parseString(Tokeniser& tokeniser, ShaderString& string)
276 const char* token = tokeniser.getToken();
279 Tokeniser_unexpectedError(tokeniser, token, "#string");
288 typedef std::list<ShaderVariable> ShaderParameters;
289 typedef std::list<ShaderVariable> ShaderArguments;
291 typedef std::pair<ShaderVariable, ShaderVariable> BlendFuncExpression;
295 std::size_t m_refcount;
299 ShaderParameters m_params;
301 TextureExpression m_textureName;
302 TextureExpression m_diffuse;
303 TextureExpression m_bump;
304 ShaderValue m_heightmapScale;
305 TextureExpression m_specular;
306 TextureExpression m_lightFalloffImage;
312 IShader::EAlphaFunc m_AlphaFunc;
315 IShader::ECull m_Cull;
330 ASSERT_MESSAGE(m_refcount != 0, "shader reference-count going below zero");
331 if(--m_refcount == 0)
337 std::size_t refcount()
342 const char* getName() const
344 return m_Name.c_str();
346 void setName(const char* name)
351 // -----------------------------------------
353 bool parseDoom3(Tokeniser& tokeniser);
354 bool parseQuake3(Tokeniser& tokeniser);
355 bool parseTemplate(Tokeniser& tokeniser);
358 void CreateDefault(const char *name)
360 if(g_enableDefaultShaders)
362 m_textureName = name;
372 class MapLayerTemplate
374 TextureExpression m_texture;
375 BlendFuncExpression m_blendFunc;
376 bool m_clampToBorder;
377 ShaderValue m_alphaTest;
379 MapLayerTemplate(const TextureExpression& texture, const BlendFuncExpression& blendFunc, bool clampToBorder, const ShaderValue& alphaTest) :
381 m_blendFunc(blendFunc),
382 m_clampToBorder(false),
383 m_alphaTest(alphaTest)
386 const TextureExpression& texture() const
390 const BlendFuncExpression& blendFunc() const
394 bool clampToBorder() const
396 return m_clampToBorder;
398 const ShaderValue& alphaTest() const
403 typedef std::vector<MapLayerTemplate> MapLayers;
408 bool Doom3Shader_parseHeightmap(Tokeniser& tokeniser, TextureExpression& bump, ShaderValue& heightmapScale)
410 RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "("));
411 RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, bump));
412 RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ","));
413 RETURN_FALSE_IF_FAIL(Tokeniser_parseString(tokeniser, heightmapScale));
414 RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")"));
418 bool Doom3Shader_parseAddnormals(Tokeniser& tokeniser, TextureExpression& bump)
420 RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "("));
421 RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, bump));
422 RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ","));
423 RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "heightmap"));
424 TextureExpression heightmapName;
425 ShaderValue heightmapScale;
426 RETURN_FALSE_IF_FAIL(Doom3Shader_parseHeightmap(tokeniser, heightmapName, heightmapScale));
427 RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")"));
431 bool Doom3Shader_parseBumpmap(Tokeniser& tokeniser, TextureExpression& bump, ShaderValue& heightmapScale)
433 const char* token = tokeniser.getToken();
436 Tokeniser_unexpectedError(tokeniser, token, "#bumpmap");
439 if(string_equal(token, "heightmap"))
441 RETURN_FALSE_IF_FAIL(Doom3Shader_parseHeightmap(tokeniser, bump, heightmapScale));
443 else if(string_equal(token, "addnormals"))
445 RETURN_FALSE_IF_FAIL(Doom3Shader_parseAddnormals(tokeniser, bump));
449 parseTextureName(bump, token);
467 TextureExpression m_texture;
468 BlendFuncExpression m_blendFunc;
469 bool m_clampToBorder;
470 ShaderValue m_alphaTest;
471 ShaderValue m_heightmapScale;
473 LayerTemplate() : m_type(LAYER_NONE), m_blendFunc("GL_ONE", "GL_ZERO"), m_clampToBorder(false), m_alphaTest("-1"), m_heightmapScale("0")
478 bool parseShaderParameters(Tokeniser& tokeniser, ShaderParameters& params)
480 Tokeniser_parseToken(tokeniser, "(");
483 const char* param = tokeniser.getToken();
484 if(string_equal(param, ")"))
488 params.push_back(param);
489 const char* comma = tokeniser.getToken();
490 if(string_equal(comma, ")"))
494 if(!string_equal(comma, ","))
496 Tokeniser_unexpectedError(tokeniser, comma, ",");
503 bool ShaderTemplate::parseTemplate(Tokeniser& tokeniser)
505 m_Name = tokeniser.getToken();
506 if(!parseShaderParameters(tokeniser, m_params))
508 globalErrorStream() << "shader template: " << makeQuoted(m_Name.c_str()) << ": parameter parse failed\n";
512 return parseDoom3(tokeniser);
515 bool ShaderTemplate::parseDoom3(Tokeniser& tokeniser)
517 LayerTemplate currentLayer;
520 // we need to read until we hit a balanced }
524 tokeniser.nextLine();
525 const char* token = tokeniser.getToken();
530 if(string_equal(token, "{"))
535 else if(string_equal(token, "}"))
538 if(depth < 0) // error
542 if(depth == 0) // end of shader
546 if(depth == 1) // end of layer
548 if(currentLayer.m_type == LAYER_DIFFUSEMAP)
550 m_diffuse = currentLayer.m_texture;
552 else if(currentLayer.m_type == LAYER_BUMPMAP)
554 m_bump = currentLayer.m_texture;
556 else if(currentLayer.m_type == LAYER_SPECULARMAP)
558 m_specular = currentLayer.m_texture;
560 else if(!string_empty(currentLayer.m_texture.c_str()))
562 m_layers.push_back(MapLayerTemplate(
563 currentLayer.m_texture.c_str(),
564 currentLayer.m_blendFunc,
565 currentLayer.m_clampToBorder,
566 currentLayer.m_alphaTest
569 currentLayer.m_type = LAYER_NONE;
570 currentLayer.m_texture = "";
575 if(depth == 2) // in layer
577 if(string_equal_nocase(token, "blend"))
579 const char* blend = tokeniser.getToken();
583 Tokeniser_unexpectedError(tokeniser, blend, "#blend");
587 if(string_equal_nocase(blend, "diffusemap"))
589 currentLayer.m_type = LAYER_DIFFUSEMAP;
591 else if(string_equal_nocase(blend, "bumpmap"))
593 currentLayer.m_type = LAYER_BUMPMAP;
595 else if(string_equal_nocase(blend, "specularmap"))
597 currentLayer.m_type = LAYER_SPECULARMAP;
601 currentLayer.m_blendFunc.first = blend;
603 const char* comma = tokeniser.getToken();
607 Tokeniser_unexpectedError(tokeniser, comma, "#comma");
611 if(string_equal(comma, ","))
613 RETURN_FALSE_IF_FAIL(Tokeniser_parseString(tokeniser, currentLayer.m_blendFunc.second));
617 currentLayer.m_blendFunc.second = "";
618 tokeniser.ungetToken();
622 else if(string_equal_nocase(token, "map"))
624 if(currentLayer.m_type == LAYER_BUMPMAP)
626 RETURN_FALSE_IF_FAIL(Doom3Shader_parseBumpmap(tokeniser, currentLayer.m_texture, currentLayer.m_heightmapScale));
630 const char* map = tokeniser.getToken();
634 Tokeniser_unexpectedError(tokeniser, map, "#map");
638 if(string_equal(map, "makealpha"))
640 RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "("));
641 const char* texture = tokeniser.getToken();
644 Tokeniser_unexpectedError(tokeniser, texture, "#texture");
647 currentLayer.m_texture = texture;
648 RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")"));
652 parseTextureName(currentLayer.m_texture, map);
656 else if(string_equal_nocase(token, "zeroclamp"))
658 currentLayer.m_clampToBorder = true;
661 else if(string_equal_nocase(token, "alphaTest"))
663 Tokeniser_getFloat(tokeniser, currentLayer.m_alphaTest);
669 if(string_equal_nocase(token, "qer_editorimage"))
671 RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, m_textureName));
673 else if (string_equal_nocase(token, "qer_trans"))
675 m_fTrans = string_read_float(tokeniser.getToken());
676 m_nFlags |= QER_TRANS;
678 else if (string_equal_nocase(token, "translucent"))
681 m_nFlags |= QER_TRANS;
683 else if (string_equal(token, "DECAL_MACRO"))
686 m_nFlags |= QER_TRANS;
688 else if (string_equal_nocase(token, "bumpmap"))
690 RETURN_FALSE_IF_FAIL(Doom3Shader_parseBumpmap(tokeniser, m_bump, m_heightmapScale));
692 else if (string_equal_nocase(token, "diffusemap"))
694 RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, m_diffuse));
696 else if (string_equal_nocase(token, "specularmap"))
698 RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, m_specular));
700 else if (string_equal_nocase(token, "twosided"))
702 m_Cull = IShader::eCullNone;
703 m_nFlags |= QER_CULL;
705 else if (string_equal_nocase(token, "nodraw"))
707 m_nFlags |= QER_NODRAW;
709 else if (string_equal_nocase(token, "nonsolid"))
711 m_nFlags |= QER_NONSOLID;
713 else if (string_equal_nocase(token, "liquid"))
715 m_nFlags |= QER_WATER;
717 else if (string_equal_nocase(token, "areaportal"))
719 m_nFlags |= QER_AREAPORTAL;
721 else if (string_equal_nocase(token, "playerclip")
722 || string_equal_nocase(token, "monsterclip")
723 || string_equal_nocase(token, "ikclip")
724 || string_equal_nocase(token, "moveableclip"))
726 m_nFlags |= QER_CLIP;
728 if (string_equal_nocase(token, "fogLight"))
732 else if (!isFog && string_equal_nocase(token, "lightFalloffImage"))
734 const char* lightFalloffImage = tokeniser.getToken();
735 if(lightFalloffImage == 0)
737 Tokeniser_unexpectedError(tokeniser, lightFalloffImage, "#lightFalloffImage");
740 if(string_equal_nocase(lightFalloffImage, "makeintensity"))
742 RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "("));
743 TextureExpression name;
744 RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, name));
745 m_lightFalloffImage = name;
746 RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")"));
750 m_lightFalloffImage = lightFalloffImage;
756 if(string_empty(m_textureName.c_str()))
758 m_textureName = m_diffuse;
764 typedef SmartPointer<ShaderTemplate> ShaderTemplatePointer;
765 typedef std::map<CopiedString, ShaderTemplatePointer> ShaderTemplateMap;
767 ShaderTemplateMap g_shaders;
768 ShaderTemplateMap g_shaderTemplates;
770 ShaderTemplate* findTemplate(const char* name)
772 ShaderTemplateMap::iterator i = g_shaderTemplates.find(name);
773 if(i != g_shaderTemplates.end())
775 return (*i).second.get();
780 class ShaderDefinition
783 ShaderDefinition(ShaderTemplate* shaderTemplate, const ShaderArguments& args, const char* filename)
784 : shaderTemplate(shaderTemplate), args(args), filename(filename)
787 ShaderTemplate* shaderTemplate;
788 ShaderArguments args;
789 const char* filename;
792 typedef std::map<CopiedString, ShaderDefinition> ShaderDefinitionMap;
794 ShaderDefinitionMap g_shaderDefinitions;
796 bool parseTemplateInstance(Tokeniser& tokeniser, const char* filename)
799 RETURN_FALSE_IF_FAIL(Tokeniser_parseShaderName(tokeniser, name));
800 const char* templateName = tokeniser.getToken();
801 ShaderTemplate* shaderTemplate = findTemplate(templateName);
802 if(shaderTemplate == 0)
804 globalErrorStream() << "shader instance: " << makeQuoted(name.c_str()) << ": shader template not found: " << makeQuoted(templateName) << "\n";
807 ShaderArguments args;
808 if(!parseShaderParameters(tokeniser, args))
810 globalErrorStream() << "shader instance: " << makeQuoted(name.c_str()) << ": argument parse failed\n";
814 if(shaderTemplate != 0)
816 if(!g_shaderDefinitions.insert(ShaderDefinitionMap::value_type(name, ShaderDefinition(shaderTemplate, args, filename))).second)
818 globalErrorStream() << "shader instance: " << makeQuoted(name.c_str()) << ": already exists, second definition ignored\n";
825 const char* evaluateShaderValue(const char* value, const ShaderParameters& params, const ShaderArguments& args)
827 ShaderArguments::const_iterator j = args.begin();
828 for(ShaderParameters::const_iterator i = params.begin(); i != params.end(); ++i, ++j)
830 const char* other = (*i).c_str();
831 if(string_equal(value, other))
839 ///\todo BlendFunc parsing
840 BlendFunc evaluateBlendFunc(const BlendFuncExpression& blendFunc, const ShaderParameters& params, const ShaderArguments& args)
842 return BlendFunc(BLEND_ONE, BLEND_ZERO);
845 qtexture_t* evaluateTexture(const TextureExpression& texture, const ShaderParameters& params, const ShaderArguments& args, const LoadImageCallback& loader = GlobalTexturesCache().defaultLoader())
847 StringOutputStream result(64);
848 const char* expression = texture.c_str();
849 const char* end = expression + string_length(expression);
850 if(!string_empty(expression))
854 const char* best = end;
855 const char* bestParam = 0;
856 const char* bestArg = 0;
857 ShaderArguments::const_iterator j = args.begin();
858 for(ShaderParameters::const_iterator i = params.begin(); i != params.end(); ++i, ++j)
860 const char* found = strstr(expression, (*i).c_str());
861 if(found != 0 && found < best)
864 bestParam = (*i).c_str();
865 bestArg = (*j).c_str();
870 result << StringRange(expression, best);
871 result << PathCleaned(bestArg);
872 expression = best + string_length(bestParam);
879 result << expression;
881 return GlobalTexturesCache().capture(loader, result.c_str());
884 float evaluateFloat(const ShaderValue& value, const ShaderParameters& params, const ShaderArguments& args)
886 const char* result = evaluateShaderValue(value.c_str(), params, args);
888 if(!string_parse_float(result, f))
890 globalErrorStream() << "parsing float value failed: " << makeQuoted(result) << "\n";
895 BlendFactor evaluateBlendFactor(const ShaderValue& value, const ShaderParameters& params, const ShaderArguments& args)
897 const char* result = evaluateShaderValue(value.c_str(), params, args);
899 if(string_equal_nocase(result, "gl_zero"))
903 if(string_equal_nocase(result, "gl_one"))
907 if(string_equal_nocase(result, "gl_src_color"))
909 return BLEND_SRC_COLOUR;
911 if(string_equal_nocase(result, "gl_one_minus_src_color"))
913 return BLEND_ONE_MINUS_SRC_COLOUR;
915 if(string_equal_nocase(result, "gl_src_alpha"))
917 return BLEND_SRC_ALPHA;
919 if(string_equal_nocase(result, "gl_one_minus_src_alpha"))
921 return BLEND_ONE_MINUS_SRC_ALPHA;
923 if(string_equal_nocase(result, "gl_dst_color"))
925 return BLEND_DST_COLOUR;
927 if(string_equal_nocase(result, "gl_one_minus_dst_color"))
929 return BLEND_ONE_MINUS_DST_COLOUR;
931 if(string_equal_nocase(result, "gl_dst_alpha"))
933 return BLEND_DST_ALPHA;
935 if(string_equal_nocase(result, "gl_one_minus_dst_alpha"))
937 return BLEND_ONE_MINUS_DST_ALPHA;
939 if(string_equal_nocase(result, "gl_src_alpha_saturate"))
941 return BLEND_SRC_ALPHA_SATURATE;
944 globalErrorStream() << "parsing blend-factor value failed: " << makeQuoted(result) << "\n";
948 class CShader : public IShader
950 std::size_t m_refcount;
952 const ShaderTemplate& m_template;
953 const ShaderArguments& m_args;
954 const char* m_filename;
955 // name is shader-name, otherwise texture-name (if not a real shader)
958 qtexture_t* m_pTexture;
959 qtexture_t* m_notfound;
960 qtexture_t* m_pDiffuse;
961 float m_heightmapScale;
963 qtexture_t* m_pSpecular;
964 qtexture_t* m_pLightFalloffImage;
965 BlendFunc m_blendFunc;
971 static bool m_lightingEnabled;
973 CShader(const ShaderDefinition& definition) :
975 m_template(*definition.shaderTemplate),
976 m_args(definition.args),
977 m_filename(definition.filename),
978 m_blendFunc(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA),
994 ASSERT_MESSAGE(m_refcount == 0, "deleting active shader");
997 // IShaders implementation -----------------
1004 ASSERT_MESSAGE(m_refcount != 0, "shader reference-count going below zero");
1005 if(--m_refcount == 0)
1011 std::size_t refcount()
1016 // get/set the qtexture_t* Radiant uses to represent this shader object
1017 qtexture_t* getTexture() const
1021 qtexture_t* getDiffuse() const
1025 qtexture_t* getBump() const
1029 qtexture_t* getSpecular() const
1034 const char* getName() const
1036 return m_Name.c_str();
1038 bool IsInUse() const
1042 void SetInUse(bool bInUse)
1045 g_ActiveShadersChangedNotify();
1047 // get the shader flags
1048 int getFlags() const
1050 return m_template.m_nFlags;
1052 // get the transparency value
1053 float getTrans() const
1055 return m_template.m_fTrans;
1057 // test if it's a true shader, or a default shader created to wrap around a texture
1058 bool IsDefault() const
1060 return string_empty(m_filename);
1062 // get the alphaFunc
1063 void getAlphaFunc(EAlphaFunc *func, float *ref) { *func = m_template.m_AlphaFunc; *ref = m_template.m_AlphaRef; };
1064 BlendFunc getBlendFunc() const
1068 // get the cull type
1071 return m_template.m_Cull;
1073 // get shader file name (ie the file where this one is defined)
1074 const char* getShaderFileName() const
1078 // -----------------------------------------
1082 m_pTexture = evaluateTexture(m_template.m_textureName, m_template.m_params, m_args);
1084 if(m_pTexture->texture_number == 0)
1086 m_notfound = m_pTexture;
1089 StringOutputStream name(256);
1090 name << GlobalRadiant().getAppPath() << "bitmaps/" << (IsDefault() ? "notex.bmp" : "shadernotex.bmp");
1091 m_pTexture = GlobalTexturesCache().capture(LoadImageCallback(0, loadBitmap), name.c_str());
1100 GlobalTexturesCache().release(m_pTexture);
1104 GlobalTexturesCache().release(m_notfound);
1107 unrealiseLighting();
1110 void realiseLighting()
1112 if(m_lightingEnabled)
1114 LoadImageCallback loader = GlobalTexturesCache().defaultLoader();
1115 if(!string_empty(m_template.m_heightmapScale.c_str()))
1117 m_heightmapScale = evaluateFloat(m_template.m_heightmapScale, m_template.m_params, m_args);
1118 loader = LoadImageCallback(&m_heightmapScale, loadHeightmap);
1120 m_pDiffuse = evaluateTexture(m_template.m_diffuse, m_template.m_params, m_args);
1121 m_pBump = evaluateTexture(m_template.m_bump, m_template.m_params, m_args, loader);
1122 m_pSpecular = evaluateTexture(m_template.m_specular, m_template.m_params, m_args);
1123 m_pLightFalloffImage = evaluateTexture(m_template.m_lightFalloffImage, m_template.m_params, m_args);
1125 for(ShaderTemplate::MapLayers::const_iterator i = m_template.m_layers.begin(); i != m_template.m_layers.end(); ++i)
1127 m_layers.push_back(evaluateLayer(*i, m_template.m_params, m_args));
1130 if(m_layers.size() == 1)
1132 const BlendFuncExpression& blendFunc = m_template.m_layers.front().blendFunc();
1133 if(!string_empty(blendFunc.second.c_str()))
1135 m_blendFunc = BlendFunc(
1136 evaluateBlendFactor(blendFunc.first.c_str(), m_template.m_params, m_args),
1137 evaluateBlendFactor(blendFunc.second.c_str(), m_template.m_params, m_args)
1142 const char* blend = evaluateShaderValue(blendFunc.first.c_str(), m_template.m_params, m_args);
1144 if(string_equal_nocase(blend, "add"))
1146 m_blendFunc = BlendFunc(BLEND_ONE, BLEND_ONE);
1148 else if(string_equal_nocase(blend, "filter"))
1150 m_blendFunc = BlendFunc(BLEND_DST_COLOUR, BLEND_ZERO);
1152 else if(string_equal_nocase(blend, "blend"))
1154 m_blendFunc = BlendFunc(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA);
1158 globalErrorStream() << "parsing blend value failed: " << makeQuoted(blend) << "\n";
1165 void unrealiseLighting()
1167 if(m_lightingEnabled)
1169 GlobalTexturesCache().release(m_pDiffuse);
1170 GlobalTexturesCache().release(m_pBump);
1171 GlobalTexturesCache().release(m_pSpecular);
1173 GlobalTexturesCache().release(m_pLightFalloffImage);
1175 for(MapLayers::iterator i = m_layers.begin(); i != m_layers.end(); ++i)
1177 GlobalTexturesCache().release((*i).texture());
1181 m_blendFunc = BlendFunc(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA);
1186 void setName(const char* name)
1191 class MapLayer : public ShaderLayer
1193 qtexture_t* m_texture;
1194 BlendFunc m_blendFunc;
1195 bool m_clampToBorder;
1198 MapLayer(qtexture_t* texture, BlendFunc blendFunc, bool clampToBorder, float alphaTest) :
1200 m_blendFunc(blendFunc),
1201 m_clampToBorder(false),
1202 m_alphaTest(alphaTest)
1205 qtexture_t* texture() const
1209 BlendFunc blendFunc() const
1213 bool clampToBorder() const
1215 return m_clampToBorder;
1217 float alphaTest() const
1223 static MapLayer evaluateLayer(const ShaderTemplate::MapLayerTemplate& layerTemplate, const ShaderParameters& params, const ShaderArguments& args)
1226 evaluateTexture(layerTemplate.texture(), params, args),
1227 evaluateBlendFunc(layerTemplate.blendFunc(), params, args),
1228 layerTemplate.clampToBorder(),
1229 evaluateFloat(layerTemplate.alphaTest(), params, args)
1233 typedef std::vector<MapLayer> MapLayers;
1236 const ShaderLayer* firstLayer() const
1238 if(m_layers.empty())
1242 return &m_layers.front();
1244 void forEachLayer(const ShaderLayerCallback& callback) const
1246 for(MapLayers::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i)
1252 qtexture_t* lightFalloffImage() const
1254 if(!string_empty(m_template.m_lightFalloffImage.c_str()))
1256 return m_pLightFalloffImage;
1262 bool CShader::m_lightingEnabled = false;
1264 typedef SmartPointer<CShader> ShaderPointer;
1265 typedef std::map<CopiedString, ShaderPointer, shader_less_t> shaders_t;
1267 shaders_t g_ActiveShaders;
1269 static shaders_t::iterator g_ActiveShadersIterator;
1271 void ActiveShaders_IteratorBegin()
1273 g_ActiveShadersIterator = g_ActiveShaders.begin();
1276 bool ActiveShaders_IteratorAtEnd()
1278 return g_ActiveShadersIterator == g_ActiveShaders.end();
1281 IShader *ActiveShaders_IteratorCurrent()
1283 return static_cast<CShader*>(g_ActiveShadersIterator->second);
1286 void ActiveShaders_IteratorIncrement()
1288 ++g_ActiveShadersIterator;
1291 void debug_check_shaders(shaders_t& shaders)
1293 for(shaders_t::iterator i = shaders.begin(); i != shaders.end(); ++i)
1295 ASSERT_MESSAGE(i->second->refcount() == 1, "orphan shader still referenced");
1299 // will free all GL binded qtextures and shaders
1300 // NOTE: doesn't make much sense out of Radiant exit or called during a reload
1304 // empty the actives shaders list
1305 debug_check_shaders(g_ActiveShaders);
1306 g_ActiveShaders.clear();
1308 g_shaderTemplates.clear();
1309 g_shaderDefinitions.clear();
1310 g_ActiveShadersChangedNotify();
1313 bool ShaderTemplate::parseQuake3(Tokeniser& tokeniser)
1315 // name of the qtexture_t we'll use to represent this shader (this one has the "textures\" before)
1316 m_textureName = m_Name.c_str();
1318 tokeniser.nextLine();
1320 // we need to read until we hit a balanced }
1324 tokeniser.nextLine();
1325 const char* token = tokeniser.getToken();
1330 if(string_equal(token, "{"))
1335 else if(string_equal(token, "}"))
1338 if(depth < 0) // underflow
1342 if(depth == 0) // end of shader
1352 if (string_equal_nocase(token, "qer_nocarve"))
1354 m_nFlags |= QER_NOCARVE;
1356 else if (string_equal_nocase(token, "qer_trans"))
1358 RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, m_fTrans));
1359 m_nFlags |= QER_TRANS;
1361 else if (string_equal_nocase(token, "qer_editorimage"))
1363 RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, m_textureName));
1365 else if (string_equal_nocase(token, "qer_alphafunc"))
1367 const char* alphafunc = tokeniser.getToken();
1371 Tokeniser_unexpectedError(tokeniser, alphafunc, "#alphafunc");
1375 if(string_equal_nocase(alphafunc, "equal"))
1377 m_AlphaFunc = IShader::eEqual;
1379 else if(string_equal_nocase(alphafunc, "greater"))
1381 m_AlphaFunc = IShader::eGreater;
1383 else if(string_equal_nocase(alphafunc, "less"))
1385 m_AlphaFunc = IShader::eLess;
1387 else if(string_equal_nocase(alphafunc, "gequal"))
1389 m_AlphaFunc = IShader::eGEqual;
1391 else if(string_equal_nocase(alphafunc, "lequal"))
1393 m_AlphaFunc = IShader::eLEqual;
1397 m_AlphaFunc = IShader::eAlways;
1400 m_nFlags |= QER_ALPHATEST;
1402 RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, m_AlphaRef));
1404 else if (string_equal_nocase(token, "cull"))
1406 const char* cull = tokeniser.getToken();
1410 Tokeniser_unexpectedError(tokeniser, cull, "#cull");
1414 if(string_equal_nocase(cull, "none")
1415 || string_equal_nocase(cull, "twosided")
1416 || string_equal_nocase(cull, "disable"))
1418 m_Cull = IShader::eCullNone;
1420 else if(string_equal_nocase(cull, "back")
1421 || string_equal_nocase(cull, "backside")
1422 || string_equal_nocase(cull, "backsided"))
1424 m_Cull = IShader::eCullBack;
1428 m_Cull = IShader::eCullBack;
1431 m_nFlags |= QER_CULL;
1433 else if (string_equal_nocase(token, "surfaceparm"))
1435 const char* surfaceparm = tokeniser.getToken();
1437 if(surfaceparm == 0)
1439 Tokeniser_unexpectedError(tokeniser, surfaceparm, "#surfaceparm");
1443 if (string_equal_nocase(surfaceparm, "fog"))
1445 m_nFlags |= QER_FOG;
1446 if (m_fTrans == 1.0f) // has not been explicitly set by qer_trans
1451 else if (string_equal_nocase(surfaceparm, "nodraw"))
1453 m_nFlags |= QER_NODRAW;
1455 else if (string_equal_nocase(surfaceparm, "nonsolid"))
1457 m_nFlags |= QER_NONSOLID;
1459 else if (string_equal_nocase(surfaceparm, "water"))
1461 m_nFlags |= QER_WATER;
1463 else if (string_equal_nocase(surfaceparm, "lava"))
1465 m_nFlags |= QER_LAVA;
1467 else if (string_equal_nocase(surfaceparm, "areaportal"))
1469 m_nFlags |= QER_AREAPORTAL;
1471 else if (string_equal_nocase(surfaceparm, "playerclip"))
1473 m_nFlags |= QER_CLIP;
1475 else if (string_equal_nocase(surfaceparm, "botclip"))
1477 m_nFlags |= QER_BOTCLIP;
1490 TextureExpression m_texture;
1491 BlendFunc m_blendFunc;
1492 bool m_clampToBorder;
1494 float m_heightmapScale;
1496 Layer() : m_type(LAYER_NONE), m_blendFunc(BLEND_ONE, BLEND_ZERO), m_clampToBorder(false), m_alphaTest(-1), m_heightmapScale(0)
1501 std::list<CopiedString> g_shaderFilenames;
1503 void ParseShaderFile(Tokeniser& tokeniser, const char* filename)
1505 g_shaderFilenames.push_back(filename);
1506 filename = g_shaderFilenames.back().c_str();
1507 tokeniser.nextLine();
1510 const char* token = tokeniser.getToken();
1517 if(string_equal(token, "table"))
1519 if(tokeniser.getToken() == 0)
1521 Tokeniser_unexpectedError(tokeniser, 0, "#table-name");
1524 if(!Tokeniser_parseToken(tokeniser, "{"))
1530 const char* option = tokeniser.getToken();
1531 if(string_equal(option, "{"))
1535 const char* value = tokeniser.getToken();
1536 if(string_equal(value, "}"))
1542 if(!Tokeniser_parseToken(tokeniser, "}"))
1552 if(string_equal(token, "guide"))
1554 parseTemplateInstance(tokeniser, filename);
1558 if(!string_equal(token, "material")
1559 && !string_equal(token, "particle")
1560 && !string_equal(token, "skin"))
1562 tokeniser.ungetToken();
1564 // first token should be the path + name.. (from base)
1566 if(!Tokeniser_parseShaderName(tokeniser, name))
1569 ShaderTemplatePointer shaderTemplate(new ShaderTemplate());
1570 shaderTemplate->setName(name.c_str());
1572 g_shaders.insert(ShaderTemplateMap::value_type(shaderTemplate->getName(), shaderTemplate));
1574 bool result = (g_shaderLanguage == SHADERLANGUAGE_QUAKE3)
1575 ? shaderTemplate->parseQuake3(tokeniser)
1576 : shaderTemplate->parseDoom3(tokeniser);
1579 // do we already have this shader?
1580 if(!g_shaderDefinitions.insert(ShaderDefinitionMap::value_type(shaderTemplate->getName(), ShaderDefinition(shaderTemplate.get(), ShaderArguments(), filename))).second)
1583 globalOutputStream() << "WARNING: shader " << shaderTemplate->getName() << " is already in memory, definition in " << filename << " ignored.\n";
1589 globalErrorStream() << "Error parsing shader " << shaderTemplate->getName() << "\n";
1597 void parseGuideFile(Tokeniser& tokeniser, const char* filename)
1599 tokeniser.nextLine();
1602 const char* token = tokeniser.getToken();
1609 if(string_equal(token, "guide"))
1611 // first token should be the path + name.. (from base)
1612 ShaderTemplatePointer shaderTemplate(new ShaderTemplate);
1613 shaderTemplate->parseTemplate(tokeniser);
1614 if(!g_shaderTemplates.insert(ShaderTemplateMap::value_type(shaderTemplate->getName(), shaderTemplate)).second)
1616 globalErrorStream() << "guide " << makeQuoted(shaderTemplate->getName()) << ": already defined, second definition ignored\n";
1619 else if(string_equal(token, "inlineGuide"))
1621 // skip entire inlineGuide definition
1622 std::size_t depth = 0;
1625 tokeniser.nextLine();
1626 token = tokeniser.getToken();
1627 if(string_equal(token, "{"))
1631 else if(string_equal(token, "}"))
1643 void LoadShaderFile(const char* filename)
1645 ArchiveTextFile* file = GlobalFileSystem().openTextFile(filename);
1649 globalOutputStream() << "Parsing shaderfile " << filename << "\n";
1651 Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser(file->getInputStream());
1653 ParseShaderFile(tokeniser, filename);
1655 tokeniser.release();
1660 globalOutputStream() << "Unable to read shaderfile " << filename << "\n";
1664 typedef FreeCaller1<const char*, LoadShaderFile> LoadShaderFileCaller;
1667 void loadGuideFile(const char* filename)
1669 StringOutputStream fullname(256);
1670 fullname << "guides/" << filename;
1671 ArchiveTextFile* file = GlobalFileSystem().openTextFile(fullname.c_str());
1675 globalOutputStream() << "Parsing guide file " << fullname.c_str() << "\n";
1677 Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser(file->getInputStream());
1679 parseGuideFile(tokeniser, fullname.c_str());
1681 tokeniser.release();
1686 globalOutputStream() << "Unable to read guide file " << fullname.c_str() << "\n";
1690 typedef FreeCaller1<const char*, loadGuideFile> LoadGuideFileCaller;
1693 CShader* Try_Shader_ForName(const char* name)
1696 shaders_t::iterator i = g_ActiveShaders.find(name);
1697 if(i != g_ActiveShaders.end())
1702 // active shader was not found
1704 // find matching shader definition
1705 ShaderDefinitionMap::iterator i = g_shaderDefinitions.find(name);
1706 if(i == g_shaderDefinitions.end())
1708 // shader definition was not found
1710 // create new shader definition from default shader template
1711 ShaderTemplatePointer shaderTemplate(new ShaderTemplate());
1712 shaderTemplate->CreateDefault(name);
1713 g_shaderTemplates.insert(ShaderTemplateMap::value_type(shaderTemplate->getName(), shaderTemplate));
1715 i = g_shaderDefinitions.insert(ShaderDefinitionMap::value_type(name, ShaderDefinition(shaderTemplate.get(), ShaderArguments(), ""))).first;
1718 // create shader from existing definition
1719 ShaderPointer pShader(new CShader((*i).second));
1720 pShader->setName(name);
1721 g_ActiveShaders.insert(shaders_t::value_type(name, pShader));
1722 g_ActiveShadersChangedNotify();
1726 IShader *Shader_ForName(const char *name)
1728 ASSERT_NOTNULL(name);
1730 IShader *pShader = Try_Shader_ForName(name);
1738 // the list of scripts/*.shader files we need to work with
1739 // those are listed in shaderlist file
1740 GSList *l_shaderfiles = 0;
1742 GSList* Shaders_getShaderFileList()
1744 return l_shaderfiles;
1749 DumpUnreferencedShaders
1750 usefull function: dumps the list of .shader files that are not referenced to the console
1753 void IfFound_dumpUnreferencedShader(bool& bFound, const char* filename)
1755 bool listed = false;
1757 for(GSList* sh = l_shaderfiles; sh != 0; sh = g_slist_next(sh))
1759 if(!strcmp((char*)sh->data, filename))
1771 globalOutputStream() << "Following shader files are not referenced in any shaderlist.txt:\n";
1773 globalOutputStream() << "\t" << filename << "\n";
1776 typedef ReferenceCaller1<bool, const char*, IfFound_dumpUnreferencedShader> IfFoundDumpUnreferencedShaderCaller;
1778 void DumpUnreferencedShaders()
1780 bool bFound = false;
1781 GlobalFileSystem().forEachFile(g_shadersDirectory, g_shadersExtension, IfFoundDumpUnreferencedShaderCaller(bFound));
1784 void ShaderList_addShaderFile(const char* dirstring)
1788 for(GSList* tmp = l_shaderfiles; tmp != 0; tmp = tmp->next)
1790 if(string_equal_nocase(dirstring, (char*)tmp->data))
1793 globalOutputStream() << "duplicate entry \"" << (char*)tmp->data << "\" in shaderlist.txt\n";
1800 l_shaderfiles = g_slist_append(l_shaderfiles, strdup(dirstring));
1804 typedef FreeCaller1<const char*, ShaderList_addShaderFile> AddShaderFileCaller;
1810 build a CStringList of shader names
1813 void BuildShaderList(TextInputStream& shaderlist)
1815 Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewSimpleTokeniser(shaderlist);
1816 tokeniser.nextLine();
1817 const char* token = tokeniser.getToken();
1818 StringOutputStream shaderFile(64);
1821 // each token should be a shader filename
1822 shaderFile << token << "." << g_shadersExtension;
1824 ShaderList_addShaderFile(shaderFile.c_str());
1826 tokeniser.nextLine();
1827 token = tokeniser.getToken();
1831 tokeniser.release();
1834 void FreeShaderList()
1836 while(l_shaderfiles != 0)
1838 free(l_shaderfiles->data);
1839 l_shaderfiles = g_slist_remove(l_shaderfiles, l_shaderfiles->data);
1843 void ShaderList_addFromArchive(const char *archivename)
1845 const char *shaderpath = GlobalRadiant().getGameDescriptionKeyValue("shaderpath");
1846 if (string_empty(shaderpath))
1849 StringOutputStream shaderlist(256);
1850 shaderlist << DirectoryCleaned(shaderpath) << "shaderlist.txt";
1852 Archive *archive = GlobalFileSystem().getArchive(archivename, false);
1855 ArchiveTextFile *file = archive->openTextFile(shaderlist.c_str());
1858 globalOutputStream() << "Found shaderlist.txt in " << archivename << "\n";
1859 BuildShaderList(file->getInputStream());
1865 typedef FreeCaller1<const char *, ShaderList_addFromArchive> AddShaderListFromArchiveCaller;
1867 #include "stream/filestream.h"
1869 bool shaderlist_findOrInstall(const char* enginePath, const char* toolsPath, const char* shaderPath, const char* gamename)
1871 StringOutputStream absShaderList(256);
1872 absShaderList << enginePath << gamename << '/' << shaderPath << "shaderlist.txt";
1873 if(file_exists(absShaderList.c_str()))
1878 StringOutputStream directory(256);
1879 directory << enginePath << gamename << '/' << shaderPath;
1880 if(!file_exists(directory.c_str()) && !Q_mkdir(directory.c_str()))
1886 StringOutputStream defaultShaderList(256);
1887 defaultShaderList << toolsPath << gamename << '/' << "default_shaderlist.txt";
1888 if(file_exists(defaultShaderList.c_str()))
1890 return file_copy(defaultShaderList.c_str(), absShaderList.c_str());
1898 if(g_shaderLanguage == SHADERLANGUAGE_QUAKE4)
1900 GlobalFileSystem().forEachFile("guides/", "guide", LoadGuideFileCaller(), 0);
1903 const char* shaderPath = GlobalRadiant().getGameDescriptionKeyValue("shaderpath");
1904 if(!string_empty(shaderPath))
1906 StringOutputStream path(256);
1907 path << DirectoryCleaned(shaderPath);
1911 // preload shader files that have been listed in shaderlist.txt
1912 const char* basegame = GlobalRadiant().getRequiredGameDescriptionKeyValue("basegame");
1913 const char* gamename = GlobalRadiant().getGameName();
1914 const char* enginePath = GlobalRadiant().getEnginePath();
1915 const char* toolsPath = GlobalRadiant().getGameToolsPath();
1917 bool isMod = !string_equal(basegame, gamename);
1919 if(!isMod || !shaderlist_findOrInstall(enginePath, toolsPath, path.c_str(), gamename))
1921 gamename = basegame;
1922 shaderlist_findOrInstall(enginePath, toolsPath, path.c_str(), gamename);
1925 GlobalFileSystem().forEachArchive(AddShaderListFromArchiveCaller(), false, true);
1926 DumpUnreferencedShaders();
1930 GlobalFileSystem().forEachFile(path.c_str(), g_shadersExtension, AddShaderFileCaller(), 0);
1933 GSList *lst = l_shaderfiles;
1934 StringOutputStream shadername(256);
1937 shadername << path.c_str() << reinterpret_cast<const char*>(lst->data);
1938 LoadShaderFile(shadername.c_str());
1944 //StringPool_analyse(ShaderPool::instance());
1951 g_shaderFilenames.clear();
1954 ModuleObservers g_observers;
1956 std::size_t g_shaders_unrealised = 1; // wait until filesystem and is realised before loading anything
1957 bool Shaders_realised()
1959 return g_shaders_unrealised == 0;
1961 void Shaders_Realise()
1963 if(--g_shaders_unrealised == 0)
1966 g_observers.realise();
1969 void Shaders_Unrealise()
1971 if(++g_shaders_unrealised == 1)
1973 g_observers.unrealise();
1978 void Shaders_Refresh()
1980 Shaders_Unrealise();
1984 class Quake3ShaderSystem : public ShaderSystem, public ModuleObserver
1993 Shaders_Unrealise();
2000 IShader* getShaderForName(const char* name)
2002 return Shader_ForName(name);
2005 void foreachShaderName(const ShaderNameCallback& callback)
2007 for(ShaderDefinitionMap::const_iterator i = g_shaderDefinitions.begin(); i != g_shaderDefinitions.end(); ++i)
2009 callback((*i).first.c_str());
2013 void beginActiveShadersIterator()
2015 ActiveShaders_IteratorBegin();
2017 bool endActiveShadersIterator()
2019 return ActiveShaders_IteratorAtEnd();
2021 IShader* dereferenceActiveShadersIterator()
2023 return ActiveShaders_IteratorCurrent();
2025 void incrementActiveShadersIterator()
2027 ActiveShaders_IteratorIncrement();
2029 void setActiveShadersChangedNotify(const Callback& notify)
2031 g_ActiveShadersChangedNotify = notify;
2034 void attach(ModuleObserver& observer)
2036 g_observers.attach(observer);
2038 void detach(ModuleObserver& observer)
2040 g_observers.detach(observer);
2043 void setLightingEnabled(bool enabled)
2045 if(CShader::m_lightingEnabled != enabled)
2047 for(shaders_t::const_iterator i = g_ActiveShaders.begin(); i != g_ActiveShaders.end(); ++i)
2049 (*i).second->unrealiseLighting();
2051 CShader::m_lightingEnabled = enabled;
2052 for(shaders_t::const_iterator i = g_ActiveShaders.begin(); i != g_ActiveShaders.end(); ++i)
2054 (*i).second->realiseLighting();
2059 const char* getTexturePrefix() const
2061 return g_texturePrefix;
2065 Quake3ShaderSystem g_Quake3ShaderSystem;
2067 ShaderSystem& GetShaderSystem()
2069 return g_Quake3ShaderSystem;
2072 void Shaders_Construct()
2074 GlobalFileSystem().attach(g_Quake3ShaderSystem);
2076 void Shaders_Destroy()
2078 GlobalFileSystem().detach(g_Quake3ShaderSystem);
2080 if(Shaders_realised())