]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/shaders/shaders.cpp
a78a660202f834d07595963ca2c2d81be810774a
[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 void forEachLayer( const ShaderLayerCallback& callback ) const {
1116         for ( MapLayers::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i )
1117         {
1118                 callback( *i );
1119         }
1120 }
1121
1122 qtexture_t* lightFalloffImage() const {
1123         if ( !string_empty( m_template.m_lightFalloffImage.c_str() ) ) {
1124                 return m_pLightFalloffImage;
1125         }
1126         return 0;
1127 }
1128 };
1129
1130 bool CShader::m_lightingEnabled = false;
1131
1132 typedef SmartPointer<CShader> ShaderPointer;
1133 typedef std::map<CopiedString, ShaderPointer, shader_less_t> shaders_t;
1134
1135 shaders_t g_ActiveShaders;
1136
1137 static shaders_t::iterator g_ActiveShadersIterator;
1138
1139 void ActiveShaders_IteratorBegin(){
1140         g_ActiveShadersIterator = g_ActiveShaders.begin();
1141 }
1142
1143 bool ActiveShaders_IteratorAtEnd(){
1144         return g_ActiveShadersIterator == g_ActiveShaders.end();
1145 }
1146
1147 IShader *ActiveShaders_IteratorCurrent(){
1148         return static_cast<CShader*>( g_ActiveShadersIterator->second );
1149 }
1150
1151 void ActiveShaders_IteratorIncrement(){
1152         ++g_ActiveShadersIterator;
1153 }
1154
1155 void debug_check_shaders( shaders_t& shaders ){
1156         for ( shaders_t::iterator i = shaders.begin(); i != shaders.end(); ++i )
1157         {
1158                 ASSERT_MESSAGE( i->second->refcount() == 1, "orphan shader still referenced" );
1159         }
1160 }
1161
1162 // will free all GL binded qtextures and shaders
1163 // NOTE: doesn't make much sense out of Radiant exit or called during a reload
1164 void FreeShaders(){
1165         // reload shaders
1166         // empty the actives shaders list
1167         debug_check_shaders( g_ActiveShaders );
1168         g_ActiveShaders.clear();
1169         g_shaders.clear();
1170         g_shaderTemplates.clear();
1171         g_shaderDefinitions.clear();
1172         g_ActiveShadersChangedNotify();
1173 }
1174
1175 bool ShaderTemplate::parseQuake3( Tokeniser& tokeniser ){
1176         // name of the qtexture_t we'll use to represent this shader ( this one has the "textures\" before )
1177         m_textureName = m_Name.c_str();
1178
1179         tokeniser.nextLine();
1180
1181         // we need to read until we hit a balanced }
1182         int depth = 0;
1183         for (;; )
1184         {
1185                 tokeniser.nextLine();
1186                 const char* token = tokeniser.getToken();
1187
1188                 if ( token == 0 ) {
1189                         return false;
1190                 }
1191
1192                 if ( string_equal( token, "{" ) ) {
1193                         ++depth;
1194                         continue;
1195                 }
1196                 else if ( string_equal( token, "}" ) ) {
1197                         --depth;
1198                         if ( depth < 0 ) { // underflow
1199                                 return false;
1200                         }
1201                         if ( depth == 0 ) { // end of shader
1202                                 break;
1203                         }
1204
1205                         continue;
1206                 }
1207
1208                 if ( depth == 1 ) {
1209                         if ( string_equal_nocase( token, "qer_nocarve" ) ) {
1210                                 m_nFlags |= QER_NOCARVE;
1211                         }
1212                         else if ( string_equal_nocase( token, "qer_trans" ) ) {
1213                                 RETURN_FALSE_IF_FAIL( Tokeniser_getFloat( tokeniser, m_fTrans ) );
1214                                 m_nFlags |= QER_TRANS;
1215                         }
1216                         else if ( string_equal_nocase( token, "qer_editorimage" ) ) {
1217                                 RETURN_FALSE_IF_FAIL( Tokeniser_parseTextureName( tokeniser, m_textureName ) );
1218                         }
1219                         else if ( string_equal_nocase( token, "qer_alphafunc" ) ) {
1220                                 const char* alphafunc = tokeniser.getToken();
1221
1222                                 if ( alphafunc == 0 ) {
1223                                         Tokeniser_unexpectedError( tokeniser, alphafunc, "#alphafunc" );
1224                                         return false;
1225                                 }
1226
1227                                 if ( string_equal_nocase( alphafunc, "equal" ) ) {
1228                                         m_AlphaFunc = IShader::eEqual;
1229                                 }
1230                                 else if ( string_equal_nocase( alphafunc, "greater" ) ) {
1231                                         m_AlphaFunc = IShader::eGreater;
1232                                 }
1233                                 else if ( string_equal_nocase( alphafunc, "less" ) ) {
1234                                         m_AlphaFunc = IShader::eLess;
1235                                 }
1236                                 else if ( string_equal_nocase( alphafunc, "gequal" ) ) {
1237                                         m_AlphaFunc = IShader::eGEqual;
1238                                 }
1239                                 else if ( string_equal_nocase( alphafunc, "lequal" ) ) {
1240                                         m_AlphaFunc = IShader::eLEqual;
1241                                 }
1242                                 else
1243                                 {
1244                                         m_AlphaFunc = IShader::eAlways;
1245                                 }
1246
1247                                 m_nFlags |= QER_ALPHATEST;
1248
1249                                 RETURN_FALSE_IF_FAIL( Tokeniser_getFloat( tokeniser, m_AlphaRef ) );
1250                         }
1251                         else if ( string_equal_nocase( token, "cull" ) ) {
1252                                 const char* cull = tokeniser.getToken();
1253
1254                                 if ( cull == 0 ) {
1255                                         Tokeniser_unexpectedError( tokeniser, cull, "#cull" );
1256                                         return false;
1257                                 }
1258
1259                                 if ( string_equal_nocase( cull, "none" )
1260                                          || string_equal_nocase( cull, "twosided" )
1261                                          || string_equal_nocase( cull, "disable" ) ) {
1262                                         m_Cull = IShader::eCullNone;
1263                                 }
1264                                 else if ( string_equal_nocase( cull, "back" )
1265                                                   || string_equal_nocase( cull, "backside" )
1266                                                   || string_equal_nocase( cull, "backsided" ) ) {
1267                                         m_Cull = IShader::eCullBack;
1268                                 }
1269                                 else
1270                                 {
1271                                         m_Cull = IShader::eCullBack;
1272                                 }
1273
1274                                 m_nFlags |= QER_CULL;
1275                         }
1276                         else if ( string_equal_nocase( token, "surfaceparm" ) ) {
1277                                 const char* surfaceparm = tokeniser.getToken();
1278
1279                                 if ( surfaceparm == 0 ) {
1280                                         Tokeniser_unexpectedError( tokeniser, surfaceparm, "#surfaceparm" );
1281                                         return false;
1282                                 }
1283
1284                                 if ( string_equal_nocase( surfaceparm, "fog" ) ) {
1285                                         m_nFlags |= QER_FOG;
1286                                         if ( m_fTrans == 1.0f ) { // has not been explicitly set by qer_trans
1287                                                 m_fTrans = 0.35f;
1288                                         }
1289                                 }
1290                                 else if ( string_equal_nocase( surfaceparm, "nodraw" ) ) {
1291                                         m_nFlags |= QER_NODRAW;
1292                                 }
1293                                 else if ( string_equal_nocase( surfaceparm, "nonsolid" ) ) {
1294                                         m_nFlags |= QER_NONSOLID;
1295                                 }
1296                                 else if ( string_equal_nocase( surfaceparm, "water" ) ) {
1297                                         m_nFlags |= QER_WATER;
1298                                 }
1299                                 else if ( string_equal_nocase( surfaceparm, "lava" ) ) {
1300                                         m_nFlags |= QER_LAVA;
1301                                 }
1302                                 else if ( string_equal_nocase( surfaceparm, "areaportal" ) ) {
1303                                         m_nFlags |= QER_AREAPORTAL;
1304                                 }
1305                                 else if ( string_equal_nocase( surfaceparm, "playerclip" ) ) {
1306                                         m_nFlags |= QER_CLIP;
1307                                 }
1308                                 else if ( string_equal_nocase( surfaceparm, "botclip" ) ) {
1309                                         m_nFlags |= QER_BOTCLIP;
1310                                 }
1311                         }
1312                 }
1313         }
1314
1315         return true;
1316 }
1317
1318 class Layer
1319 {
1320 public:
1321 LayerTypeId m_type;
1322 TextureExpression m_texture;
1323 BlendFunc m_blendFunc;
1324 bool m_clampToBorder;
1325 float m_alphaTest;
1326 float m_heightmapScale;
1327
1328 Layer() : m_type( LAYER_NONE ), m_blendFunc( BLEND_ONE, BLEND_ZERO ), m_clampToBorder( false ), m_alphaTest( -1 ), m_heightmapScale( 0 ){
1329 }
1330 };
1331
1332 std::list<CopiedString> g_shaderFilenames;
1333
1334 void ParseShaderFile( Tokeniser& tokeniser, const char* filename ){
1335         g_shaderFilenames.push_back( filename );
1336         filename = g_shaderFilenames.back().c_str();
1337         tokeniser.nextLine();
1338         for (;; )
1339         {
1340                 const char* token = tokeniser.getToken();
1341
1342                 if ( token == 0 ) {
1343                         break;
1344                 }
1345
1346                 if ( string_equal( token, "table" ) ) {
1347                         if ( tokeniser.getToken() == 0 ) {
1348                                 Tokeniser_unexpectedError( tokeniser, 0, "#table-name" );
1349                                 return;
1350                         }
1351                         if ( !Tokeniser_parseToken( tokeniser, "{" ) ) {
1352                                 return;
1353                         }
1354                         for (;; )
1355                         {
1356                                 const char* option = tokeniser.getToken();
1357                                 if ( string_equal( option, "{" ) ) {
1358                                         for (;; )
1359                                         {
1360                                                 const char* value = tokeniser.getToken();
1361                                                 if ( string_equal( value, "}" ) ) {
1362                                                         break;
1363                                                 }
1364                                         }
1365
1366                                         if ( !Tokeniser_parseToken( tokeniser, "}" ) ) {
1367                                                 return;
1368                                         }
1369                                         break;
1370                                 }
1371                         }
1372                 }
1373                 else
1374                 {
1375                         if ( string_equal( token, "guide" ) ) {
1376                                 parseTemplateInstance( tokeniser, filename );
1377                         }
1378                         else
1379                         {
1380                                 if ( !string_equal( token, "material" )
1381                                          && !string_equal( token, "particle" )
1382                                          && !string_equal( token, "skin" ) ) {
1383                                         tokeniser.ungetToken();
1384                                 }
1385                                 // first token should be the path + name.. ( from base )
1386                                 CopiedString name;
1387                                 if ( !Tokeniser_parseShaderName( tokeniser, name ) ) {
1388                                 }
1389                                 ShaderTemplatePointer shaderTemplate( new ShaderTemplate() );
1390                                 shaderTemplate->setName( name.c_str() );
1391
1392                                 g_shaders.insert( ShaderTemplateMap::value_type( shaderTemplate->getName(), shaderTemplate ) );
1393
1394                                 bool result = ( g_shaderLanguage == SHADERLANGUAGE_QUAKE3 )
1395                                                           ? shaderTemplate->parseQuake3( tokeniser )
1396                                                           : shaderTemplate->parseDoom3( tokeniser );
1397                                 if ( result ) {
1398                                         // do we already have this shader?
1399                                         if ( !g_shaderDefinitions.insert( ShaderDefinitionMap::value_type( shaderTemplate->getName(), ShaderDefinition( shaderTemplate.get(), ShaderArguments(), filename ) ) ).second ) {
1400 #if GDEF_DEBUG
1401                                                 globalOutputStream() << "WARNING: shader " << shaderTemplate->getName() << " is already in memory, definition in " << filename << " ignored.\n";
1402 #endif
1403                                         }
1404                                 }
1405                                 else
1406                                 {
1407                                         globalErrorStream() << "Error parsing shader " << shaderTemplate->getName() << "\n";
1408                                         return;
1409                                 }
1410                         }
1411                 }
1412         }
1413 }
1414
1415 void parseGuideFile( Tokeniser& tokeniser, const char* filename ){
1416         tokeniser.nextLine();
1417         for (;; )
1418         {
1419                 const char* token = tokeniser.getToken();
1420
1421                 if ( token == 0 ) {
1422                         break;
1423                 }
1424
1425                 if ( string_equal( token, "guide" ) ) {
1426                         // first token should be the path + name.. ( from base )
1427                         ShaderTemplatePointer shaderTemplate( new ShaderTemplate );
1428                         shaderTemplate->parseTemplate( tokeniser );
1429                         if ( !g_shaderTemplates.insert( ShaderTemplateMap::value_type( shaderTemplate->getName(), shaderTemplate ) ).second ) {
1430                                 globalErrorStream() << "guide " << makeQuoted( shaderTemplate->getName() ) << ": already defined, second definition ignored\n";
1431                         }
1432                 }
1433                 else if ( string_equal( token, "inlineGuide" ) ) {
1434                         // skip entire inlineGuide definition
1435                         std::size_t depth = 0;
1436                         for (;; )
1437                         {
1438                                 tokeniser.nextLine();
1439                                 token = tokeniser.getToken();
1440                                 if ( string_equal( token, "{" ) ) {
1441                                         ++depth;
1442                                 }
1443                                 else if ( string_equal( token, "}" ) ) {
1444                                         if ( --depth == 0 ) {
1445                                                 break;
1446                                         }
1447                                 }
1448                         }
1449                 }
1450         }
1451 }
1452
1453 void LoadShaderFile( const char* filename ){
1454         ArchiveTextFile* file = GlobalFileSystem().openTextFile( filename );
1455
1456         if ( file != 0 ) {
1457                 globalOutputStream() << "Parsing shaderfile " << filename << "\n";
1458
1459                 Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser( file->getInputStream() );
1460
1461                 ParseShaderFile( tokeniser, filename );
1462
1463                 tokeniser.release();
1464                 file->release();
1465         }
1466         else
1467         {
1468                 globalOutputStream() << "Unable to read shaderfile " << filename << "\n";
1469         }
1470 }
1471
1472 void loadGuideFile( const char* filename ){
1473         StringOutputStream fullname( 256 );
1474         fullname << "guides/" << filename;
1475         ArchiveTextFile* file = GlobalFileSystem().openTextFile( fullname.c_str() );
1476
1477         if ( file != 0 ) {
1478                 globalOutputStream() << "Parsing guide file " << fullname.c_str() << "\n";
1479
1480                 Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser( file->getInputStream() );
1481
1482                 parseGuideFile( tokeniser, fullname.c_str() );
1483
1484                 tokeniser.release();
1485                 file->release();
1486         }
1487         else
1488         {
1489                 globalOutputStream() << "Unable to read guide file " << fullname.c_str() << "\n";
1490         }
1491 }
1492
1493 CShader* Try_Shader_ForName( const char* name ){
1494         {
1495                 shaders_t::iterator i = g_ActiveShaders.find( name );
1496                 if ( i != g_ActiveShaders.end() ) {
1497                         return ( *i ).second;
1498                 }
1499         }
1500         // active shader was not found
1501
1502         // find matching shader definition
1503         ShaderDefinitionMap::iterator i = g_shaderDefinitions.find( name );
1504         if ( i == g_shaderDefinitions.end() ) {
1505                 // shader definition was not found
1506
1507                 // create new shader definition from default shader template
1508                 ShaderTemplatePointer shaderTemplate( new ShaderTemplate() );
1509                 shaderTemplate->CreateDefault( name );
1510                 g_shaderTemplates.insert( ShaderTemplateMap::value_type( shaderTemplate->getName(), shaderTemplate ) );
1511
1512                 i = g_shaderDefinitions.insert( ShaderDefinitionMap::value_type( name, ShaderDefinition( shaderTemplate.get(), ShaderArguments(), "" ) ) ).first;
1513         }
1514
1515         // create shader from existing definition
1516         ShaderPointer pShader( new CShader( ( *i ).second ) );
1517         pShader->setName( name );
1518         g_ActiveShaders.insert( shaders_t::value_type( name, pShader ) );
1519         g_ActiveShadersChangedNotify();
1520         return pShader;
1521 }
1522
1523 IShader *Shader_ForName( const char *name ){
1524         ASSERT_NOTNULL( name );
1525
1526         IShader *pShader = Try_Shader_ForName( name );
1527         pShader->IncRef();
1528         return pShader;
1529 }
1530
1531
1532 // the list of scripts/*.shader files we need to work with
1533 // those are listed in shaderlist file
1534 GSList *l_shaderfiles = 0;
1535
1536 GSList* Shaders_getShaderFileList(){
1537         return l_shaderfiles;
1538 }
1539
1540 /*
1541    ==================
1542    DumpUnreferencedShaders
1543    usefull function: dumps the list of .shader files that are not referenced to the console
1544    ==================
1545  */
1546 void IfFound_dumpUnreferencedShader( bool& bFound, const char* filename ){
1547         bool listed = false;
1548
1549         for ( GSList* sh = l_shaderfiles; sh != 0; sh = g_slist_next( sh ) )
1550         {
1551                 if ( !strcmp( (char*)sh->data, filename ) ) {
1552                         listed = true;
1553                         break;
1554                 }
1555         }
1556
1557         if ( !listed ) {
1558                 if ( !bFound ) {
1559                         bFound = true;
1560                         globalOutputStream() << "Following shader files are not referenced in any shaderlist.txt:\n";
1561                 }
1562                 globalOutputStream() << "\t" << filename << "\n";
1563         }
1564 }
1565
1566 typedef ReferenceCaller<bool, void(const char*), IfFound_dumpUnreferencedShader> IfFoundDumpUnreferencedShaderCaller;
1567
1568 void DumpUnreferencedShaders(){
1569         bool bFound = false;
1570         GlobalFileSystem().forEachFile( g_shadersDirectory, g_shadersExtension, IfFoundDumpUnreferencedShaderCaller( bFound ) );
1571 }
1572
1573 void ShaderList_addShaderFile( const char* dirstring ){
1574         bool found = false;
1575
1576         for ( GSList* tmp = l_shaderfiles; tmp != 0; tmp = tmp->next )
1577         {
1578                 if ( string_equal_nocase( dirstring, (char*)tmp->data ) ) {
1579                         found = true;
1580                         globalOutputStream() << "duplicate entry \"" << (char*)tmp->data << "\" in shaderlist.txt\n";
1581                         break;
1582                 }
1583         }
1584
1585         if ( !found ) {
1586                 l_shaderfiles = g_slist_append( l_shaderfiles, strdup( dirstring ) );
1587         }
1588 }
1589
1590 /*
1591    ==================
1592    BuildShaderList
1593    build a CStringList of shader names
1594    ==================
1595  */
1596 void BuildShaderList( TextInputStream& shaderlist ){
1597         Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewSimpleTokeniser( shaderlist );
1598         tokeniser.nextLine();
1599         const char* token = tokeniser.getToken();
1600         StringOutputStream shaderFile( 64 );
1601         while ( token != 0 )
1602         {
1603                 // each token should be a shader filename
1604                 shaderFile << token << "." << g_shadersExtension;
1605
1606                 ShaderList_addShaderFile( shaderFile.c_str() );
1607
1608                 tokeniser.nextLine();
1609                 token = tokeniser.getToken();
1610
1611                 shaderFile.clear();
1612         }
1613         tokeniser.release();
1614 }
1615
1616 void FreeShaderList(){
1617         while ( l_shaderfiles != 0 )
1618         {
1619                 free( l_shaderfiles->data );
1620                 l_shaderfiles = g_slist_remove( l_shaderfiles, l_shaderfiles->data );
1621         }
1622 }
1623
1624 void ShaderList_addFromArchive( const char *archivename ){
1625         const char *shaderpath = GlobalRadiant().getGameDescriptionKeyValue( "shaderpath" );
1626         if ( string_empty( shaderpath ) ) {
1627                 return;
1628         }
1629
1630         StringOutputStream shaderlist( 256 );
1631         shaderlist << DirectoryCleaned( shaderpath ) << "shaderlist.txt";
1632
1633         Archive *archive = GlobalFileSystem().getArchive( archivename, false );
1634         if ( archive ) {
1635                 ArchiveTextFile *file = archive->openTextFile( shaderlist.c_str() );
1636                 if ( file ) {
1637                         globalOutputStream() << "Found shaderlist.txt in " << archivename << "\n";
1638                         BuildShaderList( file->getInputStream() );
1639                         file->release();
1640                 }
1641         }
1642 }
1643
1644 #include "stream/filestream.h"
1645
1646 bool shaderlist_findOrInstall( const char* enginePath, const char* toolsPath, const char* shaderPath, const char* gamename ){
1647         StringOutputStream absShaderList( 256 );
1648         absShaderList << enginePath << gamename << '/' << shaderPath << "shaderlist.txt";
1649         if ( file_exists( absShaderList.c_str() ) ) {
1650                 return true;
1651         }
1652         {
1653                 StringOutputStream directory( 256 );
1654                 directory << enginePath << gamename << '/' << shaderPath;
1655                 if ( !file_exists( directory.c_str() ) && !Q_mkdir( directory.c_str() ) ) {
1656                         return false;
1657                 }
1658         }
1659         {
1660                 StringOutputStream defaultShaderList( 256 );
1661                 defaultShaderList << toolsPath << gamename << '/' << "default_shaderlist.txt";
1662                 if ( file_exists( defaultShaderList.c_str() ) ) {
1663                         return file_copy( defaultShaderList.c_str(), absShaderList.c_str() );
1664                 }
1665         }
1666         return false;
1667 }
1668
1669 void Shaders_Load(){
1670         if ( g_shaderLanguage == SHADERLANGUAGE_QUAKE4 ) {
1671                 GlobalFileSystem().forEachFile("guides/", "guide", makeCallbackF(loadGuideFile), 0);
1672         }
1673
1674         const char* shaderPath = GlobalRadiant().getGameDescriptionKeyValue( "shaderpath" );
1675         if ( !string_empty( shaderPath ) ) {
1676                 StringOutputStream path( 256 );
1677                 path << DirectoryCleaned( shaderPath );
1678
1679                 if ( g_useShaderList ) {
1680                         // preload shader files that have been listed in shaderlist.txt
1681                         const char* basegame = GlobalRadiant().getRequiredGameDescriptionKeyValue( "basegame" );
1682                         const char* gamename = GlobalRadiant().getGameName();
1683                         const char* enginePath = GlobalRadiant().getEnginePath();
1684                         const char* toolsPath = GlobalRadiant().getGameToolsPath();
1685
1686                         bool isMod = !string_equal( basegame, gamename );
1687
1688                         if ( !isMod || !shaderlist_findOrInstall( enginePath, toolsPath, path.c_str(), gamename ) ) {
1689                                 gamename = basegame;
1690                                 shaderlist_findOrInstall( enginePath, toolsPath, path.c_str(), gamename );
1691                         }
1692
1693                         GlobalFileSystem().forEachArchive(makeCallbackF(ShaderList_addFromArchive), false, true);
1694                         DumpUnreferencedShaders();
1695                 }
1696                 else
1697                 {
1698                         GlobalFileSystem().forEachFile(path.c_str(), g_shadersExtension, makeCallbackF(ShaderList_addShaderFile), 0);
1699                 }
1700
1701                 GSList *lst = l_shaderfiles;
1702                 StringOutputStream shadername( 256 );
1703                 while ( lst )
1704                 {
1705                         shadername << path.c_str() << reinterpret_cast<const char*>( lst->data );
1706                         LoadShaderFile( shadername.c_str() );
1707                         shadername.clear();
1708                         lst = lst->next;
1709                 }
1710         }
1711
1712         //StringPool_analyse( ShaderPool::instance() );
1713 }
1714
1715 void Shaders_Free(){
1716         FreeShaders();
1717         FreeShaderList();
1718         g_shaderFilenames.clear();
1719 }
1720
1721 ModuleObservers g_observers;
1722
1723 std::size_t g_shaders_unrealised = 1; // wait until filesystem and is realised before loading anything
1724 bool Shaders_realised(){
1725         return g_shaders_unrealised == 0;
1726 }
1727
1728 void Shaders_Realise(){
1729         if ( --g_shaders_unrealised == 0 ) {
1730                 Shaders_Load();
1731                 g_observers.realise();
1732         }
1733 }
1734
1735 void Shaders_Unrealise(){
1736         if ( ++g_shaders_unrealised == 1 ) {
1737                 g_observers.unrealise();
1738                 Shaders_Free();
1739         }
1740 }
1741
1742 void Shaders_Refresh(){
1743         Shaders_Unrealise();
1744         Shaders_Realise();
1745 }
1746
1747 class Quake3ShaderSystem : public ShaderSystem, public ModuleObserver
1748 {
1749 public:
1750 void realise(){
1751         Shaders_Realise();
1752 }
1753
1754 void unrealise(){
1755         Shaders_Unrealise();
1756 }
1757
1758 void refresh(){
1759         Shaders_Refresh();
1760 }
1761
1762 IShader* getShaderForName( const char* name ){
1763         return Shader_ForName( name );
1764 }
1765
1766 void foreachShaderName( const ShaderNameCallback& callback ){
1767         for ( ShaderDefinitionMap::const_iterator i = g_shaderDefinitions.begin(); i != g_shaderDefinitions.end(); ++i )
1768         {
1769                 callback( ( *i ).first.c_str() );
1770         }
1771 }
1772
1773 void beginActiveShadersIterator(){
1774         ActiveShaders_IteratorBegin();
1775 }
1776
1777 bool endActiveShadersIterator(){
1778         return ActiveShaders_IteratorAtEnd();
1779 }
1780
1781 IShader* dereferenceActiveShadersIterator(){
1782         return ActiveShaders_IteratorCurrent();
1783 }
1784
1785 void incrementActiveShadersIterator(){
1786         ActiveShaders_IteratorIncrement();
1787 }
1788
1789 void setActiveShadersChangedNotify( const Callback<void()>& notify ){
1790         g_ActiveShadersChangedNotify = notify;
1791 }
1792
1793 void attach( ModuleObserver& observer ){
1794         g_observers.attach( observer );
1795 }
1796
1797 void detach( ModuleObserver& observer ){
1798         g_observers.detach( observer );
1799 }
1800
1801 void setLightingEnabled( bool enabled ){
1802         if ( CShader::m_lightingEnabled != enabled ) {
1803                 for ( shaders_t::const_iterator i = g_ActiveShaders.begin(); i != g_ActiveShaders.end(); ++i )
1804                 {
1805                         ( *i ).second->unrealiseLighting();
1806                 }
1807                 CShader::m_lightingEnabled = enabled;
1808                 for ( shaders_t::const_iterator i = g_ActiveShaders.begin(); i != g_ActiveShaders.end(); ++i )
1809                 {
1810                         ( *i ).second->realiseLighting();
1811                 }
1812         }
1813 }
1814
1815 const char* getTexturePrefix() const {
1816         return g_texturePrefix;
1817 }
1818 };
1819
1820 Quake3ShaderSystem g_Quake3ShaderSystem;
1821
1822 ShaderSystem& GetShaderSystem(){
1823         return g_Quake3ShaderSystem;
1824 }
1825
1826 void Shaders_Construct(){
1827         GlobalFileSystem().attach( g_Quake3ShaderSystem );
1828 }
1829
1830 void Shaders_Destroy(){
1831         GlobalFileSystem().detach( g_Quake3ShaderSystem );
1832
1833         if ( Shaders_realised() ) {
1834                 Shaders_Free();
1835         }
1836 }