+ setName( name );
+}
+
+
+class MapLayerTemplate
+{
+TextureExpression m_texture;
+BlendFuncExpression m_blendFunc;
+bool m_clampToBorder;
+ShaderValue m_alphaTest;
+public:
+MapLayerTemplate( const TextureExpression& texture, const BlendFuncExpression& blendFunc, bool clampToBorder, const ShaderValue& alphaTest ) :
+ m_texture( texture ),
+ m_blendFunc( blendFunc ),
+ m_clampToBorder( false ),
+ m_alphaTest( alphaTest ){
+}
+const TextureExpression& texture() const {
+ return m_texture;
+}
+const BlendFuncExpression& blendFunc() const {
+ return m_blendFunc;
+}
+bool clampToBorder() const {
+ return m_clampToBorder;
+}
+const ShaderValue& alphaTest() const {
+ return m_alphaTest;
+}
+};
+typedef std::vector<MapLayerTemplate> MapLayers;
+MapLayers m_layers;
+};
+
+
+bool Doom3Shader_parseHeightmap( Tokeniser& tokeniser, TextureExpression& bump, ShaderValue& heightmapScale ){
+ RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "(" ) );
+ RETURN_FALSE_IF_FAIL( Tokeniser_parseTextureName( tokeniser, bump ) );
+ RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "," ) );
+ RETURN_FALSE_IF_FAIL( Tokeniser_parseString( tokeniser, heightmapScale ) );
+ RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, ")" ) );
+ return true;
+}
+
+bool Doom3Shader_parseAddnormals( Tokeniser& tokeniser, TextureExpression& bump ){
+ RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "(" ) );
+ RETURN_FALSE_IF_FAIL( Tokeniser_parseTextureName( tokeniser, bump ) );
+ RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "," ) );
+ RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "heightmap" ) );
+ TextureExpression heightmapName;
+ ShaderValue heightmapScale;
+ RETURN_FALSE_IF_FAIL( Doom3Shader_parseHeightmap( tokeniser, heightmapName, heightmapScale ) );
+ RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, ")" ) );
+ return true;
+}
+
+bool Doom3Shader_parseBumpmap( Tokeniser& tokeniser, TextureExpression& bump, ShaderValue& heightmapScale ){
+ const char* token = tokeniser.getToken();
+ if ( token == 0 ) {
+ Tokeniser_unexpectedError( tokeniser, token, "#bumpmap" );
+ return false;
+ }
+ if ( string_equal( token, "heightmap" ) ) {
+ RETURN_FALSE_IF_FAIL( Doom3Shader_parseHeightmap( tokeniser, bump, heightmapScale ) );
+ }
+ else if ( string_equal( token, "addnormals" ) ) {
+ RETURN_FALSE_IF_FAIL( Doom3Shader_parseAddnormals( tokeniser, bump ) );
+ }
+ else
+ {
+ parseTextureName( bump, token );
+ }
+ return true;
+}
+
+enum LayerTypeId
+{
+ LAYER_NONE,
+ LAYER_BLEND,
+ LAYER_DIFFUSEMAP,
+ LAYER_BUMPMAP,
+ LAYER_SPECULARMAP
+};
+
+class LayerTemplate
+{
+public:
+LayerTypeId m_type;
+TextureExpression m_texture;
+BlendFuncExpression m_blendFunc;
+bool m_clampToBorder;
+ShaderValue m_alphaTest;
+ShaderValue m_heightmapScale;
+
+LayerTemplate() : m_type( LAYER_NONE ), m_blendFunc( "GL_ONE", "GL_ZERO" ), m_clampToBorder( false ), m_alphaTest( "-1" ), m_heightmapScale( "0" ){
+}
+};
+
+bool parseShaderParameters( Tokeniser& tokeniser, ShaderParameters& params ){
+ Tokeniser_parseToken( tokeniser, "(" );
+ for (;; )
+ {
+ const char* param = tokeniser.getToken();
+ if ( string_equal( param, ")" ) ) {
+ break;
+ }
+ params.push_back( param );
+ const char* comma = tokeniser.getToken();
+ if ( string_equal( comma, ")" ) ) {
+ break;
+ }
+ if ( !string_equal( comma, "," ) ) {
+ Tokeniser_unexpectedError( tokeniser, comma, "," );
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ShaderTemplate::parseTemplate( Tokeniser& tokeniser ){
+ m_Name = tokeniser.getToken();
+ if ( !parseShaderParameters( tokeniser, m_params ) ) {
+ globalErrorStream() << "shader template: " << makeQuoted( m_Name.c_str() ) << ": parameter parse failed\n";
+ return false;
+ }
+
+ return parseDoom3( tokeniser );
+}
+
+bool ShaderTemplate::parseDoom3( Tokeniser& tokeniser ){
+ LayerTemplate currentLayer;
+ bool isFog = false;
+
+ // we need to read until we hit a balanced }
+ int depth = 0;
+ for (;; )
+ {
+ tokeniser.nextLine();
+ const char* token = tokeniser.getToken();
+
+ if ( token == 0 ) {
+ return false;
+ }
+
+ if ( string_equal( token, "{" ) ) {
+ ++depth;
+ continue;
+ }
+ else if ( string_equal( token, "}" ) ) {
+ --depth;
+ if ( depth < 0 ) { // error
+ return false;
+ }
+ if ( depth == 0 ) { // end of shader
+ break;
+ }
+ if ( depth == 1 ) { // end of layer
+ if ( currentLayer.m_type == LAYER_DIFFUSEMAP ) {
+ m_diffuse = currentLayer.m_texture;
+ }
+ else if ( currentLayer.m_type == LAYER_BUMPMAP ) {
+ m_bump = currentLayer.m_texture;
+ }
+ else if ( currentLayer.m_type == LAYER_SPECULARMAP ) {
+ m_specular = currentLayer.m_texture;
+ }
+ else if ( !string_empty( currentLayer.m_texture.c_str() ) ) {
+ m_layers.push_back( MapLayerTemplate(
+ currentLayer.m_texture.c_str(),
+ currentLayer.m_blendFunc,
+ currentLayer.m_clampToBorder,
+ currentLayer.m_alphaTest
+ ) );
+ }
+ currentLayer.m_type = LAYER_NONE;
+ currentLayer.m_texture = "";
+ }
+ continue;
+ }
+
+ if ( depth == 2 ) { // in layer
+ if ( string_equal_nocase( token, "blend" ) ) {
+ const char* blend = tokeniser.getToken();
+
+ if ( blend == 0 ) {
+ Tokeniser_unexpectedError( tokeniser, blend, "#blend" );
+ return false;
+ }
+
+ if ( string_equal_nocase( blend, "diffusemap" ) ) {
+ currentLayer.m_type = LAYER_DIFFUSEMAP;
+ }
+ else if ( string_equal_nocase( blend, "bumpmap" ) ) {
+ currentLayer.m_type = LAYER_BUMPMAP;
+ }
+ else if ( string_equal_nocase( blend, "specularmap" ) ) {
+ currentLayer.m_type = LAYER_SPECULARMAP;
+ }
+ else
+ {
+ currentLayer.m_blendFunc.first = blend;
+
+ const char* comma = tokeniser.getToken();
+
+ if ( comma == 0 ) {
+ Tokeniser_unexpectedError( tokeniser, comma, "#comma" );
+ return false;
+ }
+
+ if ( string_equal( comma, "," ) ) {
+ RETURN_FALSE_IF_FAIL( Tokeniser_parseString( tokeniser, currentLayer.m_blendFunc.second ) );
+ }
+ else
+ {
+ currentLayer.m_blendFunc.second = "";
+ tokeniser.ungetToken();
+ }
+ }
+ }
+ else if ( string_equal_nocase( token, "map" ) ) {
+ if ( currentLayer.m_type == LAYER_BUMPMAP ) {
+ RETURN_FALSE_IF_FAIL( Doom3Shader_parseBumpmap( tokeniser, currentLayer.m_texture, currentLayer.m_heightmapScale ) );
+ }
+ else
+ {
+ const char* map = tokeniser.getToken();
+
+ if ( map == 0 ) {
+ Tokeniser_unexpectedError( tokeniser, map, "#map" );
+ return false;
+ }
+
+ if ( string_equal( map, "makealpha" ) ) {
+ RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, "(" ) );
+ const char* texture = tokeniser.getToken();
+ if ( texture == 0 ) {
+ Tokeniser_unexpectedError( tokeniser, texture, "#texture" );
+ return false;
+ }
+ currentLayer.m_texture = texture;
+ RETURN_FALSE_IF_FAIL( Tokeniser_parseToken( tokeniser, ")" ) );
+ }
+ else
+ {
+ parseTextureName( currentLayer.m_texture, map );
+ }
+ }
+ }
+ else if ( string_equal_nocase( token, "zeroclamp" ) ) {
+ currentLayer.m_clampToBorder = true;
+ }
+#if 0
+ else if ( string_equal_nocase( token, "alphaTest" ) ) {
+ Tokeniser_getFloat( tokeniser, currentLayer.m_alphaTest );
+ }