X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=plugins%2Fshaders%2Fshaders.cpp;h=abf0ac9fa82a18462c909fa90672ae361270c2f9;hb=37787ff58d17054395ece7e19db0bd07dde08fc6;hp=7732681052b4b5b3acc80e1f3787d90d59fbf1ca;hpb=98364b324c7439b57120db191f95927f4fc10572;p=xonotic%2Fnetradiant.git diff --git a/plugins/shaders/shaders.cpp b/plugins/shaders/shaders.cpp index 77326810..abf0ac9f 100644 --- a/plugins/shaders/shaders.cpp +++ b/plugins/shaders/shaders.cpp @@ -1,32 +1,32 @@ /* -Copyright (c) 2001, Loki software, inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list -of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, this -list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -Neither the name of Loki software nor the names of its contributors may be used -to endorse or promote products derived from this software without specific prior -written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY -DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ + Copyright (c) 2001, Loki software, inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + Neither the name of Loki software nor the names of its contributors may be used + to endorse or promote products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ // // Shaders Manager Plugin @@ -35,6 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #include "shaders.h" +#include "globaldefs.h" #include #include @@ -48,10 +49,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "qerplugin.h" #include "irender.h" -#include +#include #include "debugging/debugging.h" -#include "string/string.h" +#include "string/pooledstring.h" #include "math/vector.h" #include "generic/callback.h" #include "generic/referencecounted.h" @@ -69,697 +70,608 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #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; + +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); /*! -NOTE TTimo: there is an important distinction between SHADER_NOT_FOUND and SHADER_NOTEX: -SHADER_NOT_FOUND means we didn't find the raw texture or the shader for this -SHADER_NOTEX means we recognize this as a shader script, but we are missing the texture to represent it -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 -*/ + NOTE TTimo: there is an important distinction between SHADER_NOT_FOUND and SHADER_NOTEX: + SHADER_NOT_FOUND means we didn't find the raw texture or the shader for this + SHADER_NOTEX means we recognize this as a shader script, but we are missing the texture to represent it + 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) +Image *loadBitmap(void *environment, const char *name) { - DirectoryArchiveFile file(name, name); - if(!file.failed()) - { - return g_bitmapModule->loadImage(file); - } - return 0; + 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) +inline byte *getPixel(byte *pixels, int width, int height, int x, int y) { - return pixels + (((((y + height) % height) * width) + ((x + width) % width)) * 4); + 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) +Image &convertHeightmapToNormalmap(Image &heightmap, float scale) { - int w = heightmap.getWidth(); - int h = heightmap.getHeight(); - - Image& normalmap = *(new RGBAImage(heightmap.getWidth(), heightmap.getHeight())); - - byte* in = heightmap.getRGBAPixels(); - byte* out = normalmap.getRGBAPixels(); + int w = heightmap.getWidth(); + int h = heightmap.getHeight(); + + Image &normalmap = *(new RGBAImage(heightmap.getWidth(), heightmap.getHeight())); + + 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 - const int kernelSize = 6; - KernelElement kernel_du[kernelSize] = { - {-1, 1,-1.0f }, - {-1, 0,-1.0f }, - {-1,-1,-1.0f }, - { 1, 1, 1.0f }, - { 1, 0, 1.0f }, - { 1,-1, 1.0f } - }; - KernelElement kernel_dv[kernelSize] = { - {-1, 1, 1.0f }, - { 0, 1, 1.0f }, - { 1, 1, 1.0f }, - {-1,-1,-1.0f }, - { 0,-1,-1.0f }, - { 1,-1,-1.0f } - }; + // 3x3 Prewitt + const int kernelSize = 6; + KernelElement kernel_du[kernelSize] = { + {-1, 1,-1.0f }, + {-1, 0,-1.0f }, + {-1,-1,-1.0f }, + { 1, 1, 1.0f }, + { 1, 0, 1.0f }, + { 1,-1, 1.0f } + }; + KernelElement kernel_dv[kernelSize] = { + {-1, 1, 1.0f }, + { 0, 1, 1.0f }, + { 1, 1, 1.0f }, + {-1,-1,-1.0f }, + { 0,-1,-1.0f }, + { 1,-1,-1.0f } + }; #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; + 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 *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 *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) +Image *loadSpecial(void *environment, const char *name) { - if(*name == '_') // special image - { - StringOutputStream bitmapName(256); - bitmapName << GlobalRadiant().getAppPath() << "bitmaps/" << name + 1 << ".bmp"; - Image* image = loadBitmap(environment, bitmapName.c_str()); - if(image != 0) - { - return image; + 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); + return GlobalTexturesCache().loadImage(name); } +class ShaderPoolContext { +}; +typedef Static ShaderPool; +typedef PooledString ShaderString; +typedef ShaderString ShaderVariable; +typedef ShaderString ShaderValue; +typedef CopiedString TextureExpression; // clean a texture name to the qtexture_t name format we use internally // NOTE: case sensitivity: the engine is case sensitive. we store the shader name with case information and save with case // information as well. but we assume there won't be any case conflict and so when doing lookups based on shader name, // 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 -void parseTextureName(CopiedString& name, const char* token) -{ - StringOutputStream cleaned(256); - cleaned << PathCleaned(token); - name = CopiedString(cleaned.c_str(), path_get_filename_base_end(cleaned.c_str())); // remove extension -} - -bool Tokeniser_parseTextureName(Tokeniser& tokeniser, CopiedString& name) +template +void parseTextureName(StringType &name, const char *token) { - const char* token = tokeniser.getToken(); - if(token == 0) - { - Tokeniser_unexpectedError(tokeniser, token, "#texture-name"); - return false; - } - parseTextureName(name, token); - return true; + 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 parseShaderName(CopiedString& name, const char* token) +bool Tokeniser_parseTextureName(Tokeniser &tokeniser, TextureExpression &name) { - StringOutputStream cleaned(256); - cleaned << PathCleaned(token); - name = cleaned.c_str(); + 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) +bool Tokeniser_parseShaderName(Tokeniser &tokeniser, CopiedString &name) { - const char* token = tokeniser.getToken(); - if(token == 0) - { - Tokeniser_unexpectedError(tokeniser, token, "#shader-name"); - return false; - } - parseShaderName(name, token); - return true; + 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, CopiedString& string) +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; + 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::list ShaderParameters; -typedef std::list ShaderArguments; - -typedef CopiedString TextureExpression; -typedef CopiedString ShaderValue; -typedef std::pair BlendFuncExpression; +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; - - TextureExpression m_textureName; - TextureExpression m_diffuse; - TextureExpression m_bump; - ShaderValue m_heightmapScale; - TextureExpression m_specular; - TextureExpression m_lightFalloffImage; - - int m_nFlags; - float m_fTrans; - - // alphafunc stuff - 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; - } + ShaderParameters m_params; - const char* getName() const - { - return m_Name.c_str(); - } - void setName(const char* name) - { - m_Name = name; - } + TextureExpression m_textureName; + TextureExpression m_diffuse; + TextureExpression m_bump; + ShaderValue m_heightmapScale; + TextureExpression m_specular; + TextureExpression m_lightFalloffImage; - // ----------------------------------------- + int m_nFlags; + float m_fTrans; - bool parseDoom3(Tokeniser& tokeniser); - bool parseQuake3(Tokeniser& tokeniser); - bool parseTemplate(Tokeniser& tokeniser); +// alphafunc stuff + IShader::EAlphaFunc m_AlphaFunc; + float m_AlphaRef; +// cull stuff + IShader::ECull m_Cull; - - void CreateDefault(const char *name) - { - if(g_enableDefaultShaders) + ShaderTemplate() : + m_refcount(0) { - m_textureName = name; + m_nFlags = 0; + m_fTrans = 1.0f; } - else + + void IncRef() { - m_textureName = ""; + ++m_refcount; } - 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) + void DecRef() { + ASSERT_MESSAGE(m_refcount != 0, "shader reference-count going below zero"); + if (--m_refcount == 0) { + delete this; + } } - const TextureExpression& texture() const + + std::size_t refcount() { - return m_texture; + return m_refcount; } - const BlendFuncExpression& blendFunc() const + + const char *getName() const { - return m_blendFunc; + return m_Name.c_str(); } - bool clampToBorder() const + + void setName(const char *name) { - return m_clampToBorder; + m_Name = name; } - const ShaderValue& alphaTest() const + +// ----------------------------------------- + + bool parseDoom3(Tokeniser &tokeniser); + + bool parseQuake3(Tokeniser &tokeniser); + + bool parseTemplate(Tokeniser &tokeniser); + + + void CreateDefault(const char *name) { - return m_alphaTest; + if (g_enableDefaultShaders) { + m_textureName = name; + } else { + m_textureName = ""; + } + setName(name); } - }; - typedef std::vector MapLayers; - MapLayers m_layers; -}; -bool Doom3Shader_parseHeightmap(Tokeniser& tokeniser, CopiedString& bump, CopiedString& 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; -} + 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) + { + } -bool Doom3Shader_parseAddnormals(Tokeniser& tokeniser, CopiedString& 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")); - CopiedString heightmapName; - CopiedString heightmapScale; - RETURN_FALSE_IF_FAIL(Doom3Shader_parseHeightmap(tokeniser, heightmapName, heightmapScale)); - RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")")); - return true; -} + const TextureExpression &texture() const + { + return m_texture; + } -bool Doom3Shader_parseBumpmap(Tokeniser& tokeniser, CopiedString& bump, CopiedString& 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; -} + const BlendFuncExpression &blendFunc() const + { + return m_blendFunc; + } -enum LayerTypeId -{ - LAYER_NONE, - LAYER_BLEND, - LAYER_DIFFUSEMAP, - LAYER_BUMPMAP, - LAYER_SPECULARMAP -}; + bool clampToBorder() const + { + return m_clampToBorder; + } -class LayerTemplate -{ -public: - LayerTypeId m_type; - CopiedString 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") - { - } + const ShaderValue &alphaTest() const + { + return m_alphaTest; + } + }; + + typedef std::vector MapLayers; + MapLayers m_layers; }; -bool parseShaderParameters(Tokeniser& tokeniser, ShaderParameters& params) + +bool Doom3Shader_parseHeightmap(Tokeniser &tokeniser, TextureExpression &bump, ShaderValue &heightmapScale) { - 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; + 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 ShaderTemplate::parseTemplate(Tokeniser& tokeniser) +bool Doom3Shader_parseAddnormals(Tokeniser &tokeniser, TextureExpression &bump) { - 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_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 ShaderTemplate::parseDoom3(Tokeniser& tokeniser) +bool Doom3Shader_parseBumpmap(Tokeniser &tokeniser, TextureExpression &bump, ShaderValue &heightmapScale) { - LayerTemplate currentLayer; - bool isFog = false; + 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; +} - // we need to read until we hit a balanced } - int depth = 0; - for(;;) - { - tokeniser.nextLine(); - const char* token = tokeniser.getToken(); +enum LayerTypeId { + LAYER_NONE, + LAYER_BLEND, + LAYER_DIFFUSEMAP, + LAYER_BUMPMAP, + LAYER_SPECULARMAP +}; - if(token == 0) - return false; +class LayerTemplate { +public: + LayerTypeId m_type; + TextureExpression m_texture; + BlendFuncExpression m_blendFunc; + bool m_clampToBorder; + ShaderValue m_alphaTest; + ShaderValue m_heightmapScale; - if(string_equal(token, "{")) + LayerTemplate() : m_type(LAYER_NONE), m_blendFunc("GL_ONE", "GL_ZERO"), m_clampToBorder(false), m_alphaTest("-1"), + m_heightmapScale("0") { - ++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; +}; + +bool parseShaderParameters(Tokeniser &tokeniser, ShaderParameters ¶ms) +{ + Tokeniser_parseToken(tokeniser, "("); + for (;;) { + const char *param = tokeniser.getToken(); + if (string_equal(param, ")")) { + break; } - else if(currentLayer.m_type == LAYER_SPECULARMAP) - { - m_specular = currentLayer.m_texture; + params.push_back(param); + const char *comma = tokeniser.getToken(); + if (string_equal(comma, ")")) { + break; } - 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 - )); + if (!string_equal(comma, ",")) { + Tokeniser_unexpectedError(tokeniser, comma, ","); + return false; } - currentLayer.m_type = LAYER_NONE; - currentLayer.m_texture = ""; - } - continue; } + return true; +} - if(depth == 2) // in layer - { - if(string_equal_nocase(token, "blend")) - { - const char* blend = tokeniser.getToken(); +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; + } - if(blend == 0) - { - Tokeniser_unexpectedError(tokeniser, blend, "#blend"); - return false; - } + return parseDoom3(tokeniser); +} - 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; +bool ShaderTemplate::parseDoom3(Tokeniser &tokeniser) +{ + LayerTemplate currentLayer; + bool isFog = false; - const char* comma = tokeniser.getToken(); + // we need to read until we hit a balanced } + int depth = 0; + for (;;) { + tokeniser.nextLine(); + const char *token = tokeniser.getToken(); - if(comma == 0) - { - Tokeniser_unexpectedError(tokeniser, comma, "#comma"); + if (token == 0) { 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; + 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; } - currentLayer.m_texture = texture; - RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")")); - } - else - { - parseTextureName(currentLayer.m_texture, map); - } + 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; } - } - else if(string_equal_nocase(token, "zeroclamp")) - { - currentLayer.m_clampToBorder = true; - } + + 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")) - { - Tokeniser_getFloat(tokeniser, currentLayer.m_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, "(")); - CopiedString name; - RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(tokeniser, name)); - m_lightFalloffImage = name.c_str(); - RETURN_FALSE_IF_FAIL(Tokeniser_parseToken(tokeniser, ")")); - } - else - { - m_lightFalloffImage = lightFalloffImage; + } 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; - } + if (string_empty(m_textureName.c_str())) { + m_textureName = m_diffuse; + } - return true; + return true; } typedef SmartPointer ShaderTemplatePointer; @@ -768,1169 +680,1056 @@ typedef std::map ShaderTemplateMap; ShaderTemplateMap g_shaders; ShaderTemplateMap g_shaderTemplates; -ShaderTemplate* findTemplate(const char* name) +ShaderTemplate *findTemplate(const char *name) { - ShaderTemplateMap::iterator i = g_shaderTemplates.find(name); - if(i != g_shaderTemplates.end()) - { - return (*i).second.get(); - } - return 0; + 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) +bool parseTemplateInstance(Tokeniser &tokeniser, const char *filename) { - CopiedString name; - RETURN_FALSE_IF_FAIL(Tokeniser_parseTextureName(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; - } + 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"; + } - 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"; + 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; + return true; } -const char* evaluateShaderValue(const char* value, const ShaderParameters& params, const ShaderArguments& args) +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(); + 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; + return value; } ///\todo BlendFunc parsing -BlendFunc evaluateBlendFunc(const BlendFuncExpression& blendFunc, const ShaderParameters& params, const ShaderArguments& args) +BlendFunc +evaluateBlendFunc(const BlendFuncExpression &blendFunc, const ShaderParameters ¶ms, const ShaderArguments &args) { - return BlendFunc(BLEND_ONE, BLEND_ZERO); + return BlendFunc(BLEND_ONE, BLEND_ZERO); } -qtexture_t* evaluateTexture(const TextureExpression& texture, const ShaderParameters& params, const ShaderArguments& args, const LoadImageCallback& loader = GlobalTexturesCache().defaultLoader()) +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(); + 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; + } } - } - 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()); + result << expression; + } + return GlobalTexturesCache().capture(loader, result.c_str()); } -float evaluateFloat(const ShaderValue& value, const ShaderParameters& params, const ShaderArguments& args) +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; + const char *result = evaluateShaderValue(value.c_str(), params, args); + float f = 0; + if (!string_parse_float(result, f)) { + globalErrorStream() << "parsing float value failed: " << makeQuoted(result) << "\n"; + } + return f; } -BlendFactor evaluateBlendFactor(const ShaderValue& value, const ShaderParameters& params, const ShaderArguments& args) +BlendFactor evaluateBlendFactor(const ShaderValue &value, const ShaderParameters ¶ms, const ShaderArguments &args) { - const char* result = evaluateShaderValue(value.c_str(), params, 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; + } - if(string_equal_nocase(result, "gl_zero")) - { + globalErrorStream() << "parsing blend-factor value failed: " << makeQuoted(result) << "\n"; 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; - - 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; +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; + + 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(); - - ASSERT_MESSAGE(m_refcount == 0, "deleting active shader"); - } - - // 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; - } - - // 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; - } - // ----------------------------------------- - - void realise() - { - m_pTexture = evaluateTexture(m_template.m_textureName, m_template.m_params, m_args); - - if(m_pTexture->texture_number == 0) + 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_notfound = m_pTexture; + m_pTexture = 0; + m_pDiffuse = 0; + m_pBump = 0; + m_pSpecular = 0; - { - StringOutputStream name(256); - name << GlobalRadiant().getAppPath() << "bitmaps/" << (IsDefault() ? "notex.bmp" : "shadernotex.bmp"); - m_pTexture = GlobalTexturesCache().capture(LoadImageCallback(0, loadBitmap), name.c_str()); - } + m_notfound = 0; + + realise(); } - realiseLighting(); + virtual ~CShader() + { + unrealise(); + + ASSERT_MESSAGE(m_refcount == 0, "deleting active shader"); + } - if(m_layers.size() == 1) +// IShaders implementation ----------------- + void IncRef() { - 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); + ++m_refcount; + } - 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 DecRef() + { + ASSERT_MESSAGE(m_refcount != 0, "shader reference-count going below zero"); + if (--m_refcount == 0) { + delete this; } - } } - } - void unrealise() - { - GlobalTexturesCache().release(m_pTexture); - - if(m_notfound != 0) + std::size_t refcount() { - GlobalTexturesCache().release(m_notfound); + return m_refcount; } - unrealiseLighting(); - } +// get/set the qtexture_t* Radiant uses to represent this shader object + qtexture_t *getTexture() const + { + return m_pTexture; + } - void realiseLighting() - { - if(m_lightingEnabled) + qtexture_t *getDiffuse() const { - 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)); - } - } - } - - void unrealiseLighting() - { - if(m_lightingEnabled) + return m_pDiffuse; + } + + qtexture_t *getBump() const { - GlobalTexturesCache().release(m_pDiffuse); - GlobalTexturesCache().release(m_pBump); - GlobalTexturesCache().release(m_pSpecular); + return m_pBump; + } - GlobalTexturesCache().release(m_pLightFalloffImage); + qtexture_t *getSpecular() const + { + return m_pSpecular; + } - for(MapLayers::iterator i = m_layers.begin(); i != m_layers.end(); ++i) - { - GlobalTexturesCache().release((*i).texture()); - } - m_layers.clear(); +// get shader name + const char *getName() const + { + return m_Name.c_str(); } - } - // set shader name - void setName(const char* name) - { - m_Name = name; - } + bool IsInUse() const + { + return m_bInUse; + } - 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) + void SetInUse(bool bInUse) { + m_bInUse = bInUse; + g_ActiveShadersChangedNotify(); } - qtexture_t* texture() const + +// get the shader flags + int getFlags() const { - return m_texture; + return m_template.m_nFlags; } - BlendFunc blendFunc() const + +// get the transparency value + float getTrans() const { - return m_blendFunc; + return m_template.m_fTrans; } - bool clampToBorder() const + +// test if it's a true shader, or a default shader created to wrap around a texture + bool IsDefault() const { - return m_clampToBorder; + return string_empty(m_filename); } - float alphaTest() const + +// get the alphaFunc + void getAlphaFunc(EAlphaFunc *func, float *ref) { - 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()) + *func = m_template.m_AlphaFunc; + *ref = m_template.m_AlphaRef; + }; + + BlendFunc getBlendFunc() const { - return 0; + return m_blendFunc; } - return &m_layers.front(); - } - void forEachLayer(const ShaderLayerCallback& callback) const - { - for(MapLayers::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) + +// get the cull type + ECull getCull() { - callback(*i); - } - } + return m_template.m_Cull; + }; - qtexture_t* lightFalloffImage() const - { - if(!string_empty(m_template.m_lightFalloffImage.c_str())) +// get shader file name (ie the file where this one is defined) + const char *getShaderFileName() const { - return m_pLightFalloffImage; + return m_filename; } - return 0; - } -}; +// ----------------------------------------- -bool CShader::m_lightingEnabled = false; + void realise() + { + m_pTexture = evaluateTexture(m_template.m_textureName, m_template.m_params, m_args); -typedef SmartPointer ShaderPointer; -typedef std::map shaders_t; + if (m_pTexture->texture_number == 0) { + m_notfound = m_pTexture; -shaders_t g_ActiveShaders; + { + StringOutputStream name(256); + name << GlobalRadiant().getAppPath() << "bitmaps/" << (IsDefault() ? "notex.png" : "shadernotex.png"); + m_pTexture = GlobalTexturesCache().capture(LoadImageCallback(0, loadBitmap), name.c_str()); + } + } -static shaders_t::iterator g_ActiveShadersIterator; + realiseLighting(); + } -void ActiveShaders_IteratorBegin() -{ - g_ActiveShadersIterator = g_ActiveShaders.begin(); -} + void unrealise() + { + GlobalTexturesCache().release(m_pTexture); -bool ActiveShaders_IteratorAtEnd() -{ - return g_ActiveShadersIterator == g_ActiveShaders.end(); -} - -IShader *ActiveShaders_IteratorCurrent() -{ - return static_cast(g_ActiveShadersIterator->second); -} - -void ActiveShaders_IteratorIncrement() -{ - ++g_ActiveShadersIterator; -} + if (m_notfound != 0) { + GlobalTexturesCache().release(m_notfound); + } -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"); - } -} + unrealiseLighting(); + } -// 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(); -} + 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)); + } -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; + 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"; + } + } + } + } + } - tokeniser.nextLine(); + void unrealiseLighting() + { + if (m_lightingEnabled) { + GlobalTexturesCache().release(m_pDiffuse); + GlobalTexturesCache().release(m_pBump); + GlobalTexturesCache().release(m_pSpecular); - // we need to read until we hit a balanced } - int depth = 0; - for(;;) - { - tokeniser.nextLine(); - const char* token = tokeniser.getToken(); + GlobalTexturesCache().release(m_pLightFalloffImage); - if(token == 0) - return false; + for (MapLayers::iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + GlobalTexturesCache().release((*i).texture()); + } + m_layers.clear(); - if(string_equal(token, "{")) - { - ++depth; - continue; + m_blendFunc = BlendFunc(BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA); + } } - else if(string_equal(token, "}")) - { - --depth; - if(depth < 0) // underflow - { - return false; - } - if(depth == 0) // end of shader - { - break; - } - continue; +// set shader name + void setName(const char *name) + { + m_Name = name; } - 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) + 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) { - Tokeniser_unexpectedError(tokeniser, alphafunc, "#alphafunc"); - return false; } - if(string_equal_nocase(alphafunc, "equal")) - { - m_AlphaFunc = IShader::eEqual; - } - else if(string_equal_nocase(alphafunc, "greater")) + qtexture_t *texture() const { - m_AlphaFunc = IShader::eGreater; + return m_texture; } - else if(string_equal_nocase(alphafunc, "less")) - { - m_AlphaFunc = IShader::eLess; - } - else if(string_equal_nocase(alphafunc, "gequal")) + + BlendFunc blendFunc() const { - m_AlphaFunc = IShader::eGEqual; + return m_blendFunc; } - else if(string_equal_nocase(alphafunc, "lequal")) + + bool clampToBorder() const { - m_AlphaFunc = IShader::eLEqual; + return m_clampToBorder; } - else + + float alphaTest() const { - m_AlphaFunc = IShader::eAlways; + return m_alphaTest; } + }; - m_nFlags |= QER_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) + ); + } - RETURN_FALSE_IF_FAIL(Tokeniser_getFloat(tokeniser, m_AlphaRef)); - } - else if (string_equal_nocase(token, "cull")) - { - const char* cull = tokeniser.getToken(); + typedef std::vector MapLayers; + MapLayers m_layers; - if(cull == 0) - { - Tokeniser_unexpectedError(tokeniser, cull, "#cull"); - return false; + const ShaderLayer *firstLayer() const + { + if (m_layers.empty()) { + return 0; } + return &m_layers.front(); + } - 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; + void forEachLayer(const ShaderLayerCallback &callback) const + { + for (MapLayers::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) { + callback(*i); } - else - { - m_Cull = IShader::eCullBack; + } + + qtexture_t *lightFalloffImage() const + { + if (!string_empty(m_template.m_lightFalloffImage.c_str())) { + return m_pLightFalloffImage; } + return 0; + } +}; - m_nFlags |= QER_CULL; - } - else if (string_equal_nocase(token, "surfaceparm")) - { - const char* surfaceparm = tokeniser.getToken(); +bool CShader::m_lightingEnabled = false; - if(surfaceparm == 0) - { - Tokeniser_unexpectedError(tokeniser, surfaceparm, "#surfaceparm"); - return false; - } +typedef SmartPointer ShaderPointer; +typedef std::map shaders_t; - 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; - } - } - } - } +shaders_t g_ActiveShaders; - return true; +static shaders_t::iterator g_ActiveShadersIterator; + +void ActiveShaders_IteratorBegin() +{ + g_ActiveShadersIterator = g_ActiveShaders.begin(); } -class Layer +bool ActiveShaders_IteratorAtEnd() { -public: - LayerTypeId m_type; - CopiedString 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) - { - } -}; + return g_ActiveShadersIterator == g_ActiveShaders.end(); +} -std::list g_shaderFilenames; +IShader *ActiveShaders_IteratorCurrent() +{ + return static_cast( g_ActiveShadersIterator->second ); +} -void ParseShaderFile(Tokeniser& tokeniser, const char* filename) +void ActiveShaders_IteratorIncrement() { - g_shaderFilenames.push_back(filename); - filename = g_shaderFilenames.back().c_str(); - tokeniser.nextLine(); - for(;;) - { - const char* token = tokeniser.getToken(); - - if(token == 0) - { - break; + ++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"); } +} - 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; - } - } +// 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(); +} - 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)) - { +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; } - ShaderTemplatePointer shaderTemplate(new ShaderTemplate()); - shaderTemplate->setName(name.c_str()); - g_shaders.insert(ShaderTemplateMap::value_type(shaderTemplate->getName(), shaderTemplate)); + 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; + } - 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) - { - #ifdef _DEBUG - globalOutputStream() << "WARNING: shader " << shaderTemplate->getName() << " is already in memory, definition in " << filename << " ignored.\n"; - #endif - } + continue; } - else - { - globalErrorStream() << "Error parsing shader " << shaderTemplate->getName() << "\n"; - return; + + 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 parseGuideFile(Tokeniser& tokeniser, const char* filename) -{ - tokeniser.nextLine(); - for(;;) - { - const char* token = tokeniser.getToken(); +class Layer { +public: + LayerTypeId m_type; + TextureExpression m_texture; + BlendFunc m_blendFunc; + bool m_clampToBorder; + float m_alphaTest; + float m_heightmapScale; - if(token == 0) + Layer() : m_type(LAYER_NONE), m_blendFunc(BLEND_ONE, BLEND_ZERO), m_clampToBorder(false), m_alphaTest(-1), + m_heightmapScale(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) - { +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 LoadShaderFile(const char* filename) +void parseGuideFile(Tokeniser &tokeniser, const char *filename) { - ArchiveTextFile* file = GlobalFileSystem().openTextFile(filename); + tokeniser.nextLine(); + for (;;) { + const char *token = tokeniser.getToken(); - if(file != 0) - { - globalOutputStream() << "Parsing shaderfile " << filename << "\n"; + if (token == 0) { + break; + } - Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser(file->getInputStream()); + 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; + } + } + } + } + } +} - ParseShaderFile(tokeniser, filename); +void LoadShaderFile(const char *filename) +{ + ArchiveTextFile *file = GlobalFileSystem().openTextFile(filename); - tokeniser.release(); - file->release(); - } - else - { - globalOutputStream() << "Unable to read shaderfile " << filename << "\n"; - } -} + if (file != 0) { + globalOutputStream() << "Parsing shaderfile " << filename << "\n"; -typedef FreeCaller1 LoadShaderFileCaller; + Tokeniser &tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser(file->getInputStream()); + ParseShaderFile(tokeniser, filename); -void loadGuideFile(const char* filename) + 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()); + StringOutputStream fullname(256); + fullname << "guides/" << filename; + ArchiveTextFile *file = GlobalFileSystem().openTextFile(fullname.c_str()); - if(file != 0) - { - globalOutputStream() << "Parsing guide file " << fullname.c_str() << "\n"; + if (file != 0) { + globalOutputStream() << "Parsing guide file " << fullname.c_str() << "\n"; - Tokeniser& tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser(file->getInputStream()); + Tokeniser &tokeniser = GlobalScriptLibrary().m_pfnNewScriptTokeniser(file->getInputStream()); - parseGuideFile(tokeniser, fullname.c_str()); + parseGuideFile(tokeniser, fullname.c_str()); - tokeniser.release(); - file->release(); - } - else - { - globalOutputStream() << "Unable to read guide file " << fullname.c_str() << "\n"; - } + tokeniser.release(); + file->release(); + } else { + globalOutputStream() << "Unable to read guide file " << fullname.c_str() << "\n"; + } } -typedef FreeCaller1 LoadGuideFileCaller; - - -CShader* Try_Shader_ForName(const char* name) +CShader *Try_Shader_ForName(const char *name) { - { - shaders_t::iterator i = g_ActiveShaders.find(name); - if(i != g_ActiveShaders.end()) { - return (*i).second; - } - } - - // not found, create it - ShaderDefinitionMap::iterator i = g_shaderDefinitions.find(name); - if(i == g_shaderDefinitions.end()) - { - 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; - } - - ShaderPointer pShader(new CShader((*i).second)); - pShader->setName(name); - g_ActiveShaders.insert(shaders_t::value_type(name, pShader)); - g_ActiveShadersChangedNotify(); - return pShader; + 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); + 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() +GSList *Shaders_getShaderFileList() { - return l_shaderfiles; + return l_shaderfiles; } /* -================== -DumpUnreferencedShaders -usefull function: dumps the list of .shader files that are not referenced to the console -================== -*/ -void IfFound_dumpUnreferencedShader(bool& bFound, const char* filename) + ================== + DumpUnreferencedShaders + 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; + bool listed = false; - for(GSList* sh = l_shaderfiles; sh != 0; sh = g_slist_next(sh)) - { - if(!strcmp((char*)sh->data, filename)) - { - listed = true; - break; + 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 shaderlist.txt:\n"; + if (!listed) { + if (!bFound) { + bFound = true; + globalOutputStream() << "Following shader files are not referenced in any shaderlist.txt:\n"; + } + globalOutputStream() << "\t" << filename << "\n"; } - globalOutputStream() << filename << "\n"; - } } -typedef ReferenceCaller1 IfFoundDumpUnreferencedShaderCaller; + +typedef ReferenceCaller IfFoundDumpUnreferencedShaderCaller; void DumpUnreferencedShaders() { - bool bFound = false; - GlobalFileSystem().forEachFile(g_shadersDirectory, g_shadersExtension, IfFoundDumpUnreferencedShaderCaller(bFound)); + bool bFound = false; + GlobalFileSystem().forEachFile(g_shadersDirectory, g_shadersExtension, IfFoundDumpUnreferencedShaderCaller(bFound)); } -void ShaderList_addShaderFile(const char* dirstring) +void ShaderList_addShaderFile(const char *dirstring) { - bool found = false; + 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)); - } -} - -typedef FreeCaller1 AddShaderFileCaller; + 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)); + } +} /* -================== -BuildShaderList -build a CStringList of shader names -================== -*/ -void BuildShaderList(TextInputStream& shaderlist) + ================== + BuildShaderList + 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 &tokeniser = GlobalScriptLibrary().m_pfnNewSimpleTokeniser(shaderlist); tokeniser.nextLine(); - token = tokeniser.getToken(); + const char *token = tokeniser.getToken(); + StringOutputStream shaderFile(64); + while (token != 0) { + // each token should be a shader filename + shaderFile << token << "." << g_shadersExtension; - shaderFile.clear(); - } - tokeniser.release(); + 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); - } + while (l_shaderfiles != 0) { + free(l_shaderfiles->data); + l_shaderfiles = g_slist_remove(l_shaderfiles, l_shaderfiles->data); + } +} + +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(); + } + } } #include "stream/filestream.h" -bool shaderlist_findOrInstall(const char* enginePath, const char* toolsPath, const char* shaderPath, const char* gamename) +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())) + StringOutputStream absShaderList(256); + absShaderList << enginePath << gamename << '/' << shaderPath << "shaderlist.txt"; + if (file_exists(absShaderList.c_str())) { + return true; + } { - return false; + 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()); + 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; + return false; } void Shaders_Load() { - if(g_shaderLanguage == SHADERLANGUAGE_QUAKE4) - { - GlobalFileSystem().forEachFile("guides/", "guide", LoadGuideFileCaller(), 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); - } - - StringOutputStream absShaderList(256); - absShaderList << enginePath << gamename << '/' << path.c_str() << "shaderlist.txt"; - - { - globalOutputStream() << "Parsing shader files from " << absShaderList.c_str() << "\n"; - TextFileInputStream shaderlistFile(absShaderList.c_str()); - if(shaderlistFile.failed()) - { - globalErrorStream() << "Couldn't find '" << absShaderList.c_str() << "'\n"; + 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); } - else - { - BuildShaderList(shaderlistFile); - DumpUnreferencedShaders(); + + 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; } - } - } - else - { - GlobalFileSystem().forEachFile(path.c_str(), g_shadersExtension, AddShaderFileCaller(), 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(); + FreeShaders(); + FreeShaderList(); + g_shaderFilenames.clear(); } ModuleObservers g_observers; @@ -1938,129 +1737,131 @@ ModuleObservers g_observers; std::size_t g_shaders_unrealised = 1; // wait until filesystem and is realised before loading anything bool Shaders_realised() { - return g_shaders_unrealised == 0; + return g_shaders_unrealised == 0; } + void Shaders_Realise() { - if(--g_shaders_unrealised == 0) - { - Shaders_Load(); - g_observers.realise(); - } + if (--g_shaders_unrealised == 0) { + Shaders_Load(); + g_observers.realise(); + } } + void Shaders_Unrealise() { - if(++g_shaders_unrealised == 1) - { - g_observers.unrealise(); - Shaders_Free(); - } + if (++g_shaders_unrealised == 1) { + g_observers.unrealise(); + Shaders_Free(); + } } -void Shaders_Refresh() +void Shaders_Refresh() { - Shaders_Unrealise(); - Shaders_Realise(); + Shaders_Unrealise(); + Shaders_Realise(); } -class Quake3ShaderSystem : public ShaderSystem, public ModuleObserver -{ +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) + void realise() { - 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& notify) - { - 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) + Shaders_Realise(); + } + + void unrealise() + { + Shaders_Unrealise(); + } + + void refresh() { - 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; - } + 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() +ShaderSystem &GetShaderSystem() { - return g_Quake3ShaderSystem; + return g_Quake3ShaderSystem; } void Shaders_Construct() { - GlobalFileSystem().attach(g_Quake3ShaderSystem); + GlobalFileSystem().attach(g_Quake3ShaderSystem); } + void Shaders_Destroy() { - GlobalFileSystem().detach(g_Quake3ShaderSystem); + GlobalFileSystem().detach(g_Quake3ShaderSystem); - if(Shaders_realised()) - { - Shaders_Free(); - } + if (Shaders_realised()) { + Shaders_Free(); + } }