Browse Source

Merge pull request #962 from BeamNG/fix_opengl_new_terrain_blend

Fix OpenGL new terrain blend
LuisAntonRebollo 10 years ago
parent
commit
a0d5dc25cd
39 changed files with 842 additions and 86 deletions
  1. 223 47
      Engine/source/terrain/glsl/terrFeatureGLSL.cpp
  2. 9 0
      Engine/source/terrain/terrCellMaterial.cpp
  3. 4 0
      Engine/source/terrain/terrCellMaterial.h
  4. 5 1
      Engine/source/terrain/terrMaterial.cpp
  5. 7 0
      Engine/source/terrain/terrMaterial.h
  6. BIN
      Templates/Empty/game/art/terrains/Example/dirt_grass_n.png
  7. BIN
      Templates/Empty/game/art/terrains/Example/grass1_dry.jpg
  8. BIN
      Templates/Empty/game/art/terrains/Example/grass1_dry_d.png
  9. BIN
      Templates/Empty/game/art/terrains/Example/grass1_dry_n.png
  10. BIN
      Templates/Empty/game/art/terrains/Example/grass1_n.png
  11. BIN
      Templates/Empty/game/art/terrains/Example/grass2_n.png
  12. BIN
      Templates/Empty/game/art/terrains/Example/road_n.png
  13. BIN
      Templates/Empty/game/art/terrains/Example/rocks1_n.png
  14. BIN
      Templates/Empty/game/art/terrains/Example/rocktest_n.png
  15. BIN
      Templates/Empty/game/art/terrains/Example/sand_n.png
  16. BIN
      Templates/Empty/game/art/terrains/Example/snowtop_n.png
  17. BIN
      Templates/Empty/game/art/terrains/Example/stone_n.png
  18. 225 0
      Templates/Empty/game/art/terrains/materials.cs
  19. 5 3
      Templates/Empty/game/shaders/common/terrain/terrain.glsl
  20. 51 5
      Templates/Empty/game/tools/worldEditor/gui/TerrainPainterToolbar.ed.gui
  21. 82 10
      Templates/Empty/game/tools/worldEditor/gui/guiTerrainMaterialDlg.ed.gui
  22. 7 0
      Templates/Empty/game/tools/worldEditor/scripts/editors/terrainEditor.ed.cs
  23. 23 1
      Templates/Empty/game/tools/worldEditor/scripts/interfaces/terrainMaterialDlg.ed.cs
  24. BIN
      Templates/Full/game/art/terrains/Example/dirt_grass_n.png
  25. BIN
      Templates/Full/game/art/terrains/Example/grass1_dry_n.png
  26. BIN
      Templates/Full/game/art/terrains/Example/grass1_n.png
  27. BIN
      Templates/Full/game/art/terrains/Example/grass2_n.png
  28. BIN
      Templates/Full/game/art/terrains/Example/road_n.png
  29. BIN
      Templates/Full/game/art/terrains/Example/rocks1_n.png
  30. BIN
      Templates/Full/game/art/terrains/Example/rocktest_n.png
  31. BIN
      Templates/Full/game/art/terrains/Example/sand_n.png
  32. BIN
      Templates/Full/game/art/terrains/Example/snowtop_n.png
  33. BIN
      Templates/Full/game/art/terrains/Example/stone_n.png
  34. 33 0
      Templates/Full/game/art/terrains/materials.cs
  35. 5 3
      Templates/Full/game/shaders/common/terrain/terrain.glsl
  36. 51 5
      Templates/Full/game/tools/worldEditor/gui/TerrainPainterToolbar.ed.gui
  37. 82 10
      Templates/Full/game/tools/worldEditor/gui/guiTerrainMaterialDlg.ed.gui
  38. 7 0
      Templates/Full/game/tools/worldEditor/scripts/editors/terrainEditor.ed.cs
  39. 23 1
      Templates/Full/game/tools/worldEditor/scripts/interfaces/terrainMaterialDlg.ed.cs

+ 223 - 47
Engine/source/terrain/glsl/terrFeatureGLSL.cpp

