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