+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;
+
+
+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 ) {
+ m_notfound = m_pTexture;
+
+ {
+ StringOutputStream name( 256 );
+ name << GlobalRadiant().getAppPath() << "bitmaps/" << ( IsDefault() ? "notex.bmp" : "shadernotex.bmp" );
+ m_pTexture = GlobalTexturesCache().capture( LoadImageCallback( 0, loadBitmap ), name.c_str() );
+ }
+ }
+
+ realiseLighting();
+}
+
+void unrealise(){
+ GlobalTexturesCache().release( m_pTexture );
+
+ if ( m_notfound != 0 ) {
+ GlobalTexturesCache().release( m_notfound );
+ }
+
+ unrealiseLighting();
+}
+
+void realiseLighting(){
+ if ( m_lightingEnabled ) {
+ LoadImageCallback loader = GlobalTexturesCache().defaultLoader();
+ if ( !string_empty( m_template.m_heightmapScale.c_str() ) ) {
+ m_heightmapScale = evaluateFloat( m_template.m_heightmapScale, m_template.m_params, m_args );
+ loader = LoadImageCallback( &m_heightmapScale, loadHeightmap );
+ }
+ m_pDiffuse = evaluateTexture( m_template.m_diffuse, m_template.m_params, m_args );
+ m_pBump = evaluateTexture( m_template.m_bump, m_template.m_params, m_args, loader );
+ m_pSpecular = evaluateTexture( m_template.m_specular, m_template.m_params, m_args );
+ m_pLightFalloffImage = evaluateTexture( m_template.m_lightFalloffImage, m_template.m_params, m_args );
+
+ for ( ShaderTemplate::MapLayers::const_iterator i = m_template.m_layers.begin(); i != m_template.m_layers.end(); ++i )
+ {
+ m_layers.push_back( evaluateLayer( *i, m_template.m_params, m_args ) );
+ }
+
+ if ( m_layers.size() == 1 ) {
+ const BlendFuncExpression& blendFunc = m_template.m_layers.front().blendFunc();
+ if ( !string_empty( blendFunc.second.c_str() ) ) {
+ m_blendFunc = BlendFunc(
+ evaluateBlendFactor( blendFunc.first.c_str(), m_template.m_params, m_args ),
+ evaluateBlendFactor( blendFunc.second.c_str(), m_template.m_params, m_args )
+ );
+ }
+ else
+ {
+ const char* blend = evaluateShaderValue( blendFunc.first.c_str(), m_template.m_params, m_args );
+
+ if ( string_equal_nocase( blend, "add" ) ) {
+ m_blendFunc = BlendFunc( BLEND_ONE, BLEND_ONE );
+ }
+ else if ( string_equal_nocase( blend, "filter" ) ) {
+ m_blendFunc = BlendFunc( BLEND_DST_COLOUR, BLEND_ZERO );
+ }
+ else if ( string_equal_nocase( blend, "blend" ) ) {
+ m_blendFunc = BlendFunc( BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA );
+ }
+ else
+ {
+ globalErrorStream() << "parsing blend value failed: " << makeQuoted( blend ) << "\n";
+ }
+ }
+ }
+ }
+}
+
+void unrealiseLighting(){
+ if ( m_lightingEnabled ) {
+ GlobalTexturesCache().release( m_pDiffuse );
+ GlobalTexturesCache().release( m_pBump );
+ GlobalTexturesCache().release( m_pSpecular );
+
+ GlobalTexturesCache().release( m_pLightFalloffImage );
+
+ for ( MapLayers::iterator i = m_layers.begin(); i != m_layers.end(); ++i )
+ {
+ GlobalTexturesCache().release( ( *i ).texture() );
+ }
+ m_layers.clear();
+
+ m_blendFunc = BlendFunc( BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA );
+ }
+}
+
+// set shader name
+void setName( const char* name ){
+ m_Name = name;