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