示例#1
0
        private void _initBlendMaps(Axiom.Components.Terrain.Terrain terrain)
        {
            var  blendMap0  = terrain.GetLayerBlendMap(1);
            var  blendMap1  = terrain.GetLayerBlendMap(2);
            Real minHeight0 = 70;
            Real fadeDist0  = 40;
            Real minHeight1 = 70;
            Real fadeDist1  = 15;

            var pBlend1  = blendMap1.BlendPointer;
            var blendIdx = 0;

            for (var y = 0; y < terrain.LayerBlendMapSize; y++)
            {
                for (var x = 0; x < terrain.LayerBlendMapSize; x++)
                {
                    Real tx = 0;
                    Real ty = 0;
                    blendMap0.ConvertImageToTerrainSpace(x, y, ref tx, ref ty);
                    Real height = terrain.GetHeightAtTerrainPosition(tx, ty);
                    Real val    = (height - minHeight0) / fadeDist0;
                    val = Utility.Clamp(val, 0, 1);

                    val = (height - minHeight1) / fadeDist1;
                    val = Utility.Clamp(val, 0, 1);
                    pBlend1[blendIdx++] = val;
                }
            }

            blendMap0.Dirty();
            blendMap1.Dirty();
            blendMap0.Update();
            blendMap1.Update();
        }
