/// <summary>
			/// 
			/// </summary>
			/// <param name="mat"></param>
			/// <param name="terrain"></param>
			/// <param name="tt"></param>
			protected void AddTechnique( Material mat, Terrain terrain, TechniqueType tt )
			{
				string ttStr = string.Empty;
				switch ( tt )
				{
					case TechniqueType.HighLod:
						ttStr += "hl";
						break;
					case TechniqueType.LowLod:
						ttStr += "ll";
						break;
					case TechniqueType.RenderCompositeMap:
						ttStr += "rc";
						break;
				}
				LogManager.Instance.Write( "AddTechique:" + ttStr, null );

				Technique tech = mat.CreateTechnique();

				//only supporting one pass
				Pass pass = tech.CreatePass();

				GpuProgramManager gmgr = GpuProgramManager.Instance;
				HighLevelGpuProgramManager hmgr = HighLevelGpuProgramManager.Instance;

				if ( this.mShaderGen == null )
				{
					bool check2x = this.mLayerNormalMappingEnabled || this.mLayerParallaxMappingEnabled;

					/* if (hmgr.IsLanguageSupported("cg") &&
                         (check2x && (gmgr.IsSyntaxSupported("fp40") || gmgr.IsSyntaxSupported("ps_2_x"))) ||
                         (gmgr.IsSyntaxSupported("ps_2_0")))
                         mShaderGen = new ShaderHelperCG();
                     else*/
					if ( hmgr.IsLanguageSupported( "hlsl" ) )
					{
						this.mShaderGen = new ShaderHelperHLSL();
					}
					else if ( hmgr.IsLanguageSupported( "glsl" ) )
					{
						this.mShaderGen = new ShaderHelperGLSL();
					}
					else
					{
						//TODO
					}
				}
				HighLevelGpuProgram vprog = this.mShaderGen.GenerateVertexProgram( this, terrain, tt );
				HighLevelGpuProgram fprog = this.mShaderGen.GenerateFragmentProgram( this, terrain, tt );

				pass.SetVertexProgram( vprog.Name );
				pass.SetFragmentProgram( fprog.Name );

				if ( tt == TechniqueType.HighLod || tt == TechniqueType.RenderCompositeMap )
				{
					//global normal map
					TextureUnitState tu = pass.CreateTextureUnitState();
					tu.SetTextureName( terrain.TerrainNormalMap.Name );
					tu.SetTextureAddressingMode( TextureAddressing.Clamp );

					//global color map
					if ( terrain.IsGlobalColorMapEnabled && IsGlobalColorMapEnabled )
					{
						tu = pass.CreateTextureUnitState( terrain.GlobalColorMap.Name );
						tu.SetTextureAddressingMode( TextureAddressing.Clamp );
					}

					//light map
					if ( IsLightMapEnabled )
					{
						tu = pass.CreateTextureUnitState( terrain.LightMap.Name );
						tu.SetTextureAddressingMode( TextureAddressing.Clamp );
					}

					//blend maps
					uint maxLayers = GetMaxLayers( terrain );

					uint numBlendTextures = Utility.Min( terrain.GetBlendTextureCount( (byte)maxLayers ),
					                                     terrain.GetBlendTextureCount() );
					uint numLayers = Utility.Min( maxLayers, (uint)terrain.LayerCount );
					for ( uint i = 0; i < numBlendTextures; ++i )
					{
						tu = pass.CreateTextureUnitState( terrain.GetBlendTextureName( (byte)i ) );
						tu.SetTextureAddressingMode( TextureAddressing.Clamp );
					}

					//layer textures
					for ( uint i = 0; i < numLayers; ++i )
					{
						//diffuse / specular
						string name = terrain.GetLayerTextureName( (byte)i, 0 );
						tu = pass.CreateTextureUnitState( terrain.GetLayerTextureName( (byte)i, 0 ) );
						//normal / height
						tu = pass.CreateTextureUnitState( terrain.GetLayerTextureName( (byte)i, 1 ) );
					}
				} //end if
				else if ( this.mCompositeMapEnabled )
				{
					// LOW_LOD textures
					// composite map
					TextureUnitState tu = pass.CreateTextureUnitState();
					tu.SetTextureName( terrain.CompositeMap.Name );
					tu.SetTextureAddressingMode( TextureAddressing.Clamp );


					// That's it!
				}
			}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <param name="source"></param>
				protected override void GenerateFpHeader( SM2Profile prof, Terrain terrain, TechniqueType tt, ref string source )
				{
					// Main header
					source += // helpers
						"float4 expand(float4 v)\n" + "{ \n" + "	return v * 2 - 1;\n" + "}\n\n\n" + "float4 main_fp(\n" +
						"float2 uv : TEXCOORD0,\n" + "float4 position : TEXCOORD1,\n";

					// UV's premultiplied, packed as xy/zw
					uint maxLayers = prof.GetMaxLayers( terrain );
					uint numBlendTextures = Utility.Min( terrain.GetBlendTextureCount( (byte)maxLayers ),
					                                     terrain.GetBlendTextureCount() );
					uint numLayers = Utility.Min( maxLayers, (uint)terrain.LayerCount );
					uint numUVSets = numLayers/2;
					if ( numLayers%2 != 0 )
					{
						++numUVSets;
					}

					uint texCoordSet = 2;
					if ( tt != TechniqueType.LowLod )
					{
						for ( uint i = 0; i < numUVSets; ++i )
						{
							source += "float4 layerUV" + i + " : TEXCOORD" + texCoordSet++ + ", \n";
						}
					}
					if ( prof.Parent.DebugLevel != 0 && tt != TechniqueType.RenderCompositeMap )
					{
						source += "float2 lodInfo : TEXCOORD" + texCoordSet++ + ", \n";
					}

					source += // Only 1 light supported in this version
						// deferred shading profile / generator later, ok? :)
						"uniform float4 ambient,\n" + "uniform float4 lightPosObjSpace,\n" + "uniform float3 lightDiffuseColor,\n" +
						"uniform float3 lightSpecularColor,\n" + "uniform float3 eyePosObjSpace,\n" + // pack scale, bias and specular
						"uniform float4 scaleBiasSpecular,\n";

					if ( tt == TechniqueType.LowLod )
					{
						// single composite map covers all the others below
						source += "uniform sampler2D compositeMap : register(s0)\n";
					}
					else
					{
						source += "uniform sampler2D globalNormal : register(s0)\n";

						uint currentSamplerIdx = 1;
						if ( terrain.IsGlobalColorMapEnabled && prof.IsGlobalColorMapEnabled )
						{
							source += ", uniform sampler2D globalColorMap : register(s" + currentSamplerIdx++ + ")\n";
						}
						if ( prof.IsLightMapEnabled )
						{
							source += ", uniform sampler2D lightMap : register(s" + currentSamplerIdx++ + ")\n";
						}
						// Blend textures - sampler definitions
						for ( uint i = 0; i < numBlendTextures; ++i )
						{
							source += ", uniform sampler2D blendTex" + i + " : register(s" + currentSamplerIdx++ + ")\n";
						}

						// Layer textures - sampler definitions & UV multipliers
						for ( uint i = 0; i < numLayers; ++i )
						{
							source += ", uniform sampler2D difftex" + i + " : register(s" + currentSamplerIdx++ + ")\n";
							source += ", uniform sampler2D normtex" + i + " : register(s" + currentSamplerIdx++ + ")\n";
						}
					}

					source += ") : COLOR\n" + "{\n" + "	float4 outputCol;\n" + "	float shadow = 1.0;\n" + // base colour
					          "	outputCol = float4(0,0,0,1);\n";

					if ( tt != TechniqueType.LowLod )
					{
						source += // global normal
							"	float3 normal = expand(tex2D(globalNormal, uv)).rgb;\n";
					}

					source += "	float3 lightDir = \n" + "		lightPosObjSpace.xyz -  (position.xyz * lightPosObjSpace.w);\n" +
					          "	float3 eyeDir = eyePosObjSpace - position.xyz;\n" + // set up accumulation areas
					          "	float3 diffuse = float3(0,0,0);\n" + "	float specular = 0;\n";

					if ( tt == TechniqueType.LowLod )
					{
						// we just do a single calculation from composite map
						source += "	float4 composite = tex2D(compositeMap, uv);\n" + "	diffuse = composite.rgb;\n";
						// TODO - specular; we'll need normals for this!
					}
					else
					{
						// set up the blend values
						for ( uint i = 0; i < numBlendTextures; ++i )
						{
							source += "	float4 blendTexVal" + i + " = tex2D(blendTex" + i + ", uv);\n";
						}

						if ( prof.IsLayerNormalMappingEnabled )
						{
							// derive the tangent space basis
							// we do this in the pixel shader because we don't have per-vertex normals
							// because of the LOD, we use a normal map
							// tangent is always +x or -z in object space depending on alignment
							switch ( terrain.Alignment )
							{
								case Alignment.Align_X_Y:
								case Alignment.Align_X_Z:
									source += "	float3 tangent = float3(1, 0, 0);\n";
									break;
								case Alignment.Align_Y_Z:
									source += "	float3 tangent = float3(0, 0, -1);\n";
									break;
							}

							source += "	float3 binormal = normalize(cross(tangent, normal));\n";
							// note, now we need to re-cross to derive tangent again because it wasn't orthonormal
							source += "	tangent = normalize(cross(normal, binormal));\n";
							// derive final matrix
							source += "	float3x3 TBN = float3x3(tangent, binormal, normal);\n";

							// set up lighting result placeholders for interpolation
							source += "	float4 litRes, litResLayer;\n";
							source += "	float3 TSlightDir, TSeyeDir, TShalfAngle, TSnormal;\n";
							if ( prof.IsLayerParallaxMappingEnabled )
							{
								source += "	float displacement;\n";
							}
							// move 
							source += "	TSlightDir = normalize(mul(TBN, lightDir));\n";
							source += "	TSeyeDir = normalize(mul(TBN, eyeDir));\n";
						}
						else
						{
							// simple per-pixel lighting with no normal mapping
							source += "	lightDir = normalize(lightDir);\n";
							source += "	eyeDir = normalize(eyeDir);\n";
							source += "	float3 halfAngle = normalize(lightDir + eyeDir);\n";
							source += "	float4 litRes = lit(dot(lightDir, normal), dot(halfAngle, normal), scaleBiasSpecular.z);\n";
						}
					}
				}