@@ -379,6 +379,9 @@ void TerrainDetailMapFeatGLSL::processPix(   Vector<ShaderComponent*> &component
    const U32 detailIndex = getProcessIndex();
    Var *inTex = getVertTexCoord( "texCoord" );
 
+   // new terrain
+   bool hasNormal = fd.features.hasFeature(MFT_TerrainNormalMap, detailIndex);
+
    MultiLine *meta = new MultiLine;
 
    // We need the negative tangent space view vector
@@ -447,6 +450,95 @@ void TerrainDetailMapFeatGLSL::processPix(   Vector<ShaderComponent*> &component
    meta->addStatement( new GenOp( "   @ = calcBlend( @.x, @.xy, @, @ );\r\n", 
                                     new DecOp( detailBlend ), detailInfo, inTex, layerSize, layerSample ) );
 
+   // New terrain
+
+   Var *lerpBlend = (Var*)LangElement::find("lerpBlend");
+   if (!lerpBlend)
+   {
+	   lerpBlend = new Var;
+	   lerpBlend->setType("float");
+	   lerpBlend->setName("lerpBlend");
+	   lerpBlend->uniform = true;
+	   lerpBlend->constSortPos = cspPrimitive;
+   }
+
+
+   Var *blendDepth = (Var*)LangElement::find(String::ToString("blendDepth%d", detailIndex));
+   if (!blendDepth)
+   {
+	   blendDepth = new Var;
+	   blendDepth->setType("float");
+	   blendDepth->setName(String::ToString("blendDepth%d", detailIndex));
+	   blendDepth->uniform = true;
+	   blendDepth->constSortPos = cspPrimitive;
+   }
+
+   Var *baseColor = (Var*)LangElement::find("baseColor");
+   Var *outColor = (Var*)LangElement::find(getOutputTargetVarName(DefaultTarget));
+
+   if (!outColor)
+   {
+	   // create color var
+	   outColor = new Var;
+	   outColor->setType("float4");
+	   outColor->setName("col");
+	   meta->addStatement(new GenOp("   @;\r\n", new DecOp(outColor)));
+	   //outColor->setStructName("OUT");
+   }
+
+   Var *detailColor = (Var*)LangElement::find("detailColor");
+   if (!detailColor)
+   {
+	   detailColor = new Var;
+	   detailColor->setType("float4");
+	   detailColor->setName("detailColor");
+	   meta->addStatement(new GenOp("   @;\r\n", new DecOp(detailColor)));
+   }
+
+   // Get the detail texture.
+   Var *detailMap = new Var;
+   detailMap->setType("sampler2D");
+   detailMap->setName(String::ToString("detailMap%d", detailIndex));
+   detailMap->uniform = true;
+   detailMap->sampler = true;
+   detailMap->constNum = Var::getTexUnitNum();     // used as texture unit num here
+
+   // Get the normal map texture.
+   Var *normalMap = _getNormalMapTex();
+
+   // Issue happens somewhere here -----
+
+   // Sample the normal map.
+   //
+   // We take two normal samples and lerp between them for
+   // side projection layers... else a single sample.
+   LangElement *texOp;
+
+   // Note that we're doing the standard greyscale detail 
+   // map technique here which can darken and lighten the 
+   // diffuse texture.
+   //
+   // We take two color samples and lerp between them for
+   // side projection layers... else a single sample.
+   //
+   if (fd.features.hasFeature(MFT_TerrainSideProject, detailIndex))
+   {
+	   meta->addStatement(new GenOp("   @ = ( lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z ) * 2.0 ) - 1.0;\r\n",
+		   detailColor, detailMap, inDet, detailMap, inDet, inTex));
+
+	   texOp = new GenOp("lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z )",
+		   normalMap, inDet, normalMap, inDet, inTex);
+   }
+   else
+   {
+	   meta->addStatement(new GenOp("   @ = ( tex2D( @, @.xy ) * 2.0 ) - 1.0;\r\n",
+		   detailColor, detailMap, inDet));
+
+	   texOp = new GenOp("tex2D(@, @.xy)", normalMap, inDet);
+   }
+
+   // New terrain
+
    // Get a var and accumulate the blend amount.
    Var *blendTotal = (Var*)LangElement::find( "blendTotal" );
    if ( !blendTotal )
@@ -460,6 +552,95 @@ void TerrainDetailMapFeatGLSL::processPix(   Vector<ShaderComponent*> &component
    // Add to the blend total.
    meta->addStatement( new GenOp( "   @ = max( @, @ );\r\n", blendTotal, blendTotal, detailBlend ) );
 
+   // New terrain
+   Var *bumpNorm = (Var*)LangElement::find("bumpNormal");
+   Var *invBlend = (Var*)LangElement::find("invBlend");
+   Var *currentAlpha = (Var*)LangElement::find("currentAlpha");
+   Var *ma = (Var*)LangElement::find("ma");
+   Var *b1 = (Var*)LangElement::find("b1");
+   Var *b2 = (Var*)LangElement::find("b2");
+
+   // Get a var and accumulate the blend amount.
+   if (!currentAlpha)
+   {
+	   currentAlpha = new Var;
+	   currentAlpha->setName("currentAlpha");
+	   currentAlpha->setType("float");
+	   meta->addStatement(new GenOp("   @ = 0;\r\n", new DecOp(currentAlpha)));
+   }
+
+   if (hasNormal)
+   {
+	   // create bump normal
+	   bool bumpNormWasDefined = bumpNorm ? true : false;
+	   LangElement *bumpNormDecl = bumpNorm;
+
+	   if (!bumpNormWasDefined)
+	   {
+		   bumpNorm = new Var;
+		   bumpNorm->setName("bumpNormal");
+		   bumpNorm->setType("float4");
+		   bumpNormDecl = new DecOp(bumpNorm);
+	   }
+	   meta->addStatement(new GenOp("   @ = @;\r\n", bumpNormDecl, texOp));
+	   meta->addStatement(new GenOp("   @.a = max(@.a, 0.000001);\r\n", bumpNorm, bumpNorm));
+
+	   // -----
+
+	   // Get a var and accumulate the blend amount.
+	   if (!invBlend)
+	   {
+		   invBlend = new Var;
+		   invBlend->setName("invBlend");
+		   invBlend->setType("float");
+		   meta->addStatement(new GenOp("   @;\r\n", new DecOp(invBlend)));
+	   }
+
+	   // Get a var and accumulate the blend amount.
+	   if (!ma)
+	   {
+		   ma = new Var;
+		   ma->setName("ma");
+		   ma->setType("float");
+		   meta->addStatement(new GenOp("   @;\r\n", new DecOp(ma)));
+	   }
+
+	   // Get a var and accumulate the blend amount.
+	   if (!b1)
+	   {
+		   b1 = new Var;
+		   b1->setName("b1");
+		   b1->setType("float");
+		   meta->addStatement(new GenOp("   @;\r\n", new DecOp(b1)));
+	   }
+	   // Get a var and accumulate the blend amount.
+	   if (!b2)
+	   {
+		   b2 = new Var;
+		   b2->setName("b2");
+		   b2->setType("float");
+		   meta->addStatement(new GenOp("   @;\r\n", new DecOp(b2)));
+	   }
+
+	   meta->addStatement(new GenOp("   if( @ <= 0 ) \r\n   { \r\n", lerpBlend));
+
+	   meta->addStatement(new GenOp("      @ = 1-@;\r\n", invBlend, detailBlend));
+
+	   meta->addStatement(new GenOp("      @ = max(@.a + @, @ + @) - @;\r\n", ma, bumpNorm, detailBlend, currentAlpha, invBlend, blendDepth));
+
+	   meta->addStatement(new GenOp("      @ = max(@.a + @ - @, 0);\r\n", b1, bumpNorm, detailBlend, ma));
+
+	   meta->addStatement(new GenOp("      @ = max(@ + @ - @, 0);\r\n", b2, currentAlpha, invBlend, ma));
+
+	   meta->addStatement(new GenOp("   }\r\n"));
+   }
+   else
+   {
+	   meta->addStatement(new GenOp("   @ = max(@,@);\r\n", currentAlpha, currentAlpha, detailBlend));
+   }
+
+   // New terrain
+
    // If we had a parallax feature... then factor in the parallax
    // amount so that it fades out with the layer blending.
    if ( fd.features.hasFeature( MFT_TerrainParallaxMap, detailIndex ) )
@@ -495,58 +676,36 @@ void TerrainDetailMapFeatGLSL::processPix(   Vector<ShaderComponent*> &component
       return;
    }
 
-   Var *detailColor = (Var*)LangElement::find( "detailColor" ); 
-   if ( !detailColor )
-   {
-      detailColor = new Var;
-      detailColor->setType( "vec4" );
-      detailColor->setName( "detailColor" );
-      meta->addStatement( new GenOp( "   @;\r\n", new DecOp( detailColor ) ) );
-   }
-
-   // Get the detail texture.
-   Var *detailMap = new Var;
-   detailMap->setType( "sampler2D" );
-   detailMap->setName( String::ToString( "detailMap%d", detailIndex ) );
-   detailMap->uniform = true;
-   detailMap->sampler = true;
-   detailMap->constNum = Var::getTexUnitNum();     // used as texture unit num here
+   // used as texture unit num here
 
    // If we're using SM 3.0 then take advantage of 
    // dynamic branching to skip layers per-pixel.
-   
 
    if ( GFX->getPixelShaderVersion() >= 3.0f )
       meta->addStatement( new GenOp( "   if ( @ > 0.0f )\r\n", detailBlend ) );
 
    meta->addStatement( new GenOp( "   {\r\n" ) );
 
-   // Note that we're doing the standard greyscale detail 
-   // map technique here which can darken and lighten the 
-   // diffuse texture.
-   //
-   // We take two color samples and lerp between them for
-   // side projection layers... else a single sample.
-   //
-   if ( fd.features.hasFeature( MFT_TerrainSideProject, detailIndex ) )
-   {
-      meta->addStatement( new GenOp( "      @ = ( lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z ) * 2.0 ) - 1.0;\r\n", 
-                                                detailColor, detailMap, inDet, detailMap, inDet, inTex ) );
-   }
-   else
-   {
-      meta->addStatement( new GenOp( "      @ = ( tex2D( @, @.xy ) * 2.0 ) - 1.0;\r\n", 
-                                       detailColor, detailMap, inDet ) );
-   }
 
    meta->addStatement( new GenOp( "      @ *= @.y * @.w;\r\n",
                                     detailColor, detailInfo, inDet ) );
 
-   Var *baseColor = (Var*)LangElement::find( "baseColor" );
-   Var *outColor = (Var*)LangElement::find( "col" );
+   // New terrain
+   if (hasNormal)
+   {
+	   meta->addStatement(new GenOp("      if( @ <= 0 ) \r\n", lerpBlend));
+	   meta->addStatement(new GenOp("         @.rgb = ((@ + @).rgb * @ + @.rgb * @) / (@ + @);\r\n", outColor, baseColor, detailColor, b1, outColor, b2, b1, b2));
+	   meta->addStatement(new GenOp("      else\r\n"));
+   }
 
-   meta->addStatement( new GenOp( "      @ = lerp( @, @ + @, @ );\r\n",
-                                    outColor, outColor, baseColor, detailColor, detailBlend ) );
+   meta->addStatement(new GenOp("      @ += @ * @;\r\n", outColor, detailColor, detailBlend));
+
+   // New terrain
+   if (hasNormal)
+   {
+	   meta->addStatement(new GenOp("      if( @ <= 0 ) \r\n", lerpBlend));
+	   meta->addStatement(new GenOp("         @ = (@.a * @ + @ * @) / (@ + @);\r\n", currentAlpha, bumpNorm, b1, currentAlpha, b2, b1, b2));
+   }
 
    meta->addStatement( new GenOp( "   }\r\n" ) );
 
@@ -812,13 +971,9 @@ void TerrainMacroMapFeatGLSL::processPix(   Vector<ShaderComponent*> &componentL
    meta->addStatement( new GenOp( "      @ *= @.y * @.w;\r\n",
                                     detailColor, detailInfo, inDet ) );
 
-   Var *baseColor = (Var*)LangElement::find( "baseColor" );
    Var *outColor = (Var*)LangElement::find( "col" );
 
-   meta->addStatement( new GenOp( "      @ = lerp( @, @ + @, @ );\r\n",
-                                    outColor, outColor, outColor, detailColor, detailBlend ) );
-   //outColor, outColor, baseColor, detailColor, detailBlend ) );
-
+   meta->addStatement(new GenOp("      @ += @ * @;\r\n", outColor, detailColor, detailBlend));
    meta->addStatement( new GenOp( "   }\r\n" ) );
 
    output = meta;
@@ -920,15 +1075,36 @@ void TerrainNormalMapFeatGLSL::processPix(   Vector<ShaderComponent*> &component
       texOp = new GenOp( "tex2D(@, @.xy)", normalMap, inDet );
 
    // create bump normal
-   Var *bumpNorm = new Var;
-   bumpNorm->setName( "bumpNormal" );
-   bumpNorm->setType( "vec4" );
+   // New terrain
+   Var *bumpNorm = (Var*)LangElement::find("bumpNormal");
+   bool bumpNormWasDefined = bumpNorm ? true : false;
+   LangElement *bumpNormDecl = bumpNorm;
+
+   if (!bumpNormWasDefined)
+   {
+	   bumpNorm = new Var;
+	   bumpNorm->setName("bumpNormal");
+	   bumpNorm->setType("float4");
+	   bumpNormDecl = new DecOp(bumpNorm);
+   }
 
-   LangElement *bumpNormDecl = new DecOp( bumpNorm );
    meta->addStatement( expandNormalMap( texOp, bumpNormDecl, bumpNorm, fd ) );
 
+   // New terrain
+   Var *lerpBlend = (Var*)LangElement::find("lerpBlend");
+   AssertFatal(lerpBlend, "The lerpBlend is missing!");
+   Var *b1 = (Var*)LangElement::find("b1");
+   AssertFatal(b1, "The b1 is missing!");
+   Var *b2 = (Var*)LangElement::find("b2");
+   AssertFatal(b2, "The b2 is missing!");
+
    // Normalize is done later... 
    // Note: The reverse mul order is intentional. Affine matrix.
+
+   // New terrain
+   meta->addStatement(new GenOp("      if( @ <= 0 ) \r\n", lerpBlend));
+   meta->addStatement(new GenOp("         @ = (tMul( @.xyz, @ ).rgb * @ + @.rgb * @) / (@ + @);\r\n", gbNormal, bumpNorm, viewToTangent, b1, gbNormal, b2, b1, b2));
+   meta->addStatement(new GenOp("      else\r\n"));
    meta->addStatement( new GenOp( "      @ = lerp( @, tMul( @.xyz, @ ), min( @, @.w ) );\r\n", 
       gbNormal, gbNormal, bumpNorm, viewToTangent, detailBlend, inDet ) );
 

+ 9 - 0
Engine/source/terrain/terrCellMaterial.cpp

@@ -574,6 +574,10 @@ bool TerrainCellMaterial::_createPass( Vector<MaterialInfo*> *materials,
       matInfo->detailInfoVConst = pass->shader->getShaderConstHandle( avar( "$detailScaleAndFade%d", i ) );
       matInfo->detailInfoPConst = pass->shader->getShaderConstHandle( avar( "$detailIdStrengthParallax%d", i ) );
 
+	  // New blending
+	  matInfo->lerpBlend = pass->shader->getShaderConstHandle("$lerpBlend");
+	  matInfo->blendDepth = pass->shader->getShaderConstHandle(avar("$blendDepth%d", i));
+
       matInfo->detailTexConst = pass->shader->getShaderConstHandle( avar( "$detailMap%d", i ) );
       if ( matInfo->detailTexConst->isValid() )
       {
@@ -711,6 +715,11 @@ void TerrainCellMaterial::_updateMaterialConsts( Pass *pass )
       pass->consts->setSafe( matInfo->detailInfoVConst, detailScaleAndFade );
       pass->consts->setSafe( matInfo->detailInfoPConst, detailIdStrengthParallax );
 
+	  // New blending
+	  bool lerpBlend = Con::getBoolVariable("$Pref::Terrain::LerpBlend", true);
+	  pass->consts->setSafe(matInfo->lerpBlend, lerpBlend ? 1.0f : 0.0f);
+	  pass->consts->setSafe(matInfo->blendDepth, matInfo->mat->getBlendDepth());
+
 	// macro texture info
 
       F32 macroSize = matInfo->mat->getMacroSize();

+ 4 - 0
Engine/source/terrain/terrCellMaterial.h

@@ -80,6 +80,10 @@ protected:
       GFXShaderConstHandle *detailInfoVConst;
       GFXShaderConstHandle *detailInfoPConst;
 
+	  // New blending
+	  GFXShaderConstHandle *lerpBlend;
+	  GFXShaderConstHandle *blendDepth;
+
 	  GFXShaderConstHandle *macroInfoVConst;
       GFXShaderConstHandle *macroInfoPConst;
    };

+ 5 - 1
Engine/source/terrain/terrMaterial.cpp

@@ -65,7 +65,8 @@ TerrainMaterial::TerrainMaterial()
       mMacroSize( 200.0f ),
       mMacroStrength( 0.7f ),
       mMacroDistance( 500.0f ),
-      mParallaxScale( 0.0f )
+	  mParallaxScale(0.0f),
+	  mBlendDepth(0.4f)
 {
 }
 
@@ -97,6 +98,9 @@ void TerrainMaterial::initPersistFields()
    addField( "parallaxScale", TypeF32, Offset( mParallaxScale, TerrainMaterial ), "Used to scale the height from the normal map to give some self "
 	   "occlusion effect (aka parallax) to the terrain material" );
 
+   addField("blendDepth", TypeF32, Offset(mBlendDepth, TerrainMaterial), "Depth for blending the textures using the new blending method by Lukas Joergensen."
+	   "Higher numbers = larger blend radius.");
+
    Parent::initPersistFields();
 
    // Gotta call this at least once or it won't get created!

+ 7 - 0
Engine/source/terrain/terrMaterial.h

@@ -74,6 +74,11 @@ protected:
    ///
    F32 mParallaxScale;
 
+   /// Depth for blending the textures using the new
+   /// blending method. Higher numbers = larger blend
+   /// radius.
+   F32 mBlendDepth;
+
 public:
 
    TerrainMaterial();
@@ -119,6 +124,8 @@ public:
 
    F32 getParallaxScale() const { return mParallaxScale; }
 
+   F32 getBlendDepth() const { return mBlendDepth; }
+
 };
 
 #endif // _TERRMATERIAL_H_

BIN
Templates/Empty/game/art/terrains/Example/dirt_grass_n.png


BIN
Templates/Empty/game/art/terrains/Example/grass1_dry.jpg


BIN
Templates/Empty/game/art/terrains/Example/grass1_dry_d.png


BIN
Templates/Empty/game/art/terrains/Example/grass1_dry_n.png


BIN
Templates/Empty/game/art/terrains/Example/grass1_n.png


BIN
Templates/Empty/game/art/terrains/Example/grass2_n.png


BIN
Templates/Empty/game/art/terrains/Example/road_n.png


BIN
Templates/Empty/game/art/terrains/Example/rocks1_n.png


BIN
Templates/Empty/game/art/terrains/Example/rocktest_n.png


BIN
Templates/Empty/game/art/terrains/Example/sand_n.png


BIN
Templates/Empty/game/art/terrains/Example/snowtop_n.png


BIN
Templates/Empty/game/art/terrains/Example/stone_n.png


+ 225 - 0
Templates/Empty/game/art/terrains/materials.cs

@@ -20,3 +20,228 @@
 // IN THE SOFTWARE.
 //-----------------------------------------------------------------------------
 
+// ----------------------------------------------------------------------------
+// Sample grass
+// ----------------------------------------------------------------------------
+
+singleton Material(TerrainFX_grass1)
+{
+   mapTo = "grass1";
+   footstepSoundId = 0;
+   terrainMaterials = "1";
+   ShowDust = "1";
+   showFootprints = "1";
+   materialTag0 = "Terrain";
+   effectColor[0] = "0.42 0.42 0 1";
+   effectColor[1] = "0.42 0.42 0 1";
+   impactSoundId = "0";
+};
+
+new TerrainMaterial()
+{
+   internalName = "grass1";
+   diffuseMap = "art/terrains/Example/grass1";
+   detailMap = "art/terrains/Example/grass1_d";
+   detailSize = "10";
+   isManaged = "1";
+   detailBrightness = "1";
+   Enabled = "1";
+   diffuseSize = "200";
+   normalMap = "art/terrains/Example/grass1_n";
+   detailDistance = "1000";
+};
+
+singleton Material(TerrainFX_grass2)
+{
+   mapTo = "grass2";
+   footstepSoundId = 0;
+   terrainMaterials = "1";
+   ShowDust = "1";
+   showFootprints = "1";
+   materialTag0 = "Terrain";
+   effectColor[0] = "0.42 0.42 0 1";
+   effectColor[1] = "0.42 0.42 0 1";
+   impactSoundId = "0";
+};
+
+new TerrainMaterial()
+{
+   internalName = "grass2";
+   diffuseMap = "art/terrains/Example/grass2";
+   detailMap = "art/terrains/Example/grass2_d";
+   detailSize = "10";
+   isManaged = "1";
+   detailBrightness = "1";
+   Enabled = "1";
+   diffuseSize = "200";
+};
+
+singleton Material(TerrainFX_grass1dry)
+{
+   mapTo = "grass1_dry";
+   footstepSoundId = 0;
+   terrainMaterials = "1";
+   ShowDust = "1";
+   showFootprints = "1";
+   materialTag0 = "Terrain";
+   effectColor[0] = "0.63 0.55 0 1";
+};
+
+new TerrainMaterial()
+{
+   internalName = "grass1_dry";
+   diffuseMap = "art/terrains/Example/grass1_dry";
+   detailMap = "art/terrains/Example/grass1_dry_d";
+   detailSize = "10";
+   detailDistance = "100";
+   isManaged = "1";
+   detailBrightness = "1";
+   Enabled = "1";
+   diffuseSize = "250";
+   detailStrength = "2";
+   normalMap = "art/terrains/Example/grass1_dry_n";
+};
+
+singleton Material(TerrainFX_dirt_grass)
+{
+   mapTo = "dirt_grass";
+   footstepSoundId = 0;
+   terrainMaterials = "1";
+   ShowDust = "1";
+   showFootprints = "1";
+   materialTag0 = "Terrain";
+   effectColor[0] = "0.63 0.55 0 1";
+   diffuseMap = "art/terrains/Example/dirt_grass";
+   diffuseSize = "200";
+   normalMap = "art/terrains/Example/dirt_grass_n";
+   detailMap = "art/terrains/Example/dirt_grass_d";
+   detailDistance = "100";
+   internalName = "dirt_grass";
+   isManaged = "1";
+   detailBrightness = "1";
+   enabled = "1";
+};
+
+new TerrainMaterial()
+{
+   internalName = "dirt_grass";
+   diffuseMap = "art/terrains/Example/dirt_grass";
+   detailMap = "art/terrains/Example/dirt_grass_d";
+   detailSize = "5";
+   detailDistance = "100";
+   isManaged = "1";
+   detailBrightness = "1";
+   Enabled = "1";
+   diffuseSize = "200";
+};
+
+// ----------------------------------------------------------------------------
+// Sample rock
+// ----------------------------------------------------------------------------
+
+singleton Material(TerrainFX_rocktest)
+{
+   mapTo = "rocktest";
+   footstepSoundId = "1";
+   terrainMaterials = "1";
+   ShowDust = "1";
+   showFootprints = "1";
+   materialTag0 = "Terrain";
+   impactSoundId = "1";
+   effectColor[0] = "0.25 0.25 0.25 1";
+   effectColor[1] = "0.25 0.25 0.25 0";
+   diffuseMap = "art/terrains/Example/rocktest";
+   diffuseSize = "400";
+   normalMap = "art/terrains/Example/rocktest_n";
+   detailMap = "art/terrains/Example/rocktest_d";
+   detailSize = "10";
+   detailDistance = "100";
+   internalName = "rocktest";
+   isManaged = "1";
+   detailBrightness = "1";
+   enabled = "1";
+};
+
+new TerrainMaterial()
+{
+   internalName = "rocktest";
+   diffuseMap = "art/terrains/Example/rocktest";
+   detailMap = "art/terrains/Example/rocktest_d";
+   detailSize = "10";
+   detailDistance = "100";
+   isManaged = "1";
+   detailBrightness = "1";
+   Enabled = "1";
+   diffuseSize = "400";
+};
+
+// ----------------------------------------------------------------------------
+// Sample rock
+// ----------------------------------------------------------------------------
+
+singleton Material(TerrainFX_stone)
+{
+   mapTo = "stone";
+   footstepSoundId = "1";
+   terrainMaterials = "1";
+   ShowDust = "1";
+   showFootprints = "1";
+   materialTag0 = "Terrain";
+   impactSoundId = "1";
+   effectColor[0] = "0.25 0.25 0.25 1";
+   effectColor[1] = "0.25 0.25 0.25 0";
+   diffuseMap = "art/terrains/Example/stone";
+   diffuseSize = "400";
+   normalMap = "art/terrains/Example/stone_n";
+   detailMap = "art/terrains/Example/stone_d";
+   detailSize = "10";
+   detailDistance = "100";
+   internalName = "stone";
+   isManaged = "1";
+   detailBrightness = "1";
+   enabled = "1";
+};
+
+new TerrainMaterial()
+{
+   internalName = "stone";
+   diffuseMap = "art/terrains/Example/stone";
+   detailMap = "art/terrains/Example/stone_d";
+   detailSize = "10";
+   detailDistance = "100";
+   isManaged = "1";
+   detailBrightness = "1";
+   Enabled = "1";
+   diffuseSize = "400";
+   useSideProjection = "0";
+};
+// ----------------------------------------------------------------------------
+// Sample sand
+// ----------------------------------------------------------------------------
+
+singleton Material(TerrainFX_sand)
+{
+   mapTo = "sand";
+   footstepSoundId = "3";
+   terrainMaterials = "1";
+   ShowDust = "1";
+   showFootprints = "1";
+   materialTag0 = "Terrain";
+   specularPower[0] = "1";
+   effectColor[0] = "0.84 0.71 0.5 1";
+   effectColor[1] = "0.84 0.71 0.5 0.349";
+};
+
+new TerrainMaterial()
+{
+   internalName = "sand";
+   diffuseMap = "art/terrains/Example/sand";
+   detailMap = "art/terrains/Example/sand_d";
+   detailSize = "10";
+   detailDistance = "100";
+   isManaged = "1";
+   detailBrightness = "1";
+   Enabled = "1";
+   diffuseSize = "200";
+   normalMap = "art/terrains/Example/sand_n";
+};

+ 5 - 3
Templates/Empty/game/shaders/common/terrain/terrain.glsl

@@ -32,10 +32,12 @@ float calcBlend( float texId, vec2 layerCoord, float layerSize, vec4 layerSample
    vec4 diff = clamp( abs( layerSample - texId ), 0.0, 1.0 );
    float noBlend = float(any( bvec4(1 - diff) ));
 
-   // Use step to see if any of the layer samples 
    // match the current texture id.
-   vec4 factors = step( texId, layerSample );
-
+   vec4 factors = vec4(0);
+   for(int i = 0; i < 4; i++)
+      if(layerSample[i] == texId)
+         factors[i] = 1;
+ 
    // This is a custom bilinear filter.
 
    vec2 uv = layerCoord * layerSize;

+ 51 - 5
Templates/Empty/game/tools/worldEditor/gui/TerrainPainterToolbar.ed.gui

@@ -210,7 +210,7 @@
       new GuiBitmapCtrl() {
          Enabled = "1";
          Profile = "ToolsGuiDefaultProfile";
-         position = "270 3";
+         position = "250 3";
          Extent = "2 26";
          MinExtent = "1 1";
          bitmap = "tools/gui/images/separator-h.png";
@@ -222,7 +222,7 @@
          Profile = "ToolsGuiDefaultProfile";
          HorizSizing = "right";
          VertSizing = "bottom";
-         Position = "262 5";
+         Position = "242 5";
          Extent = "256 50";
          MinExtent = "8 2";
          canSave = "1";
@@ -370,7 +370,7 @@
       new GuiBitmapCtrl() {
          Enabled = "1";
          Profile = "ToolsGuiDefaultProfile";
-         position = "525 3";
+         position = "495 3";
          Extent = "2 26";
          MinExtent = "1 1";
          bitmap = "tools/gui/images/separator-h.png";
@@ -382,7 +382,7 @@
          Profile = "ToolsGuiTransparentProfile";
          HorizSizing = "right";
          VertSizing = "bottom";
-         position = "540 5";
+         position = "510 5";
          Extent = "120 50";
          MinExtent = "8 2";
          canSave = "1";
@@ -454,6 +454,53 @@
             bitmap = "tools/gui/images/dropslider";
          }; 
       };
+	  
+      new GuiBitmapCtrl() {
+         Enabled = "1";
+         Profile = "ToolsGuiDefaultProfile";
+         position = "618 3";
+         Extent = "2 26";
+         MinExtent = "1 1";
+         bitmap = "tools/gui/images/separator-h.png";
+      };
+	  
+      new GuiControl(LerpBlendCheckButtonContainer,EditorGuiGroup) {
+            position = "628 5";
+            extent = "70 50";
+            minExtent = "8 2";
+            horizSizing = "right";
+            vertSizing = "bottom";
+            profile = "ToolsGuiTransparentProfile";
+            visible = "1";
+            active = "1";
+            tooltipProfile = "GuiToolTipProfile";
+            hovertime = "1000";
+            isContainer = "1";
+            canSave = "1";
+            canSaveDynamicFields = "0";
+
+            new GuiCheckBoxCtrl() {
+               text = " LerpBlend";
+               groupNum = "-1";
+               buttonType = "ToggleButton";
+               useMouseEvents = "0";
+               position = "0 2";
+               extent = "140 18";
+               minExtent = "8 2";
+               horizSizing = "right";
+               vertSizing = "bottom";
+               profile = "GuiCheckBoxProfile";
+               visible = "1";
+               active = "1";
+               command = "ETerrainEditor.toggleBlendType($ThisControl);";
+               tooltipProfile = "GuiToolTipProfile";
+               hovertime = "1000";
+               isContainer = "0";
+               internalName = "LerpBlendCheckBox";
+               canSave = "1";
+               canSaveDynamicFields = "0";
+            };
+         };
    };
 };
 //--- OBJECT WRITE END ---
@@ -634,4 +681,3 @@ new GuiMouseEventCtrl(PaintBrushSoftnessSliderCtrlContainer,EditorGuiGroup) {
       value = "0";
    };
 };
-

+ 82 - 10
Templates/Empty/game/tools/worldEditor/gui/guiTerrainMaterialDlg.ed.gui

@@ -20,8 +20,8 @@
       HorizSizing = "center";
       VertSizing = "center";
       position = "221 151";
-      Extent = "394 432";
-      MinExtent = "358 432";
+      Extent = "394 452";
+      MinExtent = "358 452";
       canSave = "1";
       Visible = "1";
       tooltipprofile = "ToolsGuiToolTipProfile";
@@ -106,7 +106,7 @@
          HorizSizing = "left";
          VertSizing = "height";
          position = "202 26";
-         Extent = "185 363";
+         Extent = "185 383";
          MinExtent = "8 2";
          canSave = "1";
          Visible = "1";
@@ -439,7 +439,7 @@
             HorizSizing = "width";
             VertSizing = "bottom";
             position = "6 295";
-            Extent = "185 50";
+            Extent = "185 80";
             MinExtent = "8 2";
             canSave = "1";
             Visible = "1";
@@ -621,6 +621,78 @@
                sinkAllKeyEvents = "0";
                passwordMask = "*";
             };
+            new GuiSliderCtrl(TerrainMaterialDlgBlendDepthSlider) {
+               range = "0.01 0.99";
+               ticks = "0";
+               snap = "0";
+               value = "0.5";
+               position = "39 61";
+               extent = "70 14";
+               minExtent = "8 2";
+               horizSizing = "right";
+               vertSizing = "bottom";
+               profile = "GuiSliderProfile";
+               visible = "1";
+               active = "1";
+               tooltipProfile = "GuiToolTipProfile";
+               hovertime = "1000";
+               isContainer = "0";
+               internalName = "blendDepthSliderCtrl";
+               canSave = "1";
+               canSaveDynamicFields = "0";
+            };
+            new GuiTextCtrl() {
+               text = "Blend Depth";
+               maxLength = "1024";
+               margin = "0 0 0 0";
+               padding = "0 0 0 0";
+               anchorTop = "1";
+               anchorBottom = "0";
+               anchorLeft = "1";
+               anchorRight = "0";
+               position = "115 61";
+               extent = "58 15";
+               minExtent = "8 2";
+               horizSizing = "right";
+               vertSizing = "bottom";
+               profile = "GuiTextProfile";
+               visible = "1";
+               active = "1";
+               tooltipProfile = "GuiToolTipProfile";
+               hovertime = "1000";
+               isContainer = "1";
+               canSave = "1";
+               canSaveDynamicFields = "0";
+            };
+            new GuiTextEditCtrl(TerrainMaterialDlgBlendDepthTextEdit) {
+               historySize = "0";
+               tabComplete = "0";
+               sinkAllKeyEvents = "0";
+               password = "0";
+               passwordMask = "*";
+               text = "0.3";
+               maxLength = "1024";
+               margin = "0 0 0 0";
+               padding = "0 0 0 0";
+               anchorTop = "0";
+               anchorBottom = "0";
+               anchorLeft = "0";
+               anchorRight = "0";
+               position = "1 59";
+               extent = "35 18";
+               minExtent = "8 2";
+               horizSizing = "right";
+               vertSizing = "bottom";
+               profile = "ToolsGuiTextEditProfile";
+               visible = "1";
+               active = "1";
+               tooltipProfile = "ToolsGuiToolTipProfile";
+               hovertime = "1000";
+               isContainer = "0";
+               internalName = "blendDepthTextEditCtrl";
+               canSave = "1";
+               canSaveDynamicFields = "0";
+            };
          };
 
          new GuiBitmapCtrl() {
@@ -1260,7 +1332,7 @@
          HorizSizing = "width";
          VertSizing = "height";
          position = "6 42";
-         Extent = "189 373";
+         Extent = "189 393";
          MinExtent = "8 2";
          canSave = "1";
          Visible = "1";
@@ -1274,7 +1346,7 @@
             HorizSizing = "width";
             VertSizing = "height";
             position = "0 0";
-            Extent = "189 374";
+            Extent = "189 394";
             MinExtent = "8 2";
             canSave = "1";
             Visible = "1";
@@ -1333,7 +1405,7 @@
          Profile = "ToolsGuiButtonProfile";
          HorizSizing = "left";
          VertSizing = "top";
-         position = "202 394";
+         position = "202 414";
          Extent = "98 22";
          MinExtent = "8 2";
          canSave = "1";
@@ -1352,7 +1424,7 @@
          Profile = "ToolsGuiButtonProfile";
          HorizSizing = "left";
          VertSizing = "top";
-         position = "307 394";
+         position = "307 414";
          Extent = "80 22";
          MinExtent = "8 2";
          canSave = "1";
@@ -1372,7 +1444,7 @@
          HorizSizing = "left";
          VertSizing = "height";
          position = "199 23";
-         Extent = "190 267";
+         Extent = "190 287";
          isContainer = true;
          Visible = false;
          bitmap = "tools/gui/images/inactive-overlay";
@@ -1389,4 +1461,4 @@
       };
    };
 };
