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