]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/shaders/shaders.cpp
0f0de908d6418678f0ebcf1be3ec9e204f3eb782
[xonotic/netradiant.git] / plugins / shaders / shaders.cpp
1 /*
2    Copyright (c) 2001, Loki software, inc.
3    All rights reserved.
4
5    Redistribution and use in source and binary forms, with or without modification,
6    are permitted provided that the following conditions are met:
7
8    Redistributions of source code must retain the above copyright notice, this list
9    of conditions and the following disclaimer.
10
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.
14
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
17    written permission.
18
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.
29  */
30
31 //
32 // Shaders Manager Plugin
33 //
34 // Leonardo Zide (leo@lokigames.com)
35 //
36
37 #include "defaults.h"
38 #include "shaders.h"
39 #include "globaldefs.h"
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <map>
44 #include <list>
45
46 #include "ifilesystem.h"
47 #include "ishaders.h"
48 #include "iscriplib.h"
49 #include "itextures.h"
50 #include "qerplugin.h"
51 #include "irender.h"
52
53 #include <glib.h>
54
55 #include "debugging/debugging.h"
56 #include "string/pooledstring.h"
57 #include "math/vector.h"
58 #include "generic/callback.h"
59 #include "generic/referencecounted.h"
60 #include "stream/memstream.h"
61 #include "stream/stringstream.h"
62 #include "stream/textfilestream.h"
63 #include "os/path.h"
64 #include "os/dir.h"
65 #include "os/file.h"
66 #include "stringio.h"
67 #include "shaderlib.h"
68 #include "texturelib.h"
69 #include "cmdlib.h"
70 #include "moduleobservers.h"
71 #include "archivelib.h"
72 #include "imagelib.h"
73
74 const char *g_shadersExtension = "";
75 const char *g_shadersDirectory = "";
76 bool g_enableDefaultShaders = true;
77 ShaderLanguage g_shaderLanguage = SHADERLANGUAGE_QUAKE3;
78 bool g_useShaderList = true;
79 _QERPlugImageTable *g_bitmapModule = 0;
80 const char *g_texturePrefix = DEFAULT_TEXTURE_DIRNAME;
81
82 void ActiveShaders_IteratorBegin();
83
84 bool ActiveShaders_IteratorAtEnd();
85
86 IShader *ActiveShaders_IteratorCurrent();
87
88 void ActiveShaders_IteratorIncrement();
89
90 Callback<void()> g_ActiveShadersChangedNotify;
91
92 void FreeShaders();
93
94 void LoadShaderFile(const char *filename);
95
96 qtexture_t *Texture_ForName(const char *filename);
97
98
99 /*!
100    NOTE TTimo: there is an important distinction between SHADER_NOT_FOUND and SHADER_NOTEX:
101    SHADER_NOT_FOUND means we didn't find the raw texture or the shader for this
102    SHADER_NOTEX means we recognize this as a shader script, but we are missing the texture to represent it
103    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
104  */
105
106 Image *loadBitmap(void *environment, const char *name)
107 {
108     DirectoryArchiveFile file(name, name);
109     if (!file.failed()) {
110         return g_bitmapModule->loadImage(file);
111     }
112     return 0;
113 }
114
115 inline byte *getPixel(byte *pixels, int width, int height, int x, int y)
116 {
117     return pixels + (((((y + height) % height) * width) + ((x + width) % width)) * 4);
118 }
119
120 class KernelElement {
121 public:
122     int x, y;
123     float w;
124 };
125
126 Image &convertHeightmapToNormalmap(Image &heightmap, float scale)
127 {
128     int w = heightmap.getWidth();
129     int h = heightmap.getHeight();
130
131     Image &normalmap = *(new RGBAImage(heightmap.getWidth(), heightmap.getHeight()));
132
133     byte *in = heightmap.getRGBAPixels();
134     byte *out = normalmap.getRGBAPixels();
135
136 #if 1
137     // no filtering
138     const int kernelSize = 2;
139     KernelElement kernel_du[kernelSize] = {
140             {-1, 0, -0.5f},
141             {1,  0, 0.5f}
142     };
143     KernelElement kernel_dv[kernelSize] = {
144             {0, 1, 0.5f},
145             {0, -1, -0.5f}
146     };
147 #else
148                                                                                                                             // 3x3 Prewitt
149         const int kernelSize = 6;
150         KernelElement kernel_du[kernelSize] = {
151                 {-1, 1,-1.0f },
152                 {-1, 0,-1.0f },
153                 {-1,-1,-1.0f },
154                 { 1, 1, 1.0f },
155                 { 1, 0, 1.0f },
156                 { 1,-1, 1.0f }
157         };
158         KernelElement kernel_dv[kernelSize] = {
159                 {-1, 1, 1.0f },
160                 { 0, 1, 1.0f },
161                 { 1, 1, 1.0f },
162                 {-1,-1,-1.0f },
163                 { 0,-1,-1.0f },
164                 { 1,-1,-1.0f }
165         };
166 #endif
167
168     int x, y = 0;
169     while (y < h) {
170         x = 0;
171         while (x < w) {
172             float du = 0;
173             for (KernelElement *i = kernel_du; i != kernel_du + kernelSize; ++i) {
174                 du += (getPixel(in, w, h, x + (*i).x, y + (*i).y)[0] / 255.0) * (*i).w;
175             }
176             float dv = 0;
177             for (KernelElement *i = kernel_dv; i != kernel_dv + kernelSize; ++i) {
178                 dv += (getPixel(in, w, h, x + (*i).x, y + (*i).y)[0] / 255.0) * (*i).w;
179             }
180
181             float nx = -du * scale;
182             float ny = -dv * scale;
183             float nz = 1.0;
184
185             // Normalize
186             float norm = 1.0 / sqrt(nx * nx + ny * ny + nz * nz);
187             out[0] = float_to_integer(((nx * norm) + 1) * 127.5);
188             out[1] = float_to_integer(((ny * norm) + 1) * 127.5);
189             out[2] = float_to_integer(((nz * norm) + 1) * 127.5);
190             out[3] = 255;
191
192             x++;
193             out += 4;
194         }
195
196         y++;
197     }
198
199     return normalmap;
200 }
201
202 Image *loadHeightmap(void *environment, const char *name)
203 {
204     Image *heightmap = GlobalTexturesCache().loadImage(name);
205     if (heightmap != 0) {
206         Image &normalmap = convertHeightmapToNormalmap(*heightmap, *reinterpret_cast<float *>( environment ));
207         heightmap->release();
208         return &normalmap;
209     }
210     return 0;
211 }
212
213
214 Image *loadSpecial(void *environment, const char *name)
215 {
216     if (*name == '_') { // special image
217         StringOutputStream bitmapName(256);
218         bitmapName << GlobalRadiant().getAppPath() << "bitmaps/" << name + 1 << ".png";
219         Image *image = loadBitmap(environment, bitmapName.c_str());
220         if (image != 0) {
221             return image;
222         }
223     }
224     return GlobalTexturesCache().loadImage(name);
225 }
226
227 class ShaderPoolContext {
228 };
229
230 typedef Static<StringPool, ShaderPoolContext> ShaderPool;
231 typedef PooledString<ShaderPool> ShaderString;
232 typedef ShaderString ShaderVariable;
233 typedef ShaderString ShaderValue;
234 typedef CopiedString TextureExpression;
235
236 // clean a texture name to the qtexture_t name format we use internally
237 // NOTE: case sensitivity: the engine is case sensitive. we store the shader name with case information and save with case
238 // information as well. but we assume there won't be any case conflict and so when doing lookups based on shader name,
239 // we compare as case insensitive. That is Radiant is case insensitive, but knows that the engine is case sensitive.
240 //++timo FIXME: we need to put code somewhere to detect when two shaders that are case insensitive equal are present
241 template<typename StringType>
242 void parseTextureName(StringType &name, const char *token)
243 {
244     StringOutputStream cleaned(256);
245     cleaned << PathCleaned(token);
246     name = CopiedString(
247             StringRange(cleaned.c_str(), path_get_filename_base_end(cleaned.c_str()))).c_str(); // remove extension
248 }
249
250 bool Tokeniser_parseTextureName(Tokeniser &tokeniser, TextureExpression &name)
251 {
252     const char *token = tokeniser.getToken();
253     if (token == 0) {
254         Tokeniser_unexpectedError(tokeniser, token, "#texture-name");
255         return false;
256     }
257     parseTextureName(name, token);
258     return true;
259 }
260
261 bool Tokeniser_parseShaderName(Tokeniser &tokeniser, CopiedString &name)
262 {
263     const char *token = tokeniser.getToken();
264     if (token == 0) {
265         Tokeniser_unexpectedError(tokeniser, token, "#shader-name");
266         return false;
267     }
268     parseTextureName(name, token);
269     return true;
270 }
271
272 bool Tokeniser_parseString(Tokeniser &tokeniser, ShaderString &string)
273 {
274     const char *token = tokeniser.getToken();
275     if (token == 0) {
276         Tokeniser_unexpectedError(tokeniser, token, "#string");
277         return false;
278     }
279     string = token;
280     return true;
281 }
282
283
284 typedef std::list<ShaderVariable> ShaderParameters;
285 typedef std::list<ShaderVariable> ShaderArguments;
286
287 typedef std::pair<ShaderVariable, ShaderVariable> BlendFuncExpression;
288
289 class ShaderTemplate {
290     std::size_t m_refcount;
291     CopiedString m_Name;
292 public:
293
294     ShaderParameters m_params;
295
296     TextureExpression m_textureName;
297     TextureExpression m_diffuse;
298     TextureExpression m_bump;
299     ShaderValue m_heightmapScale;
300     TextureExpression m_specular;
301     TextureExpression m_lightFalloffImage;
302
303     int m_nFlags;
304     float m_fTrans;
305
306 // alphafunc stuff
307     IShader::EAlphaFunc m_AlphaFunc;
308     float m_AlphaRef;
309 // cull stuff
310     IShader::ECull m_Cull;
311
312     ShaderTemplate() :
313             m_refcount(0)
314     {
315         m_nFlags = 0;
316         m_fTrans = 1.0f;
317     }
318
319     void IncRef()
320     {
321         ++m_refcount;
322     }
323
324     void DecRef()
325     {
326         ASSERT_MESSAGE(m_refcount != 0, "shader reference-count going below zero");
327         if (--m_refcount == 0) {
328             delete this;
329         }
330     }
331
332     std::size_t refcount()
333     {
334         return m_refcount;
335     }
336
337     const char *getName() const
338     {
339         return m_Name.c_str();
340     }
341
342     void setName(const char *name)
343     {
344         m_Name = name;
345     }
346
347 // -----------------------------------------
348
349     bool parseDoom3(Tokeniser &tokeniser);
350
351     bool parseQuake3(Tokeniser &tokeniser);
352
353     bool parseTemplate(Tokeniser &tokeniser);
354
355
356     void CreateDefault(const char *name)
357     {
358         if (g_enableDefaultShaders) {
359             m_textureName = name;
360         } else {
361             m_textureName = "";
362         }
363         setName(name);
364     }
365
366
367     class MapLayerTemplate {
368         TextureExpression m_texture;
369         BlendFuncExpression m_blendFunc;
370         bool m_clampToBorder;
371         ShaderValue m_alphaTest;
372     public:
373         MapLayerTemplate(const TextureExpression &texture, const BlendFuncExpression &blendFunc, bool clampToBorder,
374                          const ShaderValue &alphaTest) :
375                 m_texture(texture),
376                 m_blendFunc(blendFunc),
377                 m_clampToBorder(false),
378                 m_alphaTest(alphaTest)
379         {
380         }
381
382         const TextureExpression &texture() const
383         {
384             return m_texture;
385         }
386
387         const BlendFuncExpression &blendFunc() const
388         {
389             return m_blendFunc;
390         }
391
392         bool clampToBorder() const
393         {
394             return m_clampToBorder;
395         }
396
397         const ShaderValue &alphaTest() const
398         {
399             return m_alphaTest;
400         }
401     };
402
403     typedef std::vector<MapLayerTemplate> MapLayers;
404     MapLayers m_layers;
405 };
406
407
408 bool Doom3Shader_parseHeightmap(Tokeniser &tokeniser, TextureExpression &bump, ShaderValue &heightmapScale)
409 {
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, ")"));
415     return true;
416 }
417
418 bool Doom3Shader_parseAddnormals(Tokeniser &tokeniser, TextureExpression &bump)
419 {
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, ")"));
428     return true;
429 }
430
431 bool Doom3Shader_parseBumpmap(Tokeniser &tokeniser, TextureExpression &bump, ShaderValue &heightmapScale)
432 {
433     const char *token = tokeniser.getToken();
434     if (token == 0) {
435         Tokeniser_unexpectedError(tokeniser, token, "#bumpmap");
436         return false;
437     }
438     if (string_equal(token, "heightmap")) {
439         RETURN_FALSE_IF_FAIL(Doom3Shader_parseHeightmap(tokeniser, bump, heightmapScale));
440     } else if (string_equal(token, "addnormals")) {
441         RETURN_FALSE_IF_FAIL(Doom3Shader_parseAddnormals(tokeniser, bump));
442     } else {
443         parseTextureName(bump, token);
444     }
445     return true;
446 }
447
448 enum LayerTypeId {
449     LAYER_NONE,
450     LAYER_BLEND,
451     LAYER_DIFFUSEMAP,
452     LAYER_BUMPMAP,
453     LAYER_SPECULARMAP
454 };
455
456 class LayerTemplate {
457 public:
458     LayerTypeId m_type;
459     TextureExpression m_texture;
460     BlendFuncExpression m_blendFunc;
461     bool m_clampToBorder;
462     ShaderValue m_alphaTest;
463     ShaderValue m_heightmapScale;
464
465     LayerTemplate() : m_type(LAYER_NONE), m_blendFunc("GL_ONE", "GL_ZERO"), m_clampToBorder(false), m_alphaTest("-1"),
466                       m_heightmapScale("0")
467     {
468     }
469 };
470
471 bool parseShaderParameters(Tokeniser &tokeniser, ShaderParameters &params)
472 {
473     Tokeniser_parseToken(tokeniser, "(");
474     for (;;) {
475         const char *param = tokeniser.getToken();
476         if (string_equal(param, ")")) {
477             break;
478         }
479         params.push_back(param);
480         const char *comma = tokeniser.getToken();
481         if (string_equal(comma, ")")) {
482             break;
483         }
484         if (!string_equal(comma, ",")) {
485             Tokeniser_unexpectedError(tokeniser, comma, ",");
486             return false;
487         }
488     }
489     return true;
490 }
491
492 bool ShaderTemplate::parseTemplate(Tokeniser &tokeniser)
493 {
494     m_Name = tokeniser.getToken();
495     if (!parseShaderParameters(tokeniser, m_params)) {
496         globalErrorStream() << "shader template: " << makeQuoted(m_Name.c_str()) << ": parameter parse failed\n";
497         return false;
498     }
499
500     return parseDoom3(tokeniser);
501 }
502
503 bool ShaderTemplate::parseDoom3(Tokeniser &tokeniser)
504 {
505     LayerTemplate currentLayer;
506     bool isFog = false;
507
508     // we need to read until we hit a balanced }
509     int depth = 0;
510     for (;;) {
511         tokeniser.nextLine();
512         const char *token = tokeniser.getToken();
513
514         if (token == 0) {
515             return false;
516         }
517
518         if (string_equal(token, "{")) {
519             ++depth;
520             continue;
521         } else if (string_equal(token, "}")) {
522             --depth;
523             if (depth < 0) { // error
524                 return false;
525             }
526             if (depth == 0) { // end of shader
527                 break;
528             }
529             if (depth == 1) { // end of layer
530                 if (currentLayer.m_type == LAYER_DIFFUSEMAP) {
531                     m_diffuse = currentLayer.m_texture;
532                 } else if (currentLayer.m_type == LAYER_BUMPMAP) {
533                     m_bump = currentLayer.m_texture;
534                 } else if (currentLayer.m_type == LAYER_SPECULARMAP) {
535                     m_specular = currentLayer.m_texture;
536                 } else if (!string_empty(currentLayer.m_texture.c_str())) {
537                     m_layers.push_back(MapLayerTemplate(
538                             currentLayer.m_texture.c_str(),
539                             currentLayer.m_blendFunc,
540                             currentLayer.m_clampToBorder,
541                             currentLayer.m_alphaTest
542                     ));
543                 }
544                 currentLayer.m_type = LAYER_NONE;
545                 currentLayer.m_texture = "";
546             }
547             continue;
548         }
549
550         if (depth == 2) { // in layer
551             if (string_equal_nocase(token, "blend")) {
552                 const char *blend = tokeniser.getToken();
553
554                 if (blend == 0) {
555                     Tokeniser_unexpectedError(tokeniser, blend, "#blend");
556                     return false;
557                 }
558
559                 if (string_equal_nocase(blend, "diffusemap")) {
560                     currentLayer.m_type = LAYER_DIFFUSEMAP;
561                 } else if (string_equal_nocase(blend, "bumpmap")) {
562                     currentLayer.m_type = LAYER_BUMPMAP;
563                 } else if (string_equal_nocase(blend, "specularmap")) {
564                     currentLayer.m_type = LAYER_SPECULARMAP;
565                 } else {
566                     currentLayer.m_blendFunc.first = blend;
567
568                     const char *comma = tokeniser.getToken();
569
570                     if (comma == 0) {
571                         Tokeniser_unexpectedError(tokeniser, comma, "#comma");
572                         return false;
573                     }
574
575                     if (string_equal(comma, ",")) {
576                         RETURN_FALSE_IF_FAIL(Tokeniser_parseString(tokeniser, currentLayer.m_blendFunc.second));
577                     } else {
578                         currentLayer.m_blendFunc.second = "";
579                         tokeniser.ungetToken();
580                     }
581                 }
582             } else if (string_equal_nocase(token, "map")) {
583                 if (currentLayer.m_type == LAYER_BUMPMAP) {
584                     RETURN_FALSE_IF_FAIL(
585                             Doom3Shader_parseBumpmap(tokeniser, currentLayer.m_texture, currentLayer.m_heightmapScale));
586                 } else {
587                     const char *map = tokeniser.getToken();
588
589                     if (map == 0) {
590                         Tokeniser_unexpectedError(tokeniser, map, "#map");
591                         return false;
592                     }
593
594                     if (string_equal(map, "makealpha")) {
595                         RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "("));
596                         const char *texture = tokeniser.getToken();
597                         if (texture == 0) {
598                             Tokeniser_unexpectedError(tokeniser, texture, "#texture");
599                             return false;
600                         }
601                         currentLayer.m_texture = texture;
602                         RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")"));
603                     } else {
604                         parseTextureName(currentLayer.m_texture, map);
605                     }
606                 }
607             } else if (string_equal_nocase(token, "zeroclamp")) {
608                 currentLayer.m_clampToBorder = true;
609             }
610 #if 0
611                                                                                                                                     else if ( string_equal_nocase( token, "alphaTest" ) ) {
612                                 Tokeniser_getFloat( tokeniser, currentLayer.m_alphaTest );
613                         }
614 #endif
615         } else if (depth == 1) {
616             if (string_equal_nocase(token, "qer_editorimage")) {
617                 RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, m_textureName));
618             } else if (string_equal_nocase(token, "qer_trans")) {
619                 m_fTrans = string_read_float(tokeniser.getToken());
620                 m_nFlags |= QER_TRANS;
621             } else if (string_equal_nocase(token, "translucent")) {
622                 m_fTrans = 1;
623                 m_nFlags |= QER_TRANS;
624             } else if (string_equal(token, "DECAL_MACRO")) {
625                 m_fTrans = 1;
626                 m_nFlags |= QER_TRANS;
627             } else if (string_equal_nocase(token, "bumpmap")) {
628                 RETURN_FALSE_IF_FAIL(Doom3Shader_parseBumpmap(tokeniser, m_bump, m_heightmapScale));
629             } else if (string_equal_nocase(token, "diffusemap")) {
630                 RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, m_diffuse));
631             } else if (string_equal_nocase(token, "specularmap")) {
632                 RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, m_specular));
633             } else if (string_equal_nocase(token, "twosided")) {
634                 m_Cull = IShader::eCullNone;
635                 m_nFlags |= QER_CULL;
636             } else if (string_equal_nocase(token, "nodraw")) {
637                 m_nFlags |= QER_NODRAW;
638             } else if (string_equal_nocase(token, "nonsolid")) {
639                 m_nFlags |= QER_NONSOLID;
640             } else if (string_equal_nocase(token, "liquid")) {
641                 m_nFlags |= QER_WATER;
642             } else if (string_equal_nocase(token, "areaportal")) {
643                 m_nFlags |= QER_AREAPORTAL;
644             } else if (string_equal_nocase(token, "playerclip")
645                        || string_equal_nocase(token, "monsterclip")
646                        || string_equal_nocase(token, "ikclip")
647                        || string_equal_nocase(token, "moveableclip")) {
648                 m_nFlags |= QER_CLIP;
649             }
650             if (string_equal_nocase(token, "fogLight")) {
651                 isFog = true;
652             } else if (!isFog && string_equal_nocase(token, "lightFalloffImage")) {
653                 const char *lightFalloffImage = tokeniser.getToken();
654                 if (lightFalloffImage == 0) {
655                     Tokeniser_unexpectedError(tokeniser, lightFalloffImage, "#lightFalloffImage");
656                     return false;
657                 }
658                 if (string_equal_nocase(lightFalloffImage, "makeintensity")) {
659                     RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "("));
660                     TextureExpression name;
661                     RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, name));
662                     m_lightFalloffImage = name;
663                     RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")"));
664                 } else {
665                     m_lightFalloffImage = lightFalloffImage;
666                 }
667             }
668         }
669     }
670
671     if (string_empty(m_textureName.c_str())) {
672         m_textureName = m_diffuse;
673     }
674
675     return true;
676 }
677
678 typedef SmartPointer<ShaderTemplate> ShaderTemplatePointer;
679 typedef std::map<CopiedString, ShaderTemplatePointer> ShaderTemplateMap;
680
681 ShaderTemplateMap g_shaders;
682 ShaderTemplateMap g_shaderTemplates;
683
684 ShaderTemplate *findTemplate(const char *name)
685 {
686     ShaderTemplateMap::iterator i = g_shaderTemplates.find(name);
687     if (i != g_shaderTemplates.end()) {
688         return (*i).second.get();
689     }
690     return 0;
691 }
692
693 class ShaderDefinition {
694 public:
695     ShaderDefinition(ShaderTemplate *shaderTemplate, const ShaderArguments &args, const char *filename)
696             : shaderTemplate(shaderTemplate), args(args), filename(filename)
697     {
698     }
699
700     ShaderTemplate *shaderTemplate;
701     ShaderArguments args;
702     const char *filename;
703 };
704
705 typedef std::map<CopiedString, ShaderDefinition> ShaderDefinitionMap;
706
707 ShaderDefinitionMap g_shaderDefinitions;
708
709 bool parseTemplateInstance(Tokeniser &tokeniser, const char *filename)
710 {
711     CopiedString name;
712     RETURN_FALSE_IF_FAIL(Tokeniser_parseShaderName(tokeniser, name));
713     const char *templateName = tokeniser.getToken();
714     ShaderTemplate *shaderTemplate = findTemplate(templateName);
715     if (shaderTemplate == 0) {
716         globalErrorStream() << "shader instance: " << makeQuoted(name.c_str()) << ": shader template not found: "
717                             << makeQuoted(templateName) << "\n";
718     }
719
720     ShaderArguments args;
721     if (!parseShaderParameters(tokeniser, args)) {
722         globalErrorStream() << "shader instance: " << makeQuoted(name.c_str()) << ": argument parse failed\n";
723         return false;
724     }
725
726     if (shaderTemplate != 0) {
727         if (!g_shaderDefinitions.insert(
728                 ShaderDefinitionMap::value_type(name, ShaderDefinition(shaderTemplate, args, filename))).second) {
729             globalErrorStream() << "shader instance: " << makeQuoted(name.c_str())
730                                 << ": already exists, second definition ignored\n";
731         }
732     }
733     return true;
734 }
735
736
737 const char *evaluateShaderValue(const char *value, const ShaderParameters &params, const ShaderArguments &args)
738 {
739     ShaderArguments::const_iterator j = args.begin();
740     for (ShaderParameters::const_iterator i = params.begin(); i != params.end(); ++i, ++j) {
741         const char *other = (*i).c_str();
742         if (string_equal(value, other)) {
743             return (*j).c_str();
744         }
745     }
746     return value;
747 }
748
749 ///\todo BlendFunc parsing
750 BlendFunc
751 evaluateBlendFunc(const BlendFuncExpression &blendFunc, const ShaderParameters &params, const ShaderArguments &args)
752 {
753     return BlendFunc(BLEND_ONE, BLEND_ZERO);
754 }
755
756 qtexture_t *
757 evaluateTexture(const TextureExpression &texture, const ShaderParameters &params, const ShaderArguments &args,
758                 const LoadImageCallback &loader = GlobalTexturesCache().defaultLoader())
759 {
760     StringOutputStream result(64);
761     const char *expression = texture.c_str();
762     const char *end = expression + string_length(expression);
763     if (!string_empty(expression)) {
764         for (;;) {
765             const char *best = end;
766             const char *bestParam = 0;
767             const char *bestArg = 0;
768             ShaderArguments::const_iterator j = args.begin();
769             for (ShaderParameters::const_iterator i = params.begin(); i != params.end(); ++i, ++j) {
770                 const char *found = strstr(expression, (*i).c_str());
771                 if (found != 0 && found < best) {
772                     best = found;
773                     bestParam = (*i).c_str();
774                     bestArg = (*j).c_str();
775                 }
776             }
777             if (best != end) {
778                 result << StringRange(expression, best);
779                 result << PathCleaned(bestArg);
780                 expression = best + string_length(bestParam);
781             } else {
782                 break;
783             }
784         }
785         result << expression;
786     }
787     return GlobalTexturesCache().capture(loader, result.c_str());
788 }
789
790 float evaluateFloat(const ShaderValue &value, const ShaderParameters &params, const ShaderArguments &args)
791 {
792     const char *result = evaluateShaderValue(value.c_str(), params, args);
793     float f;
794     if (!string_parse_float(result, f)) {
795         globalErrorStream() << "parsing float value failed: " << makeQuoted(result) << "\n";
796     }
797     return f;
798 }
799
800 BlendFactor evaluateBlendFactor(const ShaderValue &value, const ShaderParameters &params, const ShaderArguments &args)
801 {
802     const char *result = evaluateShaderValue(value.c_str(), params, args);
803
804     if (string_equal_nocase(result, "gl_zero")) {
805         return BLEND_ZERO;
806     }
807     if (string_equal_nocase(result, "gl_one")) {
808         return BLEND_ONE;
809     }
810     if (string_equal_nocase(result, "gl_src_color")) {
811         return BLEND_SRC_COLOUR;
812     }
813     if (string_equal_nocase(result, "gl_one_minus_src_color")) {
814         return BLEND_ONE_MINUS_SRC_COLOUR;
815     }
816     if (string_equal_nocase(result, "gl_src_alpha")) {
817         return BLEND_SRC_ALPHA;
818     }
819     if (string_equal_nocase(result, "gl_one_minus_src_alpha")) {
820         return BLEND_ONE_MINUS_SRC_ALPHA;
821     }
822     if (string_equal_nocase(result, "gl_dst_color")) {
823         return BLEND_DST_COLOUR;
824     }
825     if (string_equal_nocase(result, "gl_one_minus_dst_color")) {
826         return BLEND_ONE_MINUS_DST_COLOUR;
827     }
828     if (string_equal_nocase(result, "gl_dst_alpha")) {
829         return BLEND_DST_ALPHA;
830     }
831     if (string_equal_nocase(result, "gl_one_minus_dst_alpha")) {
832         return BLEND_ONE_MINUS_DST_ALPHA;
833     }
834     if (string_equal_nocase(result, "gl_src_alpha_saturate")) {
835         return BLEND_SRC_ALPHA_SATURATE;
836     }
837
838     globalErrorStream() << "parsing blend-factor value failed: " << makeQuoted(result) << "\n";
839     return BLEND_ZERO;
840 }
841
842 class CShader : public IShader {
843     std::size_t m_refcount;
844
845     const ShaderTemplate &m_template;
846     const ShaderArguments &m_args;
847     const char *m_filename;
848 // name is shader-name, otherwise texture-name (if not a real shader)
849     CopiedString m_Name;
850
851     qtexture_t *m_pTexture;
852     qtexture_t *m_notfound;
853     qtexture_t *m_pDiffuse;
854     float m_heightmapScale;
855     qtexture_t *m_pBump;
856     qtexture_t *m_pSpecular;
857     qtexture_t *m_pLightFalloffImage;
858     BlendFunc m_blendFunc;
859
860     bool m_bInUse;
861
862
863 public:
864     static bool m_lightingEnabled;
865
866     CShader(const ShaderDefinition &definition) :
867             m_refcount(0),
868             m_template(*definition.shaderTemplate),
869             m_args(definition.args),
870             m_filename(definition.filename),
871             m_blendFunc(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA),
872             m_bInUse(false)
873     {
874         m_pTexture = 0;
875         m_pDiffuse = 0;
876         m_pBump = 0;
877         m_pSpecular = 0;
878
879         m_notfound = 0;
880
881         realise();
882     }
883
884     virtual ~CShader()
885     {
886         unrealise();
887
888         ASSERT_MESSAGE(m_refcount == 0, "deleting active shader");
889     }
890
891 // IShaders implementation -----------------
892     void IncRef()
893     {
894         ++m_refcount;
895     }
896
897     void DecRef()
898     {
899         ASSERT_MESSAGE(m_refcount != 0, "shader reference-count going below zero");
900         if (--m_refcount == 0) {
901             delete this;
902         }
903     }
904
905     std::size_t refcount()
906     {
907         return m_refcount;
908     }
909
910 // get/set the qtexture_t* Radiant uses to represent this shader object
911     qtexture_t *getTexture() const
912     {
913         return m_pTexture;
914     }
915
916     qtexture_t *getDiffuse() const
917     {
918         return m_pDiffuse;
919     }
920
921     qtexture_t *getBump() const
922     {
923         return m_pBump;
924     }
925
926     qtexture_t *getSpecular() const
927     {
928         return m_pSpecular;
929     }
930
931 // get shader name
932     const char *getName() const
933     {
934         return m_Name.c_str();
935     }
936
937     bool IsInUse() const
938     {
939         return m_bInUse;
940     }
941
942     void SetInUse(bool bInUse)
943     {
944         m_bInUse = bInUse;
945         g_ActiveShadersChangedNotify();
946     }
947
948 // get the shader flags
949     int getFlags() const
950     {
951         return m_template.m_nFlags;
952     }
953
954 // get the transparency value
955     float getTrans() const
956     {
957         return m_template.m_fTrans;
958     }
959
960 // test if it's a true shader, or a default shader created to wrap around a texture
961     bool IsDefault() const
962     {
963         return string_empty(m_filename);
964     }
965
966 // get the alphaFunc
967     void getAlphaFunc(EAlphaFunc *func, float *ref)
968     {
969         *func = m_template.m_AlphaFunc;
970         *ref = m_template.m_AlphaRef;
971     };
972
973     BlendFunc getBlendFunc() const
974     {
975         return m_blendFunc;
976     }
977
978 // get the cull type
979     ECull getCull()
980     {
981         return m_template.m_Cull;
982     };
983
984 // get shader file name (ie the file where this one is defined)
985     const char *getShaderFileName() const
986     {
987         return m_filename;
988     }
989 // -----------------------------------------
990
991     void realise()
992     {
993         m_pTexture = evaluateTexture(m_template.m_textureName, m_template.m_params, m_args);
994
995         if (m_pTexture->texture_number == 0) {
996             m_notfound = m_pTexture;
997
998             {
999                 m_pTexture = GlobalTexturesCache().capture(IsDefault() ? DEFAULT_NOTEX_NAME : DEFAULT_SHADERNOTEX_NAME);
1000             }
1001         }
1002
1003         realiseLighting();
1004     }
1005
1006     void unrealise()
1007     {
1008         GlobalTexturesCache().release(m_pTexture);
1009
1010         if (m_notfound != 0) {
1011             GlobalTexturesCache().release(m_notfound);
1012         }
1013
1014         unrealiseLighting();
1015     }
1016
1017     void realiseLighting()
1018     {
1019         if (m_lightingEnabled) {
1020             LoadImageCallback loader = GlobalTexturesCache().defaultLoader();
1021             if (!string_empty(m_template.m_heightmapScale.c_str())) {
1022                 m_heightmapScale = evaluateFloat(m_template.m_heightmapScale, m_template.m_params, m_args);
1023                 loader = LoadImageCallback(&m_heightmapScale, loadHeightmap);
1024             }
1025             m_pDiffuse = evaluateTexture(m_template.m_diffuse, m_template.m_params, m_args);
1026             m_pBump = evaluateTexture(m_template.m_bump, m_template.m_params, m_args, loader);
1027             m_pSpecular = evaluateTexture(m_template.m_specular, m_template.m_params, m_args);
1028             m_pLightFalloffImage = evaluateTexture(m_template.m_lightFalloffImage, m_template.m_params, m_args);
1029
1030             for (ShaderTemplate::MapLayers::const_iterator i = m_template.m_layers.begin();
1031                  i != m_template.m_layers.end(); ++i) {
1032                 m_layers.push_back(evaluateLayer(*i, m_template.m_params, m_args));
1033             }
1034
1035             if (m_layers.size() == 1) {
1036                 const BlendFuncExpression &blendFunc = m_template.m_layers.front().blendFunc();
1037                 if (!string_empty(blendFunc.second.c_str())) {
1038                     m_blendFunc = BlendFunc(
1039                             evaluateBlendFactor(blendFunc.first.c_str(), m_template.m_params, m_args),
1040                             evaluateBlendFactor(blendFunc.second.c_str(), m_template.m_params, m_args)
1041                     );
1042                 } else {
1043                     const char *blend = evaluateShaderValue(blendFunc.first.c_str(), m_template.m_params, m_args);
1044
1045                     if (string_equal_nocase(blend, "add")) {
1046                         m_blendFunc = BlendFunc(BLEND_ONE, BLEND_ONE);
1047                     } else if (string_equal_nocase(blend, "filter")) {
1048                         m_blendFunc = BlendFunc(BLEND_DST_COLOUR, BLEND_ZERO);
1049                     } else if (string_equal_nocase(blend, "blend")) {
1050                         m_blendFunc = BlendFunc(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA);
1051                     } else {
1052                         globalErrorStream() << "parsing blend value failed: " << makeQuoted(blend) << "\n";
1053                     }
1054                 }
1055             }
1056         }
1057     }
1058
1059     void unrealiseLighting()
1060     {
1061         if (m_lightingEnabled) {
1062             GlobalTexturesCache().release(m_pDiffuse);
1063             GlobalTexturesCache().release(m_pBump);
1064             GlobalTexturesCache().release(m_pSpecular);
1065
1066             GlobalTexturesCache().release(m_pLightFalloffImage);
1067
1068             for (MapLayers::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
1069                 GlobalTexturesCache().release((*i).texture());
1070             }
1071             m_layers.clear();
1072
1073             m_blendFunc = BlendFunc(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA);
1074         }
1075     }
1076
1077 // set shader name
1078     void setName(const char *name)
1079     {
1080         m_Name = name;
1081     }
1082
1083     class MapLayer : public ShaderLayer {
1084         qtexture_t *m_texture;
1085         BlendFunc m_blendFunc;
1086         bool m_clampToBorder;
1087         float m_alphaTest;
1088     public:
1089         MapLayer(qtexture_t *texture, BlendFunc blendFunc, bool clampToBorder, float alphaTest) :
1090                 m_texture(texture),
1091                 m_blendFunc(blendFunc),
1092                 m_clampToBorder(false),
1093                 m_alphaTest(alphaTest)
1094         {
1095         }
1096
1097         qtexture_t *texture() const
1098         {
1099             return m_texture;
1100         }
1101
1102         BlendFunc blendFunc() const
1103         {
1104             return m_blendFunc;
1105         }
1106
1107         bool clampToBorder() const
1108         {
1109             return m_clampToBorder;
1110         }
1111
1112         float alphaTest() const
1113         {
1114             return m_alphaTest;
1115         }
1116     };
1117
1118     static MapLayer evaluateLayer(const ShaderTemplate::MapLayerTemplate &layerTemplate, const ShaderParameters &params,
1119                                   const ShaderArguments &args)
1120     {
1121         return MapLayer(
1122                 evaluateTexture(layerTemplate.texture(), params, args),
1123                 evaluateBlendFunc(layerTemplate.blendFunc(), params, args),
1124                 layerTemplate.clampToBorder(),
1125                 evaluateFloat(layerTemplate.alphaTest(), params, args)
1126         );
1127     }
1128
1129     typedef std::vector<MapLayer> MapLayers;
1130     MapLayers m_layers;
1131
1132     const ShaderLayer *firstLayer() const
1133     {
1134         if (m_layers.empty()) {
1135             return 0;
1136         }
1137         return &m_layers.front();
1138     }
1139
1140     void forEachLayer(const ShaderLayerCallback &callback) const
1141     {
1142         for (MapLayers::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
1143             callback(*i);
1144         }
1145     }
1146
1147     qtexture_t *lightFalloffImage() const
1148     {
1149         if (!string_empty(m_template.m_lightFalloffImage.c_str())) {
1150             return m_pLightFalloffImage;
1151         }
1152         return 0;
1153     }
1154 };
1155
1156 bool CShader::m_lightingEnabled = false;
1157
1158 typedef SmartPointer<CShader> ShaderPointer;
1159 typedef std::map<CopiedString, ShaderPointer, shader_less_t> shaders_t;
1160
1161 shaders_t g_ActiveShaders;
1162
1163 static shaders_t::iterator g_ActiveShadersIterator;
1164
1165 void ActiveShaders_IteratorBegin()
1166 {
1167     g_ActiveShadersIterator = g_ActiveShaders.begin();
1168 }
1169
1170 bool ActiveShaders_IteratorAtEnd()
1171 {
1172     return g_ActiveShadersIterator == g_ActiveShaders.end();
1173 }
1174
1175 IShader *ActiveShaders_IteratorCurrent()
1176 {
1177     return static_cast<CShader *>( g_ActiveShadersIterator->second );
1178 }
1179
1180 void ActiveShaders_IteratorIncrement()
1181 {
1182     ++g_ActiveShadersIterator;
1183 }
1184
1185 void debug_check_shaders(shaders_t &shaders)
1186 {
1187     for (shaders_t::iterator i = shaders.begin(); i != shaders.end(); ++i) {
1188         ASSERT_MESSAGE(i->second->refcount() == 1, "orphan shader still referenced");
1189     }
1190 }
1191
1192 // will free all GL binded qtextures and shaders
1193 // NOTE: doesn't make much sense out of Radiant exit or called during a reload
1194 void FreeShaders()
1195 {
1196     // reload shaders
1197     // empty the actives shaders list
1198     debug_check_shaders(g_ActiveShaders);
1199     g_ActiveShaders.clear();
1200     g_shaders.clear();
1201     g_shaderTemplates.clear();
1202     g_shaderDefinitions.clear();
1203     g_ActiveShadersChangedNotify();
1204 }
1205
1206 bool ShaderTemplate::parseQuake3(Tokeniser &tokeniser)
1207 {
1208     // name of the qtexture_t we'll use to represent this shader (this one has the "textures\" before)
1209     m_textureName = m_Name.c_str();
1210
1211     tokeniser.nextLine();
1212
1213     // we need to read until we hit a balanced }
1214     int depth = 0;
1215     for (;;) {
1216         tokeniser.nextLine();
1217         const char *token = tokeniser.getToken();
1218
1219         if (token == 0) {
1220             return false;
1221         }
1222
1223         if (string_equal(token, "{")) {
1224             ++depth;
1225             continue;
1226         } else if (string_equal(token, "}")) {
1227             --depth;
1228             if (depth < 0) { // underflow
1229                 return false;
1230             }
1231             if (depth == 0) { // end of shader
1232                 break;
1233             }
1234
1235             continue;
1236         }
1237
1238         if (depth == 1) {
1239             if (string_equal_nocase(token, "qer_nocarve")) {
1240                 m_nFlags |= QER_NOCARVE;
1241             } else if (string_equal_nocase(token, "qer_trans")) {
1242                 RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, m_fTrans));
1243                 m_nFlags |= QER_TRANS;
1244             } else if (string_equal_nocase(token, "qer_editorimage")) {
1245                 RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, m_textureName));
1246             } else if (string_equal_nocase(token, "qer_alphafunc")) {
1247                 const char *alphafunc = tokeniser.getToken();
1248
1249                 if (alphafunc == 0) {
1250                     Tokeniser_unexpectedError(tokeniser, alphafunc, "#alphafunc");
1251                     return false;
1252                 }
1253
1254                 if (string_equal_nocase(alphafunc, "equal")) {
1255                     m_AlphaFunc = IShader::eEqual;
1256                 } else if (string_equal_nocase(alphafunc, "greater")) {
1257                     m_AlphaFunc = IShader::eGreater;
1258                 } else if (string_equal_nocase(alphafunc, "less")) {
1259                     m_AlphaFunc = IShader::eLess;
1260                 } else if (string_equal_nocase(alphafunc, "gequal")) {
1261                     m_AlphaFunc = IShader::eGEqual;
1262                 } else if (string_equal_nocase(alphafunc, "lequal")) {
1263                     m_AlphaFunc = IShader::eLEqual;
1264                 } else {
1265                     m_AlphaFunc = IShader::eAlways;
1266                 }
1267
1268                 m_nFlags |= QER_ALPHATEST;
1269
1270                 RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, m_AlphaRef));
1271             } else if (string_equal_nocase(token, "cull")) {
1272                 const char *cull = tokeniser.getToken();
1273
1274                 if (cull == 0) {
1275                     Tokeniser_unexpectedError(tokeniser, cull, "#cull");
1276                     return false;
1277                 }
1278
1279                 if (string_equal_nocase(cull, "none")
1280                     || string_equal_nocase(cull, "twosided")
1281                     || string_equal_nocase(cull, "disable")) {
1282                     m_Cull = IShader::eCullNone;
1283                 } else if (string_equal_nocase(cull, "back")
1284                            || string_equal_nocase(cull, "backside")
1285                            || string_equal_nocase(cull, "backsided")) {
1286                     m_Cull = IShader::eCullBack;
1287                 } else {
1288                     m_Cull = IShader::eCullBack;
1289                 }
1290
1291                 m_nFlags |= QER_CULL;
1292             } else if (string_equal_nocase(token, "surfaceparm")) {
1293                 const char *surfaceparm = tokeniser.getToken();
1294
1295                 if (surfaceparm == 0) {
1296                     Tokeniser_unexpectedError(tokeniser, surfaceparm, "#surfaceparm");
1297                     return false;
1298                 }
1299
1300                 if (string_equal_nocase(surfaceparm, "fog")) {
1301                     m_nFlags |= QER_FOG;
1302                     if (m_fTrans == 1.0f) { // has not been explicitly set by qer_trans
1303                         m_fTrans = 0.35f;
1304                     }
1305                 } else if (string_equal_nocase(surfaceparm, "nodraw")) {
1306                     m_nFlags |= QER_NODRAW;
1307                 } else if (string_equal_nocase(surfaceparm, "nonsolid")) {
1308                     m_nFlags |= QER_NONSOLID;
1309                 } else if (string_equal_nocase(surfaceparm, "water")) {
1310                     m_nFlags |= QER_WATER;
1311                 } else if (string_equal_nocase(surfaceparm, "lava")) {
1312                     m_nFlags |= QER_LAVA;
1313                 } else if (string_equal_nocase(surfaceparm, "areaportal")) {
1314                     m_nFlags |= QER_AREAPORTAL;
1315                 } else if (string_equal_nocase(surfaceparm, "playerclip")) {
1316                     m_nFlags |= QER_CLIP;
1317                 } else if (string_equal_nocase(surfaceparm, "botclip")) {
1318                     m_nFlags |= QER_BOTCLIP;
1319                 }
1320             }
1321         }
1322     }
1323
1324     return true;
1325 }
1326
1327 class Layer {
1328 public:
1329     LayerTypeId m_type;
1330     TextureExpression m_texture;
1331     BlendFunc m_blendFunc;
1332     bool m_clampToBorder;
1333     float m_alphaTest;
1334     float m_heightmapScale;
1335
1336     Layer() : m_type(LAYER_NONE), m_blendFunc(BLEND_ONE, BLEND_ZERO), m_clampToBorder(false), m_alphaTest(-1),
1337               m_heightmapScale(0)
1338     {
1339     }
1340 };
1341
1342 std::list<CopiedString> g_shaderFilenames;
1343
1344 void ParseShaderFile(Tokeniser &tokeniser, const char *filename)
1345 {
1346     g_shaderFilenames.push_back(filename);
1347     filename = g_shaderFilenames.back().c_str();
1348     tokeniser.nextLine();
1349     for (;;) {
1350         const char *token = tokeniser.getToken();
1351
1352         if (token == 0) {
1353             break;
1354         }
1355
1356         if (string_equal(token, "table")) {
1357             if (tokeniser.getToken() == 0) {
1358                 Tokeniser_unexpectedError(tokeniser, 0, "#table-name");
1359                 return;
1360             }
1361             if (!Tokeniser_parseToken(tokeniser, "{")) {
1362                 return;
1363             }
1364             for (;;) {
1365                 const char *option = tokeniser.getToken();
1366                 if (string_equal(option, "{")) {
1367                     for (;;) {
1368                         const char *value = tokeniser.getToken();
1369                         if (string_equal(value, "}")) {
1370                             break;
1371                         }
1372                     }
1373
1374                     if (!Tokeniser_parseToken(tokeniser, "}")) {
1375                         return;
1376                     }
1377                     break;
1378                 }
1379             }
1380         } else {
1381             if (string_equal(token, "guide")) {
1382                 parseTemplateInstance(tokeniser, filename);
1383             } else {
1384                 if (!string_equal(token, "material")
1385                     && !string_equal(token, "particle")
1386                     && !string_equal(token, "skin")) {
1387                     tokeniser.ungetToken();
1388                 }
1389                 // first token should be the path + name.. (from base)
1390                 CopiedString name;
1391                 if (!Tokeniser_parseShaderName(tokeniser, name)) {
1392                 }
1393                 ShaderTemplatePointer shaderTemplate(new ShaderTemplate());
1394                 shaderTemplate->setName(name.c_str());
1395
1396                 g_shaders.insert(ShaderTemplateMap::value_type(shaderTemplate->getName(), shaderTemplate));
1397
1398                 bool result = (g_shaderLanguage == SHADERLANGUAGE_QUAKE3)
1399                               ? shaderTemplate->parseQuake3(tokeniser)
1400                               : shaderTemplate->parseDoom3(tokeniser);
1401                 if (result) {
1402                     // do we already have this shader?
1403                     if (!g_shaderDefinitions.insert(ShaderDefinitionMap::value_type(shaderTemplate->getName(),
1404                                                                                     ShaderDefinition(
1405                                                                                             shaderTemplate.get(),
1406                                                                                             ShaderArguments(),
1407                                                                                             filename))).second) {
1408 #if GDEF_DEBUG
1409                         globalOutputStream() << "WARNING: shader " << shaderTemplate->getName()
1410                                              << " is already in memory, definition in " << filename << " ignored.\n";
1411 #endif
1412                     }
1413                 } else {
1414                     globalErrorStream() << "Error parsing shader " << shaderTemplate->getName() << "\n";
1415                     return;
1416                 }
1417             }
1418         }
1419     }
1420 }
1421
1422 void parseGuideFile(Tokeniser &tokeniser, const char *filename)
1423 {
1424     tokeniser.nextLine();
1425     for (;;) {
1426         const char *token = tokeniser.getToken();
1427
1428         if (token == 0) {
1429             break;
1430         }
1431
1432         if (string_equal(token, "guide")) {
1433             // first token should be the path + name.. (from base)
1434             ShaderTemplatePointer shaderTemplate(new ShaderTemplate);
1435             shaderTemplate->parseTemplate(tokeniser);
1436             if (!g_shaderTemplates.insert(
1437                     ShaderTemplateMap::value_type(shaderTemplate->getName(), shaderTemplate)).second) {
1438                 globalErrorStream() << "guide " << makeQuoted(shaderTemplate->getName())
1439                                     << ": already defined, second definition ignored\n";
1440             }
1441         } else if (string_equal(token, "inlineGuide")) {
1442             // skip entire inlineGuide definition
1443             std::size_t depth = 0;
1444             for (;;) {
1445                 tokeniser.nextLine();
1446                 token = tokeniser.getToken();
1447                 if (string_equal(token, "{")) {
1448                     ++depth;
1449                 } else if (string_equal(token, "}")) {
1450                     if (--depth == 0) {
1451                         break;
1452                     }
1453                 }
1454             }
1455         }
1456     }
1457 }
1458
1459 void LoadShaderFile(const char *filename)
1460 {
1461     ArchiveTextFile *file = GlobalFileSystem().openTextFile(filename);
1462
1463     if (file != 0) {
1464         globalOutputStream() << "Parsing shaderfile " << filename << "\n";
1465
1466         Tokeniser &tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser(file->getInputStream());
1467
1468         ParseShaderFile(tokeniser, filename);
1469
1470         tokeniser.release();
1471         file->release();
1472     } else {
1473         globalOutputStream() << "Unable to read shaderfile " << filename << "\n";
1474     }
1475 }
1476
1477 void loadGuideFile(const char *filename)
1478 {
1479     StringOutputStream fullname(256);
1480     fullname << "guides/" << filename;
1481     ArchiveTextFile *file = GlobalFileSystem().openTextFile(fullname.c_str());
1482
1483     if (file != 0) {
1484         globalOutputStream() << "Parsing guide file " << fullname.c_str() << "\n";
1485
1486         Tokeniser &tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser(file->getInputStream());
1487
1488         parseGuideFile(tokeniser, fullname.c_str());
1489
1490         tokeniser.release();
1491         file->release();
1492     } else {
1493         globalOutputStream() << "Unable to read guide file " << fullname.c_str() << "\n";
1494     }
1495 }
1496
1497 CShader *Try_Shader_ForName(const char *name)
1498 {
1499     {
1500         shaders_t::iterator i = g_ActiveShaders.find(name);
1501         if (i != g_ActiveShaders.end()) {
1502             return (*i).second;
1503         }
1504     }
1505     // active shader was not found
1506
1507     // find matching shader definition
1508     ShaderDefinitionMap::iterator i = g_shaderDefinitions.find(name);
1509     if (i == g_shaderDefinitions.end()) {
1510         // shader definition was not found
1511
1512         // create new shader definition from default shader template
1513         ShaderTemplatePointer shaderTemplate(new ShaderTemplate());
1514         shaderTemplate->CreateDefault(name);
1515         g_shaderTemplates.insert(ShaderTemplateMap::value_type(shaderTemplate->getName(), shaderTemplate));
1516
1517         i = g_shaderDefinitions.insert(ShaderDefinitionMap::value_type(name, ShaderDefinition(shaderTemplate.get(),
1518                                                                                               ShaderArguments(),
1519                                                                                               ""))).first;
1520     }
1521
1522     // create shader from existing definition
1523     ShaderPointer pShader(new CShader((*i).second));
1524     pShader->setName(name);
1525     g_ActiveShaders.insert(shaders_t::value_type(name, pShader));
1526     g_ActiveShadersChangedNotify();
1527     return pShader;
1528 }
1529
1530 IShader *Shader_ForName(const char *name)
1531 {
1532     ASSERT_NOTNULL(name);
1533
1534     IShader *pShader = Try_Shader_ForName(name);
1535     pShader->IncRef();
1536     return pShader;
1537 }
1538
1539
1540 // the list of scripts/*.shader files we need to work with
1541 // those are listed in shaderlist file
1542 GSList *l_shaderfiles = 0;
1543
1544 GSList *Shaders_getShaderFileList()
1545 {
1546     return l_shaderfiles;
1547 }
1548
1549 /*
1550    ==================
1551    DumpUnreferencedShaders
1552    usefull function: dumps the list of .shader files that are not referenced to the console
1553    ==================
1554  */
1555 void IfFound_dumpUnreferencedShader(bool &bFound, const char *filename)
1556 {
1557     bool listed = false;
1558
1559     for (GSList *sh = l_shaderfiles; sh != 0; sh = g_slist_next(sh)) {
1560         if (!strcmp((char *) sh->data, filename)) {
1561             listed = true;
1562             break;
1563         }
1564     }
1565
1566     if (!listed) {
1567         if (!bFound) {
1568             bFound = true;
1569             globalOutputStream() << "Following shader files are not referenced in any shaderlist.txt:\n";
1570         }
1571         globalOutputStream() << "\t" << filename << "\n";
1572     }
1573 }
1574
1575 typedef ReferenceCaller<bool, void(const char *), IfFound_dumpUnreferencedShader> IfFoundDumpUnreferencedShaderCaller;
1576
1577 void DumpUnreferencedShaders()
1578 {
1579     bool bFound = false;
1580     GlobalFileSystem().forEachFile(g_shadersDirectory, g_shadersExtension, IfFoundDumpUnreferencedShaderCaller(bFound));
1581 }
1582
1583 void ShaderList_addShaderFile(const char *dirstring)
1584 {
1585     bool found = false;
1586
1587     for (GSList *tmp = l_shaderfiles; tmp != 0; tmp = tmp->next) {
1588         if (string_equal_nocase(dirstring, (char *) tmp->data)) {
1589             found = true;
1590             globalOutputStream() << "duplicate entry \"" << (char *) tmp->data << "\" in shaderlist.txt\n";
1591             break;
1592         }
1593     }
1594
1595     if (!found) {
1596         l_shaderfiles = g_slist_append(l_shaderfiles, strdup(dirstring));
1597     }
1598 }
1599
1600 /*
1601    ==================
1602    BuildShaderList
1603    build a CStringList of shader names
1604    ==================
1605  */
1606 void BuildShaderList(TextInputStream &shaderlist)
1607 {
1608     Tokeniser &tokeniser = GlobalScriptLibrary().m_pfnNewSimpleTokeniser(shaderlist);
1609     tokeniser.nextLine();
1610     const char *token = tokeniser.getToken();
1611     StringOutputStream shaderFile(64);
1612     while (token != 0) {
1613         // each token should be a shader filename
1614         shaderFile << token << "." << g_shadersExtension;
1615
1616         ShaderList_addShaderFile(shaderFile.c_str());
1617
1618         tokeniser.nextLine();
1619         token = tokeniser.getToken();
1620
1621         shaderFile.clear();
1622     }
1623     tokeniser.release();
1624 }
1625
1626 void FreeShaderList()
1627 {
1628     while (l_shaderfiles != 0) {
1629         free(l_shaderfiles->data);
1630         l_shaderfiles = g_slist_remove(l_shaderfiles, l_shaderfiles->data);
1631     }
1632 }
1633
1634 void ShaderList_addFromArchive(const char *archivename)
1635 {
1636     const char *shaderpath = GlobalRadiant().getGameDescriptionKeyValue("shaderpath");
1637     if (string_empty(shaderpath)) {
1638         return;
1639     }
1640
1641     StringOutputStream shaderlist(256);
1642     shaderlist << DirectoryCleaned(shaderpath) << "shaderlist.txt";
1643
1644     Archive *archive = GlobalFileSystem().getArchive(archivename, false);
1645     if (archive) {
1646         ArchiveTextFile *file = archive->openTextFile(shaderlist.c_str());
1647         if (file) {
1648             globalOutputStream() << "Found shaderlist.txt in " << archivename << "\n";
1649             BuildShaderList(file->getInputStream());
1650             file->release();
1651         }
1652     }
1653 }
1654
1655 #include "stream/filestream.h"
1656
1657 bool
1658 shaderlist_findOrInstall(const char *enginePath, const char *toolsPath, const char *shaderPath, const char *gamename)
1659 {
1660     StringOutputStream absShaderList(256);
1661     absShaderList << enginePath << gamename << '/' << shaderPath << "shaderlist.txt";
1662     if (file_exists(absShaderList.c_str())) {
1663         return true;
1664     }
1665     {
1666         StringOutputStream directory(256);
1667         directory << enginePath << gamename << '/' << shaderPath;
1668         if (!file_exists(directory.c_str()) && !Q_mkdir(directory.c_str())) {
1669             return false;
1670         }
1671     }
1672     {
1673         StringOutputStream defaultShaderList(256);
1674         defaultShaderList << toolsPath << gamename << '/' << "default_shaderlist.txt";
1675         if (file_exists(defaultShaderList.c_str())) {
1676             return file_copy(defaultShaderList.c_str(), absShaderList.c_str());
1677         }
1678     }
1679     return false;
1680 }
1681
1682 void Shaders_Load()
1683 {
1684     if (g_shaderLanguage == SHADERLANGUAGE_QUAKE4) {
1685         GlobalFileSystem().forEachFile("guides/", "guide", makeCallbackF(loadGuideFile), 0);
1686     }
1687
1688     const char *shaderPath = GlobalRadiant().getGameDescriptionKeyValue("shaderpath");
1689     if (!string_empty(shaderPath)) {
1690         StringOutputStream path(256);
1691         path << DirectoryCleaned(shaderPath);
1692
1693         if (g_useShaderList) {
1694             // preload shader files that have been listed in shaderlist.txt
1695             const char *basegame = GlobalRadiant().getRequiredGameDescriptionKeyValue("basegame");
1696             const char *gamename = GlobalRadiant().getGameName();
1697             const char *enginePath = GlobalRadiant().getEnginePath();
1698             const char *toolsPath = GlobalRadiant().getGameToolsPath();
1699
1700             bool isMod = !string_equal(basegame, gamename);
1701
1702             if (!isMod || !shaderlist_findOrInstall(enginePath, toolsPath, path.c_str(), gamename)) {
1703                 gamename = basegame;
1704                 shaderlist_findOrInstall(enginePath, toolsPath, path.c_str(), gamename);
1705             }
1706
1707             GlobalFileSystem().forEachArchive(makeCallbackF(ShaderList_addFromArchive), false, true);
1708             DumpUnreferencedShaders();
1709         } else {
1710             GlobalFileSystem().forEachFile(path.c_str(), g_shadersExtension, makeCallbackF(ShaderList_addShaderFile),
1711                                            0);
1712         }
1713
1714         GSList *lst = l_shaderfiles;
1715         StringOutputStream shadername(256);
1716         while (lst) {
1717             shadername << path.c_str() << reinterpret_cast<const char *>( lst->data );
1718             LoadShaderFile(shadername.c_str());
1719             shadername.clear();
1720             lst = lst->next;
1721         }
1722     }
1723
1724     //StringPool_analyse(ShaderPool::instance());
1725 }
1726
1727 void Shaders_Free()
1728 {
1729     FreeShaders();
1730     FreeShaderList();
1731     g_shaderFilenames.clear();
1732 }
1733
1734 ModuleObservers g_observers;
1735
1736 std::size_t g_shaders_unrealised = 1; // wait until filesystem and is realised before loading anything
1737 bool Shaders_realised()
1738 {
1739     return g_shaders_unrealised == 0;
1740 }
1741
1742 void Shaders_Realise()
1743 {
1744     if (--g_shaders_unrealised == 0) {
1745         Shaders_Load();
1746         g_observers.realise();
1747     }
1748 }
1749
1750 void Shaders_Unrealise()
1751 {
1752     if (++g_shaders_unrealised == 1) {
1753         g_observers.unrealise();
1754         Shaders_Free();
1755     }
1756 }
1757
1758 void Shaders_Refresh()
1759 {
1760     Shaders_Unrealise();
1761     Shaders_Realise();
1762 }
1763
1764 class Quake3ShaderSystem : public ShaderSystem, public ModuleObserver {
1765 public:
1766     void realise()
1767     {
1768         Shaders_Realise();
1769     }
1770
1771     void unrealise()
1772     {
1773         Shaders_Unrealise();
1774     }
1775
1776     void refresh()
1777     {
1778         Shaders_Refresh();
1779     }
1780
1781     IShader *getShaderForName(const char *name)
1782     {
1783         return Shader_ForName(name);
1784     }
1785
1786     void foreachShaderName(const ShaderNameCallback &callback)
1787     {
1788         for (ShaderDefinitionMap::const_iterator i = g_shaderDefinitions.begin(); i != g_shaderDefinitions.end(); ++i) {
1789             callback((*i).first.c_str());
1790         }
1791     }
1792
1793     void beginActiveShadersIterator()
1794     {
1795         ActiveShaders_IteratorBegin();
1796     }
1797
1798     bool endActiveShadersIterator()
1799     {
1800         return ActiveShaders_IteratorAtEnd();
1801     }
1802
1803     IShader *dereferenceActiveShadersIterator()
1804     {
1805         return ActiveShaders_IteratorCurrent();
1806     }
1807
1808     void incrementActiveShadersIterator()
1809     {
1810         ActiveShaders_IteratorIncrement();
1811     }
1812
1813     void setActiveShadersChangedNotify(const Callback<void()> &notify)
1814     {
1815         g_ActiveShadersChangedNotify = notify;
1816     }
1817
1818     void attach(ModuleObserver &observer)
1819     {
1820         g_observers.attach(observer);
1821     }
1822
1823     void detach(ModuleObserver &observer)
1824     {
1825         g_observers.detach(observer);
1826     }
1827
1828     void setLightingEnabled(bool enabled)
1829     {
1830         if (CShader::m_lightingEnabled != enabled) {
1831             for (shaders_t::const_iterator i = g_ActiveShaders.begin(); i != g_ActiveShaders.end(); ++i) {
1832                 (*i).second->unrealiseLighting();
1833             }
1834             CShader::m_lightingEnabled = enabled;
1835             for (shaders_t::const_iterator i = g_ActiveShaders.begin(); i != g_ActiveShaders.end(); ++i) {
1836                 (*i).second->realiseLighting();
1837             }
1838         }
1839     }
1840
1841     const char *getTexturePrefix() const
1842     {
1843         return g_texturePrefix;
1844     }
1845 };
1846
1847 Quake3ShaderSystem g_Quake3ShaderSystem;
1848
1849 ShaderSystem &GetShaderSystem()
1850 {
1851     return g_Quake3ShaderSystem;
1852 }
1853
1854 void Shaders_Construct()
1855 {
1856     GlobalFileSystem().attach(g_Quake3ShaderSystem);
1857 }
1858
1859 void Shaders_Destroy()
1860 {
1861     GlobalFileSystem().detach(g_Quake3ShaderSystem);
1862
1863     if (Shaders_realised()) {
1864         Shaders_Free();
1865     }
1866 }