-//--- OBJECT WRITE END ---
+//--- OBJECT WRITE END ---

+ 7 - 0
Templates/Empty/game/tools/worldEditor/scripts/editors/terrainEditor.ed.cs

@@ -222,6 +222,8 @@ function EPainter::setup( %this, %matIndex )
    ETerrainEditor.setAction( ETerrainEditor.currentAction );
    EditorGuiStatusBar.setInfo(ETerrainEditor.currentActionDesc);
    ETerrainEditor.renderVertexSelection = true;
+   
+   EWTerrainPainterToolbar-->LerpBlendCheckBox.setValue($Pref::Terrain::LerpBlend);
 }
 
 function onNeedRelight()
@@ -257,6 +259,11 @@ function TerrainEditor::toggleBrushType( %this, %brush )
    %this.setBrushType( %brush.internalName );
 }
 
+function TerrainEditor::toggleBlendType( %this, %check )
+{
+   $Pref::Terrain::LerpBlend = %check.getValue();
+}
+
 function TerrainEditor::offsetBrush(%this, %x, %y)
 {
    %curPos = %this.getBrushPos();

+ 23 - 1
Templates/Empty/game/tools/worldEditor/scripts/interfaces/terrainMaterialDlg.ed.cs

@@ -401,6 +401,9 @@ function TerrainMaterialDlg::setActiveMaterial( %this, %mat )
       %this-->detDistanceCtrl.setText( %mat.detailDistance );      
       %this-->sideProjectionCtrl.setValue( %mat.useSideProjection );
       %this-->parallaxScaleCtrl.setText( %mat.parallaxScale );
+      %blendDepth = mFloor(%mat.blendDepth * 1000)/1000;
+      %this-->blendDepthTextEditCtrl.setText( %blendDepth );
+      %this-->blendDepthSliderCtrl.setValue( %mat.blendDepth );
 
       %this-->macroSizeCtrl.setText( %mat.macroSize );
       %this-->macroStrengthCtrl.setText( %mat.macroStrength );
@@ -454,6 +457,7 @@ function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat )
    %detailDistance = %this-->detDistanceCtrl.getText();   
    %useSideProjection = %this-->sideProjectionCtrl.getValue();   
    %parallaxScale = %this-->parallaxScaleCtrl.getText();
