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