示例#2
0
 /// <summary>
 /// 
 /// </summary>
 /// <param name="x"></param>
 /// <param name="y"></param>
 public TerrainSlot(long x, long y)
 {
     X = x;
     Y = y;
     Instance = null;
     Def = new TerrainSlotDefinition();
 }
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <returns></returns>
				protected override HighLevelGpuProgram CreateFragmentProgram( SM2Profile prof, Terrain terrain, TechniqueType tt )
				{
					HighLevelGpuProgramManager mgr = HighLevelGpuProgramManager.Instance;
					string progName = GetFragmentProgramName( prof, terrain, tt );

					var ret = (HighLevelGpuProgram)mgr.GetByName( progName );
					if ( ret == null )
					{
						ret = mgr.CreateProgram( progName, ResourceGroupManager.DefaultResourceGroupName, "cg", GpuProgramType.Fragment );
					}
					else
					{
						ret.Unload();
					}

					if ( prof.IsLayerNormalMappingEnabled || prof.IsLayerParallaxMappingEnabled )
					{
						//ret.SetParam( "profiles", "ps_2_x fp40" );
						ret.Properties[ "profiles" ] = "ps_2_x fp40";
					}
					else
					{
						//ret.SetParam( "profiles", "ps_2_0 fp30" );
						ret.Properties[ "profiles" ] = "ps_2_0 fp30";
					}

					//ret.SetParam( "entry_point", "main_fp" );
					ret.Properties[ "entry_point" ] = "main_fp";

					return ret;
				}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <param name="gpuparams"></param>
				public virtual void UpdateFpParams( SM2Profile prof, Terrain terrain, TechniqueType tt,
				                                    GpuProgramParameters gpuparams )
				{
					gpuparams.IgnoreMissingParameters = true;
					// TODO - parameterise this?
					var scaleBiasSpecular = new Vector4( 0.03f, -0.04f, 32, 1 );
					gpuparams.SetNamedConstant( "scaleBiasSpecular", scaleBiasSpecular );
				}
				/// <summary>
				/// /
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <param name="prog"></param>
				public virtual void DefaultFpParams( SM2Profile prof, Terrain terrain, TechniqueType tt, HighLevelGpuProgram prog )
				{
					GpuProgramParameters gparams = prog.DefaultParameters;
					gparams.IgnoreMissingParameters = true;
#if true
					gparams.SetNamedAutoConstant( "ambient", GpuProgramParameters.AutoConstantType.AmbientLightColor, 0 );
					gparams.SetNamedAutoConstant( "lightPosObjSpace", GpuProgramParameters.AutoConstantType.LightPositionObjectSpace, 0 );
					gparams.SetNamedAutoConstant( "lightDiffuseColor", GpuProgramParameters.AutoConstantType.LightDiffuseColor, 0 );
					gparams.SetNamedAutoConstant( "lightSpecularColor", GpuProgramParameters.AutoConstantType.LightSpecularColor, 0 );
					gparams.SetNamedAutoConstant( "eyePosObjSpace", GpuProgramParameters.AutoConstantType.CameraPositionObjectSpace, 0 );
#warning missing auto constant type "FogColor"
					//gparams.SetNamedAutoConstant("fogColor", GpuProgramParameters.AutoConstantType.FogParams, 0);
#endif
				}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <param name="source"></param>
				protected abstract void GenerateVpFooter( SM2Profile prof, Terrain terrain, TechniqueType tt, ref string source );
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <param name="source"></param>
				protected virtual void GenerateFragmetProgramSource( SM2Profile prof, Terrain terrain, TechniqueType tt,
				                                                     ref string source )
				{
					GenerateFpHeader( prof, terrain, tt, ref source );

					if ( tt != TechniqueType.LowLod )
					{
						uint maxLayers = prof.GetMaxLayers( terrain );
						uint numLayers = Utility.Min( maxLayers, (uint)terrain.LayerCount );

						for ( uint i = 0; i < numLayers; ++i )
						{
							GenerateFpLayer( prof, terrain, tt, i, ref source );
						}
					}

					GenerateFpFooter( prof, terrain, tt, ref source );
				}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <returns></returns>
				protected virtual string GetFragmentProgramName( SM2Profile prof, Terrain terrain, TechniqueType tt )
				{
					string progName = terrain.MaterialName + "/sm2/fp";

					switch ( tt )
					{
						case TechniqueType.HighLod:
							progName += "/hlod";
							break;
						case TechniqueType.LowLod:
							progName += "/llod";
							break;
						case TechniqueType.RenderCompositeMap:
							progName += "/comp";
							break;
					}

					return progName;
				}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <returns></returns>
				public virtual HighLevelGpuProgram GenerateFragmentProgram( SM2Profile prof, Terrain terrain, TechniqueType tt )
				{
					HighLevelGpuProgram ret = CreateFragmentProgram( prof, terrain, tt );

					string sourceStr = string.Empty;
					GenerateFragmetProgramSource( prof, terrain, tt, ref sourceStr );
					ret.Source = sourceStr;
					DefaultFpParams( prof, terrain, tt, ret );
#if AXIOM_DEBUG_MODE
                    LogManager.Instance.Write(LogMessageLevel.Trivial, false, "*** Terrain Fragment Program: "
                        + ret.Name + " ***\n" + ret.Source + "\n***  ***");
#endif
					return ret;
				}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <returns></returns>
				protected override HighLevelGpuProgram CreateVertexProgram( SM2Profile prof, Terrain terrain, TechniqueType tt )
				{
					HighLevelGpuProgramManager mgr = HighLevelGpuProgramManager.Instance;
					string progName = GetVertexProgramName( prof, terrain, tt );
					switch ( tt )
					{
						case TechniqueType.HighLod:
							progName += "/hlod";
							break;
						case TechniqueType.LowLod:
							progName += "/llod";
							break;
						case TechniqueType.RenderCompositeMap:
							progName += "/comp";
							break;
					}

					var ret = (HighLevelGpuProgram)mgr.GetByName( progName );
					if ( ret == null )
					{
						ret = mgr.CreateProgram( progName, ResourceGroupManager.DefaultResourceGroupName, "glsl", GpuProgramType.Vertex );
					}
					else
					{
						ret.Unload();
					}

					return ret;
				}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <returns></returns>
				protected override HighLevelGpuProgram CreateFragmentProgram( SM2Profile prof, Terrain terrain, TechniqueType tt )
				{
					HighLevelGpuProgramManager mgr = HighLevelGpuProgramManager.Instance;
					string progName = GetFragmentProgramName( prof, terrain, tt );

					var ret = (HighLevelGpuProgram)mgr.GetByName( progName );
					if ( ret == null )
					{
						ret = mgr.CreateProgram( progName, ResourceGroupManager.DefaultResourceGroupName, "hlsl", GpuProgramType.Fragment );
					}
					else
					{
						ret.Unload();
					}
#warning very high shader version
					//ret.SetParam( "target", "ps_3_0" );
					//ret.SetParam( "entry_point", "main_fp" );
					ret.Properties[ "target" ] = "ps_3_0";
					ret.Properties[ "entry_point" ] = "main_fp";

					return ret;
				}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <param name="source"></param>
				protected override void GenerateFpFooter( SM2Profile prof, Terrain terrain, TechniqueType tt, ref string source )
				{
					if ( tt == TechniqueType.LowLod )
					{
						source += "	outputCol.rgb = diffuse;\n";
					}
					else
					{
						if ( terrain.IsGlobalColorMapEnabled && prof.IsGlobalColorMapEnabled )
						{
							// sample colour map and apply to diffuse
							source += "	diffuse *= tex2D(globalColorMap, uv).rgb;\n";
						}
						if ( prof.IsLightMapEnabled )
						{
							// sample lightmap
							source += "	shadow = tex2D(lightMap, uv).r;\n";
						}

						// diffuse lighting
						source += "	outputCol.rgb += ambient * diffuse + litRes.y * lightDiffuseColor * diffuse * shadow;\n";

						// specular default
						if ( !prof.IsLayerSpecularMappingEnabled )
						{
							source += "	specular = 1.0;\n";
						}

						if ( tt == TechniqueType.RenderCompositeMap )
						{
							// Raw specular is embedded in composite map alpha
							source += "	outputCol.a = specular * shadow;\n";
						}
						else
						{
							// Apply specular
							source += "	outputCol.rgb += litRes.z * lightSpecularColor * specular * shadow;\n";

							if ( prof.Parent.DebugLevel != 0 )
							{
								source += "	outputCol.rg += lodInfo.xy;\n";
							}
						}
					}
					// Final return
					source += "	return outputCol;\n" + "}\n";
				}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <param name="source"></param>
				protected override void GenerateVpFooter( SM2Profile prof, Terrain terrain, TechniqueType tt, ref string source )
				{
					source += "	oPos = mul(viewProjMatrix, worldPos);\n" + "	oUV = uv.xy;\n" + "}\n";
				}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <param name="layer"></param>
				/// <param name="source"></param>
				protected override void GenerateFpLayer( SM2Profile prof, Terrain terrain, TechniqueType tt, uint layer,
				                                         ref string source )
				{
					uint uvIdx = layer/2;
					string uvChannels = layer%2 != 0 ? ".zw" : ".xy";
					uint blendIdx = ( layer - 1 )/4;

					// generate UV
					source += "	float2 uv" + layer + " = layerUV" + uvIdx + uvChannels + ";\n";

					// calculate lighting here if normal mapping
					if ( prof.IsLayerNormalMappingEnabled )
					{
						if ( prof.IsLayerParallaxMappingEnabled && tt != TechniqueType.RenderCompositeMap )
						{
							// modify UV - note we have to sample an extra time
							source += "	displacement = tex2D(normtex" + layer + ", uv" + layer + ").a\n" +
							          "		* scaleBiasSpecular.x + scaleBiasSpecular.y;\n";
							source += "	uv" + layer + " += TSeyeDir.xy * displacement;\n";
						}

						// access TS normal map
						source += "	TSnormal = expand(tex2D(normtex" + layer + ", uv" + layer + ")).rgb;\n";
						source += "	TShalfAngle = normalize(TSlightDir + TSeyeDir);\n";
						source += "	litResLayer = lit(dot(TSlightDir, TSnormal), dot(TShalfAngle, TSnormal), scaleBiasSpecular.z);\n";
						if ( layer == 0 )
						{
							source += "	litRes = litResLayer;\n";
						}
						else
						{
							source += "	litRes = lerp(litRes, litResLayer, blendTexVal" + blendIdx + "." + GetChannel( layer - 1 ) + ");\n";
						}
					}

					// sample diffuse texture
					source += "	float4 diffuseSpecTex" + layer + " = tex2D(difftex" + layer + ", uv" + layer + ");\n";

					// apply to common
					if ( layer == 0 )
					{
						source += "	diffuse = diffuseSpecTex0.rgb;\n";
						if ( prof.IsLayerSpecularMappingEnabled )
						{
							source += "	specular = diffuseSpecTex0.a;\n";
						}
					}
					else
					{
						source += "	diffuse = lerp(diffuse, diffuseSpecTex" + layer + ".rgb, blendTexVal" + blendIdx + "." +
						          GetChannel( layer - 1 ) + ");\n";
						if ( prof.IsLayerSpecularMappingEnabled )
						{
							source += "	specular = lerp(specular, diffuseSpecTex" + layer + ".a, blendTexVal" + blendIdx + "." +
							          GetChannel( layer - 1 ) + ");\n";
						}
					}
				}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <param name="layer"></param>
				/// <param name="source"></param>
				protected override void GenerateVpLayer( SM2Profile prof, Terrain terrain, TechniqueType tt, uint layer,
				                                         ref string source )
				{
					// nothing to do
				}