+   %blendDepth = %this-->blendDepthTextEditCtrl.getText();
 
    %macroSize = %this-->macroSizeCtrl.getText();      
    %macroStrength = %this-->macroStrengthCtrl.getText();
@@ -475,7 +479,8 @@ function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat )
          %mat.macroSize == %macroSize &&
          %mat.macroStrength == %macroStrength &&
          %mat.macroDistance == %macroDistance &&         
-         %mat.parallaxScale == %parallaxScale )               
+         %mat.parallaxScale == %parallaxScale &&
+         %mat.blendDepth == %blendDepth)               
       return;
       
    // Make sure the material name is unique.
@@ -509,6 +514,7 @@ function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat )
    %mat.macroDistance = %macroDistance;    
    %mat.useSideProjection = %useSideProjection;
    %mat.parallaxScale = %parallaxScale;
+   %mat.blendDepth = %blendDepth;
    
    // Mark the material as dirty and needing saving.
    
@@ -554,6 +560,7 @@ function TerrainMaterialDlg::snapshotMaterials( %this )
          macroDistance = %mat.macroDistance;
          useSideProjection = %mat.useSideProjection;
          parallaxScale = %mat.parallaxScale;
+         blendDepth = %mat.blendDepth;
       };
    }
 }
@@ -588,6 +595,7 @@ function TerrainMaterialDlg::restoreMaterials( %this )
       %mat.macroDistance = %obj.macroDistance;
       %mat.useSideProjection = %obj.useSideProjection;
       %mat.parallaxScale = %obj.parallaxScale;
