X-Git-Url: http://git.xonotic.org/?p=xonotic%2Fnetradiant.git;a=blobdiff_plain;f=plugins%2Fshaders%2Fshaders.cpp;fp=plugins%2Fshaders%2Fshaders.cpp;h=99dac811d9a4baf2ff882d1b8876ff04ad7abd61;hp=0499bee5e2948f872c97a5061e4248971e32f8e9;hb=9dfae1c9b270ee369c6362903a9205b30751b95f;hpb=0a6d5683b0cfa1a0bd7ef64bada105dbe4ddd6e1 diff --git a/plugins/shaders/shaders.cpp b/plugins/shaders/shaders.cpp index 0499bee5..99dac811 100644 --- a/plugins/shaders/shaders.cpp +++ b/plugins/shaders/shaders.cpp @@ -70,23 +70,29 @@ #include "archivelib.h" #include "imagelib.h" -const char* g_shadersExtension = ""; -const char* g_shadersDirectory = ""; +const char *g_shadersExtension = ""; +const char *g_shadersDirectory = ""; bool g_enableDefaultShaders = true; ShaderLanguage g_shaderLanguage = SHADERLANGUAGE_QUAKE3; bool g_useShaderList = true; -_QERPlugImageTable* g_bitmapModule = 0; -const char* g_texturePrefix = "textures/"; +_QERPlugImageTable *g_bitmapModule = 0; +const char *g_texturePrefix = "textures/"; void ActiveShaders_IteratorBegin(); + bool ActiveShaders_IteratorAtEnd(); + IShader *ActiveShaders_IteratorCurrent(); + void ActiveShaders_IteratorIncrement(); + Callback g_ActiveShadersChangedNotify; void FreeShaders(); -void LoadShaderFile( const char *filename ); -qtexture_t *Texture_ForName( const char *filename ); + +void LoadShaderFile(const char *filename); + +qtexture_t *Texture_ForName(const char *filename); /*! @@ -96,47 +102,49 @@ qtexture_t *Texture_ForName( const char *filename ); 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 */ -Image* loadBitmap( void* environment, const char* name ){ - DirectoryArchiveFile file( name, name ); - if ( !file.failed() ) { - return g_bitmapModule->loadImage( file ); - } - return 0; +Image *loadBitmap(void *environment, const char *name) +{ + DirectoryArchiveFile file(name, name); + if (!file.failed()) { + return g_bitmapModule->loadImage(file); + } + return 0; } -inline byte* getPixel( byte* pixels, int width, int height, int x, int y ){ - return pixels + ( ( ( ( ( y + height ) % height ) * width ) + ( ( x + width ) % width ) ) * 4 ); +inline byte *getPixel(byte *pixels, int width, int height, int x, int y) +{ + return pixels + (((((y + height) % height) * width) + ((x + width) % width)) * 4); } -class KernelElement -{ +class KernelElement { public: -int x, y; -float w; + int x, y; + float w; }; -Image& convertHeightmapToNormalmap( Image& heightmap, float scale ){ - int w = heightmap.getWidth(); - int h = heightmap.getHeight(); +Image &convertHeightmapToNormalmap(Image &heightmap, float scale) +{ + int w = heightmap.getWidth(); + int h = heightmap.getHeight(); - Image& normalmap = *( new RGBAImage( heightmap.getWidth(), heightmap.getHeight() ) ); + Image &normalmap = *(new RGBAImage(heightmap.getWidth(), heightmap.getHeight())); - byte* in = heightmap.getRGBAPixels(); - byte* out = normalmap.getRGBAPixels(); + byte *in = heightmap.getRGBAPixels(); + byte *out = normalmap.getRGBAPixels(); #if 1 - // no filtering - const int kernelSize = 2; - KernelElement kernel_du[kernelSize] = { - {-1, 0,-0.5f }, - { 1, 0, 0.5f } - }; - KernelElement kernel_dv[kernelSize] = { - { 0, 1, 0.5f }, - { 0,-1,-0.5f } - }; + // no filtering + const int kernelSize = 2; + KernelElement kernel_du[kernelSize] = { + {-1, 0, -0.5f}, + {1, 0, 0.5f} + }; + KernelElement kernel_dv[kernelSize] = { + {0, 1, 0.5f}, + {0, -1, -0.5f} + }; #else - // 3x3 Prewitt + // 3x3 Prewitt const int kernelSize = 6; KernelElement kernel_du[kernelSize] = { {-1, 1,-1.0f }, @@ -156,70 +164,68 @@ Image& convertHeightmapToNormalmap( Image& heightmap, float scale ){ }; #endif - int x, y = 0; - while ( y < h ) - { - x = 0; - while ( x < w ) - { - float du = 0; - for ( KernelElement* i = kernel_du; i != kernel_du + kernelSize; ++i ) - { - du += ( getPixel( in, w, h, x + ( *i ).x, y + ( *i ).y )[0] / 255.0 ) * ( *i ).w; - } - float dv = 0; - for ( KernelElement* i = kernel_dv; i != kernel_dv + kernelSize; ++i ) - { - dv += ( getPixel( in, w, h, x + ( *i ).x, y + ( *i ).y )[0] / 255.0 ) * ( *i ).w; - } - - float nx = -du * scale; - float ny = -dv * scale; - float nz = 1.0; - - // Normalize - float norm = 1.0 / sqrt( nx * nx + ny * ny + nz * nz ); - out[0] = float_to_integer( ( ( nx * norm ) + 1 ) * 127.5 ); - out[1] = float_to_integer( ( ( ny * norm ) + 1 ) * 127.5 ); - out[2] = float_to_integer( ( ( nz * norm ) + 1 ) * 127.5 ); - out[3] = 255; - - x++; - out += 4; - } - - y++; - } - - return normalmap; -} - -Image* loadHeightmap( void* environment, const char* name ){ - Image* heightmap = GlobalTexturesCache().loadImage( name ); - if ( heightmap != 0 ) { - Image& normalmap = convertHeightmapToNormalmap( *heightmap, *reinterpret_cast( environment ) ); - heightmap->release(); - return &normalmap; - } - return 0; + int x, y = 0; + while (y < h) { + x = 0; + while (x < w) { + float du = 0; + for (KernelElement *i = kernel_du; i != kernel_du + kernelSize; ++i) { + du += (getPixel(in, w, h, x + (*i).x, y + (*i).y)[0] / 255.0) * (*i).w; + } + float dv = 0; + for (KernelElement *i = kernel_dv; i != kernel_dv + kernelSize; ++i) { + dv += (getPixel(in, w, h, x + (*i).x, y + (*i).y)[0] / 255.0) * (*i).w; + } + + float nx = -du * scale; + float ny = -dv * scale; + float nz = 1.0; + + // Normalize + float norm = 1.0 / sqrt(nx * nx + ny * ny + nz * nz); + out[0] = float_to_integer(((nx * norm) + 1) * 127.5); + out[1] = float_to_integer(((ny * norm) + 1) * 127.5); + out[2] = float_to_integer(((nz * norm) + 1) * 127.5); + out[3] = 255; + + x++; + out += 4; + } + + y++; + } + + return normalmap; +} + +Image *loadHeightmap(void *environment, const char *name) +{ + Image *heightmap = GlobalTexturesCache().loadImage(name); + if (heightmap != 0) { + Image &normalmap = convertHeightmapToNormalmap(*heightmap, *reinterpret_cast( environment )); + heightmap->release(); + return &normalmap; + } + return 0; } -Image* loadSpecial( void* environment, const char* name ){ - if ( *name == '_' ) { // special image - StringOutputStream bitmapName( 256 ); - bitmapName << GlobalRadiant().getAppPath() << "bitmaps/" << name + 1 << ".png"; - Image* image = loadBitmap( environment, bitmapName.c_str() ); - if ( image != 0 ) { - return image; - } - } - return GlobalTexturesCache().loadImage( name ); -} - -class ShaderPoolContext +Image *loadSpecial(void *environment, const char *name) { + if (*name == '_') { // special image + StringOutputStream bitmapName(256); + bitmapName << GlobalRadiant().getAppPath() << "bitmaps/" << name + 1 << ".png"; + Image *image = loadBitmap(environment, bitmapName.c_str()); + if (image != 0) { + return image; + } + } + return GlobalTexturesCache().loadImage(name); +} + +class ShaderPoolContext { }; + typedef Static ShaderPool; typedef PooledString ShaderString; typedef ShaderString ShaderVariable; @@ -232,448 +238,440 @@ typedef CopiedString TextureExpression; // we compare as case insensitive. That is Radiant is case insensitive, but knows that the engine is case sensitive. //++timo FIXME: we need to put code somewhere to detect when two shaders that are case insensitive equal are present template -void parseTextureName( StringType& name, const char* token ){ - StringOutputStream cleaned( 256 ); - cleaned << PathCleaned( token ); - name = CopiedString( StringRange( cleaned.c_str(), path_get_filename_base_end( cleaned.c_str() ) ) ).c_str(); // remove extension +void parseTextureName(StringType &name, const char *token) +{ + StringOutputStream cleaned(256); + cleaned << PathCleaned(token); + name = CopiedString( + StringRange(cleaned.c_str(), path_get_filename_base_end(cleaned.c_str()))).c_str(); // remove extension } -bool Tokeniser_parseTextureName( Tokeniser& tokeniser, TextureExpression& name ){ - const char* token = tokeniser.getToken(); - if ( token == 0 ) { - Tokeniser_unexpectedError( tokeniser, token, "#texture-name" ); - return false; - } - parseTextureName( name, token ); - return true; +bool Tokeniser_parseTextureName(Tokeniser &tokeniser, TextureExpression &name) +{ + const char *token = tokeniser.getToken(); + if (token == 0) { + Tokeniser_unexpectedError(tokeniser, token, "#texture-name"); + return false; + } + parseTextureName(name, token); + return true; } -bool Tokeniser_parseShaderName( Tokeniser& tokeniser, CopiedString& name ){ - const char* token = tokeniser.getToken(); - if ( token == 0 ) { - Tokeniser_unexpectedError( tokeniser, token, "#shader-name" ); - return false; - } - parseTextureName( name, token ); - return true; +bool Tokeniser_parseShaderName(Tokeniser &tokeniser, CopiedString &name) +{ + const char *token = tokeniser.getToken(); + if (token == 0) { + Tokeniser_unexpectedError(tokeniser, token, "#shader-name"); + return false; + } + parseTextureName(name, token); + return true; } -bool Tokeniser_parseString( Tokeniser& tokeniser, ShaderString& string ){ - const char* token = tokeniser.getToken(); - if ( token == 0 ) { - Tokeniser_unexpectedError( tokeniser, token, "#string" ); - return false; - } - string = token; - return true; +bool Tokeniser_parseString(Tokeniser &tokeniser, ShaderString &string) +{ + const char *token = tokeniser.getToken(); + if (token == 0) { + Tokeniser_unexpectedError(tokeniser, token, "#string"); + return false; + } + string = token; + return true; } - typedef std::list ShaderParameters; typedef std::list ShaderArguments; typedef std::pair BlendFuncExpression; -class ShaderTemplate -{ -std::size_t m_refcount; -CopiedString m_Name; +class ShaderTemplate { + std::size_t m_refcount; + CopiedString m_Name; public: -ShaderParameters m_params; + ShaderParameters m_params; -TextureExpression m_textureName; -TextureExpression m_diffuse; -TextureExpression m_bump; -ShaderValue m_heightmapScale; -TextureExpression m_specular; -TextureExpression m_lightFalloffImage; + TextureExpression m_textureName; + TextureExpression m_diffuse; + TextureExpression m_bump; + ShaderValue m_heightmapScale; + TextureExpression m_specular; + TextureExpression m_lightFalloffImage; -int m_nFlags; -float m_fTrans; + int m_nFlags; + float m_fTrans; // alphafunc stuff -IShader::EAlphaFunc m_AlphaFunc; -float m_AlphaRef; + IShader::EAlphaFunc m_AlphaFunc; + float m_AlphaRef; // cull stuff -IShader::ECull m_Cull; - -ShaderTemplate() : - m_refcount( 0 ){ - m_nFlags = 0; - m_fTrans = 1.0f; -} - -void IncRef(){ - ++m_refcount; -} -void DecRef(){ - ASSERT_MESSAGE( m_refcount != 0, "shader reference-count going below zero" ); - if ( --m_refcount == 0 ) { - delete this; - } -} - -std::size_t refcount(){ - return m_refcount; -} - -const char* getName() const { - return m_Name.c_str(); -} -void setName( const char* name ){ - m_Name = name; -} + IShader::ECull m_Cull; + + ShaderTemplate() : + m_refcount(0) + { + m_nFlags = 0; + m_fTrans = 1.0f; + } + + void IncRef() + { + ++m_refcount; + } + + void DecRef() + { + ASSERT_MESSAGE(m_refcount != 0, "shader reference-count going below zero"); + if (--m_refcount == 0) { + delete this; + } + } + + std::size_t refcount() + { + return m_refcount; + } + + const char *getName() const + { + return m_Name.c_str(); + } + + void setName(const char *name) + { + m_Name = name; + } // ----------------------------------------- -bool parseDoom3( Tokeniser& tokeniser ); -bool parseQuake3( Tokeniser& tokeniser ); -bool parseTemplate( Tokeniser& tokeniser ); - - -void CreateDefault( const char *name ){ - if ( g_enableDefaultShaders ) { - m_textureName = name; - } - else - { - m_textureName = ""; - } - setName( name ); -} - - -class MapLayerTemplate -{ -TextureExpression m_texture; -BlendFuncExpression m_blendFunc; -bool m_clampToBorder; -ShaderValue m_alphaTest; -public: -MapLayerTemplate( const TextureExpression& texture, const BlendFuncExpression& blendFunc, bool clampToBorder, const ShaderValue& alphaTest ) : - m_texture( texture ), - m_blendFunc( blendFunc ), - m_clampToBorder( false ), - m_alphaTest( alphaTest ){ -} -const TextureExpression& texture() const { - return m_texture; -} -const BlendFuncExpression& blendFunc() const { - return m_blendFunc; -} -bool clampToBorder() const { - return m_clampToBorder; -} -const ShaderValue& alphaTest() const { - return m_alphaTest; -} + bool parseDoom3(Tokeniser &tokeniser); + + bool parseQuake3(Tokeniser &tokeniser); + + bool parseTemplate(Tokeniser &tokeniser); + + + void CreateDefault(const char *name) + { + if (g_enableDefaultShaders) { + m_textureName = name; + } else { + m_textureName = ""; + } + setName(name); + } + + + class MapLayerTemplate { + TextureExpression m_texture; + BlendFuncExpression m_blendFunc; + bool m_clampToBorder; + ShaderValue m_alphaTest; + public: + MapLayerTemplate(const TextureExpression &texture, const BlendFuncExpression &blendFunc, bool clampToBorder, + const ShaderValue &alphaTest) : + m_texture(texture), + m_blendFunc(blendFunc), + m_clampToBorder(false), + m_alphaTest(alphaTest) + { + } + + const TextureExpression &texture() const + { + return m_texture; + } + + const BlendFuncExpression &blendFunc() const + { + return m_blendFunc; + } + + bool clampToBorder() const + { + return m_clampToBorder; + } + + const ShaderValue &alphaTest() const + { + return m_alphaTest; + } + }; + + typedef std::vector MapLayers; + MapLayers m_layers; }; -typedef std::vector MapLayers; -MapLayers m_layers; -}; - - -bool Doom3Shader_parseHeightmap( Tokeniser& tokeniser, TextureExpression& bump, ShaderValue& heightmapScale ){ - RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "(" ) ); - RETURN_FALSE_IF_FAIL( Tokeniser_parseTextureName( tokeniser, bump ) ); - RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "," ) ); - RETURN_FALSE_IF_FAIL( Tokeniser_parseString( tokeniser, heightmapScale ) ); - RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, ")" ) ); - return true; -} -bool Doom3Shader_parseAddnormals( Tokeniser& tokeniser, TextureExpression& bump ){ - RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "(" ) ); - RETURN_FALSE_IF_FAIL( Tokeniser_parseTextureName( tokeniser, bump ) ); - RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "," ) ); - RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "heightmap" ) ); - TextureExpression heightmapName; - ShaderValue heightmapScale; - RETURN_FALSE_IF_FAIL( Doom3Shader_parseHeightmap( tokeniser, heightmapName, heightmapScale ) ); - RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, ")" ) ); - return true; -} -bool Doom3Shader_parseBumpmap( Tokeniser& tokeniser, TextureExpression& bump, ShaderValue& heightmapScale ){ - const char* token = tokeniser.getToken(); - if ( token == 0 ) { - Tokeniser_unexpectedError( tokeniser, token, "#bumpmap" ); - return false; - } - if ( string_equal( token, "heightmap" ) ) { - RETURN_FALSE_IF_FAIL( Doom3Shader_parseHeightmap( tokeniser, bump, heightmapScale ) ); - } - else if ( string_equal( token, "addnormals" ) ) { - RETURN_FALSE_IF_FAIL( Doom3Shader_parseAddnormals( tokeniser, bump ) ); - } - else - { - parseTextureName( bump, token ); - } - return true; +bool Doom3Shader_parseHeightmap(Tokeniser &tokeniser, TextureExpression &bump, ShaderValue &heightmapScale) +{ + RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "(")); + RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, bump)); + RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ",")); + RETURN_FALSE_IF_FAIL(Tokeniser_parseString(tokeniser, heightmapScale)); + RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")")); + return true; } -enum LayerTypeId +bool Doom3Shader_parseAddnormals(Tokeniser &tokeniser, TextureExpression &bump) { - LAYER_NONE, - LAYER_BLEND, - LAYER_DIFFUSEMAP, - LAYER_BUMPMAP, - LAYER_SPECULARMAP + RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "(")); + RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, bump)); + RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ",")); + RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "heightmap")); + TextureExpression heightmapName; + ShaderValue heightmapScale; + RETURN_FALSE_IF_FAIL(Doom3Shader_parseHeightmap(tokeniser, heightmapName, heightmapScale)); + RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")")); + return true; +} + +bool Doom3Shader_parseBumpmap(Tokeniser &tokeniser, TextureExpression &bump, ShaderValue &heightmapScale) +{ + const char *token = tokeniser.getToken(); + if (token == 0) { + Tokeniser_unexpectedError(tokeniser, token, "#bumpmap"); + return false; + } + if (string_equal(token, "heightmap")) { + RETURN_FALSE_IF_FAIL(Doom3Shader_parseHeightmap(tokeniser, bump, heightmapScale)); + } else if (string_equal(token, "addnormals")) { + RETURN_FALSE_IF_FAIL(Doom3Shader_parseAddnormals(tokeniser, bump)); + } else { + parseTextureName(bump, token); + } + return true; +} + +enum LayerTypeId { + LAYER_NONE, + LAYER_BLEND, + LAYER_DIFFUSEMAP, + LAYER_BUMPMAP, + LAYER_SPECULARMAP }; -class LayerTemplate -{ +class LayerTemplate { public: -LayerTypeId m_type; -TextureExpression m_texture; -BlendFuncExpression m_blendFunc; -bool m_clampToBorder; -ShaderValue m_alphaTest; -ShaderValue m_heightmapScale; - -LayerTemplate() : m_type( LAYER_NONE ), m_blendFunc( "GL_ONE", "GL_ZERO" ), m_clampToBorder( false ), m_alphaTest( "-1" ), m_heightmapScale( "0" ){ -} + LayerTypeId m_type; + TextureExpression m_texture; + BlendFuncExpression m_blendFunc; + bool m_clampToBorder; + ShaderValue m_alphaTest; + ShaderValue m_heightmapScale; + + LayerTemplate() : m_type(LAYER_NONE), m_blendFunc("GL_ONE", "GL_ZERO"), m_clampToBorder(false), m_alphaTest("-1"), + m_heightmapScale("0") + { + } }; -bool parseShaderParameters( Tokeniser& tokeniser, ShaderParameters& params ){ - Tokeniser_parseToken( tokeniser, "(" ); - for (;; ) - { - const char* param = tokeniser.getToken(); - if ( string_equal( param, ")" ) ) { - break; - } - params.push_back( param ); - const char* comma = tokeniser.getToken(); - if ( string_equal( comma, ")" ) ) { - break; - } - if ( !string_equal( comma, "," ) ) { - Tokeniser_unexpectedError( tokeniser, comma, "," ); - return false; - } - } - return true; -} - -bool ShaderTemplate::parseTemplate( Tokeniser& tokeniser ){ - m_Name = tokeniser.getToken(); - if ( !parseShaderParameters( tokeniser, m_params ) ) { - globalErrorStream() << "shader template: " << makeQuoted( m_Name.c_str() ) << ": parameter parse failed\n"; - return false; - } +bool parseShaderParameters(Tokeniser &tokeniser, ShaderParameters ¶ms) +{ + Tokeniser_parseToken(tokeniser, "("); + for (;;) { + const char *param = tokeniser.getToken(); + if (string_equal(param, ")")) { + break; + } + params.push_back(param); + const char *comma = tokeniser.getToken(); + if (string_equal(comma, ")")) { + break; + } + if (!string_equal(comma, ",")) { + Tokeniser_unexpectedError(tokeniser, comma, ","); + return false; + } + } + return true; +} + +bool ShaderTemplate::parseTemplate(Tokeniser &tokeniser) +{ + m_Name = tokeniser.getToken(); + if (!parseShaderParameters(tokeniser, m_params)) { + globalErrorStream() << "shader template: " << makeQuoted(m_Name.c_str()) << ": parameter parse failed\n"; + return false; + } - return parseDoom3( tokeniser ); + return parseDoom3(tokeniser); } -bool ShaderTemplate::parseDoom3( Tokeniser& tokeniser ){ - LayerTemplate currentLayer; - bool isFog = false; - - // we need to read until we hit a balanced } - int depth = 0; - for (;; ) - { - tokeniser.nextLine(); - const char* token = tokeniser.getToken(); - - if ( token == 0 ) { - return false; - } - - if ( string_equal( token, "{" ) ) { - ++depth; - continue; - } - else if ( string_equal( token, "}" ) ) { - --depth; - if ( depth < 0 ) { // error - return false; - } - if ( depth == 0 ) { // end of shader - break; - } - if ( depth == 1 ) { // end of layer - if ( currentLayer.m_type == LAYER_DIFFUSEMAP ) { - m_diffuse = currentLayer.m_texture; - } - else if ( currentLayer.m_type == LAYER_BUMPMAP ) { - m_bump = currentLayer.m_texture; - } - else if ( currentLayer.m_type == LAYER_SPECULARMAP ) { - m_specular = currentLayer.m_texture; - } - else if ( !string_empty( currentLayer.m_texture.c_str() ) ) { - m_layers.push_back( MapLayerTemplate( - currentLayer.m_texture.c_str(), - currentLayer.m_blendFunc, - currentLayer.m_clampToBorder, - currentLayer.m_alphaTest - ) ); - } - currentLayer.m_type = LAYER_NONE; - currentLayer.m_texture = ""; - } - continue; - } - - if ( depth == 2 ) { // in layer - if ( string_equal_nocase( token, "blend" ) ) { - const char* blend = tokeniser.getToken(); - - if ( blend == 0 ) { - Tokeniser_unexpectedError( tokeniser, blend, "#blend" ); - return false; - } - - if ( string_equal_nocase( blend, "diffusemap" ) ) { - currentLayer.m_type = LAYER_DIFFUSEMAP; - } - else if ( string_equal_nocase( blend, "bumpmap" ) ) { - currentLayer.m_type = LAYER_BUMPMAP; - } - else if ( string_equal_nocase( blend, "specularmap" ) ) { - currentLayer.m_type = LAYER_SPECULARMAP; - } - else - { - currentLayer.m_blendFunc.first = blend; - - const char* comma = tokeniser.getToken(); - - if ( comma == 0 ) { - Tokeniser_unexpectedError( tokeniser, comma, "#comma" ); - return false; - } - - if ( string_equal( comma, "," ) ) { - RETURN_FALSE_IF_FAIL( Tokeniser_parseString( tokeniser, currentLayer.m_blendFunc.second ) ); - } - else - { - currentLayer.m_blendFunc.second = ""; - tokeniser.ungetToken(); - } - } - } - else if ( string_equal_nocase( token, "map" ) ) { - if ( currentLayer.m_type == LAYER_BUMPMAP ) { - RETURN_FALSE_IF_FAIL( Doom3Shader_parseBumpmap( tokeniser, currentLayer.m_texture, currentLayer.m_heightmapScale ) ); - } - else - { - const char* map = tokeniser.getToken(); - - if ( map == 0 ) { - Tokeniser_unexpectedError( tokeniser, map, "#map" ); - return false; - } - - if ( string_equal( map, "makealpha" ) ) { - RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "(" ) ); - const char* texture = tokeniser.getToken(); - if ( texture == 0 ) { - Tokeniser_unexpectedError( tokeniser, texture, "#texture" ); - return false; - } - currentLayer.m_texture = texture; - RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, ")" ) ); - } - else - { - parseTextureName( currentLayer.m_texture, map ); - } - } - } - else if ( string_equal_nocase( token, "zeroclamp" ) ) { - currentLayer.m_clampToBorder = true; - } +bool ShaderTemplate::parseDoom3(Tokeniser &tokeniser) +{ + LayerTemplate currentLayer; + bool isFog = false; + + // we need to read until we hit a balanced } + int depth = 0; + for (;;) { + tokeniser.nextLine(); + const char *token = tokeniser.getToken(); + + if (token == 0) { + return false; + } + + if (string_equal(token, "{")) { + ++depth; + continue; + } else if (string_equal(token, "}")) { + --depth; + if (depth < 0) { // error + return false; + } + if (depth == 0) { // end of shader + break; + } + if (depth == 1) { // end of layer + if (currentLayer.m_type == LAYER_DIFFUSEMAP) { + m_diffuse = currentLayer.m_texture; + } else if (currentLayer.m_type == LAYER_BUMPMAP) { + m_bump = currentLayer.m_texture; + } else if (currentLayer.m_type == LAYER_SPECULARMAP) { + m_specular = currentLayer.m_texture; + } else if (!string_empty(currentLayer.m_texture.c_str())) { + m_layers.push_back(MapLayerTemplate( + currentLayer.m_texture.c_str(), + currentLayer.m_blendFunc, + currentLayer.m_clampToBorder, + currentLayer.m_alphaTest + )); + } + currentLayer.m_type = LAYER_NONE; + currentLayer.m_texture = ""; + } + continue; + } + + if (depth == 2) { // in layer + if (string_equal_nocase(token, "blend")) { + const char *blend = tokeniser.getToken(); + + if (blend == 0) { + Tokeniser_unexpectedError(tokeniser, blend, "#blend"); + return false; + } + + if (string_equal_nocase(blend, "diffusemap")) { + currentLayer.m_type = LAYER_DIFFUSEMAP; + } else if (string_equal_nocase(blend, "bumpmap")) { + currentLayer.m_type = LAYER_BUMPMAP; + } else if (string_equal_nocase(blend, "specularmap")) { + currentLayer.m_type = LAYER_SPECULARMAP; + } else { + currentLayer.m_blendFunc.first = blend; + + const char *comma = tokeniser.getToken(); + + if (comma == 0) { + Tokeniser_unexpectedError(tokeniser, comma, "#comma"); + return false; + } + + if (string_equal(comma, ",")) { + RETURN_FALSE_IF_FAIL(Tokeniser_parseString(tokeniser, currentLayer.m_blendFunc.second)); + } else { + currentLayer.m_blendFunc.second = ""; + tokeniser.ungetToken(); + } + } + } else if (string_equal_nocase(token, "map")) { + if (currentLayer.m_type == LAYER_BUMPMAP) { + RETURN_FALSE_IF_FAIL( + Doom3Shader_parseBumpmap(tokeniser, currentLayer.m_texture, currentLayer.m_heightmapScale)); + } else { + const char *map = tokeniser.getToken(); + + if (map == 0) { + Tokeniser_unexpectedError(tokeniser, map, "#map"); + return false; + } + + if (string_equal(map, "makealpha")) { + RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "(")); + const char *texture = tokeniser.getToken(); + if (texture == 0) { + Tokeniser_unexpectedError(tokeniser, texture, "#texture"); + return false; + } + currentLayer.m_texture = texture; + RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")")); + } else { + parseTextureName(currentLayer.m_texture, map); + } + } + } else if (string_equal_nocase(token, "zeroclamp")) { + currentLayer.m_clampToBorder = true; + } #if 0 - else if ( string_equal_nocase( token, "alphaTest" ) ) { + else if ( string_equal_nocase( token, "alphaTest" ) ) { Tokeniser_getFloat( tokeniser, currentLayer.m_alphaTest ); } #endif - } - else if ( depth == 1 ) { - if ( string_equal_nocase( token, "qer_editorimage" ) ) { - RETURN_FALSE_IF_FAIL( Tokeniser_parseTextureName( tokeniser, m_textureName ) ); - } - else if ( string_equal_nocase( token, "qer_trans" ) ) { - m_fTrans = string_read_float( tokeniser.getToken() ); - m_nFlags |= QER_TRANS; - } - else if ( string_equal_nocase( token, "translucent" ) ) { - m_fTrans = 1; - m_nFlags |= QER_TRANS; - } - else if ( string_equal( token, "DECAL_MACRO" ) ) { - m_fTrans = 1; - m_nFlags |= QER_TRANS; - } - else if ( string_equal_nocase( token, "bumpmap" ) ) { - RETURN_FALSE_IF_FAIL( Doom3Shader_parseBumpmap( tokeniser, m_bump, m_heightmapScale ) ); - } - else if ( string_equal_nocase( token, "diffusemap" ) ) { - RETURN_FALSE_IF_FAIL( Tokeniser_parseTextureName( tokeniser, m_diffuse ) ); - } - else if ( string_equal_nocase( token, "specularmap" ) ) { - RETURN_FALSE_IF_FAIL( Tokeniser_parseTextureName( tokeniser, m_specular ) ); - } - else if ( string_equal_nocase( token, "twosided" ) ) { - m_Cull = IShader::eCullNone; - m_nFlags |= QER_CULL; - } - else if ( string_equal_nocase( token, "nodraw" ) ) { - m_nFlags |= QER_NODRAW; - } - else if ( string_equal_nocase( token, "nonsolid" ) ) { - m_nFlags |= QER_NONSOLID; - } - else if ( string_equal_nocase( token, "liquid" ) ) { - m_nFlags |= QER_WATER; - } - else if ( string_equal_nocase( token, "areaportal" ) ) { - m_nFlags |= QER_AREAPORTAL; - } - else if ( string_equal_nocase( token, "playerclip" ) - || string_equal_nocase( token, "monsterclip" ) - || string_equal_nocase( token, "ikclip" ) - || string_equal_nocase( token, "moveableclip" ) ) { - m_nFlags |= QER_CLIP; - } - if ( string_equal_nocase( token, "fogLight" ) ) { - isFog = true; - } - else if ( !isFog && string_equal_nocase( token, "lightFalloffImage" ) ) { - const char* lightFalloffImage = tokeniser.getToken(); - if ( lightFalloffImage == 0 ) { - Tokeniser_unexpectedError( tokeniser, lightFalloffImage, "#lightFalloffImage" ); - return false; - } - if ( string_equal_nocase( lightFalloffImage, "makeintensity" ) ) { - RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "(" ) ); - TextureExpression name; - RETURN_FALSE_IF_FAIL( Tokeniser_parseTextureName( tokeniser, name ) ); - m_lightFalloffImage = name; - RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, ")" ) ); - } - else - { - m_lightFalloffImage = lightFalloffImage; - } - } - } - } - - if ( string_empty( m_textureName.c_str() ) ) { - m_textureName = m_diffuse; - } - - return true; + } else if (depth == 1) { + if (string_equal_nocase(token, "qer_editorimage")) { + RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, m_textureName)); + } else if (string_equal_nocase(token, "qer_trans")) { + m_fTrans = string_read_float(tokeniser.getToken()); + m_nFlags |= QER_TRANS; + } else if (string_equal_nocase(token, "translucent")) { + m_fTrans = 1; + m_nFlags |= QER_TRANS; + } else if (string_equal(token, "DECAL_MACRO")) { + m_fTrans = 1; + m_nFlags |= QER_TRANS; + } else if (string_equal_nocase(token, "bumpmap")) { + RETURN_FALSE_IF_FAIL(Doom3Shader_parseBumpmap(tokeniser, m_bump, m_heightmapScale)); + } else if (string_equal_nocase(token, "diffusemap")) { + RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, m_diffuse)); + } else if (string_equal_nocase(token, "specularmap")) { + RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, m_specular)); + } else if (string_equal_nocase(token, "twosided")) { + m_Cull = IShader::eCullNone; + m_nFlags |= QER_CULL; + } else if (string_equal_nocase(token, "nodraw")) { + m_nFlags |= QER_NODRAW; + } else if (string_equal_nocase(token, "nonsolid")) { + m_nFlags |= QER_NONSOLID; + } else if (string_equal_nocase(token, "liquid")) { + m_nFlags |= QER_WATER; + } else if (string_equal_nocase(token, "areaportal")) { + m_nFlags |= QER_AREAPORTAL; + } else if (string_equal_nocase(token, "playerclip") + || string_equal_nocase(token, "monsterclip") + || string_equal_nocase(token, "ikclip") + || string_equal_nocase(token, "moveableclip")) { + m_nFlags |= QER_CLIP; + } + if (string_equal_nocase(token, "fogLight")) { + isFog = true; + } else if (!isFog && string_equal_nocase(token, "lightFalloffImage")) { + const char *lightFalloffImage = tokeniser.getToken(); + if (lightFalloffImage == 0) { + Tokeniser_unexpectedError(tokeniser, lightFalloffImage, "#lightFalloffImage"); + return false; + } + if (string_equal_nocase(lightFalloffImage, "makeintensity")) { + RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, "(")); + TextureExpression name; + RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, name)); + m_lightFalloffImage = name; + RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")")); + } else { + m_lightFalloffImage = lightFalloffImage; + } + } + } + } + + if (string_empty(m_textureName.c_str())) { + m_textureName = m_diffuse; + } + + return true; } typedef SmartPointer ShaderTemplatePointer; @@ -682,422 +680,478 @@ typedef std::map ShaderTemplateMap; ShaderTemplateMap g_shaders; ShaderTemplateMap g_shaderTemplates; -ShaderTemplate* findTemplate( const char* name ){ - ShaderTemplateMap::iterator i = g_shaderTemplates.find( name ); - if ( i != g_shaderTemplates.end() ) { - return ( *i ).second.get(); - } - return 0; +ShaderTemplate *findTemplate(const char *name) +{ + ShaderTemplateMap::iterator i = g_shaderTemplates.find(name); + if (i != g_shaderTemplates.end()) { + return (*i).second.get(); + } + return 0; } -class ShaderDefinition -{ +class ShaderDefinition { public: -ShaderDefinition( ShaderTemplate* shaderTemplate, const ShaderArguments& args, const char* filename ) - : shaderTemplate( shaderTemplate ), args( args ), filename( filename ){ -} -ShaderTemplate* shaderTemplate; -ShaderArguments args; -const char* filename; + ShaderDefinition(ShaderTemplate *shaderTemplate, const ShaderArguments &args, const char *filename) + : shaderTemplate(shaderTemplate), args(args), filename(filename) + { + } + + ShaderTemplate *shaderTemplate; + ShaderArguments args; + const char *filename; }; typedef std::map ShaderDefinitionMap; ShaderDefinitionMap g_shaderDefinitions; -bool parseTemplateInstance( Tokeniser& tokeniser, const char* filename ){ - CopiedString name; - RETURN_FALSE_IF_FAIL( Tokeniser_parseShaderName( tokeniser, name ) ); - const char* templateName = tokeniser.getToken(); - ShaderTemplate* shaderTemplate = findTemplate( templateName ); - if ( shaderTemplate == 0 ) { - globalErrorStream() << "shader instance: " << makeQuoted( name.c_str() ) << ": shader template not found: " << makeQuoted( templateName ) << "\n"; - } - - ShaderArguments args; - if ( !parseShaderParameters( tokeniser, args ) ) { - globalErrorStream() << "shader instance: " << makeQuoted( name.c_str() ) << ": argument parse failed\n"; - return false; - } - - if ( shaderTemplate != 0 ) { - if ( !g_shaderDefinitions.insert( ShaderDefinitionMap::value_type( name, ShaderDefinition( shaderTemplate, args, filename ) ) ).second ) { - globalErrorStream() << "shader instance: " << makeQuoted( name.c_str() ) << ": already exists, second definition ignored\n"; - } - } - return true; -} - - -const char* evaluateShaderValue( const char* value, const ShaderParameters& params, const ShaderArguments& args ){ - ShaderArguments::const_iterator j = args.begin(); - for ( ShaderParameters::const_iterator i = params.begin(); i != params.end(); ++i, ++j ) - { - const char* other = ( *i ).c_str(); - if ( string_equal( value, other ) ) { - return ( *j ).c_str(); - } - } - return value; +bool parseTemplateInstance(Tokeniser &tokeniser, const char *filename) +{ + CopiedString name; + RETURN_FALSE_IF_FAIL(Tokeniser_parseShaderName(tokeniser, name)); + const char *templateName = tokeniser.getToken(); + ShaderTemplate *shaderTemplate = findTemplate(templateName); + if (shaderTemplate == 0) { + globalErrorStream() << "shader instance: " << makeQuoted(name.c_str()) << ": shader template not found: " + << makeQuoted(templateName) << "\n"; + } + + ShaderArguments args; + if (!parseShaderParameters(tokeniser, args)) { + globalErrorStream() << "shader instance: " << makeQuoted(name.c_str()) << ": argument parse failed\n"; + return false; + } + + if (shaderTemplate != 0) { + if (!g_shaderDefinitions.insert( + ShaderDefinitionMap::value_type(name, ShaderDefinition(shaderTemplate, args, filename))).second) { + globalErrorStream() << "shader instance: " << makeQuoted(name.c_str()) + << ": already exists, second definition ignored\n"; + } + } + return true; +} + + +const char *evaluateShaderValue(const char *value, const ShaderParameters ¶ms, const ShaderArguments &args) +{ + ShaderArguments::const_iterator j = args.begin(); + for (ShaderParameters::const_iterator i = params.begin(); i != params.end(); ++i, ++j) { + const char *other = (*i).c_str(); + if (string_equal(value, other)) { + return (*j).c_str(); + } + } + return value; } ///\todo BlendFunc parsing -BlendFunc evaluateBlendFunc( const BlendFuncExpression& blendFunc, const ShaderParameters& params, const ShaderArguments& args ){ - return BlendFunc( BLEND_ONE, BLEND_ZERO ); -} - -qtexture_t* evaluateTexture( const TextureExpression& texture, const ShaderParameters& params, const ShaderArguments& args, const LoadImageCallback& loader = GlobalTexturesCache().defaultLoader() ){ - StringOutputStream result( 64 ); - const char* expression = texture.c_str(); - const char* end = expression + string_length( expression ); - if ( !string_empty( expression ) ) { - for (;; ) - { - const char* best = end; - const char* bestParam = 0; - const char* bestArg = 0; - ShaderArguments::const_iterator j = args.begin(); - for ( ShaderParameters::const_iterator i = params.begin(); i != params.end(); ++i, ++j ) - { - const char* found = strstr( expression, ( *i ).c_str() ); - if ( found != 0 && found < best ) { - best = found; - bestParam = ( *i ).c_str(); - bestArg = ( *j ).c_str(); - } - } - if ( best != end ) { - result << StringRange( expression, best ); - result << PathCleaned( bestArg ); - expression = best + string_length( bestParam ); - } - else - { - break; - } - } - result << expression; - } - return GlobalTexturesCache().capture( loader, result.c_str() ); -} - -float evaluateFloat( const ShaderValue& value, const ShaderParameters& params, const ShaderArguments& args ){ - const char* result = evaluateShaderValue( value.c_str(), params, args ); - float f; - if ( !string_parse_float( result, f ) ) { - globalErrorStream() << "parsing float value failed: " << makeQuoted( result ) << "\n"; - } - return f; +BlendFunc +evaluateBlendFunc(const BlendFuncExpression &blendFunc, const ShaderParameters ¶ms, const ShaderArguments &args) +{ + return BlendFunc(BLEND_ONE, BLEND_ZERO); } -BlendFactor evaluateBlendFactor( const ShaderValue& value, const ShaderParameters& params, const ShaderArguments& args ){ - const char* result = evaluateShaderValue( value.c_str(), params, args ); - - if ( string_equal_nocase( result, "gl_zero" ) ) { - return BLEND_ZERO; - } - if ( string_equal_nocase( result, "gl_one" ) ) { - return BLEND_ONE; - } - if ( string_equal_nocase( result, "gl_src_color" ) ) { - return BLEND_SRC_COLOUR; - } - if ( string_equal_nocase( result, "gl_one_minus_src_color" ) ) { - return BLEND_ONE_MINUS_SRC_COLOUR; - } - if ( string_equal_nocase( result, "gl_src_alpha" ) ) { - return BLEND_SRC_ALPHA; - } - if ( string_equal_nocase( result, "gl_one_minus_src_alpha" ) ) { - return BLEND_ONE_MINUS_SRC_ALPHA; - } - if ( string_equal_nocase( result, "gl_dst_color" ) ) { - return BLEND_DST_COLOUR; - } - if ( string_equal_nocase( result, "gl_one_minus_dst_color" ) ) { - return BLEND_ONE_MINUS_DST_COLOUR; - } - if ( string_equal_nocase( result, "gl_dst_alpha" ) ) { - return BLEND_DST_ALPHA; - } - if ( string_equal_nocase( result, "gl_one_minus_dst_alpha" ) ) { - return BLEND_ONE_MINUS_DST_ALPHA; - } - if ( string_equal_nocase( result, "gl_src_alpha_saturate" ) ) { - return BLEND_SRC_ALPHA_SATURATE; - } - - globalErrorStream() << "parsing blend-factor value failed: " << makeQuoted( result ) << "\n"; - return BLEND_ZERO; +qtexture_t * +evaluateTexture(const TextureExpression &texture, const ShaderParameters ¶ms, const ShaderArguments &args, + const LoadImageCallback &loader = GlobalTexturesCache().defaultLoader()) +{ + StringOutputStream result(64); + const char *expression = texture.c_str(); + const char *end = expression + string_length(expression); + if (!string_empty(expression)) { + for (;;) { + const char *best = end; + const char *bestParam = 0; + const char *bestArg = 0; + ShaderArguments::const_iterator j = args.begin(); + for (ShaderParameters::const_iterator i = params.begin(); i != params.end(); ++i, ++j) { + const char *found = strstr(expression, (*i).c_str()); + if (found != 0 && found < best) { + best = found; + bestParam = (*i).c_str(); + bestArg = (*j).c_str(); + } + } + if (best != end) { + result << StringRange(expression, best); + result << PathCleaned(bestArg); + expression = best + string_length(bestParam); + } else { + break; + } + } + result << expression; + } + return GlobalTexturesCache().capture(loader, result.c_str()); +} + +float evaluateFloat(const ShaderValue &value, const ShaderParameters ¶ms, const ShaderArguments &args) +{ + const char *result = evaluateShaderValue(value.c_str(), params, args); + float f; + if (!string_parse_float(result, f)) { + globalErrorStream() << "parsing float value failed: " << makeQuoted(result) << "\n"; + } + return f; } -class CShader : public IShader +BlendFactor evaluateBlendFactor(const ShaderValue &value, const ShaderParameters ¶ms, const ShaderArguments &args) { -std::size_t m_refcount; - -const ShaderTemplate& m_template; -const ShaderArguments& m_args; -const char* m_filename; + const char *result = evaluateShaderValue(value.c_str(), params, args); + + if (string_equal_nocase(result, "gl_zero")) { + return BLEND_ZERO; + } + if (string_equal_nocase(result, "gl_one")) { + return BLEND_ONE; + } + if (string_equal_nocase(result, "gl_src_color")) { + return BLEND_SRC_COLOUR; + } + if (string_equal_nocase(result, "gl_one_minus_src_color")) { + return BLEND_ONE_MINUS_SRC_COLOUR; + } + if (string_equal_nocase(result, "gl_src_alpha")) { + return BLEND_SRC_ALPHA; + } + if (string_equal_nocase(result, "gl_one_minus_src_alpha")) { + return BLEND_ONE_MINUS_SRC_ALPHA; + } + if (string_equal_nocase(result, "gl_dst_color")) { + return BLEND_DST_COLOUR; + } + if (string_equal_nocase(result, "gl_one_minus_dst_color")) { + return BLEND_ONE_MINUS_DST_COLOUR; + } + if (string_equal_nocase(result, "gl_dst_alpha")) { + return BLEND_DST_ALPHA; + } + if (string_equal_nocase(result, "gl_one_minus_dst_alpha")) { + return BLEND_ONE_MINUS_DST_ALPHA; + } + if (string_equal_nocase(result, "gl_src_alpha_saturate")) { + return BLEND_SRC_ALPHA_SATURATE; + } + + globalErrorStream() << "parsing blend-factor value failed: " << makeQuoted(result) << "\n"; + return BLEND_ZERO; +} + +class CShader : public IShader { + std::size_t m_refcount; + + const ShaderTemplate &m_template; + const ShaderArguments &m_args; + const char *m_filename; // name is shader-name, otherwise texture-name (if not a real shader) -CopiedString m_Name; + CopiedString m_Name; -qtexture_t* m_pTexture; -qtexture_t* m_notfound; -qtexture_t* m_pDiffuse; -float m_heightmapScale; -qtexture_t* m_pBump; -qtexture_t* m_pSpecular; -qtexture_t* m_pLightFalloffImage; -BlendFunc m_blendFunc; + qtexture_t *m_pTexture; + qtexture_t *m_notfound; + qtexture_t *m_pDiffuse; + float m_heightmapScale; + qtexture_t *m_pBump; + qtexture_t *m_pSpecular; + qtexture_t *m_pLightFalloffImage; + BlendFunc m_blendFunc; -bool m_bInUse; + bool m_bInUse; public: -static bool m_lightingEnabled; - -CShader( const ShaderDefinition& definition ) : - m_refcount( 0 ), - m_template( *definition.shaderTemplate ), - m_args( definition.args ), - m_filename( definition.filename ), - m_blendFunc( BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA ), - m_bInUse( false ){ - m_pTexture = 0; - m_pDiffuse = 0; - m_pBump = 0; - m_pSpecular = 0; - - m_notfound = 0; - - realise(); -} -virtual ~CShader(){ - unrealise(); + static bool m_lightingEnabled; - ASSERT_MESSAGE( m_refcount == 0, "deleting active shader" ); -} + CShader(const ShaderDefinition &definition) : + m_refcount(0), + m_template(*definition.shaderTemplate), + m_args(definition.args), + m_filename(definition.filename), + m_blendFunc(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA), + m_bInUse(false) + { + m_pTexture = 0; + m_pDiffuse = 0; + m_pBump = 0; + m_pSpecular = 0; -// IShaders implementation ----------------- -void IncRef(){ - ++m_refcount; -} -void DecRef(){ - ASSERT_MESSAGE( m_refcount != 0, "shader reference-count going below zero" ); - if ( --m_refcount == 0 ) { - delete this; - } -} + m_notfound = 0; -std::size_t refcount(){ - return m_refcount; -} + realise(); + } -// get/set the qtexture_t* Radiant uses to represent this shader object -qtexture_t* getTexture() const { - return m_pTexture; -} -qtexture_t* getDiffuse() const { - return m_pDiffuse; -} -qtexture_t* getBump() const { - return m_pBump; -} -qtexture_t* getSpecular() const { - return m_pSpecular; -} -// get shader name -const char* getName() const { - return m_Name.c_str(); -} -bool IsInUse() const { - return m_bInUse; -} -void SetInUse( bool bInUse ){ - m_bInUse = bInUse; - g_ActiveShadersChangedNotify(); -} -// get the shader flags -int getFlags() const { - return m_template.m_nFlags; -} -// get the transparency value -float getTrans() const { - return m_template.m_fTrans; -} -// test if it's a true shader, or a default shader created to wrap around a texture -bool IsDefault() const { - return string_empty( m_filename ); -} -// get the alphaFunc -void getAlphaFunc( EAlphaFunc *func, float *ref ) { *func = m_template.m_AlphaFunc; *ref = m_template.m_AlphaRef; }; -BlendFunc getBlendFunc() const { - return m_blendFunc; -} -// get the cull type -ECull getCull(){ - return m_template.m_Cull; -}; -// get shader file name (ie the file where this one is defined) -const char* getShaderFileName() const { - return m_filename; -} -// ----------------------------------------- + virtual ~CShader() + { + unrealise(); -void realise(){ - m_pTexture = evaluateTexture( m_template.m_textureName, m_template.m_params, m_args ); + ASSERT_MESSAGE(m_refcount == 0, "deleting active shader"); + } - if ( m_pTexture->texture_number == 0 ) { - m_notfound = m_pTexture; +// IShaders implementation ----------------- + void IncRef() + { + ++m_refcount; + } + + void DecRef() + { + ASSERT_MESSAGE(m_refcount != 0, "shader reference-count going below zero"); + if (--m_refcount == 0) { + delete this; + } + } + + std::size_t refcount() + { + return m_refcount; + } - { - StringOutputStream name( 256 ); - name << GlobalRadiant().getAppPath() << "bitmaps/" << ( IsDefault() ? "notex.png" : "shadernotex.png" ); - m_pTexture = GlobalTexturesCache().capture( LoadImageCallback( 0, loadBitmap ), name.c_str() ); - } - } +// get/set the qtexture_t* Radiant uses to represent this shader object + qtexture_t *getTexture() const + { + return m_pTexture; + } + + qtexture_t *getDiffuse() const + { + return m_pDiffuse; + } + + qtexture_t *getBump() const + { + return m_pBump; + } + + qtexture_t *getSpecular() const + { + return m_pSpecular; + } - realiseLighting(); -} +// get shader name + const char *getName() const + { + return m_Name.c_str(); + } + + bool IsInUse() const + { + return m_bInUse; + } + + void SetInUse(bool bInUse) + { + m_bInUse = bInUse; + g_ActiveShadersChangedNotify(); + } -void unrealise(){ - GlobalTexturesCache().release( m_pTexture ); +// get the shader flags + int getFlags() const + { + return m_template.m_nFlags; + } - if ( m_notfound != 0 ) { - GlobalTexturesCache().release( m_notfound ); - } +// get the transparency value + float getTrans() const + { + return m_template.m_fTrans; + } - unrealiseLighting(); -} +// test if it's a true shader, or a default shader created to wrap around a texture + bool IsDefault() const + { + return string_empty(m_filename); + } -void realiseLighting(){ - if ( m_lightingEnabled ) { - LoadImageCallback loader = GlobalTexturesCache().defaultLoader(); - if ( !string_empty( m_template.m_heightmapScale.c_str() ) ) { - m_heightmapScale = evaluateFloat( m_template.m_heightmapScale, m_template.m_params, m_args ); - loader = LoadImageCallback( &m_heightmapScale, loadHeightmap ); - } - m_pDiffuse = evaluateTexture( m_template.m_diffuse, m_template.m_params, m_args ); - m_pBump = evaluateTexture( m_template.m_bump, m_template.m_params, m_args, loader ); - m_pSpecular = evaluateTexture( m_template.m_specular, m_template.m_params, m_args ); - m_pLightFalloffImage = evaluateTexture( m_template.m_lightFalloffImage, m_template.m_params, m_args ); - - for ( ShaderTemplate::MapLayers::const_iterator i = m_template.m_layers.begin(); i != m_template.m_layers.end(); ++i ) - { - m_layers.push_back( evaluateLayer( *i, m_template.m_params, m_args ) ); - } - - if ( m_layers.size() == 1 ) { - const BlendFuncExpression& blendFunc = m_template.m_layers.front().blendFunc(); - if ( !string_empty( blendFunc.second.c_str() ) ) { - m_blendFunc = BlendFunc( - evaluateBlendFactor( blendFunc.first.c_str(), m_template.m_params, m_args ), - evaluateBlendFactor( blendFunc.second.c_str(), m_template.m_params, m_args ) - ); - } - else - { - const char* blend = evaluateShaderValue( blendFunc.first.c_str(), m_template.m_params, m_args ); - - if ( string_equal_nocase( blend, "add" ) ) { - m_blendFunc = BlendFunc( BLEND_ONE, BLEND_ONE ); - } - else if ( string_equal_nocase( blend, "filter" ) ) { - m_blendFunc = BlendFunc( BLEND_DST_COLOUR, BLEND_ZERO ); - } - else if ( string_equal_nocase( blend, "blend" ) ) { - m_blendFunc = BlendFunc( BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA ); - } - else - { - globalErrorStream() << "parsing blend value failed: " << makeQuoted( blend ) << "\n"; - } - } - } - } -} +// get the alphaFunc + void getAlphaFunc(EAlphaFunc *func, float *ref) + { + *func = m_template.m_AlphaFunc; + *ref = m_template.m_AlphaRef; + }; -void unrealiseLighting(){ - if ( m_lightingEnabled ) { - GlobalTexturesCache().release( m_pDiffuse ); - GlobalTexturesCache().release( m_pBump ); - GlobalTexturesCache().release( m_pSpecular ); + BlendFunc getBlendFunc() const + { + return m_blendFunc; + } - GlobalTexturesCache().release( m_pLightFalloffImage ); +// get the cull type + ECull getCull() + { + return m_template.m_Cull; + }; - for ( MapLayers::iterator i = m_layers.begin(); i != m_layers.end(); ++i ) - { - GlobalTexturesCache().release( ( *i ).texture() ); - } - m_layers.clear(); +// get shader file name (ie the file where this one is defined) + const char *getShaderFileName() const + { + return m_filename; + } +// ----------------------------------------- - m_blendFunc = BlendFunc( BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA ); - } -} + void realise() + { + m_pTexture = evaluateTexture(m_template.m_textureName, m_template.m_params, m_args); + + if (m_pTexture->texture_number == 0) { + m_notfound = m_pTexture; + + { + StringOutputStream name(256); + name << GlobalRadiant().getAppPath() << "bitmaps/" << (IsDefault() ? "notex.png" : "shadernotex.png"); + m_pTexture = GlobalTexturesCache().capture(LoadImageCallback(0, loadBitmap), name.c_str()); + } + } + + realiseLighting(); + } + + void unrealise() + { + GlobalTexturesCache().release(m_pTexture); + + if (m_notfound != 0) { + GlobalTexturesCache().release(m_notfound); + } + + unrealiseLighting(); + } + + void realiseLighting() + { + if (m_lightingEnabled) { + LoadImageCallback loader = GlobalTexturesCache().defaultLoader(); + if (!string_empty(m_template.m_heightmapScale.c_str())) { + m_heightmapScale = evaluateFloat(m_template.m_heightmapScale, m_template.m_params, m_args); + loader = LoadImageCallback(&m_heightmapScale, loadHeightmap); + } + m_pDiffuse = evaluateTexture(m_template.m_diffuse, m_template.m_params, m_args); + m_pBump = evaluateTexture(m_template.m_bump, m_template.m_params, m_args, loader); + m_pSpecular = evaluateTexture(m_template.m_specular, m_template.m_params, m_args); + m_pLightFalloffImage = evaluateTexture(m_template.m_lightFalloffImage, m_template.m_params, m_args); + + for (ShaderTemplate::MapLayers::const_iterator i = m_template.m_layers.begin(); + i != m_template.m_layers.end(); ++i) { + m_layers.push_back(evaluateLayer(*i, m_template.m_params, m_args)); + } + + if (m_layers.size() == 1) { + const BlendFuncExpression &blendFunc = m_template.m_layers.front().blendFunc(); + if (!string_empty(blendFunc.second.c_str())) { + m_blendFunc = BlendFunc( + evaluateBlendFactor(blendFunc.first.c_str(), m_template.m_params, m_args), + evaluateBlendFactor(blendFunc.second.c_str(), m_template.m_params, m_args) + ); + } else { + const char *blend = evaluateShaderValue(blendFunc.first.c_str(), m_template.m_params, m_args); + + if (string_equal_nocase(blend, "add")) { + m_blendFunc = BlendFunc(BLEND_ONE, BLEND_ONE); + } else if (string_equal_nocase(blend, "filter")) { + m_blendFunc = BlendFunc(BLEND_DST_COLOUR, BLEND_ZERO); + } else if (string_equal_nocase(blend, "blend")) { + m_blendFunc = BlendFunc(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA); + } else { + globalErrorStream() << "parsing blend value failed: " << makeQuoted(blend) << "\n"; + } + } + } + } + } + + void unrealiseLighting() + { + if (m_lightingEnabled) { + GlobalTexturesCache().release(m_pDiffuse); + GlobalTexturesCache().release(m_pBump); + GlobalTexturesCache().release(m_pSpecular); + + GlobalTexturesCache().release(m_pLightFalloffImage); + + for (MapLayers::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + GlobalTexturesCache().release((*i).texture()); + } + m_layers.clear(); + + m_blendFunc = BlendFunc(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA); + } + } // set shader name -void setName( const char* name ){ - m_Name = name; -} - -class MapLayer : public ShaderLayer -{ -qtexture_t* m_texture; -BlendFunc m_blendFunc; -bool m_clampToBorder; -float m_alphaTest; -public: -MapLayer( qtexture_t* texture, BlendFunc blendFunc, bool clampToBorder, float alphaTest ) : - m_texture( texture ), - m_blendFunc( blendFunc ), - m_clampToBorder( false ), - m_alphaTest( alphaTest ){ -} -qtexture_t* texture() const { - return m_texture; -} -BlendFunc blendFunc() const { - return m_blendFunc; -} -bool clampToBorder() const { - return m_clampToBorder; -} -float alphaTest() const { - return m_alphaTest; -} -}; - -static MapLayer evaluateLayer( const ShaderTemplate::MapLayerTemplate& layerTemplate, const ShaderParameters& params, const ShaderArguments& args ){ - return MapLayer( - evaluateTexture( layerTemplate.texture(), params, args ), - evaluateBlendFunc( layerTemplate.blendFunc(), params, args ), - layerTemplate.clampToBorder(), - evaluateFloat( layerTemplate.alphaTest(), params, args ) - ); -} - -typedef std::vector MapLayers; -MapLayers m_layers; - -const ShaderLayer* firstLayer() const { - if ( m_layers.empty() ) { - return 0; - } - return &m_layers.front(); -} -void forEachLayer( const ShaderLayerCallback& callback ) const { - for ( MapLayers::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i ) - { - callback( *i ); - } -} - -qtexture_t* lightFalloffImage() const { - if ( !string_empty( m_template.m_lightFalloffImage.c_str() ) ) { - return m_pLightFalloffImage; - } - return 0; -} + void setName(const char *name) + { + m_Name = name; + } + + class MapLayer : public ShaderLayer { + qtexture_t *m_texture; + BlendFunc m_blendFunc; + bool m_clampToBorder; + float m_alphaTest; + public: + MapLayer(qtexture_t *texture, BlendFunc blendFunc, bool clampToBorder, float alphaTest) : + m_texture(texture), + m_blendFunc(blendFunc), + m_clampToBorder(false), + m_alphaTest(alphaTest) + { + } + + qtexture_t *texture() const + { + return m_texture; + } + + BlendFunc blendFunc() const + { + return m_blendFunc; + } + + bool clampToBorder() const + { + return m_clampToBorder; + } + + float alphaTest() const + { + return m_alphaTest; + } + }; + + static MapLayer evaluateLayer(const ShaderTemplate::MapLayerTemplate &layerTemplate, const ShaderParameters ¶ms, + const ShaderArguments &args) + { + return MapLayer( + evaluateTexture(layerTemplate.texture(), params, args), + evaluateBlendFunc(layerTemplate.blendFunc(), params, args), + layerTemplate.clampToBorder(), + evaluateFloat(layerTemplate.alphaTest(), params, args) + ); + } + + typedef std::vector MapLayers; + MapLayers m_layers; + + const ShaderLayer *firstLayer() const + { + if (m_layers.empty()) { + return 0; + } + return &m_layers.front(); + } + + void forEachLayer(const ShaderLayerCallback &callback) const + { + for (MapLayers::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + callback(*i); + } + } + + qtexture_t *lightFalloffImage() const + { + if (!string_empty(m_template.m_lightFalloffImage.c_str())) { + return m_pLightFalloffImage; + } + return 0; + } }; bool CShader::m_lightingEnabled = false; @@ -1109,407 +1163,388 @@ shaders_t g_ActiveShaders; static shaders_t::iterator g_ActiveShadersIterator; -void ActiveShaders_IteratorBegin(){ - g_ActiveShadersIterator = g_ActiveShaders.begin(); +void ActiveShaders_IteratorBegin() +{ + g_ActiveShadersIterator = g_ActiveShaders.begin(); } -bool ActiveShaders_IteratorAtEnd(){ - return g_ActiveShadersIterator == g_ActiveShaders.end(); +bool ActiveShaders_IteratorAtEnd() +{ + return g_ActiveShadersIterator == g_ActiveShaders.end(); } -IShader *ActiveShaders_IteratorCurrent(){ - return static_cast( g_ActiveShadersIterator->second ); +IShader *ActiveShaders_IteratorCurrent() +{ + return static_cast( g_ActiveShadersIterator->second ); } -void ActiveShaders_IteratorIncrement(){ - ++g_ActiveShadersIterator; +void ActiveShaders_IteratorIncrement() +{ + ++g_ActiveShadersIterator; } -void debug_check_shaders( shaders_t& shaders ){ - for ( shaders_t::iterator i = shaders.begin(); i != shaders.end(); ++i ) - { - ASSERT_MESSAGE( i->second->refcount() == 1, "orphan shader still referenced" ); - } +void debug_check_shaders(shaders_t &shaders) +{ + for (shaders_t::iterator i = shaders.begin(); i != shaders.end(); ++i) { + ASSERT_MESSAGE(i->second->refcount() == 1, "orphan shader still referenced"); + } } // will free all GL binded qtextures and shaders // NOTE: doesn't make much sense out of Radiant exit or called during a reload -void FreeShaders(){ - // reload shaders - // empty the actives shaders list - debug_check_shaders( g_ActiveShaders ); - g_ActiveShaders.clear(); - g_shaders.clear(); - g_shaderTemplates.clear(); - g_shaderDefinitions.clear(); - g_ActiveShadersChangedNotify(); -} - -bool ShaderTemplate::parseQuake3( Tokeniser& tokeniser ){ - // name of the qtexture_t we'll use to represent this shader (this one has the "textures\" before) - m_textureName = m_Name.c_str(); - - tokeniser.nextLine(); - - // we need to read until we hit a balanced } - int depth = 0; - for (;; ) - { - tokeniser.nextLine(); - const char* token = tokeniser.getToken(); - - if ( token == 0 ) { - return false; - } - - if ( string_equal( token, "{" ) ) { - ++depth; - continue; - } - else if ( string_equal( token, "}" ) ) { - --depth; - if ( depth < 0 ) { // underflow - return false; - } - if ( depth == 0 ) { // end of shader - break; - } - - continue; - } - - if ( depth == 1 ) { - if ( string_equal_nocase( token, "qer_nocarve" ) ) { - m_nFlags |= QER_NOCARVE; - } - else if ( string_equal_nocase( token, "qer_trans" ) ) { - RETURN_FALSE_IF_FAIL( Tokeniser_getFloat( tokeniser, m_fTrans ) ); - m_nFlags |= QER_TRANS; - } - else if ( string_equal_nocase( token, "qer_editorimage" ) ) { - RETURN_FALSE_IF_FAIL( Tokeniser_parseTextureName( tokeniser, m_textureName ) ); - } - else if ( string_equal_nocase( token, "qer_alphafunc" ) ) { - const char* alphafunc = tokeniser.getToken(); - - if ( alphafunc == 0 ) { - Tokeniser_unexpectedError( tokeniser, alphafunc, "#alphafunc" ); - return false; - } - - if ( string_equal_nocase( alphafunc, "equal" ) ) { - m_AlphaFunc = IShader::eEqual; - } - else if ( string_equal_nocase( alphafunc, "greater" ) ) { - m_AlphaFunc = IShader::eGreater; - } - else if ( string_equal_nocase( alphafunc, "less" ) ) { - m_AlphaFunc = IShader::eLess; - } - else if ( string_equal_nocase( alphafunc, "gequal" ) ) { - m_AlphaFunc = IShader::eGEqual; - } - else if ( string_equal_nocase( alphafunc, "lequal" ) ) { - m_AlphaFunc = IShader::eLEqual; - } - else - { - m_AlphaFunc = IShader::eAlways; - } - - m_nFlags |= QER_ALPHATEST; - - RETURN_FALSE_IF_FAIL( Tokeniser_getFloat( tokeniser, m_AlphaRef ) ); - } - else if ( string_equal_nocase( token, "cull" ) ) { - const char* cull = tokeniser.getToken(); - - if ( cull == 0 ) { - Tokeniser_unexpectedError( tokeniser, cull, "#cull" ); - return false; - } - - if ( string_equal_nocase( cull, "none" ) - || string_equal_nocase( cull, "twosided" ) - || string_equal_nocase( cull, "disable" ) ) { - m_Cull = IShader::eCullNone; - } - else if ( string_equal_nocase( cull, "back" ) - || string_equal_nocase( cull, "backside" ) - || string_equal_nocase( cull, "backsided" ) ) { - m_Cull = IShader::eCullBack; - } - else - { - m_Cull = IShader::eCullBack; - } - - m_nFlags |= QER_CULL; - } - else if ( string_equal_nocase( token, "surfaceparm" ) ) { - const char* surfaceparm = tokeniser.getToken(); - - if ( surfaceparm == 0 ) { - Tokeniser_unexpectedError( tokeniser, surfaceparm, "#surfaceparm" ); - return false; - } - - if ( string_equal_nocase( surfaceparm, "fog" ) ) { - m_nFlags |= QER_FOG; - if ( m_fTrans == 1.0f ) { // has not been explicitly set by qer_trans - m_fTrans = 0.35f; - } - } - else if ( string_equal_nocase( surfaceparm, "nodraw" ) ) { - m_nFlags |= QER_NODRAW; - } - else if ( string_equal_nocase( surfaceparm, "nonsolid" ) ) { - m_nFlags |= QER_NONSOLID; - } - else if ( string_equal_nocase( surfaceparm, "water" ) ) { - m_nFlags |= QER_WATER; - } - else if ( string_equal_nocase( surfaceparm, "lava" ) ) { - m_nFlags |= QER_LAVA; - } - else if ( string_equal_nocase( surfaceparm, "areaportal" ) ) { - m_nFlags |= QER_AREAPORTAL; - } - else if ( string_equal_nocase( surfaceparm, "playerclip" ) ) { - m_nFlags |= QER_CLIP; - } - else if ( string_equal_nocase( surfaceparm, "botclip" ) ) { - m_nFlags |= QER_BOTCLIP; - } - } - } - } - - return true; +void FreeShaders() +{ + // reload shaders + // empty the actives shaders list + debug_check_shaders(g_ActiveShaders); + g_ActiveShaders.clear(); + g_shaders.clear(); + g_shaderTemplates.clear(); + g_shaderDefinitions.clear(); + g_ActiveShadersChangedNotify(); } -class Layer +bool ShaderTemplate::parseQuake3(Tokeniser &tokeniser) { + // name of the qtexture_t we'll use to represent this shader (this one has the "textures\" before) + m_textureName = m_Name.c_str(); + + tokeniser.nextLine(); + + // we need to read until we hit a balanced } + int depth = 0; + for (;;) { + tokeniser.nextLine(); + const char *token = tokeniser.getToken(); + + if (token == 0) { + return false; + } + + if (string_equal(token, "{")) { + ++depth; + continue; + } else if (string_equal(token, "}")) { + --depth; + if (depth < 0) { // underflow + return false; + } + if (depth == 0) { // end of shader + break; + } + + continue; + } + + if (depth == 1) { + if (string_equal_nocase(token, "qer_nocarve")) { + m_nFlags |= QER_NOCARVE; + } else if (string_equal_nocase(token, "qer_trans")) { + RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, m_fTrans)); + m_nFlags |= QER_TRANS; + } else if (string_equal_nocase(token, "qer_editorimage")) { + RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, m_textureName)); + } else if (string_equal_nocase(token, "qer_alphafunc")) { + const char *alphafunc = tokeniser.getToken(); + + if (alphafunc == 0) { + Tokeniser_unexpectedError(tokeniser, alphafunc, "#alphafunc"); + return false; + } + + if (string_equal_nocase(alphafunc, "equal")) { + m_AlphaFunc = IShader::eEqual; + } else if (string_equal_nocase(alphafunc, "greater")) { + m_AlphaFunc = IShader::eGreater; + } else if (string_equal_nocase(alphafunc, "less")) { + m_AlphaFunc = IShader::eLess; + } else if (string_equal_nocase(alphafunc, "gequal")) { + m_AlphaFunc = IShader::eGEqual; + } else if (string_equal_nocase(alphafunc, "lequal")) { + m_AlphaFunc = IShader::eLEqual; + } else { + m_AlphaFunc = IShader::eAlways; + } + + m_nFlags |= QER_ALPHATEST; + + RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, m_AlphaRef)); + } else if (string_equal_nocase(token, "cull")) { + const char *cull = tokeniser.getToken(); + + if (cull == 0) { + Tokeniser_unexpectedError(tokeniser, cull, "#cull"); + return false; + } + + if (string_equal_nocase(cull, "none") + || string_equal_nocase(cull, "twosided") + || string_equal_nocase(cull, "disable")) { + m_Cull = IShader::eCullNone; + } else if (string_equal_nocase(cull, "back") + || string_equal_nocase(cull, "backside") + || string_equal_nocase(cull, "backsided")) { + m_Cull = IShader::eCullBack; + } else { + m_Cull = IShader::eCullBack; + } + + m_nFlags |= QER_CULL; + } else if (string_equal_nocase(token, "surfaceparm")) { + const char *surfaceparm = tokeniser.getToken(); + + if (surfaceparm == 0) { + Tokeniser_unexpectedError(tokeniser, surfaceparm, "#surfaceparm"); + return false; + } + + if (string_equal_nocase(surfaceparm, "fog")) { + m_nFlags |= QER_FOG; + if (m_fTrans == 1.0f) { // has not been explicitly set by qer_trans + m_fTrans = 0.35f; + } + } else if (string_equal_nocase(surfaceparm, "nodraw")) { + m_nFlags |= QER_NODRAW; + } else if (string_equal_nocase(surfaceparm, "nonsolid")) { + m_nFlags |= QER_NONSOLID; + } else if (string_equal_nocase(surfaceparm, "water")) { + m_nFlags |= QER_WATER; + } else if (string_equal_nocase(surfaceparm, "lava")) { + m_nFlags |= QER_LAVA; + } else if (string_equal_nocase(surfaceparm, "areaportal")) { + m_nFlags |= QER_AREAPORTAL; + } else if (string_equal_nocase(surfaceparm, "playerclip")) { + m_nFlags |= QER_CLIP; + } else if (string_equal_nocase(surfaceparm, "botclip")) { + m_nFlags |= QER_BOTCLIP; + } + } + } + } + + return true; +} + +class Layer { public: -LayerTypeId m_type; -TextureExpression m_texture; -BlendFunc m_blendFunc; -bool m_clampToBorder; -float m_alphaTest; -float m_heightmapScale; - -Layer() : m_type( LAYER_NONE ), m_blendFunc( BLEND_ONE, BLEND_ZERO ), m_clampToBorder( false ), m_alphaTest( -1 ), m_heightmapScale( 0 ){ -} + LayerTypeId m_type; + TextureExpression m_texture; + BlendFunc m_blendFunc; + bool m_clampToBorder; + float m_alphaTest; + float m_heightmapScale; + + Layer() : m_type(LAYER_NONE), m_blendFunc(BLEND_ONE, BLEND_ZERO), m_clampToBorder(false), m_alphaTest(-1), + m_heightmapScale(0) + { + } }; std::list g_shaderFilenames; -void ParseShaderFile( Tokeniser& tokeniser, const char* filename ){ - g_shaderFilenames.push_back( filename ); - filename = g_shaderFilenames.back().c_str(); - tokeniser.nextLine(); - for (;; ) - { - const char* token = tokeniser.getToken(); - - if ( token == 0 ) { - break; - } - - if ( string_equal( token, "table" ) ) { - if ( tokeniser.getToken() == 0 ) { - Tokeniser_unexpectedError( tokeniser, 0, "#table-name" ); - return; - } - if ( !Tokeniser_parseToken( tokeniser, "{" ) ) { - return; - } - for (;; ) - { - const char* option = tokeniser.getToken(); - if ( string_equal( option, "{" ) ) { - for (;; ) - { - const char* value = tokeniser.getToken(); - if ( string_equal( value, "}" ) ) { - break; - } - } - - if ( !Tokeniser_parseToken( tokeniser, "}" ) ) { - return; - } - break; - } - } - } - else - { - if ( string_equal( token, "guide" ) ) { - parseTemplateInstance( tokeniser, filename ); - } - else - { - if ( !string_equal( token, "material" ) - && !string_equal( token, "particle" ) - && !string_equal( token, "skin" ) ) { - tokeniser.ungetToken(); - } - // first token should be the path + name.. (from base) - CopiedString name; - if ( !Tokeniser_parseShaderName( tokeniser, name ) ) { - } - ShaderTemplatePointer shaderTemplate( new ShaderTemplate() ); - shaderTemplate->setName( name.c_str() ); - - g_shaders.insert( ShaderTemplateMap::value_type( shaderTemplate->getName(), shaderTemplate ) ); - - bool result = ( g_shaderLanguage == SHADERLANGUAGE_QUAKE3 ) - ? shaderTemplate->parseQuake3( tokeniser ) - : shaderTemplate->parseDoom3( tokeniser ); - if ( result ) { - // do we already have this shader? - if ( !g_shaderDefinitions.insert( ShaderDefinitionMap::value_type( shaderTemplate->getName(), ShaderDefinition( shaderTemplate.get(), ShaderArguments(), filename ) ) ).second ) { - #if GDEF_DEBUG - globalOutputStream() << "WARNING: shader " << shaderTemplate->getName() << " is already in memory, definition in " << filename << " ignored.\n"; - #endif - } - } - else - { - globalErrorStream() << "Error parsing shader " << shaderTemplate->getName() << "\n"; - return; - } - } - } - } -} - -void parseGuideFile( Tokeniser& tokeniser, const char* filename ){ - tokeniser.nextLine(); - for (;; ) - { - const char* token = tokeniser.getToken(); - - if ( token == 0 ) { - break; - } - - if ( string_equal( token, "guide" ) ) { - // first token should be the path + name.. (from base) - ShaderTemplatePointer shaderTemplate( new ShaderTemplate ); - shaderTemplate->parseTemplate( tokeniser ); - if ( !g_shaderTemplates.insert( ShaderTemplateMap::value_type( shaderTemplate->getName(), shaderTemplate ) ).second ) { - globalErrorStream() << "guide " << makeQuoted( shaderTemplate->getName() ) << ": already defined, second definition ignored\n"; - } - } - else if ( string_equal( token, "inlineGuide" ) ) { - // skip entire inlineGuide definition - std::size_t depth = 0; - for (;; ) - { - tokeniser.nextLine(); - token = tokeniser.getToken(); - if ( string_equal( token, "{" ) ) { - ++depth; - } - else if ( string_equal( token, "}" ) ) { - if ( --depth == 0 ) { - break; - } - } - } - } - } +void ParseShaderFile(Tokeniser &tokeniser, const char *filename) +{ + g_shaderFilenames.push_back(filename); + filename = g_shaderFilenames.back().c_str(); + tokeniser.nextLine(); + for (;;) { + const char *token = tokeniser.getToken(); + + if (token == 0) { + break; + } + + if (string_equal(token, "table")) { + if (tokeniser.getToken() == 0) { + Tokeniser_unexpectedError(tokeniser, 0, "#table-name"); + return; + } + if (!Tokeniser_parseToken(tokeniser, "{")) { + return; + } + for (;;) { + const char *option = tokeniser.getToken(); + if (string_equal(option, "{")) { + for (;;) { + const char *value = tokeniser.getToken(); + if (string_equal(value, "}")) { + break; + } + } + + if (!Tokeniser_parseToken(tokeniser, "}")) { + return; + } + break; + } + } + } else { + if (string_equal(token, "guide")) { + parseTemplateInstance(tokeniser, filename); + } else { + if (!string_equal(token, "material") + && !string_equal(token, "particle") + && !string_equal(token, "skin")) { + tokeniser.ungetToken(); + } + // first token should be the path + name.. (from base) + CopiedString name; + if (!Tokeniser_parseShaderName(tokeniser, name)) { + } + ShaderTemplatePointer shaderTemplate(new ShaderTemplate()); + shaderTemplate->setName(name.c_str()); + + g_shaders.insert(ShaderTemplateMap::value_type(shaderTemplate->getName(), shaderTemplate)); + + bool result = (g_shaderLanguage == SHADERLANGUAGE_QUAKE3) + ? shaderTemplate->parseQuake3(tokeniser) + : shaderTemplate->parseDoom3(tokeniser); + if (result) { + // do we already have this shader? + if (!g_shaderDefinitions.insert(ShaderDefinitionMap::value_type(shaderTemplate->getName(), + ShaderDefinition( + shaderTemplate.get(), + ShaderArguments(), + filename))).second) { +#if GDEF_DEBUG + globalOutputStream() << "WARNING: shader " << shaderTemplate->getName() + << " is already in memory, definition in " << filename << " ignored.\n"; +#endif + } + } else { + globalErrorStream() << "Error parsing shader " << shaderTemplate->getName() << "\n"; + return; + } + } + } + } } -void LoadShaderFile( const char* filename ){ - ArchiveTextFile* file = GlobalFileSystem().openTextFile( filename ); +void parseGuideFile(Tokeniser &tokeniser, const char *filename) +{ + tokeniser.nextLine(); + for (;;) { + const char *token = tokeniser.getToken(); + + if (token == 0) { + break; + } + + if (string_equal(token, "guide")) { + // first token should be the path + name.. (from base) + ShaderTemplatePointer shaderTemplate(new ShaderTemplate); + shaderTemplate->parseTemplate(tokeniser); + if (!g_shaderTemplates.insert( + ShaderTemplateMap::value_type(shaderTemplate->getName(), shaderTemplate)).second) { + globalErrorStream() << "guide " << makeQuoted(shaderTemplate->getName()) + << ": already defined, second definition ignored\n"; + } + } else if (string_equal(token, "inlineGuide")) { + // skip entire inlineGuide definition + std::size_t depth = 0; + for (;;) { + tokeniser.nextLine(); + token = tokeniser.getToken(); + if (string_equal(token, "{")) { + ++depth; + } else if (string_equal(token, "}")) { + if (--depth == 0) { + break; + } + } + } + } + } +} + +void LoadShaderFile(const char *filename) +{ + ArchiveTextFile *file = GlobalFileSystem().openTextFile(filename); - if ( file != 0 ) { - globalOutputStream() << "Parsing shaderfile " << filename << "\n"; + if (file != 0) { + globalOutputStream() << "Parsing shaderfile " << filename << "\n"; - Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser( file->getInputStream() ); + Tokeniser &tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser(file->getInputStream()); - ParseShaderFile( tokeniser, filename ); + ParseShaderFile(tokeniser, filename); - tokeniser.release(); - file->release(); - } - else - { - globalOutputStream() << "Unable to read shaderfile " << filename << "\n"; - } + tokeniser.release(); + file->release(); + } else { + globalOutputStream() << "Unable to read shaderfile " << filename << "\n"; + } } -void loadGuideFile( const char* filename ){ - StringOutputStream fullname( 256 ); - fullname << "guides/" << filename; - ArchiveTextFile* file = GlobalFileSystem().openTextFile( fullname.c_str() ); - - if ( file != 0 ) { - globalOutputStream() << "Parsing guide file " << fullname.c_str() << "\n"; +void loadGuideFile(const char *filename) +{ + StringOutputStream fullname(256); + fullname << "guides/" << filename; + ArchiveTextFile *file = GlobalFileSystem().openTextFile(fullname.c_str()); - Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser( file->getInputStream() ); + if (file != 0) { + globalOutputStream() << "Parsing guide file " << fullname.c_str() << "\n"; - parseGuideFile( tokeniser, fullname.c_str() ); + Tokeniser &tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser(file->getInputStream()); - tokeniser.release(); - file->release(); - } - else - { - globalOutputStream() << "Unable to read guide file " << fullname.c_str() << "\n"; - } -} + parseGuideFile(tokeniser, fullname.c_str()); -CShader* Try_Shader_ForName( const char* name ){ - { - shaders_t::iterator i = g_ActiveShaders.find( name ); - if ( i != g_ActiveShaders.end() ) { - return ( *i ).second; - } - } - // active shader was not found - - // find matching shader definition - ShaderDefinitionMap::iterator i = g_shaderDefinitions.find( name ); - if ( i == g_shaderDefinitions.end() ) { - // shader definition was not found - - // create new shader definition from default shader template - ShaderTemplatePointer shaderTemplate( new ShaderTemplate() ); - shaderTemplate->CreateDefault( name ); - g_shaderTemplates.insert( ShaderTemplateMap::value_type( shaderTemplate->getName(), shaderTemplate ) ); - - i = g_shaderDefinitions.insert( ShaderDefinitionMap::value_type( name, ShaderDefinition( shaderTemplate.get(), ShaderArguments(), "" ) ) ).first; - } - - // create shader from existing definition - ShaderPointer pShader( new CShader( ( *i ).second ) ); - pShader->setName( name ); - g_ActiveShaders.insert( shaders_t::value_type( name, pShader ) ); - g_ActiveShadersChangedNotify(); - return pShader; + tokeniser.release(); + file->release(); + } else { + globalOutputStream() << "Unable to read guide file " << fullname.c_str() << "\n"; + } } -IShader *Shader_ForName( const char *name ){ - ASSERT_NOTNULL( name ); +CShader *Try_Shader_ForName(const char *name) +{ + { + shaders_t::iterator i = g_ActiveShaders.find(name); + if (i != g_ActiveShaders.end()) { + return (*i).second; + } + } + // active shader was not found + + // find matching shader definition + ShaderDefinitionMap::iterator i = g_shaderDefinitions.find(name); + if (i == g_shaderDefinitions.end()) { + // shader definition was not found + + // create new shader definition from default shader template + ShaderTemplatePointer shaderTemplate(new ShaderTemplate()); + shaderTemplate->CreateDefault(name); + g_shaderTemplates.insert(ShaderTemplateMap::value_type(shaderTemplate->getName(), shaderTemplate)); + + i = g_shaderDefinitions.insert(ShaderDefinitionMap::value_type(name, ShaderDefinition(shaderTemplate.get(), + ShaderArguments(), + ""))).first; + } + + // create shader from existing definition + ShaderPointer pShader(new CShader((*i).second)); + pShader->setName(name); + g_ActiveShaders.insert(shaders_t::value_type(name, pShader)); + g_ActiveShadersChangedNotify(); + return pShader; +} + +IShader *Shader_ForName(const char *name) +{ + ASSERT_NOTNULL(name); - IShader *pShader = Try_Shader_ForName( name ); - pShader->IncRef(); - return pShader; + IShader *pShader = Try_Shader_ForName(name); + pShader->IncRef(); + return pShader; } - - // the list of scripts/*.shader files we need to work with // those are listed in shaderlist file GSList *l_shaderfiles = 0; -GSList* Shaders_getShaderFileList(){ - return l_shaderfiles; +GSList *Shaders_getShaderFileList() +{ + return l_shaderfiles; } /* @@ -1518,47 +1553,49 @@ GSList* Shaders_getShaderFileList(){ usefull function: dumps the list of .shader files that are not referenced to the console ================== */ -void IfFound_dumpUnreferencedShader( bool& bFound, const char* filename ){ - bool listed = false; - - for ( GSList* sh = l_shaderfiles; sh != 0; sh = g_slist_next( sh ) ) - { - if ( !strcmp( (char*)sh->data, filename ) ) { - listed = true; - break; - } - } - - if ( !listed ) { - if ( !bFound ) { - bFound = true; - globalOutputStream() << "Following shader files are not referenced in any shaderlist.txt:\n"; - } - globalOutputStream() << "\t" << filename << "\n"; - } +void IfFound_dumpUnreferencedShader(bool &bFound, const char *filename) +{ + bool listed = false; + + for (GSList *sh = l_shaderfiles; sh != 0; sh = g_slist_next(sh)) { + if (!strcmp((char *) sh->data, filename)) { + listed = true; + break; + } + } + + if (!listed) { + if (!bFound) { + bFound = true; + globalOutputStream() << "Following shader files are not referenced in any shaderlist.txt:\n"; + } + globalOutputStream() << "\t" << filename << "\n"; + } } -typedef ReferenceCaller IfFoundDumpUnreferencedShaderCaller; -void DumpUnreferencedShaders(){ - bool bFound = false; - GlobalFileSystem().forEachFile( g_shadersDirectory, g_shadersExtension, IfFoundDumpUnreferencedShaderCaller( bFound ) ); +typedef ReferenceCaller IfFoundDumpUnreferencedShaderCaller; + +void DumpUnreferencedShaders() +{ + bool bFound = false; + GlobalFileSystem().forEachFile(g_shadersDirectory, g_shadersExtension, IfFoundDumpUnreferencedShaderCaller(bFound)); } -void ShaderList_addShaderFile( const char* dirstring ){ - bool found = false; - - for ( GSList* tmp = l_shaderfiles; tmp != 0; tmp = tmp->next ) - { - if ( string_equal_nocase( dirstring, (char*)tmp->data ) ) { - found = true; - globalOutputStream() << "duplicate entry \"" << (char*)tmp->data << "\" in shaderlist.txt\n"; - break; - } - } - - if ( !found ) { - l_shaderfiles = g_slist_append( l_shaderfiles, strdup( dirstring ) ); - } +void ShaderList_addShaderFile(const char *dirstring) +{ + bool found = false; + + for (GSList *tmp = l_shaderfiles; tmp != 0; tmp = tmp->next) { + if (string_equal_nocase(dirstring, (char *) tmp->data)) { + found = true; + globalOutputStream() << "duplicate entry \"" << (char *) tmp->data << "\" in shaderlist.txt\n"; + break; + } + } + + if (!found) { + l_shaderfiles = g_slist_append(l_shaderfiles, strdup(dirstring)); + } } /* @@ -1567,234 +1604,264 @@ void ShaderList_addShaderFile( const char* dirstring ){ build a CStringList of shader names ================== */ -void BuildShaderList( TextInputStream& shaderlist ){ - Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewSimpleTokeniser( shaderlist ); - tokeniser.nextLine(); - const char* token = tokeniser.getToken(); - StringOutputStream shaderFile( 64 ); - while ( token != 0 ) - { - // each token should be a shader filename - shaderFile << token << "." << g_shadersExtension; - - ShaderList_addShaderFile( shaderFile.c_str() ); - - tokeniser.nextLine(); - token = tokeniser.getToken(); - - shaderFile.clear(); - } - tokeniser.release(); -} - -void FreeShaderList(){ - while ( l_shaderfiles != 0 ) - { - free( l_shaderfiles->data ); - l_shaderfiles = g_slist_remove( l_shaderfiles, l_shaderfiles->data ); - } -} +void BuildShaderList(TextInputStream &shaderlist) +{ + Tokeniser &tokeniser = GlobalScriptLibrary().m_pfnNewSimpleTokeniser(shaderlist); + tokeniser.nextLine(); + const char *token = tokeniser.getToken(); + StringOutputStream shaderFile(64); + while (token != 0) { + // each token should be a shader filename + shaderFile << token << "." << g_shadersExtension; -void ShaderList_addFromArchive( const char *archivename ){ - const char *shaderpath = GlobalRadiant().getGameDescriptionKeyValue( "shaderpath" ); - if ( string_empty( shaderpath ) ) { - return; - } - - StringOutputStream shaderlist( 256 ); - shaderlist << DirectoryCleaned( shaderpath ) << "shaderlist.txt"; - - Archive *archive = GlobalFileSystem().getArchive( archivename, false ); - if ( archive ) { - ArchiveTextFile *file = archive->openTextFile( shaderlist.c_str() ); - if ( file ) { - globalOutputStream() << "Found shaderlist.txt in " << archivename << "\n"; - BuildShaderList( file->getInputStream() ); - file->release(); - } - } -} + ShaderList_addShaderFile(shaderFile.c_str()); -#include "stream/filestream.h" + tokeniser.nextLine(); + token = tokeniser.getToken(); -bool shaderlist_findOrInstall( const char* enginePath, const char* toolsPath, const char* shaderPath, const char* gamename ){ - StringOutputStream absShaderList( 256 ); - absShaderList << enginePath << gamename << '/' << shaderPath << "shaderlist.txt"; - if ( file_exists( absShaderList.c_str() ) ) { - return true; - } - { - StringOutputStream directory( 256 ); - directory << enginePath << gamename << '/' << shaderPath; - if ( !file_exists( directory.c_str() ) && !Q_mkdir( directory.c_str() ) ) { - return false; - } - } - { - StringOutputStream defaultShaderList( 256 ); - defaultShaderList << toolsPath << gamename << '/' << "default_shaderlist.txt"; - if ( file_exists( defaultShaderList.c_str() ) ) { - return file_copy( defaultShaderList.c_str(), absShaderList.c_str() ); - } - } - return false; + shaderFile.clear(); + } + tokeniser.release(); } -void Shaders_Load(){ - if ( g_shaderLanguage == SHADERLANGUAGE_QUAKE4 ) { - GlobalFileSystem().forEachFile("guides/", "guide", makeCallbackF(loadGuideFile), 0); - } - - const char* shaderPath = GlobalRadiant().getGameDescriptionKeyValue( "shaderpath" ); - if ( !string_empty( shaderPath ) ) { - StringOutputStream path( 256 ); - path << DirectoryCleaned( shaderPath ); - - if ( g_useShaderList ) { - // preload shader files that have been listed in shaderlist.txt - const char* basegame = GlobalRadiant().getRequiredGameDescriptionKeyValue( "basegame" ); - const char* gamename = GlobalRadiant().getGameName(); - const char* enginePath = GlobalRadiant().getEnginePath(); - const char* toolsPath = GlobalRadiant().getGameToolsPath(); - - bool isMod = !string_equal( basegame, gamename ); - - if ( !isMod || !shaderlist_findOrInstall( enginePath, toolsPath, path.c_str(), gamename ) ) { - gamename = basegame; - shaderlist_findOrInstall( enginePath, toolsPath, path.c_str(), gamename ); - } - - GlobalFileSystem().forEachArchive(makeCallbackF(ShaderList_addFromArchive), false, true); - DumpUnreferencedShaders(); - } - else - { - GlobalFileSystem().forEachFile(path.c_str(), g_shadersExtension, makeCallbackF(ShaderList_addShaderFile), 0); - } - - GSList *lst = l_shaderfiles; - StringOutputStream shadername( 256 ); - while ( lst ) - { - shadername << path.c_str() << reinterpret_cast( lst->data ); - LoadShaderFile( shadername.c_str() ); - shadername.clear(); - lst = lst->next; - } - } - - //StringPool_analyse(ShaderPool::instance()); +void FreeShaderList() +{ + while (l_shaderfiles != 0) { + free(l_shaderfiles->data); + l_shaderfiles = g_slist_remove(l_shaderfiles, l_shaderfiles->data); + } } -void Shaders_Free(){ - FreeShaders(); - FreeShaderList(); - g_shaderFilenames.clear(); -} +void ShaderList_addFromArchive(const char *archivename) +{ + const char *shaderpath = GlobalRadiant().getGameDescriptionKeyValue("shaderpath"); + if (string_empty(shaderpath)) { + return; + } -ModuleObservers g_observers; + StringOutputStream shaderlist(256); + shaderlist << DirectoryCleaned(shaderpath) << "shaderlist.txt"; -std::size_t g_shaders_unrealised = 1; // wait until filesystem and is realised before loading anything -bool Shaders_realised(){ - return g_shaders_unrealised == 0; -} -void Shaders_Realise(){ - if ( --g_shaders_unrealised == 0 ) { - Shaders_Load(); - g_observers.realise(); - } -} -void Shaders_Unrealise(){ - if ( ++g_shaders_unrealised == 1 ) { - g_observers.unrealise(); - Shaders_Free(); - } + Archive *archive = GlobalFileSystem().getArchive(archivename, false); + if (archive) { + ArchiveTextFile *file = archive->openTextFile(shaderlist.c_str()); + if (file) { + globalOutputStream() << "Found shaderlist.txt in " << archivename << "\n"; + BuildShaderList(file->getInputStream()); + file->release(); + } + } } -void Shaders_Refresh(){ - Shaders_Unrealise(); - Shaders_Realise(); -} +#include "stream/filestream.h" -class Quake3ShaderSystem : public ShaderSystem, public ModuleObserver +bool +shaderlist_findOrInstall(const char *enginePath, const char *toolsPath, const char *shaderPath, const char *gamename) { -public: -void realise(){ - Shaders_Realise(); -} -void unrealise(){ - Shaders_Unrealise(); -} -void refresh(){ - Shaders_Refresh(); + StringOutputStream absShaderList(256); + absShaderList << enginePath << gamename << '/' << shaderPath << "shaderlist.txt"; + if (file_exists(absShaderList.c_str())) { + return true; + } + { + StringOutputStream directory(256); + directory << enginePath << gamename << '/' << shaderPath; + if (!file_exists(directory.c_str()) && !Q_mkdir(directory.c_str())) { + return false; + } + } + { + StringOutputStream defaultShaderList(256); + defaultShaderList << toolsPath << gamename << '/' << "default_shaderlist.txt"; + if (file_exists(defaultShaderList.c_str())) { + return file_copy(defaultShaderList.c_str(), absShaderList.c_str()); + } + } + return false; +} + +void Shaders_Load() +{ + if (g_shaderLanguage == SHADERLANGUAGE_QUAKE4) { + GlobalFileSystem().forEachFile("guides/", "guide", makeCallbackF(loadGuideFile), 0); + } + + const char *shaderPath = GlobalRadiant().getGameDescriptionKeyValue("shaderpath"); + if (!string_empty(shaderPath)) { + StringOutputStream path(256); + path << DirectoryCleaned(shaderPath); + + if (g_useShaderList) { + // preload shader files that have been listed in shaderlist.txt + const char *basegame = GlobalRadiant().getRequiredGameDescriptionKeyValue("basegame"); + const char *gamename = GlobalRadiant().getGameName(); + const char *enginePath = GlobalRadiant().getEnginePath(); + const char *toolsPath = GlobalRadiant().getGameToolsPath(); + + bool isMod = !string_equal(basegame, gamename); + + if (!isMod || !shaderlist_findOrInstall(enginePath, toolsPath, path.c_str(), gamename)) { + gamename = basegame; + shaderlist_findOrInstall(enginePath, toolsPath, path.c_str(), gamename); + } + + GlobalFileSystem().forEachArchive(makeCallbackF(ShaderList_addFromArchive), false, true); + DumpUnreferencedShaders(); + } else { + GlobalFileSystem().forEachFile(path.c_str(), g_shadersExtension, makeCallbackF(ShaderList_addShaderFile), + 0); + } + + GSList *lst = l_shaderfiles; + StringOutputStream shadername(256); + while (lst) { + shadername << path.c_str() << reinterpret_cast( lst->data ); + LoadShaderFile(shadername.c_str()); + shadername.clear(); + lst = lst->next; + } + } + + //StringPool_analyse(ShaderPool::instance()); +} + +void Shaders_Free() +{ + FreeShaders(); + FreeShaderList(); + g_shaderFilenames.clear(); } -IShader* getShaderForName( const char* name ){ - return Shader_ForName( name ); -} +ModuleObservers g_observers; -void foreachShaderName( const ShaderNameCallback& callback ){ - for ( ShaderDefinitionMap::const_iterator i = g_shaderDefinitions.begin(); i != g_shaderDefinitions.end(); ++i ) - { - callback( ( *i ).first.c_str() ); - } +std::size_t g_shaders_unrealised = 1; // wait until filesystem and is realised before loading anything +bool Shaders_realised() +{ + return g_shaders_unrealised == 0; } -void beginActiveShadersIterator(){ - ActiveShaders_IteratorBegin(); -} -bool endActiveShadersIterator(){ - return ActiveShaders_IteratorAtEnd(); -} -IShader* dereferenceActiveShadersIterator(){ - return ActiveShaders_IteratorCurrent(); -} -void incrementActiveShadersIterator(){ - ActiveShaders_IteratorIncrement(); -} -void setActiveShadersChangedNotify( const Callback& notify ){ - g_ActiveShadersChangedNotify = notify; +void Shaders_Realise() +{ + if (--g_shaders_unrealised == 0) { + Shaders_Load(); + g_observers.realise(); + } } -void attach( ModuleObserver& observer ){ - g_observers.attach( observer ); -} -void detach( ModuleObserver& observer ){ - g_observers.detach( observer ); +void Shaders_Unrealise() +{ + if (++g_shaders_unrealised == 1) { + g_observers.unrealise(); + Shaders_Free(); + } } -void setLightingEnabled( bool enabled ){ - if ( CShader::m_lightingEnabled != enabled ) { - for ( shaders_t::const_iterator i = g_ActiveShaders.begin(); i != g_ActiveShaders.end(); ++i ) - { - ( *i ).second->unrealiseLighting(); - } - CShader::m_lightingEnabled = enabled; - for ( shaders_t::const_iterator i = g_ActiveShaders.begin(); i != g_ActiveShaders.end(); ++i ) - { - ( *i ).second->realiseLighting(); - } - } +void Shaders_Refresh() +{ + Shaders_Unrealise(); + Shaders_Realise(); } -const char* getTexturePrefix() const { - return g_texturePrefix; -} +class Quake3ShaderSystem : public ShaderSystem, public ModuleObserver { +public: + void realise() + { + Shaders_Realise(); + } + + void unrealise() + { + Shaders_Unrealise(); + } + + void refresh() + { + Shaders_Refresh(); + } + + IShader *getShaderForName(const char *name) + { + return Shader_ForName(name); + } + + void foreachShaderName(const ShaderNameCallback &callback) + { + for (ShaderDefinitionMap::const_iterator i = g_shaderDefinitions.begin(); i != g_shaderDefinitions.end(); ++i) { + callback((*i).first.c_str()); + } + } + + void beginActiveShadersIterator() + { + ActiveShaders_IteratorBegin(); + } + + bool endActiveShadersIterator() + { + return ActiveShaders_IteratorAtEnd(); + } + + IShader *dereferenceActiveShadersIterator() + { + return ActiveShaders_IteratorCurrent(); + } + + void incrementActiveShadersIterator() + { + ActiveShaders_IteratorIncrement(); + } + + void setActiveShadersChangedNotify(const Callback ¬ify) + { + g_ActiveShadersChangedNotify = notify; + } + + void attach(ModuleObserver &observer) + { + g_observers.attach(observer); + } + + void detach(ModuleObserver &observer) + { + g_observers.detach(observer); + } + + void setLightingEnabled(bool enabled) + { + if (CShader::m_lightingEnabled != enabled) { + for (shaders_t::const_iterator i = g_ActiveShaders.begin(); i != g_ActiveShaders.end(); ++i) { + (*i).second->unrealiseLighting(); + } + CShader::m_lightingEnabled = enabled; + for (shaders_t::const_iterator i = g_ActiveShaders.begin(); i != g_ActiveShaders.end(); ++i) { + (*i).second->realiseLighting(); + } + } + } + + const char *getTexturePrefix() const + { + return g_texturePrefix; + } }; Quake3ShaderSystem g_Quake3ShaderSystem; -ShaderSystem& GetShaderSystem(){ - return g_Quake3ShaderSystem; +ShaderSystem &GetShaderSystem() +{ + return g_Quake3ShaderSystem; } -void Shaders_Construct(){ - GlobalFileSystem().attach( g_Quake3ShaderSystem ); +void Shaders_Construct() +{ + GlobalFileSystem().attach(g_Quake3ShaderSystem); } -void Shaders_Destroy(){ - GlobalFileSystem().detach( g_Quake3ShaderSystem ); - if ( Shaders_realised() ) { - Shaders_Free(); - } +void Shaders_Destroy() +{ + GlobalFileSystem().detach(g_Quake3ShaderSystem); + + if (Shaders_realised()) { + Shaders_Free(); + } }