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