+      %mat.blendDepth = %obj.blendDepth;
    }
 }
 
@@ -623,3 +631,17 @@ function TerrainMaterialDlg::_selectTextureFileDialog( %this, %defaultFileName )
       
    return %file;
 }
+
+function TerrainMaterialDlgBlendDepthSlider::onMouseDragged(%this)
+{
+   %value = mFloor(%this.value * 1000)/1000;
+   TerrainMaterialDlgBlendDepthTextEdit.setText(%value);
+   TerrainMaterialDlg.activeMat.blendDepth = %this.value;
+   
+}
+
+function TerrainMaterialDlgBlendDepthTextEdit::onValidate(%this)
+{
+   TerrainMaterialDlgBlendDepthSlider.setValue(%this.getText());
+   TerrainMaterialDlg.activeMat.blendDepth = %this.getText();
+}

BIN
Templates/Full/game/art/terrains/Example/dirt_grass_n.png


BIN
Templates/Full/game/art/terrains/Example/grass1_dry_n.png


BIN
Templates/Full/game/art/terrains/Example/grass1_n.png


BIN
Templates/Full/game/art/terrains/Example/grass2_n.png


BIN
Templates/Full/game/art/terrains/Example/road_n.png


BIN
Templates/Full/game/art/terrains/Example/rocks1_n.png


