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