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