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