BIN
Templates/Full/game/art/terrains/Example/rocktest_n.png


BIN
Templates/Full/game/art/terrains/Example/sand_n.png


BIN
Templates/Full/game/art/terrains/Example/snowtop_n.png


BIN
Templates/Full/game/art/terrains/Example/stone_n.png


+ 33 - 0
Templates/Full/game/art/terrains/materials.cs

@@ -47,6 +47,8 @@ new TerrainMaterial()
    detailBrightness = "1";
    Enabled = "1";
    diffuseSize = "200";
+   normalMap = "art/terrains/Example/grass1_n";
+   detailDistance = "1000";
 };
 
 singleton Material(TerrainFX_grass2)
@@ -97,6 +99,7 @@ new TerrainMaterial()
    Enabled = "1";
    diffuseSize = "250";
    detailStrength = "2";
+   normalMap = "art/terrains/Example/grass1_dry_n";
 };
 
 singleton Material(TerrainFX_dirt_grass)
@@ -108,6 +111,15 @@ singleton Material(TerrainFX_dirt_grass)
    showFootprints = "1";
    materialTag0 = "Terrain";
    effectColor[0] = "0.63 0.55 0 1";
+   diffuseMap = "art/terrains/Example/dirt_grass";
+   diffuseSize = "200";
+   normalMap = "art/terrains/Example/dirt_grass_n";
+   detailMap = "art/terrains/Example/dirt_grass_d";
+   detailDistance = "100";
+   internalName = "dirt_grass";
+   isManaged = "1";
+   detailBrightness = "1";
+   enabled = "1";
 };
 
 new TerrainMaterial()
