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