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