@@ -138,6 +150,16 @@ singleton Material(TerrainFX_rocktest)
    impactSoundId = "1";
    effectColor[0] = "0.25 0.25 0.25 1";
    effectColor[1] = "0.25 0.25 0.25 0";
+   diffuseMap = "art/terrains/Example/rocktest";
+   diffuseSize = "400";
+   normalMap = "art/terrains/Example/rocktest_n";
+   detailMap = "art/terrains/Example/rocktest_d";
+   detailSize = "10";
+   detailDistance = "100";
+   internalName = "rocktest";
+   isManaged = "1";
+   detailBrightness = "1";
+   enabled = "1";
 };
 
 new TerrainMaterial()
@@ -168,6 +190,16 @@ singleton Material(TerrainFX_stone)
    impactSoundId = "1";
    effectColor[0] = "0.25 0.25 0.25 1";
    effectColor[1] = "0.25 0.25 0.25 0";
+   diffuseMap = "art/terrains/Example/stone";
+   diffuseSize = "400";
+   normalMap = "art/terrains/Example/stone_n";
+   detailMap = "art/terrains/Example/stone_d";
+   detailSize = "10";
+   detailDistance = "100";
+   internalName = "stone";
+   isManaged = "1";
+   detailBrightness = "1";
+   enabled = "1";
 };
 
 new TerrainMaterial()
@@ -211,4 +243,5 @@ new TerrainMaterial()
    detailBrightness = "1";
    Enabled = "1";
    diffuseSize = "200";
+   normalMap = "art/terrains/Example/sand_n";
 };

+ 5 - 3
Templates/Full/game/shaders/common/terrain/terrain.glsl

@@ -32,10 +32,12 @@ float calcBlend( float texId, vec2 layerCoord, float layerSize, vec4 layerSample
    vec4 diff = clamp( abs( layerSample - texId ), 0.0, 1.0 );
    float noBlend = float(any( bvec4(1 - diff) ));
 
-   // Use step to see if any of the layer samples 
    // match the current texture id.
-   vec4 factors = step( texId, layerSample );
-
+   vec4 factors = vec4(0);
+   for(int i = 0; i < 4; i++)
+      if(layerSample[i] == texId)
+         factors[i] = 1;
+ 
    // This is a custom bilinear filter.
 
    vec2 uv = layerCoord * layerSize;

+ 51 - 5
Templates/Full/game/tools/worldEditor/gui/TerrainPainterToolbar.ed.gui

@@ -210,7 +210,7 @@
       new GuiBitmapCtrl() {
          Enabled = "1";
          Profile = "ToolsGuiDefaultProfile";
-         position = "270 3";
+         position = "250 3";
          Extent = "2 26";
          MinExtent = "1 1";
          bitmap = "tools/gui/images/separator-h.png";
@@ -222,7 +222,7 @@
          Profile = "ToolsGuiDefaultProfile";
          HorizSizing = "right";
          VertSizing = "bottom";
-         Position = "262 5";
+         Position = "242 5";
          Extent = "256 50";
          MinExtent = "8 2";
          canSave = "1";
@@ -370,7 +370,7 @@
       new GuiBitmapCtrl() {
          Enabled = "1";
          Profile = "ToolsGuiDefaultProfile";
-         position = "525 3";
+         position = "495 3";
          Extent = "2 26";
          MinExtent = "1 1";
          bitmap = "tools/gui/images/separator-h.png";
@@ -382,7 +382,7 @@
          Profile = "ToolsGuiTransparentProfile";
          HorizSizing = "right";
          VertSizing = "bottom";
-         position = "540 5";
+         position = "510 5";
          Extent = "120 50";
          MinExtent = "8 2";
          canSave = "1";
@@ -454,6 +454,53 @@
             bitmap = "tools/gui/images/dropslider";
          }; 
       };
+	  
+      new GuiBitmapCtrl() {
+         Enabled = "1";
+         Profile = "ToolsGuiDefaultProfile";
+         position = "618 3";
+         Extent = "2 26";
+         MinExtent = "1 1";
+         bitmap = "tools/gui/images/separator-h.png";
+      };
+	  
+      new GuiControl(LerpBlendCheckButtonContainer,EditorGuiGroup) {
+            position = "628 5";
+            extent = "70 50";
+            minExtent = "8 2";
+            horizSizing = "right";
+            vertSizing = "bottom";
+            profile = "ToolsGuiTransparentProfile";
+            visible = "1";
+            active = "1";
+            tooltipProfile = "GuiToolTipProfile";
+            hovertime = "1000";
+            isContainer = "1";
+            canSave = "1";
+            canSaveDynamicFields = "0";
+
+            new GuiCheckBoxCtrl() {
+               text = " LerpBlend";
+               groupNum = "-1";
+               buttonType = "ToggleButton";
+               useMouseEvents = "0";
+               position = "0 2";
+               extent = "140 18";
+               minExtent = "8 2";
+               horizSizing = "right";
+               vertSizing = "bottom";
+               profile = "GuiCheckBoxProfile";
+               visible = "1";
+               active = "1";
+               command = "ETerrainEditor.toggleBlendType($ThisControl);";
+               tooltipProfile = "GuiToolTipProfile";
+               hovertime = "1000";
+               isContainer = "0";
+               internalName = "LerpBlendCheckBox";
+               canSave = "1";
+               canSaveDynamicFields = "0";
+            };
+         };
    };
 };
 //--- OBJECT WRITE END ---
@@ -634,4 +681,3 @@ new GuiMouseEventCtrl(PaintBrushSoftnessSliderCtrlContainer,EditorGuiGroup) {
       value = "0";
    };
 };
-

+ 82 - 10
Templates/Full/game/tools/worldEditor/gui/guiTerrainMaterialDlg.ed.gui

@@ -20,8 +20,8 @@
       HorizSizing = "center";
       VertSizing = "center";
       position = "221 151";
-      Extent = "394 432";
-      MinExtent = "358 432";
+      Extent = "394 452";
+      MinExtent = "358 452";
       canSave = "1";
       Visible = "1";
       tooltipprofile = "ToolsGuiToolTipProfile";
@@ -106,7 +106,7 @@
          HorizSizing = "left";
          VertSizing = "height";
          position = "202 26";
-         Extent = "185 363";
+         Extent = "185 383";
          MinExtent = "8 2";
          canSave = "1";
          Visible = "1";
@@ -439,7 +439,7 @@
             HorizSizing = "width";
             VertSizing = "bottom";
             position = "6 295";
-            Extent = "185 50";
+            Extent = "185 80";
             MinExtent = "8 2";
             canSave = "1";
             Visible = "1";
@@ -621,6 +621,78 @@
                sinkAllKeyEvents = "0";
                passwordMask = "*";
             };
