]> git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/shaders/shaders.cpp
Merge commit '6e687efe8899278a955efd1e3ba4de5300f7949a' into garux-merge
[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                                         m_nFlags |= QER_TRANS;
1280                                         if ( m_fTrans == 1.0f ) { // has not been explicitly set by qer_trans
1281                                                 m_fTrans = 0.35f;
1282                                         }
1283                                 }
1284                                 else if ( string_equal_nocase( surfaceparm, "nodraw" ) ) {
1285                                         m_nFlags |= QER_NODRAW;
1286                                 }
1287                                 else if ( string_equal_nocase( surfaceparm, "nonsolid" ) ) {
1288                                         m_nFlags |= QER_NONSOLID;
1289                                 }
1290                                 else if ( string_equal_nocase( surfaceparm, "water" ) ) {
1291                                         m_nFlags |= QER_WATER;
1292                                 }
1293                                 else if ( string_equal_nocase( surfaceparm, "lava" ) ) {
1294                                         m_nFlags |= QER_LAVA;
1295                                 }
1296                                 else if ( string_equal_nocase( surfaceparm, "areaportal" ) ) {
1297                                         m_nFlags |= QER_AREAPORTAL;
1298                                 }
1299                                 else if ( string_equal_nocase( surfaceparm, "playerclip" ) ) {
1300                                         m_nFlags |= QER_CLIP;
1301                                 }
1302                                 else if ( string_equal_nocase( surfaceparm, "botclip" ) ) {
1303                                         m_nFlags |= QER_BOTCLIP;
1304                                 }
1305                         }
1306                 }
1307         }
1308
1309         return true;
1310 }
1311
1312 class Layer
1313 {
1314 public:
1315 LayerTypeId m_type;
1316 TextureExpression m_texture;
1317 BlendFunc m_blendFunc;
1318 bool m_clampToBorder;
1319 float m_alphaTest;
1320 float m_heightmapScale;
1321
1322 Layer() : m_type( LAYER_NONE ), m_blendFunc( BLEND_ONE, BLEND_ZERO ), m_clampToBorder( false ), m_alphaTest( -1 ), m_heightmapScale( 0 ){
1323 }
1324 };
1325
1326 std::list<CopiedString> g_shaderFilenames;
1327
1328 void ParseShaderFile( Tokeniser& tokeniser, const char* filename ){
1329         g_shaderFilenames.push_back( filename );
1330         filename = g_shaderFilenames.back().c_str();
1331         tokeniser.nextLine();
1332         for (;; )
1333         {
1334                 const char* token = tokeniser.getToken();
1335
1336                 if ( token == 0 ) {
1337                         break;
1338                 }
1339
1340                 if ( string_equal( token, "table" ) ) {
1341                         if ( tokeniser.getToken() == 0 ) {
1342                                 Tokeniser_unexpectedError( tokeniser, 0, "#table-name" );
1343                                 return;
1344                         }
1345                         if ( !Tokeniser_parseToken( tokeniser, "{" ) ) {
1346                                 return;
1347                         }
1348                         for (;; )
1349                         {
1350                                 const char* option = tokeniser.getToken();
1351                                 if ( string_equal( option, "{" ) ) {
1352                                         for (;; )
1353                                         {
1354                                                 const char* value = tokeniser.getToken();
1355                                                 if ( string_equal( value, "}" ) ) {
1356                                                         break;
1357                                                 }
1358                                         }
1359
1360                                         if ( !Tokeniser_parseToken( tokeniser, "}" ) ) {
1361                                                 return;
1362                                         }
1363                                         break;
1364                                 }
1365                         }
1366                 }
1367                 else
1368                 {
1369                         if ( string_equal( token, "guide" ) ) {
1370                                 parseTemplateInstance( tokeniser, filename );
1371                         }
1372                         else
1373                         {
1374                                 if ( !string_equal( token, "material" )
1375                                          && !string_equal( token, "particle" )
1376                                          && !string_equal( token, "skin" ) ) {
1377                                         tokeniser.ungetToken();
1378                                 }
1379                                 // first token should be the path + name.. ( from base )
1380                                 CopiedString name;
1381                                 if ( !Tokeniser_parseShaderName( tokeniser, name ) ) {
1382                                 }
1383                                 ShaderTemplatePointer shaderTemplate( new ShaderTemplate() );
1384                                 shaderTemplate->setName( name.c_str() );
1385
1386                                 g_shaders.insert( ShaderTemplateMap::value_type( shaderTemplate->getName(), shaderTemplate ) );
1387
1388                                 bool result = ( g_shaderLanguage == SHADERLANGUAGE_QUAKE3 )
1389                                                           ? shaderTemplate->parseQuake3( tokeniser )
1390                                                           : shaderTemplate->parseDoom3( tokeniser );
1391                                 if ( result ) {
1392                                         // do we already have this shader?
1393                                         if ( !g_shaderDefinitions.insert( ShaderDefinitionMap::value_type( shaderTemplate->getName(), ShaderDefinition( shaderTemplate.get(), ShaderArguments(), filename ) ) ).second ) {
1394 #if GDEF_DEBUG
1395                                                 globalOutputStream() << "WARNING: shader " << shaderTemplate->getName() << " is already in memory, definition in " << filename << " ignored.\n";
1396 #endif
1397                                         }
1398                                 }
1399                                 else
1400                                 {
1401                                         globalErrorStream() << "Error parsing shader " << shaderTemplate->getName() << "\n";
1402                                         return;
1403                                 }
1404                         }
1405                 }
1406         }
1407 }
1408
1409 void parseGuideFile( Tokeniser& tokeniser, const char* filename ){
1410         tokeniser.nextLine();
1411         for (;; )
1412         {
1413                 const char* token = tokeniser.getToken();
1414
1415                 if ( token == 0 ) {
1416                         break;
1417                 }
1418
1419                 if ( string_equal( token, "guide" ) ) {
1420                         // first token should be the path + name.. ( from base )
1421                         ShaderTemplatePointer shaderTemplate( new ShaderTemplate );
1422                         shaderTemplate->parseTemplate( tokeniser );
1423                         if ( !g_shaderTemplates.insert( ShaderTemplateMap::value_type( shaderTemplate->getName(), shaderTemplate ) ).second ) {
1424                                 globalErrorStream() << "guide " << makeQuoted( shaderTemplate->getName() ) << ": already defined, second definition ignored\n";
1425                         }
1426                 }
1427                 else if ( string_equal( token, "inlineGuide" ) ) {
1428                         // skip entire inlineGuide definition
1429                         std::size_t depth = 0;
1430                         for (;; )
1431                         {
1432                                 tokeniser.nextLine();
1433                                 token = tokeniser.getToken();
1434                                 if ( string_equal( token, "{" ) ) {
1435                                         ++depth;
1436                                 }
1437                                 else if ( string_equal( token, "}" ) ) {
1438                                         if ( --depth == 0 ) {
1439                                                 break;
1440                                         }
1441                                 }
1442                         }
1443                 }
1444         }
1445 }
1446
1447 void LoadShaderFile( const char* filename ){
1448         ArchiveTextFile* file = GlobalFileSystem().openTextFile( filename );
1449
1450         if ( file != 0 ) {
1451                 globalOutputStream() << "Parsing shaderfile " << filename << "\n";
1452
1453                 Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser( file->getInputStream() );
1454
1455                 ParseShaderFile( tokeniser, filename );
1456
1457                 tokeniser.release();
1458                 file->release();
1459         }
1460         else
1461         {
1462                 globalOutputStream() << "Unable to read shaderfile " << filename << "\n";
1463         }
1464 }
1465
1466 void loadGuideFile( const char* filename ){
1467         StringOutputStream fullname( 256 );
1468         fullname << "guides/" << filename;
1469         ArchiveTextFile* file = GlobalFileSystem().openTextFile( fullname.c_str() );
1470
1471         if ( file != 0 ) {
1472                 globalOutputStream() << "Parsing guide file " << fullname.c_str() << "\n";
1473
1474                 Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser( file->getInputStream() );
1475
1476                 parseGuideFile( tokeniser, fullname.c_str() );
1477
1478                 tokeniser.release();
1479                 file->release();
1480         }
1481         else
1482         {
1483                 globalOutputStream() << "Unable to read guide file " << fullname.c_str() << "\n";
1484         }
1485 }
1486
1487 CShader* Try_Shader_ForName( const char* name ){
1488         {
1489                 shaders_t::iterator i = g_ActiveShaders.find( name );
1490                 if ( i != g_ActiveShaders.end() ) {
1491                         return ( *i ).second;
1492                 }
1493         }
1494         // active shader was not found
1495
1496         // find matching shader definition
1497         ShaderDefinitionMap::iterator i = g_shaderDefinitions.find( name );
1498         if ( i == g_shaderDefinitions.end() ) {
1499                 // shader definition was not found
1500
1501                 // create new shader definition from default shader template
1502                 ShaderTemplatePointer shaderTemplate( new ShaderTemplate() );
1503                 shaderTemplate->CreateDefault( name );
1504                 g_shaderTemplates.insert( ShaderTemplateMap::value_type( shaderTemplate->getName(), shaderTemplate ) );
1505
1506                 i = g_shaderDefinitions.insert( ShaderDefinitionMap::value_type( name, ShaderDefinition( shaderTemplate.get(), ShaderArguments(), "" ) ) ).first;
1507         }
1508
1509         // create shader from existing definition
1510         ShaderPointer pShader( new CShader( ( *i ).second ) );
1511         pShader->setName( name );
1512         g_ActiveShaders.insert( shaders_t::value_type( name, pShader ) );
1513         g_ActiveShadersChangedNotify();
1514         return pShader;
1515 }
1516
1517 IShader *Shader_ForName( const char *name ){
1518         ASSERT_NOTNULL( name );
1519
1520         IShader *pShader = Try_Shader_ForName( name );
1521         pShader->IncRef();
1522         return pShader;
1523 }
1524
1525
1526 // the list of scripts/*.shader files we need to work with
1527 // those are listed in shaderlist file
1528 GSList *l_shaderfiles = 0;
1529
1530 GSList* Shaders_getShaderFileList(){
1531         return l_shaderfiles;
1532 }
1533
1534 /*
1535    ==================
1536    DumpUnreferencedShaders
1537    usefull function: dumps the list of .shader files that are not referenced to the console
1538    ==================
1539  */
1540 void IfFound_dumpUnreferencedShader( bool& bFound, const char* filename ){
1541         bool listed = false;
1542
1543         for ( GSList* sh = l_shaderfiles; sh != 0; sh = g_slist_next( sh ) )
1544         {
1545                 if ( !strcmp( (char*)sh->data, filename ) ) {
1546                         listed = true;
1547                         break;
1548                 }
1549         }
1550
1551         if ( !listed ) {
1552                 if ( !bFound ) {
1553                         bFound = true;
1554                         globalOutputStream() << "Following shader files are not referenced in any shaderlist.txt:\n";
1555                 }
1556                 globalOutputStream() << "\t" << filename << "\n";
1557         }
1558 }
1559
1560 typedef ReferenceCaller<bool, void(const char*), IfFound_dumpUnreferencedShader> IfFoundDumpUnreferencedShaderCaller;
1561
1562 void DumpUnreferencedShaders(){
1563         bool bFound = false;
1564         GlobalFileSystem().forEachFile( g_shadersDirectory, g_shadersExtension, IfFoundDumpUnreferencedShaderCaller( bFound ) );
1565 }
1566
1567 void ShaderList_addShaderFile( const char* dirstring ){
1568         bool found = false;
1569
1570         for ( GSList* tmp = l_shaderfiles; tmp != 0; tmp = tmp->next )
1571         {
1572                 if ( string_equal_nocase( dirstring, (char*)tmp->data ) ) {
1573                         found = true;
1574                         globalOutputStream() << "duplicate entry \"" << (char*)tmp->data << "\" in shaderlist.txt\n";
1575                         break;
1576                 }
1577         }
1578
1579         if ( !found ) {
1580                 l_shaderfiles = g_slist_append( l_shaderfiles, strdup( dirstring ) );
1581         }
1582 }
1583
1584 /*
1585    ==================
1586    BuildShaderList
1587    build a CStringList of shader names
1588    ==================
1589  */
1590 void BuildShaderList( TextInputStream& shaderlist ){
1591         Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewSimpleTokeniser( shaderlist );
1592         tokeniser.nextLine();
1593         const char* token = tokeniser.getToken();
1594         StringOutputStream shaderFile( 64 );
1595         while ( token != 0 )
1596         {
1597                 // each token should be a shader filename
1598                 shaderFile << token << "." << g_shadersExtension;
1599
1600                 ShaderList_addShaderFile( shaderFile.c_str() );
1601
1602                 tokeniser.nextLine();
1603                 token = tokeniser.getToken();
1604
1605                 shaderFile.clear();
1606         }
1607         tokeniser.release();
1608 }
1609
1610 void FreeShaderList(){
1611         while ( l_shaderfiles != 0 )
1612         {
1613                 free( l_shaderfiles->data );
1614                 l_shaderfiles = g_slist_remove( l_shaderfiles, l_shaderfiles->data );
1615         }
1616 }
1617
1618 void ShaderList_addFromArchive( const char *archivename ){
1619         const char *shaderpath = GlobalRadiant().getGameDescriptionKeyValue( "shaderpath" );
1620         if ( string_empty( shaderpath ) ) {
1621                 return;
1622         }
1623
1624         StringOutputStream shaderlist( 256 );
1625         shaderlist << DirectoryCleaned( shaderpath ) << "shaderlist.txt";
1626
1627         Archive *archive = GlobalFileSystem().getArchive( archivename, false );
1628         if ( archive ) {
1629                 ArchiveTextFile *file = archive->openTextFile( shaderlist.c_str() );
1630                 if ( file ) {
1631                         globalOutputStream() << "Found shaderlist.txt in " << archivename << "\n";
1632                         BuildShaderList( file->getInputStream() );
1633                         file->release();
1634                 }
1635         }
1636 }
1637
1638 #include "stream/filestream.h"
1639
1640 bool shaderlist_findOrInstall( const char* enginePath, const char* toolsPath, const char* shaderPath, const char* gamename ){
1641         StringOutputStream absShaderList( 256 );
1642         absShaderList << enginePath << gamename << '/' << shaderPath << "shaderlist.txt";
1643         if ( file_exists( absShaderList.c_str() ) ) {
1644                 return true;
1645         }
1646         {
1647                 StringOutputStream directory( 256 );
1648                 directory << enginePath << gamename << '/' << shaderPath;
1649                 if ( !file_exists( directory.c_str() ) && !Q_mkdir( directory.c_str() ) ) {
1650                         return false;
1651                 }
1652         }
1653         {
1654                 StringOutputStream defaultShaderList( 256 );
1655                 defaultShaderList << toolsPath << gamename << '/' << "default_shaderlist.txt";
1656                 if ( file_exists( defaultShaderList.c_str() ) ) {
1657                         return file_copy( defaultShaderList.c_str(), absShaderList.c_str() );
1658                 }
1659         }
1660         return false;
1661 }
1662
1663 void Shaders_Load(){
1664         if ( g_shaderLanguage == SHADERLANGUAGE_QUAKE4 ) {
1665                 GlobalFileSystem().forEachFile("guides/", "guide", makeCallbackF(loadGuideFile), 0);
1666         }
1667
1668         const char* shaderPath = GlobalRadiant().getGameDescriptionKeyValue( "shaderpath" );
1669         if ( !string_empty( shaderPath ) ) {
1670                 StringOutputStream path( 256 );
1671                 path << DirectoryCleaned( shaderPath );
1672
1673                 if ( g_useShaderList ) {
1674                         // preload shader files that have been listed in shaderlist.txt
1675                         const char* basegame = GlobalRadiant().getRequiredGameDescriptionKeyValue( "basegame" );
1676                         const char* gamename = GlobalRadiant().getGameName();
1677                         const char* enginePath = GlobalRadiant().getEnginePath();
1678                         const char* toolsPath = GlobalRadiant().getGameToolsPath();
1679
1680                         bool isMod = !string_equal( basegame, gamename );
1681
1682                         if ( !isMod || !shaderlist_findOrInstall( enginePath, toolsPath, path.c_str(), gamename ) ) {
1683                                 gamename = basegame;
1684                                 shaderlist_findOrInstall( enginePath, toolsPath, path.c_str(), gamename );
1685                         }
1686
1687                         GlobalFileSystem().forEachArchive(makeCallbackF(ShaderList_addFromArchive), false, true);
1688                         DumpUnreferencedShaders();
1689                 }
1690                 else
1691                 {
1692                         GlobalFileSystem().forEachFile(path.c_str(), g_shadersExtension, makeCallbackF(ShaderList_addShaderFile), 0);
1693                 }
1694
1695                 GSList *lst = l_shaderfiles;
1696                 StringOutputStream shadername( 256 );
1697                 while ( lst )
1698                 {
1699                         shadername << path.c_str() << reinterpret_cast<const char*>( lst->data );
1700                         LoadShaderFile( shadername.c_str() );
1701                         shadername.clear();
1702                         lst = lst->next;
1703                 }
1704         }
1705
1706         //StringPool_analyse( ShaderPool::instance() );
1707 }
1708
1709 void Shaders_Free(){
1710         FreeShaders();
1711         FreeShaderList();
1712         g_shaderFilenames.clear();
1713 }
1714
1715 ModuleObservers g_observers;
1716
1717 std::size_t g_shaders_unrealised = 1; // wait until filesystem and is realised before loading anything
1718 bool Shaders_realised(){
1719         return g_shaders_unrealised == 0;
1720 }
1721
1722 void Shaders_Realise(){
1723         if ( --g_shaders_unrealised == 0 ) {
1724                 Shaders_Load();
1725                 g_observers.realise();
1726         }
1727 }
1728
1729 void Shaders_Unrealise(){
1730         if ( ++g_shaders_unrealised == 1 ) {
1731                 g_observers.unrealise();
1732                 Shaders_Free();
1733         }
1734 }
1735
1736 void Shaders_Refresh(){
1737         Shaders_Unrealise();
1738         Shaders_Realise();
1739 }
1740
1741 class Quake3ShaderSystem : public ShaderSystem, public ModuleObserver
1742 {
1743 public:
1744 void realise(){
1745         Shaders_Realise();
1746 }
1747
1748 void unrealise(){
1749         Shaders_Unrealise();
1750 }
1751
1752 void refresh(){
1753         Shaders_Refresh();
1754 }
1755
1756 IShader* getShaderForName( const char* name ){
1757         return Shader_ForName( name );
1758 }
1759
1760 void foreachShaderName( const ShaderNameCallback& callback ){
1761         for ( ShaderDefinitionMap::const_iterator i = g_shaderDefinitions.begin(); i != g_shaderDefinitions.end(); ++i )
1762         {
1763                 callback( ( *i ).first.c_str() );
1764         }
1765 }
1766
1767 void beginActiveShadersIterator(){
1768         ActiveShaders_IteratorBegin();
1769 }
1770
1771 bool endActiveShadersIterator(){
1772         return ActiveShaders_IteratorAtEnd();
1773 }
1774
1775 IShader* dereferenceActiveShadersIterator(){
1776         return ActiveShaders_IteratorCurrent();
1777 }
1778
1779 void incrementActiveShadersIterator(){
1780         ActiveShaders_IteratorIncrement();
1781 }
1782
1783 void setActiveShadersChangedNotify( const Callback<void()>& notify ){
1784         g_ActiveShadersChangedNotify = notify;
1785 }
1786
1787 void attach( ModuleObserver& observer ){
1788         g_observers.attach( observer );
1789 }
1790
1791 void detach( ModuleObserver& observer ){
1792         g_observers.detach( observer );
1793 }
1794
1795 void setLightingEnabled( bool enabled ){
1796         if ( CShader::m_lightingEnabled != enabled ) {
1797                 for ( shaders_t::const_iterator i = g_ActiveShaders.begin(); i != g_ActiveShaders.end(); ++i )
1798                 {
1799                         ( *i ).second->unrealiseLighting();
1800                 }
1801                 CShader::m_lightingEnabled = enabled;
1802                 for ( shaders_t::const_iterator i = g_ActiveShaders.begin(); i != g_ActiveShaders.end(); ++i )
1803                 {
1804                         ( *i ).second->realiseLighting();
1805                 }
1806         }
1807 }
1808
1809 const char* getTexturePrefix() const {
1810         return g_texturePrefix;
1811 }
1812 };
1813
1814 Quake3ShaderSystem g_Quake3ShaderSystem;
1815
1816 ShaderSystem& GetShaderSystem(){
1817         return g_Quake3ShaderSystem;
1818 }
1819
1820 void Shaders_Construct(){
1821         GlobalFileSystem().attach( g_Quake3ShaderSystem );
1822 }
1823
1824 void Shaders_Destroy(){
1825         GlobalFileSystem().detach( g_Quake3ShaderSystem );
1826
1827         if ( Shaders_realised() ) {
1828                 Shaders_Free();
1829         }
1830 }