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