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