+            new GuiSliderCtrl(TerrainMaterialDlgBlendDepthSlider) {
+               range = "0.01 0.99";
+               ticks = "0";
+               snap = "0";
+               value = "0.5";
+               position = "39 61";
+               extent = "70 14";
+               minExtent = "8 2";
+               horizSizing = "right";
+               vertSizing = "bottom";
+               profile = "GuiSliderProfile";
+               visible = "1";
+               active = "1";
+               tooltipProfile = "GuiToolTipProfile";
+               hovertime = "1000";
+               isContainer = "0";
+               internalName = "blendDepthSliderCtrl";
+               canSave = "1";
+               canSaveDynamicFields = "0";
+            };
+            new GuiTextCtrl() {
+               text = "Blend Depth";
+               maxLength = "1024";
+               margin = "0 0 0 0";
+               padding = "0 0 0 0";
+               anchorTop = "1";
+               anchorBottom = "0";
+               anchorLeft = "1";
+               anchorRight = "0";
+               position = "115 61";
+               extent = "58 15";
+               minExtent = "8 2";
+               horizSizing = "right";
+               vertSizing = "bottom";
+               profile = "GuiTextProfile";
+               visible = "1";
+               active = "1";
+               tooltipProfile = "GuiToolTipProfile";
+               hovertime = "1000";
+               isContainer = "1";
+               canSave = "1";
+               canSaveDynamicFields = "0";
+            };
+            new GuiTextEditCtrl(TerrainMaterialDlgBlendDepthTextEdit) {
+               historySize = "0";
+               tabComplete = "0";
+               sinkAllKeyEvents = "0";
+               password = "0";
+               passwordMask = "*";
+               text = "0.3";
+               maxLength = "1024";
+               margin = "0 0 0 0";
+               padding = "0 0 0 0";
+               anchorTop = "0";
+               anchorBottom = "0";
+               anchorLeft = "0";
+               anchorRight = "0";
+               position = "1 59";
+               extent = "35 18";
+               minExtent = "8 2";
+               horizSizing = "right";
+               vertSizing = "bottom";
+               profile = "ToolsGuiTextEditProfile";
+               visible = "1";
+               active = "1";
+               tooltipProfile = "ToolsGuiToolTipProfile";
+               hovertime = "1000";
+               isContainer = "0";
+               internalName = "blendDepthTextEditCtrl";
+               canSave = "1";
+               canSaveDynamicFields = "0";
+            };
          };
 
          new GuiBitmapCtrl() {
@@ -1260,7 +1332,7 @@
          HorizSizing = "width";
          VertSizing = "height";
          position = "6 42";
-         Extent = "189 373";
+         Extent = "189 393";
          MinExtent = "8 2";
          canSave = "1";
          Visible = "1";
@@ -1274,7 +1346,7 @@
             HorizSizing = "width";
             VertSizing = "height";
             position = "0 0";
-            Extent = "189 374";
+            Extent = "189 394";
             MinExtent = "8 2";
             canSave = "1";
             Visible = "1";
@@ -1333,7 +1405,7 @@
          Profile = "ToolsGuiButtonProfile";
          HorizSizing = "left";
          VertSizing = "top";
-         position = "202 394";
+         position = "202 414";
          Extent = "98 22";
          MinExtent = "8 2";
          canSave = "1";
@@ -1352,7 +1424,7 @@
          Profile = "ToolsGuiButtonProfile";
          HorizSizing = "left";
          VertSizing = "top";
-         position = "307 394";
+         position = "307 414";
          Extent = "80 22";
          MinExtent = "8 2";
          canSave = "1";
@@ -1372,7 +1444,7 @@
          HorizSizing = "left";
          VertSizing = "height";
          position = "199 23";
-         Extent = "190 267";
+         Extent = "190 287";
          isContainer = true;
          Visible = false;
          bitmap = "tools/gui/images/inactive-overlay";
@@ -1389,4 +1461,4 @@
       };
    };
 };
-//--- OBJECT WRITE END ---
+//--- OBJECT WRITE END ---

+ 7 - 0
Templates/Full/game/tools/worldEditor/scripts/editors/terrainEditor.ed.cs

@@ -222,6 +222,8 @@ function EPainter::setup( %this, %matIndex )
    ETerrainEditor.setAction( ETerrainEditor.currentAction );
    EditorGuiStatusBar.setInfo(ETerrainEditor.currentActionDesc);
    ETerrainEditor.renderVertexSelection = true;
+   
+   EWTerrainPainterToolbar-->LerpBlendCheckBox.setValue($Pref::Terrain::LerpBlend);
 }
 
 function onNeedRelight()
@@ -257,6 +259,11 @@ function TerrainEditor::toggleBrushType( %this, %brush )
    %this.setBrushType( %brush.internalName );
 }
 
+function TerrainEditor::toggleBlendType( %this, %check )
+{
+   $Pref::Terrain::LerpBlend = %check.getValue();
+}
+
 function TerrainEditor::offsetBrush(%this, %x, %y)
 {
    %curPos = %this.getBrushPos();

+ 23 - 1
Templates/Full/game/tools/worldEditor/scripts/interfaces/terrainMaterialDlg.ed.cs

@@ -401,6 +401,9 @@ function TerrainMaterialDlg::setActiveMaterial( %this, %mat )
       %this-->detDistanceCtrl.setText( %mat.detailDistance );      
       %this-->sideProjectionCtrl.setValue( %mat.useSideProjection );
       %this-->parallaxScaleCtrl.setText( %mat.parallaxScale );
+      %blendDepth = mFloor(%mat.blendDepth * 1000)/1000;
+      %this-->blendDepthTextEditCtrl.setText( %blendDepth );
+      %this-->blendDepthSliderCtrl.setValue( %mat.blendDepth );
 
       %this-->macroSizeCtrl.setText( %mat.macroSize );
       %this-->macroStrengthCtrl.setText( %mat.macroStrength );
@@ -454,6 +457,7 @@ function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat )
    %detailDistance = %this-->detDistanceCtrl.getText();   
    %useSideProjection = %this-->sideProjectionCtrl.getValue();   
    %parallaxScale = %this-->parallaxScaleCtrl.getText();
+   %blendDepth = %this-->blendDepthTextEditCtrl.getText();
 
    %macroSize = %this-->macroSizeCtrl.getText();      
    %macroStrength = %this-->macroStrengthCtrl.getText();
@@ -475,7 +479,8 @@ function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat )
          %mat.macroSize == %macroSize &&
          %mat.macroStrength == %macroStrength &&
          %mat.macroDistance == %macroDistance &&         
-         %mat.parallaxScale == %parallaxScale )               
+         %mat.parallaxScale == %parallaxScale &&
+         %mat.blendDepth == %blendDepth)               
       return;
       
    // Make sure the material name is unique.
@@ -509,6 +514,7 @@ function TerrainMaterialDlg::saveDirtyMaterial( %this, %mat )
    %mat.macroDistance = %macroDistance;    
    %mat.useSideProjection = %useSideProjection;
    %mat.parallaxScale = %parallaxScale;
+   %mat.blendDepth = %blendDepth;
    
    // Mark the material as dirty and needing saving.
    
@@ -554,6 +560,7 @@ function TerrainMaterialDlg::snapshotMaterials( %this )
          macroDistance = %mat.macroDistance;
          useSideProjection = %mat.useSideProjection;
          parallaxScale = %mat.parallaxScale;
+         blendDepth = %mat.blendDepth;
       };
    }
 }
@@ -588,6 +595,7 @@ function TerrainMaterialDlg::restoreMaterials( %this )
       %mat.macroDistance = %obj.macroDistance;
       %mat.useSideProjection = %obj.useSideProjection;
       %mat.parallaxScale = %obj.parallaxScale;
+      %mat.blendDepth = %obj.blendDepth;
    }
 }
 
@@ -623,3 +631,17 @@ function TerrainMaterialDlg::_selectTextureFileDialog( %this, %defaultFileName )
       
    return %file;
 }
+
+function TerrainMaterialDlgBlendDepthSlider::onMouseDragged(%this)
+{
+   %value = mFloor(%this.value * 1000)/1000;
+   TerrainMaterialDlgBlendDepthTextEdit.setText(%value);
+   TerrainMaterialDlg.activeMat.blendDepth = %this.value;
+   
+}
+
+function TerrainMaterialDlgBlendDepthTextEdit::onValidate(%this)
+{
+   TerrainMaterialDlgBlendDepthSlider.setValue(%this.getText());
+   TerrainMaterialDlg.activeMat.blendDepth = %this.getText();
+}