示例#16
0
        public void DoTerrainModify(Axiom.Components.Terrain.Terrain terrain, Vector3 centrepos, Real timeElapsed)
        {
            var tsPos = Vector3.Zero;

            terrain.GetTerrainPosition(centrepos, ref tsPos);

#if !(WINDOWS_PHONE || XBOX || XBOX360 || ANDROID || IOS)
            if (Keyboard.IsKeyDown(SIS.KeyCode.Key_EQUALS) || Keyboard.IsKeyDown(SIS.KeyCode.Key_ADD) ||
                Keyboard.IsKeyDown(SIS.KeyCode.Key_MINUS) || Keyboard.IsKeyDown(SIS.KeyCode.Key_SUBTRACT))
            {
                switch (this.mode)
                {
                case Mode.EditHeight:
                {
                    // we need point coords
                    Real terrainSize = (terrain.Size - 1);
                    var  startx      = (long)((tsPos.x - this.brushSizeTerrainSpace) * terrainSize);
                    var  starty      = (long)((tsPos.y - this.brushSizeTerrainSpace) * terrainSize);
                    var  endx        = (long)((tsPos.x + this.brushSizeTerrainSpace) * terrainSize);
                    var  endy        = (long)((tsPos.y + this.brushSizeTerrainSpace) * terrainSize);
                    startx = Utility.Max(startx, 0L);
                    starty = Utility.Max(starty, 0L);
                    endx   = Utility.Min(endx, (long)terrainSize);
                    endy   = Utility.Min(endy, (long)terrainSize);
                    for (long y = starty; y <= endy; ++y)
                    {
                        for (long x = startx; x <= endx; ++x)
                        {
                            Real tsXdist = (x / terrainSize) - tsPos.x;
                            Real tsYdist = (y / terrainSize) - tsPos.y;

                            Real weight = Utility.Min((Real)1.0,
                                                      Utility.Sqrt(tsYdist * tsYdist + tsXdist * tsXdist) /
                                                      (Real)(0.5 * this.brushSizeTerrainSpace));
                            weight = 1.0 - (weight * weight);

                            var   addedHeight = weight * 250.0 * timeElapsed;
                            float newheight;
                            if (Keyboard.IsKeyDown(SIS.KeyCode.Key_EQUALS) || Keyboard.IsKeyDown(SIS.KeyCode.Key_ADD))
                            {
                                newheight = terrain.GetHeightAtPoint(x, y) + addedHeight;
                            }
                            else
                            {
                                newheight = terrain.GetHeightAtPoint(x, y) - addedHeight;
                            }
                            terrain.SetHeightAtPoint(x, y, newheight);
                        }
                    }
                    if (this.heightUpdateCountDown == 0)
                    {
                        this.heightUpdateCountDown = this.heightUpdateRate;
                    }
                }
                break;

                case Mode.EditBlend:
                {
                    var layer = terrain.GetLayerBlendMap(this.layerEdit);
                    // we need image coords
                    Real imgSize = terrain.LayerBlendMapSize;
                    var  startx  = (long)((tsPos.x - this.brushSizeTerrainSpace) * imgSize);
                    var  starty  = (long)((tsPos.y - this.brushSizeTerrainSpace) * imgSize);
                    var  endx    = (long)((tsPos.x + this.brushSizeTerrainSpace) * imgSize);
                    var  endy    = (long)((tsPos.y + this.brushSizeTerrainSpace) * imgSize);
                    startx = Utility.Max(startx, 0L);
                    starty = Utility.Max(starty, 0L);
                    endx   = Utility.Min(endx, (long)imgSize);
                    endy   = Utility.Min(endy, (long)imgSize);
                    for (var y = (int)starty; y <= endy; ++y)
                    {
                        for (var x = (int)startx; x <= endx; ++x)
                        {
                            Real tsXdist = (x / imgSize) - tsPos.x;
                            Real tsYdist = (y / imgSize) - tsPos.y;

                            Real weight = Utility.Min((Real)1.0,
                                                      Utility.Sqrt(tsYdist * tsYdist + tsXdist * tsXdist) /
                                                      (Real)(0.5 * this.brushSizeTerrainSpace));
                            weight = 1.0 - (weight * weight);

                            float paint = weight * timeElapsed;
                            var   imgY  = (int)(imgSize - y);
                            float val;
                            if (Keyboard.IsKeyDown(SIS.KeyCode.Key_EQUALS) || Keyboard.IsKeyDown(SIS.KeyCode.Key_ADD))
                            {
                                val = layer.GetBlendValue(x, imgY) + paint;
                            }
                            else
                            {
                                val = layer.GetBlendValue(x, imgY) - paint;
                            }
                            val = Utility.Clamp(val, 1.0f, 0.0f);
                            layer.SetBlendValue(x, imgY, val);
                        }
                    }

                    layer.Update();
                }
                break;

                case Mode.Normal:
                case Mode.Count:
                default:
                    break;
                }
                ;
            }
#endif
        }
			/// <summary>
			/// 
			/// </summary>
			/// <param name="terrain"></param>
			public override void RequestOption( Terrain terrain )
			{
				terrain.IsMorphRequired = true;
				terrain.NormalMapRequired = true;
				terrain.SetLightMapRequired( this.mLightMapEnabled, true );
				terrain.CompositeMapRequired = this.mCompositeMapEnabled;
			}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <returns></returns>
				protected override HighLevelGpuProgram CreateFragmentProgram( SM2Profile prof, Terrain terrain, TechniqueType tt )
				{
					HighLevelGpuProgramManager mgr = HighLevelGpuProgramManager.Instance;
					string progName = GetVertexProgramName( prof, terrain, tt );

					var ret = (HighLevelGpuProgram)mgr.GetByName( progName );

					if ( ret == null )
					{
						ret = mgr.CreateProgram( progName, ResourceGroupManager.DefaultResourceGroupName, "glsl", GpuProgramType.Fragment );
					}
					else
					{
						ret.Unload();
					}

					return ret;
				}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="mat"></param>
				/// <param name="terrain"></param>
				/// <param name="compositeMap"></param>
				public virtual void UpdateParams( SM2Profile prof, Material mat, Terrain terrain, bool compositeMap )
				{
					Pass p = mat.GetTechnique( 0 ).GetPass( 0 );
					if ( compositeMap )
					{
						UpdateVpParams( prof, terrain, TechniqueType.RenderCompositeMap, p.VertexProgramParameters );
						UpdateFpParams( prof, terrain, TechniqueType.RenderCompositeMap, p.FragmentProgramParameters );
					}
					else
					{
						//high lod
						UpdateVpParams( prof, terrain, TechniqueType.HighLod, p.VertexProgramParameters );
						UpdateFpParams( prof, terrain, TechniqueType.HighLod, p.FragmentProgramParameters );

						//low lod
						p = mat.GetTechnique( 1 ).GetPass( 0 );
						UpdateVpParams( prof, terrain, TechniqueType.LowLod, p.VertexProgramParameters );
						UpdateFpParams( prof, terrain, TechniqueType.LowLod, p.FragmentProgramParameters );
					}
				}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <param name="source"></param>
				protected override void GenerateVpFooter( SM2Profile prof, Terrain terrain, TechniqueType tt, ref string source )
				{
					//not implemted yet
				}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <returns></returns>
				protected abstract HighLevelGpuProgram CreateFragmentProgram( SM2Profile prof, Terrain terrain, TechniqueType tt );
			/// <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 abstract void GenerateFpLayer( SM2Profile prof, Terrain terrain, TechniqueType tt, uint layer,
				                                         ref string source );
			/// <summary>
			/// 
			/// </summary>
			/// <param name="terrain"></param>
			/// <returns></returns>
			public override Material Generate( Terrain terrain )
			{
				// re-use old material if exists
				Material mat = terrain._Material;
				if ( mat == null )
				{
					MaterialManager matMgr = MaterialManager.Instance;
					// it's important that the names are deterministic for a given terrain, so
					// use the terrain pointer as an ID+
					string matName = terrain.MaterialName;
					mat = (Material)matMgr.GetByName( matName );
					if ( mat == null )
					{
						mat = (Material)matMgr.Create( matName, ResourceGroupManager.DefaultResourceGroupName );
					}
				}

				// clear everything
				mat.RemoveAllTechniques();
				AddTechnique( mat, terrain, TechniqueType.HighLod );

				//LOD
				if ( this.mCompositeMapEnabled )
				{
					AddTechnique( mat, terrain, TechniqueType.LowLod );
					var lodValues = new LodValueList();
					lodValues.Add( 3000 ); //TerrainGlobalOptions.CompositeMapDistance);
					mat.SetLodLevels( lodValues );
					Technique lowLodTechnique = mat.GetTechnique( 1 );
					lowLodTechnique.LodIndex = 1;
				}
				UpdateParams( mat, terrain );
				//mat.Compile(true);
				return mat;
			}
				/// <summary>
				/// /
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <param name="prog"></param>
				public virtual void DefaultVpParams( SM2Profile prof, Terrain terrain, TechniqueType tt, HighLevelGpuProgram prog )
				{
					GpuProgramParameters gparams = prog.DefaultParameters;
					gparams.IgnoreMissingParameters = true;
					gparams.SetNamedAutoConstant( "worldMatrix", GpuProgramParameters.AutoConstantType.WorldMatrix, 0 );
					gparams.SetNamedAutoConstant( "viewProjMatrix", GpuProgramParameters.AutoConstantType.ViewProjMatrix, 0 );
					gparams.SetNamedAutoConstant( "lodMorph", GpuProgramParameters.AutoConstantType.Custom,
					                              Terrain.LOD_MORPH_CUSTOM_PARAM );
					gparams.SetNamedAutoConstant( "fogParams", GpuProgramParameters.AutoConstantType.FogParams, 0 );
				}
			/// <summary>
			/// 
			/// </summary>
			/// <param name="terrain"></param>
			/// <returns></returns>
			public override Material GenerateForCompositeMap( Terrain terrain )
			{
				// re-use old material if exists
				Material mat = terrain._CompositeMapMaterial;
				if ( mat == null )
				{
					MaterialManager matMgr = MaterialManager.Instance;

					// it's important that the names are deterministic for a given terrain, so
					// use the terrain pointer as an ID
					string matName = terrain.MaterialName + "/comp";
					mat = (Material)matMgr.GetByName( matName );
					if ( mat == null )
					{
						mat = (Material)matMgr.Create( matName, ResourceGroupManager.DefaultResourceGroupName );
					}
				}
				// clear everything
				mat.RemoveAllTechniques();

				AddTechnique( mat, terrain, TechniqueType.RenderCompositeMap );
				mat.Compile( true );
				UpdateParamsForCompositeMap( mat, terrain );

				return mat;
			}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <param name="gpuparams"></param>
				public virtual void UpdateVpParams( SM2Profile prof, Terrain terrain, TechniqueType tt,
				                                    GpuProgramParameters gpuparams )
				{
					gpuparams.IgnoreMissingParameters = true;
					uint maxLayers = prof.GetMaxLayers( terrain );
					uint numLayers = Utility.Min( maxLayers, (uint)terrain.LayerCount );
					uint numUVMul = numLayers/4;
					if ( numUVMul%4 == 0 )
					{
						++numUVMul;
					}
					for ( uint i = 0; i < numUVMul; ++i )
					{
						var uvMul = new Vector4( terrain.GetLayerUVMultiplier( (byte)( i*4 ) ),
						                         terrain.GetLayerUVMultiplier( (byte)( i*4 + 1 ) ),
						                         terrain.GetLayerUVMultiplier( (byte)( i*4 + 2 ) ),
						                         terrain.GetLayerUVMultiplier( (byte)( i*4 + 3 ) ) );
#if true
						gpuparams.SetNamedConstant( "uvMul" + i.ToString(), uvMul );
#endif
					}
				}
			public override byte GetMaxLayers( Terrain terrain )
			{
				// count the texture units free
				byte freeTextureUnits = 16;
				// lightmap
				--freeTextureUnits;
				// normalmap
				--freeTextureUnits;
				// colourmap
				if ( terrain.IsGlobalColorMapEnabled )
				{
					--freeTextureUnits;
				}
				// TODO shadowmaps

				// each layer needs 2.25 units (1xdiffusespec, 1xnormalheight, 0.25xblend)
				return (byte)( freeTextureUnits/2.25f );
			}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <returns></returns>
				protected override HighLevelGpuProgram CreateVertexProgram( SM2Profile prof, Terrain terrain, TechniqueType tt )
				{
					HighLevelGpuProgramManager mgr = HighLevelGpuProgramManager.Instance;
					string progName = GetVertexProgramName( prof, terrain, tt );
					var ret = (HighLevelGpuProgram)mgr.GetByName( progName );
					if ( ret == null )
					{
						ret = mgr.CreateProgram( progName, ResourceGroupManager.DefaultResourceGroupName, "cg", GpuProgramType.Vertex );
					}
					else
					{
						ret.Unload();
					}

					//ret.SetParam( "profiles", "vs_2_0 arbvp1" );
					//ret.SetParam( "entry_point", "main_vp" );
					ret.Properties[ "profiles" ] = "vs_2_0 arbvp1";
					ret.Properties[ "entry_point" ] = "main_vp";

					return ret;
				}
			/// <summary>
			/// 
			/// </summary>
			/// <param name="mat"></param>
			/// <param name="terrain"></param>
			public override void UpdateParams( Material mat, Terrain terrain )
			{
				this.mShaderGen.UpdateParams( this, mat, terrain, false );
			}
				/// <summary>
				/// 
				/// </summary>
				/// <param name="prof"></param>
				/// <param name="terrain"></param>
				/// <param name="tt"></param>
				/// <param name="source"></param>
				protected override void GenerateVpHeader( SM2Profile prof, Terrain terrain, TechniqueType tt, ref string source )
				{
					source += "void main_vp(\n" + "float4 pos : POSITION,\n" + "float2 uv  : TEXCOORD0,\n";
					if ( tt != TechniqueType.RenderCompositeMap )
					{
						source += "float2 delta  : TEXCOORD1,\n"; // lodDelta, lodThreshold
					}

					source += "uniform float4x4 worldMatrix,\n" + "uniform float4x4 viewProjMatrix,\n" + "uniform float2   lodMorph,\n";
					// morph amount, morph LOD target

					// uv multipliers
					uint maxLayers = prof.GetMaxLayers( terrain );
					uint numLayers = Utility.Min( maxLayers, (uint)terrain.LayerCount );
					uint numUVMutipliers = ( numLayers/4 );
					if ( numLayers%4 != 0 )
					{
						++numUVMutipliers;
					}
					for ( uint i = 0; i < numUVMutipliers; ++i )
					{
						source += "uniform float4 uvMul" + i + ", \n";
					}


					source += "out float4 oPos : POSITION,\n" + "out float2 oUV : TEXCOORD0, \n" + "out float4 oPosObj : TEXCOORD1 \n";

					// layer UV's premultiplied, packed as xy/zw
					uint numUVSets = numLayers/2;
					if ( numLayers%2 != 0 )
					{
						++numUVSets;
					}
					uint texCoordSet = 2;
					if ( tt != TechniqueType.LowLod )
					{
						for ( uint i = 0; i < numUVSets; ++i )
						{
							source += ", out float4 oUV" + i + " : TEXCOORD" + texCoordSet++ + "\n";
						}
					}

					if ( prof.Parent.DebugLevel != 0 && tt != TechniqueType.RenderCompositeMap )
					{
						source += ", out float2 lodInfo : TEXCOORD" + texCoordSet++ + "\n";
					}

					source += ")\n" + "{\n" + "	float4 worldPos = mul(worldMatrix, pos);\n" + "	oPosObj = pos;\n";

					if ( tt != TechniqueType.RenderCompositeMap )
					{
						// determine whether to apply the LOD morph to this vertex
						// we store the deltas against all vertices so we only want to apply 
						// the morph to the ones which would disappear. The target LOD which is
						// being morphed to is stored in lodMorph.y, and the LOD at which 
						// the vertex should be morphed is stored in uv.w. If we subtract
						// the former from the latter, and arrange to only morph if the
						// result is negative (it will only be -1 in fact, since after that
						// the vertex will never be indexed), we will achieve our aim.
						// sign(vertexLOD - targetLOD) == -1 is to morph
						source += "	float toMorph = -min(0, sign(delta.y - lodMorph.y));\n";
						// this will either be 1 (morph) or 0 (don't morph)
						if ( prof.Parent.DebugLevel != 0 )
						{
							// x == LOD level (-1 since value is target level, we want to display actual)
							source += "lodInfo.x = (lodMorph.y - 1) / " + terrain.NumLodLevels + ";\n";
							// y == LOD morph
							source += "lodInfo.y = toMorph * lodMorph.x;\n";
						}

						//morph
						switch ( terrain.Alignment )
						{
							case Alignment.Align_X_Y:
								break;
							case Alignment.Align_X_Z:
								source += "	worldPos.y += delta.x * toMorph * lodMorph.x;\n";
								break;
							case Alignment.Align_Y_Z:
								break;
						}
					}

					// generate UVs
					if ( tt == TechniqueType.LowLod )
					{
						//passtrough
						source += "	oUV = uv;\n";
					}
					else
					{
						for ( uint i = 0; i < numUVSets; ++i )
						{
							uint layer = i*2;
							uint uvMulIdx = layer/4;

							source += "	oUV" + i + ".xy = " + " uv.xy * uvMul" + uvMulIdx + "." + GetChannel( layer ) + ";\n";
							source += "	oUV" + i + ".zw = " + " uv.xy * uvMul" + uvMulIdx + "." + GetChannel( layer + 1 ) + ";\n";
						}
					}
				}
			/// <summary>
			/// 
			/// </summary>
			/// <param name="mat"></param>
			/// <param name="terrain"></param>
			public override void UpdateParamsForCompositeMap( Material mat, Terrain terrain )
			{
				this.mShaderGen.UpdateParams( this, mat, terrain, true );
			}