Browse Source

New terrain blending

Lukas Aldershaab 4 years ago
parent
commit
4f472bf402

+ 421 - 80
Engine/source/terrain/glsl/terrFeatureGLSL.cpp

@@ -48,6 +48,7 @@ namespace
       FEATUREMGR->registerFeature( MFT_TerrainMacroMap, new NamedFeatureGLSL("TerrainMacroMap Deprecated")); // new TerrainMacroMapFeatGLSL);
       FEATUREMGR->registerFeature( MFT_TerrainMacroMap, new NamedFeatureGLSL("TerrainMacroMap Deprecated")); // new TerrainMacroMapFeatGLSL);
       FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatGLSL );
       FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatGLSL );
       FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureGLSL( "Terrain Side Projection" ) );
       FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureGLSL( "Terrain Side Projection" ) );
+      FEATUREMGR->registerFeature(MFT_TerrainHeightBlend, new TerrainHeightMapBlendGLSL);
       FEATUREMGR->registerFeature( MFT_TerrainORMMap, new TerrainORMMapFeatGLSL );
       FEATUREMGR->registerFeature( MFT_TerrainORMMap, new TerrainORMMapFeatGLSL );
       FEATUREMGR->registerFeature( MFT_DeferredTerrainBlankInfoMap, new TerrainBlankInfoMapFeatGLSL );
       FEATUREMGR->registerFeature( MFT_DeferredTerrainBlankInfoMap, new TerrainBlankInfoMapFeatGLSL );
    }
    }
@@ -311,6 +312,25 @@ void TerrainBaseMapFeatGLSL::processPix(  Vector<ShaderComponent*> &componentLis
    }
    }
    meta->addStatement( new GenOp( "   @;\r\n", assignColor( baseColor, Material::Mul,NULL,target ) ) );
    meta->addStatement( new GenOp( "   @;\r\n", assignColor( baseColor, Material::Mul,NULL,target ) ) );
 
 
+   // Set base ORM info
+   Var* ormConfig;
+   OutputTarget targ = RenderTarget1;
+   if (fd.features[MFT_isDeferred])
+   {
+      targ = RenderTarget2;
+   }
+   ormConfig = (Var*)LangElement::find(getOutputTargetVarName(targ));
+   if (!ormConfig)
+   {
+      // create color var
+      ormConfig = new Var;
+      ormConfig->setType("fragout");
+      ormConfig->setName(getOutputTargetVarName(targ));
+      ormConfig->setStructName("OUT");
+   }
+
+   meta->addStatement(new GenOp("   @ = float4(0.0, 1.0, 1.0, 0.0);\r\n", ormConfig));
+
    output = meta;
    output = meta;
 }
 }
 
 
@@ -325,7 +345,7 @@ ShaderFeature::Resources TerrainBaseMapFeatGLSL::getResources( const MaterialFea
 
 
 U32 TerrainBaseMapFeatGLSL::getOutputTargets( const MaterialFeatureData &fd ) const
 U32 TerrainBaseMapFeatGLSL::getOutputTargets( const MaterialFeatureData &fd ) const
 {
 {
-   return fd.features[MFT_isDeferred] ? ShaderFeature::RenderTarget1 : ShaderFeature::DefaultTarget;
+   return fd.features[MFT_isDeferred] ? ShaderFeature::RenderTarget1 | ShaderFeature::RenderTarget2 : ShaderFeature::DefaultTarget | ShaderFeature::RenderTarget1;
 }
 }
 
 
 TerrainDetailMapFeatGLSL::TerrainDetailMapFeatGLSL()
 TerrainDetailMapFeatGLSL::TerrainDetailMapFeatGLSL()
@@ -541,12 +561,12 @@ void TerrainDetailMapFeatGLSL::processPix(   Vector<ShaderComponent*> &component
 	   meta->addStatement(new GenOp("   @;\r\n", outColor));
 	   meta->addStatement(new GenOp("   @;\r\n", outColor));
    }
    }
 
 
-   Var *detailColor = (Var*)LangElement::find("detailColor");
+   Var *detailColor = (Var*)LangElement::find(String::ToString("detailColor%d", detailIndex));
    if (!detailColor)
    if (!detailColor)
    {
    {
 	   detailColor = new Var;
 	   detailColor = new Var;
 	   detailColor->setType("vec4");
 	   detailColor->setType("vec4");
-	   detailColor->setName("detailColor");
+	   detailColor->setName(String::ToString("detailColor%d", detailIndex));
 	   meta->addStatement(new GenOp("   @;\r\n", new DecOp(detailColor)));
 	   meta->addStatement(new GenOp("   @;\r\n", new DecOp(detailColor)));
    }
    }
 
 
@@ -556,8 +576,6 @@ void TerrainDetailMapFeatGLSL::processPix(   Vector<ShaderComponent*> &component
    // Get the normal map texture.
    // Get the normal map texture.
    Var *normalMap = _getNormalMapSampler();
    Var *normalMap = _getNormalMapSampler();
 
 
-   // Issue happens somewhere here -----
-
    // Sample the normal map.
    // Sample the normal map.
    //
    //
    // We take two normal samples and lerp between them for
    // We take two normal samples and lerp between them for
@@ -581,7 +599,8 @@ void TerrainDetailMapFeatGLSL::processPix(   Vector<ShaderComponent*> &component
 		   detailColor, detailMap, inDet, new IndexOp(detailInfo, detailIndex)));
 		   detailColor, detailMap, inDet, new IndexOp(detailInfo, detailIndex)));
    }
    }
 
 
-   // New terrain
+   meta->addStatement(new GenOp("   @ *= @.y * @.w;\r\n",
+      detailColor, new IndexOp(detailInfo, detailIndex), inDet));
 
 
    // If we had a parallax feature... then factor in the parallax
    // If we had a parallax feature... then factor in the parallax
    // amount so that it fades out with the layer blending.
    // amount so that it fades out with the layer blending.
@@ -599,62 +618,48 @@ void TerrainDetailMapFeatGLSL::processPix(   Vector<ShaderComponent*> &component
          inDet, normalMap, inDet, new IndexOp(detailInfo, detailIndex), negViewTS, new IndexOp(detailInfo, detailIndex), detailBlend));
          inDet, normalMap, inDet, new IndexOp(detailInfo, detailIndex), negViewTS, new IndexOp(detailInfo, detailIndex), detailBlend));
       }
       }
    }
    }
-   
+
    // Check to see if we have a gbuffer normal.
    // Check to see if we have a gbuffer normal.
-   Var *gbNormal = (Var*)LangElement::find( "gbNormal" );
-   
-   // If we have a gbuffer normal and we don't have a
-   // normal map feature then we need to lerp in a 
-   // default normal else the normals below this layer
-   // will show thru.
-   if (gbNormal &&
-      !fd.features.hasFeature(MFT_TerrainNormalMap, detailIndex))
+   Var* viewToTangent = (Var*)LangElement::find("viewToTangent");
+   if (!viewToTangent && fd.features.hasFeature(MFT_TerrainHeightBlend))
    {
    {
-      Var *viewToTangent = getInViewToTangent(componentList);
-
-      meta->addStatement(new GenOp("   @ = lerp( @, tGetMatrix3Row(@, 2), min( @, @.w ) );\r\n",
-         gbNormal, gbNormal, viewToTangent, detailBlend, inDet));
+      // This needs to be here, to ensure consistent ordering of texcoords, be careful with moving it
+      getInViewToTangent(componentList);
    }
    }
 
 
-   // 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 ) )
+   if (!fd.features.hasFeature(MFT_TerrainHeightBlend))
    {
    {
-      meta->addStatement( new GenOp( "      @ = ( lerp( tex2D( @, vec3(@.yz, @.x) ), tex2D( @, vec3(@.xz, @.x) ), @.z ) * 2.0 ) - 1.0;\r\n",
-                                                detailColor, detailMap, inDet, new IndexOp(detailInfo, detailIndex), detailMap, inDet, new IndexOp(detailInfo, detailIndex), inTex ) );
-   }
-   else
-   {
-      meta->addStatement( new GenOp( "      @ = ( tex2D( @, vec3(@.xy, @.x) ) * 2.0 ) - 1.0;\r\n",
-                                       detailColor, detailMap, inDet, new IndexOp(detailInfo, detailIndex)) );
-   }
+      // Check to see if we have a gbuffer normal.
+      Var* gbNormal = (Var*)LangElement::find("gbNormal");
+
+      // If we have a gbuffer normal and we don't have a
+      // normal map feature then we need to lerp in a 
+      // default normal else the normals below this layer
+      // will show thru.
+      if (gbNormal &&
+         !fd.features.hasFeature(MFT_TerrainNormalMap, detailIndex))
+      {
+         Var* viewToTangent = getInViewToTangent(componentList);
 
 
-   meta->addStatement( new GenOp( "      @ *= @.y * @.w;\r\n",
-                                    detailColor, new IndexOp(detailInfo, detailIndex), inDet ) );
+         meta->addStatement(new GenOp("   @ = lerp( @, tGetMatrix3Row(@, 2), min( @, @.w ) );\r\n",
+            gbNormal, gbNormal, viewToTangent, detailBlend, inDet));
+      }
 
 
+      // 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("      @.rgb = toGamma(@.rgb);\r\n", outColor, outColor));
+      meta->addStatement(new GenOp("   {\r\n"));
+      meta->addStatement(new GenOp("      @.rgb = toGamma(@.rgb);\r\n", outColor, outColor));
 
 
-   meta->addStatement(new GenOp("      @ += @ * @;\r\n",
-                                    outColor, detailColor, detailBlend));
+      meta->addStatement(new GenOp("      @ += @ * @;\r\n",
+         outColor, detailColor, detailBlend));
 
 
-   meta->addStatement(new GenOp("      @.rgb = toLinear(clamp(@.rgb, 0, 1));\r\n", outColor, outColor));
+      meta->addStatement(new GenOp("      @.rgb = toLinear(clamp(@.rgb, 0, 1));\r\n", outColor, outColor));
 
 
-   meta->addStatement( new GenOp( "   }\r\n" ) );
+      meta->addStatement(new GenOp("   }\r\n"));
+   }
 
 
    output = meta;
    output = meta;
 }
 }
@@ -941,31 +946,11 @@ void TerrainNormalMapFeatGLSL::processPix(   Vector<ShaderComponent*> &component
 
 
    MultiLine *meta = new MultiLine;
    MultiLine *meta = new MultiLine;
 
 
-   Var *viewToTangent = getInViewToTangent( componentList );
-
-   // This var is read from GBufferConditionerGLSL and 
-   // used in the deferred output.
-   Var *gbNormal = (Var*)LangElement::find( "gbNormal" );
-   if ( !gbNormal )
-   {
-      gbNormal = new Var;
-      gbNormal->setName( "gbNormal" );
-      gbNormal->setType( "vec3" );
-      meta->addStatement( new GenOp( "   @ = tGetMatrix3Row(@, 2);\r\n", new DecOp( gbNormal ), viewToTangent ) );
-   }
-
    const S32 normalIndex = getProcessIndex();
    const S32 normalIndex = getProcessIndex();
 
 
    Var *detailBlend = (Var*)LangElement::find( String::ToString( "detailBlend%d", normalIndex ) );
    Var *detailBlend = (Var*)LangElement::find( String::ToString( "detailBlend%d", normalIndex ) );
    AssertFatal( detailBlend, "The detail blend is missing!" );
    AssertFatal( detailBlend, "The detail blend is missing!" );
 
 
-   // 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" ) );
-
    // Get the normal map texture.
    // Get the normal map texture.
    Var *normalMap = _getNormalMapSampler();
    Var *normalMap = _getNormalMapSampler();
 
 
@@ -989,19 +974,42 @@ void TerrainNormalMapFeatGLSL::processPix(   Vector<ShaderComponent*> &component
 
 
    // create bump normal
    // create bump normal
    Var *bumpNorm = new Var;
    Var *bumpNorm = new Var;
-   bumpNorm->setName( "bumpNormal" );
+   bumpNorm->setName( String::ToString("bumpNormal%d", normalIndex) );
    bumpNorm->setType( "vec4" );
    bumpNorm->setType( "vec4" );
 
 
    LangElement *bumpNormDecl = new DecOp( bumpNorm );
    LangElement *bumpNormDecl = new DecOp( bumpNorm );
    meta->addStatement( expandNormalMap( texOp, bumpNormDecl, bumpNorm, fd ) );
    meta->addStatement( expandNormalMap( texOp, bumpNormDecl, bumpNorm, fd ) );
 
 
-   // Normalize is done later... 
-   // Note: The reverse mul order is intentional. Affine matrix.
-   meta->addStatement(new GenOp("      @ = lerp( @, tMul( @.xyz, @ ), min( @, @.w ) );\r\n",
-      gbNormal, gbNormal, bumpNorm, viewToTangent, detailBlend, inDet));
+   if (!fd.features.hasFeature(MFT_TerrainHeightBlend))
+   {
+      Var* viewToTangent = getInViewToTangent(componentList);
 
 
-   // End the conditional block.
-   meta->addStatement( new GenOp( "   }\r\n" ) );
+      // This var is read from GBufferConditionerGLSL and 
+      // used in the deferred output.
+      Var* gbNormal = (Var*)LangElement::find("gbNormal");
+      if (!gbNormal)
+      {
+         gbNormal = new Var;
+         gbNormal->setName("gbNormal");
+         gbNormal->setType("vec3");
+         meta->addStatement(new GenOp("   @ = tGetMatrix3Row(@, 2);\r\n", new DecOp(gbNormal), viewToTangent));
+      }
+
+      // 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"));
+
+      // Normalize is done later... 
+      // Note: The reverse mul order is intentional. Affine matrix.
+      meta->addStatement(new GenOp("      @ = lerp( @, tMul( @.xyz, @ ), min( @, @.w ) );\r\n",
+         gbNormal, gbNormal, bumpNorm, viewToTangent, detailBlend, inDet));
+
+      // End the conditional block.
+      meta->addStatement(new GenOp("   }\r\n"));
+   }
 
 
    output = meta;
    output = meta;
 }
 }
@@ -1204,7 +1212,7 @@ void TerrainORMMapFeatGLSL::processPix(Vector<ShaderComponent*> &componentList,
 
 
 	// search for material var
 	// search for material var
 	Var * ormConfig;
 	Var * ormConfig;
-	OutputTarget targ = DefaultTarget;
+	OutputTarget targ = RenderTarget1;
 	if (fd.features[MFT_isDeferred])
 	if (fd.features[MFT_isDeferred])
 	{
 	{
 		targ = RenderTarget2;
 		targ = RenderTarget2;
@@ -1239,7 +1247,13 @@ void TerrainORMMapFeatGLSL::processPix(Vector<ShaderComponent*> &componentList,
       meta->addStatement(new GenOp("   @.b = 1.0 - @.b;\r\n", matinfoCol, matinfoCol));
       meta->addStatement(new GenOp("   @.b = 1.0 - @.b;\r\n", matinfoCol, matinfoCol));
    }
    }
 
 
-   meta->addStatement(new GenOp("   @.gba += @ * @;\r\n", ormConfig, matinfoCol, detailBlend));
+
+   meta->addStatement(new GenOp("   @ = lerp(float3(1.0, 1.0, 0.0), @, @.y * @.w);\r\n", matinfoCol, matinfoCol, new IndexOp(detailInfo, compositeIndex), inDet));
+
+   if (!fd.features.hasFeature(MFT_TerrainHeightBlend))
+   {
+      meta->addStatement(new GenOp("   @.gba += @ * @;\r\n", ormConfig, matinfoCol, detailBlend));
+   }
 
 
 	output = meta;
 	output = meta;
 }
 }
@@ -1295,5 +1309,332 @@ void TerrainBlankInfoMapFeatGLSL::processPix(Vector<ShaderComponent*> &component
 
 
    meta->addStatement(new GenOp("   @.gba += vec3(@, @, 0.0);\r\n", material, detailBlend, detailBlend));
    meta->addStatement(new GenOp("   @.gba += vec3(@, @, 0.0);\r\n", material, detailBlend, detailBlend));
 
 
+   output = meta;
+}
+
+void TerrainHeightMapBlendGLSL::processPix(Vector<ShaderComponent*>& componentList,
+   const MaterialFeatureData& fd)
+{
+
+   ShaderFeature::OutputTarget target = ShaderFeature::DefaultTarget;
+
+   if (fd.features.hasFeature(MFT_isDeferred))
+      target = ShaderFeature::RenderTarget1;
+
+   Var* outColor = (Var*)LangElement::find(getOutputTargetVarName(target));
+
+   if (!outColor)
+      return;
+
+   MultiLine* meta = new MultiLine;
+
+   // Count the number of detail textures
+   int detailCount = 0;
+   while (true)
+   {
+      if (LangElement::find(String::ToString("detailBlend%d", detailCount)) == NULL)
+      {
+         break;
+      }
+
+      ++detailCount;
+   }
+
+   if (detailCount == 0)
+   {
+      return;
+   }
+
+   // Compute the "height" of each detail layer and store it detailHX
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx));
+      Var* bumpNormal = (Var*)LangElement::find(String::ToString("bumpNormal%d", idx));
+      Var* blendDepth = (Var*)LangElement::find(String::ToString("blendDepth%d", idx));
+      if (!blendDepth)
+      {
+         blendDepth = new Var;
+         blendDepth->setType("float");
+         blendDepth->setName(String::ToString("blendDepth%d", idx));
+         blendDepth->uniform = true;
+         blendDepth->constSortPos = cspPrimitive;
+      }
+
+      Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx));
+      if (!detailH)
+      {
+         detailH = new Var;
+         detailH->setType("float");
+         detailH->setName(String::ToString("detailH%d", idx));
+
+         meta->addStatement(new GenOp("   @ = 0;\r\n",
+            new DecOp(detailH)));
+         meta->addStatement(new GenOp("   if (@ > 0.0f) {\r\n", detailBlend));
+         if (bumpNormal != NULL)
+         {
+            meta->addStatement(new GenOp("      @ = clamp(@.a + @, 0.0, 1.0);\r\n",
+               detailH, bumpNormal, blendDepth));
+         }
+         else
+         {
+            meta->addStatement(new GenOp("      @ = clamp(0.5 + @, 0.0, 1.0);\r\n",
+               detailH, blendDepth));
+         }
+         meta->addStatement(new GenOp("   }\r\n"));
+      }
+   }
+
+   meta->addStatement(new GenOp("\r\n"));
+
+   // Compute blending factors
+   Var* depth = (Var*)LangElement::find("baseBlendDepth");
+   if (depth == NULL)
+   {
+      depth = new Var;
+      depth->setType("float");
+      depth->setName("baseBlendDepth");
+      depth->uniform = true;
+      depth->constSortPos = cspPrimitive;
+   }
+
+   Var* ma = (Var*)LangElement::find("ma");
+   if (ma == NULL)
+   {
+      ma = new Var;
+      ma->setType("float");
+      ma->setName("ma");
+      meta->addStatement(new GenOp("   @ = 0;\r\n",
+         new DecOp(ma)));
+   }
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx));
+      Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx));
+
+      meta->addStatement(new GenOp("   @ = max(@, @ + @);\r\n",
+         ma, ma, detailH, detailBlend));
+   }
+
+   meta->addStatement(new GenOp("   @ -= @;\r\n",
+      ma, depth));
+
+   meta->addStatement(new GenOp("\r\n"));
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx));
+      Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx));
+      Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx));
+      if (!detailB)
+      {
+         detailB = new Var;
+         detailB->setType("float");
+         detailB->setName(String::ToString("detailB%d", idx));
+
+         meta->addStatement(new GenOp("   @ = max(@ + @ - @, 0);\r\n",
+            new DecOp(detailB), detailH, detailBlend, ma));
+      }
+   }
+
+   meta->addStatement(new GenOp("\r\n"));
+
+   // Compute albedo
+   meta->addStatement(new GenOp("   @.rgb = toGamma(@.rgb);\r\n",
+      outColor, outColor));
+
+   meta->addStatement(new GenOp("   @.rgb += (",
+      outColor));
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detailColor = (Var*)LangElement::find(String::ToString("detailColor%d", idx));
+      Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx));
+
+
+      if (idx > 0)
+      {
+         meta->addStatement(new GenOp(" + "));
+      }
+
+      meta->addStatement(new GenOp("@.rgb * @", detailColor, detailB));
+   }
+
+   meta->addStatement(new GenOp(") / ("));
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx));
+
+      if (idx > 0)
+      {
+         meta->addStatement(new GenOp(" + "));
+      }
+
+      meta->addStatement(new GenOp("@", detailB));
+   }
+
+
+   meta->addStatement(new GenOp(");\r\n"));
+
+   meta->addStatement(new GenOp("   @.rgb = toLinear(clamp(@.rgb, 0, 1));\r\n",
+      outColor, outColor));
+
+   meta->addStatement(new GenOp("\r\n"));
+
+   // Compute ORM
+   Var* ormOutput;
+   OutputTarget targ = DefaultTarget;
+   if (fd.features[MFT_isDeferred])
+   {
+      targ = RenderTarget2;
+   }
+   ormOutput = (Var*)LangElement::find(getOutputTargetVarName(targ));
+
+   meta->addStatement(new GenOp("   @.gba = (",
+      ormOutput));
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* matinfoCol = (Var*)LangElement::find(String::ToString("matinfoCol%d", idx));
+      Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx));
+
+
+      if (idx > 0)
+      {
+         meta->addStatement(new GenOp(" + "));
+      }
+      if (matinfoCol)
+      {
+         meta->addStatement(new GenOp("@ * @", matinfoCol, detailB));
+      }
+      else
+      {
+         meta->addStatement(new GenOp("vec3(1.0, 1.0, 0.0) * @", detailB));
+      }
+   }
+
+   meta->addStatement(new GenOp(") / ("));
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx));
+
+      if (idx > 0)
+      {
+         meta->addStatement(new GenOp(" + "));
+      }
+
+      meta->addStatement(new GenOp("@", detailB));
+   }
+
+
+   meta->addStatement(new GenOp(");\r\n"));
+
+
+   meta->addStatement(new GenOp("\r\n"));
+
+   // Compute normal-specific blending factors
+   // LukasPJ: I'm not sure why this is necessary, it might not be.
+   Var* normalMa = (Var*)LangElement::find("normalMa");
+   if (normalMa == NULL)
+   {
+      normalMa = new Var;
+      normalMa->setType("float");
+      normalMa->setName("normalMa");
+      meta->addStatement(new GenOp("   @ = 0;\r\n",
+         new DecOp(normalMa)));
+   }
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detCoord = (Var*)LangElement::find(String::ToString("detCoord%d", idx));
+
+      Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx));
+      Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx));
+
+      meta->addStatement(new GenOp("   @ = max(@, @ + min(@, @.w));\r\n",
+         normalMa, normalMa, detailH, detailBlend, detCoord));
+   }
+
+   meta->addStatement(new GenOp("   @ -= @;\r\n",
+      normalMa, depth));
+
+   meta->addStatement(new GenOp("\r\n"));
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detCoord = (Var*)LangElement::find(String::ToString("detCoord%d", idx));
+
+      Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx));
+      Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx));
+      Var* normalDetailB = (Var*)LangElement::find(String::ToString("normalDetailB%d", idx));
+      if (!normalDetailB)
+      {
+         normalDetailB = new Var;
+         normalDetailB->setType("float");
+         normalDetailB->setName(String::ToString("normalDetailB%d", idx));
+
+         meta->addStatement(new GenOp("   @ = max(@ + min(@, @.w) - @, 0);\r\n",
+            new DecOp(normalDetailB), detailH, detailBlend, detCoord, normalMa));
+      }
+   }
+
+   // Compute normals
+   Var* gbNormal = (Var*)LangElement::find("gbNormal");
+   if (!gbNormal)
+   {
+      gbNormal = new Var;
+      gbNormal->setName("gbNormal");
+      gbNormal->setType("vec3");
+      meta->addStatement(new GenOp("   @;\r\n", new DecOp(gbNormal)));
+   }
+
+   if (gbNormal != NULL)
+   {
+      meta->addStatement(new GenOp("   @ = (",
+         gbNormal));
+
+      for (S32 idx = 0; idx < detailCount; ++idx)
+      {
+         Var* normalDetailB = (Var*)LangElement::find(String::ToString("normalDetailB%d", idx));
+         Var* bumpNormal = (Var*)LangElement::find(String::ToString("bumpNormal%d", idx));
+         Var* viewToTangent = getInViewToTangent(componentList);
+
+
+         if (idx > 0)
+         {
+            meta->addStatement(new GenOp(" + "));
+         }
+
+         if (bumpNormal != NULL)
+         {
+            meta->addStatement(new GenOp("tMul(@.xyz, @) * @", bumpNormal, viewToTangent, normalDetailB));
+         }
+         else
+         {
+            meta->addStatement(new GenOp("tGetMatrix3Row(@, 2) * @", viewToTangent, normalDetailB));
+         }
+      }
+
+      meta->addStatement(new GenOp(") / ("));
+
+      for (S32 idx = 0; idx < detailCount; ++idx)
+      {
+         Var* normalDetailB = (Var*)LangElement::find(String::ToString("normalDetailB%d", idx));
+
+         if (idx > 0)
+         {
+            meta->addStatement(new GenOp(" + "));
+         }
+
+         meta->addStatement(new GenOp("@", normalDetailB));
+      }
+
+
+      meta->addStatement(new GenOp(");\r\n"));
+   }
+
+
    output = meta;
    output = meta;
 }
 }

+ 10 - 0
Engine/source/terrain/glsl/terrFeatureGLSL.h

@@ -178,4 +178,14 @@ public:
    virtual String getName() { return "Blank Matinfo map"; }
    virtual String getName() { return "Blank Matinfo map"; }
 };
 };
 
 
+class TerrainHeightMapBlendGLSL : public TerrainFeatGLSL
+{
+public:
+
+   virtual void processPix(Vector<ShaderComponent*>& componentList,
+      const MaterialFeatureData& fd);
+
+   virtual String getName() { return "Terrain Heightmap Blend"; }
+};
+
 #endif // _TERRFEATUREGLSL_H_
 #endif // _TERRFEATUREGLSL_H_

+ 459 - 84
Engine/source/terrain/hlsl/terrFeatureHLSL.cpp

@@ -48,6 +48,7 @@ namespace
       FEATUREMGR->registerFeature( MFT_TerrainMacroMap, new NamedFeatureHLSL("TerrainMacroMap Deprecated")); // new TerrainMacroMapFeatHLSL);
       FEATUREMGR->registerFeature( MFT_TerrainMacroMap, new NamedFeatureHLSL("TerrainMacroMap Deprecated")); // new TerrainMacroMapFeatHLSL);
       FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatHLSL );
       FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatHLSL );
       FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureHLSL( "Terrain Side Projection" ) );
       FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureHLSL( "Terrain Side Projection" ) );
+      FEATUREMGR->registerFeature( MFT_TerrainHeightBlend, new TerrainHeightMapBlendHLSL );
       FEATUREMGR->registerFeature( MFT_TerrainORMMap, new TerrainORMMapFeatHLSL );
       FEATUREMGR->registerFeature( MFT_TerrainORMMap, new TerrainORMMapFeatHLSL );
       FEATUREMGR->registerFeature( MFT_DeferredTerrainBlankInfoMap, new TerrainBlankInfoMapFeatHLSL );
       FEATUREMGR->registerFeature( MFT_DeferredTerrainBlankInfoMap, new TerrainBlankInfoMapFeatHLSL );
    }
    }
@@ -371,6 +372,26 @@ void TerrainBaseMapFeatHLSL::processPix(  Vector<ShaderComponent*> &componentLis
    }
    }
 
 
    meta->addStatement( new GenOp( "   @;\r\n", assignColor( baseColor, Material::Mul,NULL,target ) ) );
    meta->addStatement( new GenOp( "   @;\r\n", assignColor( baseColor, Material::Mul,NULL,target ) ) );
+
+   if (fd.features[MFT_isDeferred])
+   {
+      // Set base ORM info
+      Var* ormConfig;
+      OutputTarget targ = RenderTarget1;
+      targ = RenderTarget2;
+      ormConfig = (Var*)LangElement::find(getOutputTargetVarName(targ));
+      if (!ormConfig)
+      {
+         // create color var
+         ormConfig = new Var;
+         ormConfig->setType("fragout");
+         ormConfig->setName(getOutputTargetVarName(targ));
+         ormConfig->setStructName("OUT");
+      }
+
+      meta->addStatement(new GenOp("   @ = float4(0.0, 1.0, 1.0, 0.0);\r\n", ormConfig));
+   }
+
    output = meta;
    output = meta;
 }
 }
 
 
@@ -385,7 +406,7 @@ ShaderFeature::Resources TerrainBaseMapFeatHLSL::getResources( const MaterialFea
 
 
 U32 TerrainBaseMapFeatHLSL::getOutputTargets( const MaterialFeatureData &fd ) const
 U32 TerrainBaseMapFeatHLSL::getOutputTargets( const MaterialFeatureData &fd ) const
 {
 {
-   return fd.features[MFT_isDeferred] ? ShaderFeature::RenderTarget1 : ShaderFeature::DefaultTarget;
+   return fd.features[MFT_isDeferred] ? ShaderFeature::RenderTarget1 | ShaderFeature::RenderTarget2 : ShaderFeature::DefaultTarget;
 }
 }
 
 
 TerrainDetailMapFeatHLSL::TerrainDetailMapFeatHLSL()
 TerrainDetailMapFeatHLSL::TerrainDetailMapFeatHLSL()
@@ -587,40 +608,22 @@ void TerrainDetailMapFeatHLSL::processPix(   Vector<ShaderComponent*> &component
       }
       }
      
      
    }
    }
-   
-   // Check to see if we have a gbuffer normal.
-   Var *gbNormal = (Var*)LangElement::find( "gbNormal" );
-   // If we have a gbuffer normal and we don't have a
-   // normal map feature then we need to lerp in a
-   // default normal else the normals below this layer
-   // will show thru.
-   if (gbNormal &&
-      !fd.features.hasFeature(MFT_TerrainNormalMap, detailIndex))
-   {
-      Var *viewToTangent = getInViewToTangent(componentList);
 
 
-      meta->addStatement(new GenOp("   @ = lerp( @, @[2], min( @, @.w ) );\r\n",
-         gbNormal, gbNormal, viewToTangent, detailBlend, inDet));
-   }
 
 
-   Var *detailColor = (Var*)LangElement::find( "detailColor" ); 
+   Var *detailColor = (Var*)LangElement::find(String::ToString("detailColor%d", detailIndex));
    if ( !detailColor )
    if ( !detailColor )
    {
    {
       detailColor = new Var;
       detailColor = new Var;
       detailColor->setType( "float4" );
       detailColor->setType( "float4" );
-      detailColor->setName( "detailColor" );
+      detailColor->setName( String::ToString("detailColor%d", detailIndex) );
       meta->addStatement( new GenOp( "   @;\r\n", new DecOp( detailColor ) ) );
       meta->addStatement( new GenOp( "   @;\r\n", new DecOp( detailColor ) ) );
    }
    }
 
 
-   // 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" ) );
-
+   // Sample the normal map.
+   //
+   // We take two normal samples and lerp between them for
+   // side projection layers... else a single sample.
+   //
    // Note that we're doing the standard greyscale detail 
    // Note that we're doing the standard greyscale detail 
    // map technique here which can darken and lighten the 
    // map technique here which can darken and lighten the 
    // diffuse texture.
    // diffuse texture.
@@ -635,34 +638,68 @@ void TerrainDetailMapFeatHLSL::processPix(   Vector<ShaderComponent*> &component
 
 
    if (fd.features.hasFeature(MFT_TerrainSideProject, detailIndex))
    if (fd.features.hasFeature(MFT_TerrainSideProject, detailIndex))
    {
    {
-
-      meta->addStatement(new GenOp("      @ = ( lerp( @.Sample( @, float3(@.yz, @.x) ), @.Sample( @, float3(@.xz, @.x) ), @.z ) * 2.0 ) - 1.0;\r\n",
+      meta->addStatement(new GenOp("   @ = ( lerp( @.Sample( @, float3(@.yz, @.x) ), @.Sample( @, float3(@.xz, @.x) ), @.z ) * 2.0 ) - 1.0;\r\n",
          detailColor, detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex), detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex), inTex));
          detailColor, detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex), detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex), inTex));
    }
    }
    else
    else
    {
    {
-      meta->addStatement(new GenOp("      @ = ( @.Sample( @, float3(@.xy, @.x) ) * 2.0 ) - 1.0;\r\n",
+      meta->addStatement(new GenOp("   @ = ( @.Sample( @, float3(@.xy, @.x) ) * 2.0 ) - 1.0;\r\n",
          detailColor, detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex)));
          detailColor, detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex)));
    }
    }
 
 
-   meta->addStatement( new GenOp( "      @ *= @.y * @.w;\r\n",
-                                    detailColor, new IndexOp(detailInfo, detailIndex), inDet ) );
+   meta->addStatement(new GenOp("   @ *= @.y * @.w;\r\n",
+      detailColor, new IndexOp(detailInfo, detailIndex), inDet));
 
 
-   ShaderFeature::OutputTarget target = ShaderFeature::DefaultTarget;
 
 
-   if (fd.features.hasFeature(MFT_isDeferred))
-      target= ShaderFeature::RenderTarget1;
+   // Check to see if we have a gbuffer normal.
+   Var* viewToTangent = (Var*)LangElement::find("viewToTangent");
+   if (!viewToTangent && fd.features.hasFeature(MFT_TerrainHeightBlend))
+   {
+      // This needs to be here, to ensure consistent ordering of texcoords, be careful with moving it
+      getInViewToTangent(componentList);
+   }
 
 
-   Var *outColor = (Var*)LangElement::find( getOutputTargetVarName(target) );
+   if (!fd.features.hasFeature(MFT_TerrainHeightBlend))
+   {
+      // Check to see if we have a gbuffer normal.
+      Var* gbNormal = (Var*)LangElement::find("gbNormal");
+
+      // If we have a gbuffer normal and we don't have a
+      // normal map feature then we need to lerp in a
+      // default normal else the normals below this layer
+      // will show thru.
+      if (gbNormal &&
+         !fd.features.hasFeature(MFT_TerrainNormalMap, detailIndex))
+      {
+         Var* viewToTangent = getInViewToTangent(componentList);
 
 
-   meta->addStatement(new GenOp("      @.rgb = toGamma(@.rgb);\r\n", outColor, outColor));
+         meta->addStatement(new GenOp("   @ = lerp( @, @[2], min( @, @.w ) );\r\n",
+            gbNormal, gbNormal, viewToTangent, detailBlend, inDet));
+      }
 
 
-   meta->addStatement( new GenOp( "      @ += @ * @;\r\n",
-                                    outColor, detailColor, detailBlend));
+      // 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("      @.rgb = toLinear(clamp(@.rgb, 0, 1));\r\n", outColor, outColor));
+      meta->addStatement(new GenOp("   {\r\n"));
 
 
-   meta->addStatement( new GenOp( "   }\r\n" ) );
+      ShaderFeature::OutputTarget target = ShaderFeature::DefaultTarget;
+
+      if (fd.features.hasFeature(MFT_isDeferred))
+         target = ShaderFeature::RenderTarget1;
+
+      Var* outColor = (Var*)LangElement::find(getOutputTargetVarName(target));
+
+      meta->addStatement(new GenOp("      @.rgb = toGamma(@.rgb);\r\n", outColor, outColor));
+
+      meta->addStatement(new GenOp("      @ += @ * @;\r\n",
+         outColor, detailColor, detailBlend));
+
+      meta->addStatement(new GenOp("      @.rgb = toLinear(clamp(@.rgb, 0, 1));\r\n", outColor, outColor));
+
+      meta->addStatement(new GenOp("   }\r\n"));
+   }
 
 
    output = meta;
    output = meta;
 }
 }
@@ -901,16 +938,16 @@ void TerrainMacroMapFeatHLSL::processPix(   Vector<ShaderComponent*> &componentL
    //
    //
    if (fd.features.hasFeature(MFT_TerrainSideProject, detailIndex))
    if (fd.features.hasFeature(MFT_TerrainSideProject, detailIndex))
    {
    {
-      meta->addStatement(new GenOp("      @ = ( lerp( @.Sample( @, float3(@.yz, @.x) ), @.Sample( @, float3(@.xz, @.x) ), @.z ) * 2.0 ) - 1.0;\r\n",
+      meta->addStatement(new GenOp("   @ = ( lerp( @.Sample( @, float3(@.yz, @.x) ), @.Sample( @, float3(@.xz, @.x) ), @.z ) * 2.0 ) - 1.0;\r\n",
          detailColor, detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex), detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex), inTex));
          detailColor, detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex), detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex), inTex));
    }
    }
    else
    else
    {
    {
-      meta->addStatement(new GenOp("      @ = ( @.Sample( @, float3(@.xy, @.x) ) * 2.0 ) - 1.0;\r\n",
+      meta->addStatement(new GenOp("   @ = ( @.Sample( @, float3(@.xy, @.x) ) * 2.0 ) - 1.0;\r\n",
          detailColor, detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex)));
          detailColor, detailMapArray, detailMapSampler, inDet, new IndexOp(detailInfo, detailIndex)));
    }
    }
 
 
-   meta->addStatement( new GenOp( "      @ *= @.y * @.w;\r\n",
+   meta->addStatement( new GenOp( "   @ *= @.y * @.w;\r\n",
                                     detailColor, new IndexOp(detailInfo, detailIndex), inDet ) );
                                     detailColor, new IndexOp(detailInfo, detailIndex), inDet ) );
 
 
    ShaderFeature::OutputTarget target = ShaderFeature::DefaultTarget;
    ShaderFeature::OutputTarget target = ShaderFeature::DefaultTarget;
@@ -982,42 +1019,21 @@ void TerrainNormalMapFeatHLSL::processPix(   Vector<ShaderComponent*> &component
 
 
    MultiLine *meta = new MultiLine;
    MultiLine *meta = new MultiLine;
 
 
-   Var *viewToTangent = getInViewToTangent( componentList );
-
-   // This var is read from GBufferConditionerHLSL and 
-   // used in the deferred output.
-   Var *gbNormal = (Var*)LangElement::find( "gbNormal" );
-   if ( !gbNormal )
-   {
-      gbNormal = new Var;
-      gbNormal->setName( "gbNormal" );
-      gbNormal->setType( "float3" );
-      meta->addStatement( new GenOp( "   @ = @[2];\r\n", new DecOp( gbNormal ), viewToTangent ) );
-   }
-
    const S32 normalIndex = getProcessIndex();
    const S32 normalIndex = getProcessIndex();
 
 
    Var *detailBlend = (Var*)LangElement::find( String::ToString( "detailBlend%d", normalIndex ) );
    Var *detailBlend = (Var*)LangElement::find( String::ToString( "detailBlend%d", normalIndex ) );
    AssertFatal( detailBlend, "The detail blend is missing!" );
    AssertFatal( detailBlend, "The detail blend is missing!" );
 
 
-   // 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" ) );
-
    /// Get the texture coord.
    /// Get the texture coord.
-   Var *inDet = _getInDetailCoord( componentList );
-   Var *inTex = getVertTexCoord( "texCoord" );
-
+   Var* inDet = _getInDetailCoord(componentList);
+   Var* inTex = getVertTexCoord("texCoord");
    Var* detailInfo = _getDetailIdStrengthParallax();
    Var* detailInfo = _getDetailIdStrengthParallax();
 
 
    // Sample the normal map.
    // Sample the normal map.
    //
    //
    // We take two normal samples and lerp between them for
    // We take two normal samples and lerp between them for
    // side projection layers... else a single sample.
    // side projection layers... else a single sample.
-   LangElement *texOp;
+   LangElement* texOp;
 
 
    Var* normalMapSampler = _getNormalMapSampler();
    Var* normalMapSampler = _getNormalMapSampler();
    Var* normalMapArray = _getNormalMapArray();
    Var* normalMapArray = _getNormalMapArray();
@@ -1031,20 +1047,43 @@ void TerrainNormalMapFeatHLSL::processPix(   Vector<ShaderComponent*> &component
       texOp = new GenOp("@.Sample(@, float3(@.xy, @.x))", normalMapArray, normalMapSampler, inDet, new IndexOp(detailInfo, normalIndex));
       texOp = new GenOp("@.Sample(@, float3(@.xy, @.x))", normalMapArray, normalMapSampler, inDet, new IndexOp(detailInfo, normalIndex));
 
 
    // create bump normal
    // create bump normal
-   Var *bumpNorm = new Var;
-   bumpNorm->setName( "bumpNormal" );
-   bumpNorm->setType( "float4" );
+   Var* bumpNorm = new Var;
+   bumpNorm->setName(String::ToString("bumpNormal%d", normalIndex));
+   bumpNorm->setType("float4");
 
 
-   LangElement *bumpNormDecl = new DecOp( bumpNorm );
-   meta->addStatement( expandNormalMap( texOp, bumpNormDecl, bumpNorm, fd ) );
+   LangElement* bumpNormDecl = new DecOp(bumpNorm);
+   meta->addStatement(expandNormalMap(texOp, bumpNormDecl, bumpNorm, fd));
+
+   if (!fd.features.hasFeature(MFT_TerrainHeightBlend))
+   {
+      Var* viewToTangent = getInViewToTangent(componentList);
+
+      // This var is read from GBufferConditionerHLSL and 
+      // used in the deferred output.
+      Var* gbNormal = (Var*)LangElement::find("gbNormal");
+      if (!gbNormal)
+      {
+         gbNormal = new Var;
+         gbNormal->setName("gbNormal");
+         gbNormal->setType("float3");
+         meta->addStatement(new GenOp("   @ = @[2];\r\n", new DecOp(gbNormal), viewToTangent));
+      }
+
+      // 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"));
 
 
       // Normalize is done later... 
       // Normalize is done later... 
       // Note: The reverse mul order is intentional. Affine matrix.
       // Note: The reverse mul order is intentional. Affine matrix.
-      meta->addStatement( new GenOp( "      @ = lerp( @, mul( @.xyz, @ ), min( @, @.w ) );\r\n", 
-            gbNormal, gbNormal, bumpNorm, viewToTangent, detailBlend, inDet ) );
+      meta->addStatement(new GenOp("      @ = lerp( @, mul( @.xyz, @ ), min( @, @.w ) );\r\n",
+         gbNormal, gbNormal, bumpNorm, viewToTangent, detailBlend, inDet));
 
 
-   // End the conditional block.
-   meta->addStatement( new GenOp( "   }\r\n" ) );
+      // End the conditional block.
+      meta->addStatement(new GenOp("   }\r\n"));
+   }
    
    
    output = meta;
    output = meta;
 }
 }
@@ -1247,7 +1286,7 @@ void TerrainORMMapFeatHLSL::processPix(Vector<ShaderComponent*> &componentList,
 
 
    // search for material var
    // search for material var
    Var * ormConfig;
    Var * ormConfig;
-   OutputTarget targ = DefaultTarget;
+   OutputTarget targ = RenderTarget1;
    if (fd.features[MFT_isDeferred])
    if (fd.features[MFT_isDeferred])
    {
    {
       targ = RenderTarget2;
       targ = RenderTarget2;
@@ -1282,7 +1321,12 @@ void TerrainORMMapFeatHLSL::processPix(Vector<ShaderComponent*> &componentList,
       meta->addStatement(new GenOp("   @.b = 1.0 - @.b;\r\n", matinfoCol, matinfoCol));
       meta->addStatement(new GenOp("   @.b = 1.0 - @.b;\r\n", matinfoCol, matinfoCol));
    }
    }
 
 
-   meta->addStatement(new GenOp("   @.gba += @ * @;\r\n", ormConfig, matinfoCol, detailBlend));
+   meta->addStatement(new GenOp("   @ = lerp(float3(1.0, 1.0, 0.0), @, @.y * @.w);\r\n", matinfoCol, matinfoCol, new IndexOp(detailInfo, compositeIndex), inDet));
+
+   if (!fd.features.hasFeature(MFT_TerrainHeightBlend))
+   {
+      meta->addStatement(new GenOp("   @.gba += @ * @;\r\n", ormConfig, matinfoCol, detailBlend));
+   }
 
 
    output = meta;
    output = meta;
 }
 }
@@ -1297,7 +1341,7 @@ ShaderFeature::Resources TerrainORMMapFeatHLSL::getResources(const MaterialFeatu
 // reminder, the matinfo buffer is flags, smooth, ao, metal
 // reminder, the matinfo buffer is flags, smooth, ao, metal
 U32 TerrainBlankInfoMapFeatHLSL::getOutputTargets(const MaterialFeatureData &fd) const
 U32 TerrainBlankInfoMapFeatHLSL::getOutputTargets(const MaterialFeatureData &fd) const
 {
 {
-   return fd.features[MFT_isDeferred] ? ShaderFeature::RenderTarget2 : ShaderFeature::DefaultTarget;
+   return fd.features[MFT_isDeferred] ? ShaderFeature::RenderTarget2 : ShaderFeature::RenderTarget1;
 }
 }
 
 
 void TerrainBlankInfoMapFeatHLSL::processPix(Vector<ShaderComponent*> &componentList,
 void TerrainBlankInfoMapFeatHLSL::processPix(Vector<ShaderComponent*> &componentList,
@@ -1307,12 +1351,12 @@ void TerrainBlankInfoMapFeatHLSL::processPix(Vector<ShaderComponent*> &component
 
 
    // search for material var
    // search for material var
    Var *material;
    Var *material;
-   OutputTarget targ = DefaultTarget;
+   OutputTarget ormConfig = RenderTarget1;
    if (fd.features[MFT_isDeferred])
    if (fd.features[MFT_isDeferred])
    {
    {
-      targ = RenderTarget2;
+      ormConfig = RenderTarget2;
    }
    }
-   material = (Var*)LangElement::find(getOutputTargetVarName(targ));
+   material = (Var*)LangElement::find(getOutputTargetVarName(ormConfig));
 
 
    MultiLine * meta = new MultiLine;
    MultiLine * meta = new MultiLine;
    if (!material)
    if (!material)
@@ -1320,7 +1364,7 @@ void TerrainBlankInfoMapFeatHLSL::processPix(Vector<ShaderComponent*> &component
       // create color var
       // create color var
       material = new Var;
       material = new Var;
       material->setType("fragout");
       material->setType("fragout");
-      material->setName(getOutputTargetVarName(targ));
+      material->setName(getOutputTargetVarName(ormConfig));
       material->setStructName("OUT");
       material->setStructName("OUT");
    }
    }
 
 
@@ -1334,7 +1378,338 @@ void TerrainBlankInfoMapFeatHLSL::processPix(Vector<ShaderComponent*> &component
 
 
    String matinfoName(String::ToString("matinfoCol%d", compositeIndex));
    String matinfoName(String::ToString("matinfoCol%d", compositeIndex));
 
 
-   meta->addStatement(new GenOp("   @.gba += float3(@, @, 0.0);\r\n", material, detailBlend, detailBlend));
+   if (!fd.features.hasFeature(MFT_TerrainHeightBlend))
+   {
+      meta->addStatement(new GenOp("   @.gba += float3(@, @, 0.0);\r\n", material, detailBlend, detailBlend));
+   }
+
+   output = meta;
+}
+
+void TerrainHeightMapBlendHLSL::processPix(Vector<ShaderComponent*>& componentList,
+   const MaterialFeatureData& fd)
+{
+
+   ShaderFeature::OutputTarget target = ShaderFeature::DefaultTarget;
+
+   if (fd.features.hasFeature(MFT_isDeferred))
+      target = ShaderFeature::RenderTarget1;
+
+   Var* outColor = (Var*)LangElement::find(getOutputTargetVarName(target));
+
+   if (!outColor)
+      return;
+
+   MultiLine* meta = new MultiLine;
+
+   // Count number of detail layers
+   int detailCount = 0;
+   while (true)
+   {
+      if(LangElement::find(String::ToString("detailBlend%d", detailCount)) == NULL)
+      {
+         break;
+      }
+
+      ++detailCount;
+   }
+
+   if ( detailCount == 0 )
+   {
+      return;
+   }
+
+   // Compute blend factors
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx));
+      Var* bumpNormal = (Var*)LangElement::find(String::ToString("bumpNormal%d", idx));
+      Var* blendDepth = (Var*)LangElement::find(String::ToString("blendDepth%d", idx));
+      if (!blendDepth)
+      {
+         blendDepth = new Var;
+         blendDepth->setType("float");
+         blendDepth->setName(String::ToString("blendDepth%d", idx));
+         blendDepth->uniform = true;
+         blendDepth->constSortPos = cspPrimitive;
+      }
+
+      Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx));
+      if (!detailH)
+      {
+         detailH = new Var;
+         detailH->setType("float");
+         detailH->setName(String::ToString("detailH%d", idx));
+
+         meta->addStatement(new GenOp("   @ = 0;\r\n",
+            new DecOp(detailH)));
+         meta->addStatement(new GenOp("   if (@ > 0.0f) {\r\n", detailBlend));
+         if (bumpNormal != NULL)
+         {
+            meta->addStatement(new GenOp("      @ = clamp(@.a + @, 0.0, 1.0);\r\n",
+               detailH, bumpNormal, blendDepth));
+         }
+         else
+         {
+            meta->addStatement(new GenOp("      @ = clamp(0.5 + @, 0.0, 1.0);\r\n",
+               detailH, blendDepth));
+         }
+         meta->addStatement(new GenOp("   }\r\n"));
+      }
+   }
+
+   meta->addStatement(new GenOp("\r\n"));
+
+   Var* depth = (Var*)LangElement::find("baseBlendDepth");
+   if (depth == NULL)
+   {
+      depth = new Var;
+      depth->setType("float");
+      depth->setName("baseBlendDepth");
+      depth->uniform = true;
+      depth->constSortPos = cspPrimitive;
+   }
+
+   Var* ma = (Var*)LangElement::find("ma");
+   if (ma == NULL)
+   {
+      ma = new Var;
+      ma->setType("float");
+      ma->setName("ma");
+      meta->addStatement(new GenOp("   @ = 0;\r\n",
+         new DecOp(ma)));
+   }
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detCoord = (Var*)LangElement::find(String::ToString("detCoord%d", idx));
+
+      Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx));
+      Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx));
+
+      meta->addStatement(new GenOp("   @ = max(@, @ + @);\r\n",
+         ma, ma, detailH, detailBlend));
+   }
+
+   meta->addStatement(new GenOp("   @ -= @;\r\n",
+      ma, depth));
+
+   meta->addStatement(new GenOp("\r\n"));
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx));
+      Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx));
+      Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx));
+      if (!detailB)
+      {
+         detailB = new Var;
+         detailB->setType("float");
+         detailB->setName(String::ToString("detailB%d", idx));
+
+         meta->addStatement(new GenOp("   @ = max(@ + @ - @, 0);\r\n",
+            new DecOp(detailB), detailH, detailBlend, ma));
+      }
+   }
+
+   meta->addStatement(new GenOp("\r\n"));
+
+   // Compute albedo
+   meta->addStatement(new GenOp("   @.rgb = toGamma(@.rgb);\r\n",
+      outColor, outColor));
+
+   meta->addStatement(new GenOp("   @.rgb += (",
+      outColor));
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detailColor = (Var*)LangElement::find(String::ToString("detailColor%d", idx));
+      Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx));
+
+
+      if (idx > 0)
+      {
+         meta->addStatement(new GenOp(" + "));
+      }
+
+      meta->addStatement(new GenOp("@.rgb * @", detailColor, detailB));
+   }
+
+   meta->addStatement(new GenOp(") / ("));
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx));
+
+      if (idx > 0)
+      {
+         meta->addStatement(new GenOp(" + "));
+      }
+
+      meta->addStatement(new GenOp("@", detailB));
+   }
+
+
+   meta->addStatement(new GenOp(");\r\n"));
+
+   meta->addStatement(new GenOp("   @.rgb = toLinear(clamp(@.rgb, 0, 1));\r\n",
+      outColor, outColor));
+
+   meta->addStatement(new GenOp("\r\n"));
+
+   // Compute ORM
+   Var* ormOutput;
+   OutputTarget targ = DefaultTarget;
+   if (fd.features[MFT_isDeferred])
+   {
+      targ = RenderTarget2;
+   }
+   ormOutput = (Var*)LangElement::find(getOutputTargetVarName(targ));
+
+   meta->addStatement(new GenOp("   @.gba = (",
+      ormOutput));
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* matinfoCol = (Var*)LangElement::find(String::ToString("matinfoCol%d", idx));
+      Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx));
+
+
+      if (idx > 0)
+      {
+         meta->addStatement(new GenOp(" + "));
+      }
+      if (matinfoCol)
+      {
+         meta->addStatement(new GenOp("@ * @", matinfoCol, detailB));
+      }
+      else
+      {
+         meta->addStatement(new GenOp("float3(1.0, 1.0, 0.0) * @", detailB));
+      }
+   }
+
+   meta->addStatement(new GenOp(") / ("));
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detailB = (Var*)LangElement::find(String::ToString("detailB%d", idx));
+
+      if (idx > 0)
+      {
+         meta->addStatement(new GenOp(" + "));
+      }
+
+      meta->addStatement(new GenOp("@", detailB));
+   }
+
+
+   meta->addStatement(new GenOp(");\r\n"));
+
+
+   meta->addStatement(new GenOp("\r\n"));
+
+   // Compute normal-specific blending factors
+   // LukasPJ: I'm not sure why this is necessary, it might not be.
+   Var* normalMa = (Var*)LangElement::find("normalMa");
+   if (normalMa == NULL)
+   {
+      normalMa = new Var;
+      normalMa->setType("float");
+      normalMa->setName("normalMa");
+      meta->addStatement(new GenOp("   @ = 0;\r\n",
+         new DecOp(normalMa)));
+   }
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detCoord = (Var*)LangElement::find(String::ToString("detCoord%d", idx));
+
+      Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx));
+      Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx));
+
+      meta->addStatement(new GenOp("   @ = max(@, @ + min(@, @.w));\r\n",
+         normalMa, normalMa, detailH, detailBlend, detCoord));
+   }
+
+   meta->addStatement(new GenOp("   @ -= @;\r\n",
+      normalMa, depth));
+
+   meta->addStatement(new GenOp("\r\n"));
+
+   for (S32 idx = 0; idx < detailCount; ++idx)
+   {
+      Var* detCoord = (Var*)LangElement::find(String::ToString("detCoord%d", idx));
+
+      Var* detailH = (Var*)LangElement::find(String::ToString("detailH%d", idx));
+      Var* detailBlend = (Var*)LangElement::find(String::ToString("detailBlend%d", idx));
+      Var* normalDetailB = (Var*)LangElement::find(String::ToString("normalDetailB%d", idx));
+      if (!normalDetailB)
+      {
+         normalDetailB = new Var;
+         normalDetailB->setType("float");
+         normalDetailB->setName(String::ToString("normalDetailB%d", idx));
+
+         meta->addStatement(new GenOp("   @ = max(@ + min(@, @.w) - @, 0);\r\n",
+            new DecOp(normalDetailB), detailH, detailBlend, detCoord, normalMa));
+      }
+   }
+
+   // Compute normals
+   Var* gbNormal = (Var*)LangElement::find("gbNormal");
+   if (!gbNormal)
+   {
+      gbNormal = new Var;
+      gbNormal->setName("gbNormal");
+      gbNormal->setType("float3");
+      meta->addStatement(new GenOp("   @;\r\n", new DecOp(gbNormal)));
+   }
+
+   if (gbNormal != NULL)
+   {
+      meta->addStatement(new GenOp("   @ = (",
+         gbNormal));
+
+      for (S32 idx = 0; idx < detailCount; ++idx)
+      {
+         Var* normalDetailB = (Var*)LangElement::find(String::ToString("normalDetailB%d", idx));
+         Var* bumpNormal = (Var*)LangElement::find(String::ToString("bumpNormal%d", idx));
+         Var* viewToTangent = getInViewToTangent(componentList);
+
+
+         if (idx > 0)
+         {
+            meta->addStatement(new GenOp(" + "));
+         }
+
+         if (bumpNormal != NULL)
+         {
+            meta->addStatement(new GenOp("mul(@.xyz, @) * @", bumpNormal, viewToTangent, normalDetailB));
+         }
+         else
+         {
+            meta->addStatement(new GenOp("@[2] * @", viewToTangent, normalDetailB));
+         }
+      }
+
+      meta->addStatement(new GenOp(") / ("));
+
+      for (S32 idx = 0; idx < detailCount; ++idx)
+      {
+         Var* normalDetailB = (Var*)LangElement::find(String::ToString("normalDetailB%d", idx));
+
+         if (idx > 0)
+         {
+            meta->addStatement(new GenOp(" + "));
+         }
+
+         meta->addStatement(new GenOp("@", normalDetailB));
+      }
+
+
+      meta->addStatement(new GenOp(");\r\n"));
+   }
+
 
 
    output = meta;
    output = meta;
 }
 }

+ 10 - 0
Engine/source/terrain/hlsl/terrFeatureHLSL.h

@@ -184,4 +184,14 @@ public:
    virtual String getName() { return "Blank Matinfo map"; }
    virtual String getName() { return "Blank Matinfo map"; }
 };
 };
 
 
+class TerrainHeightMapBlendHLSL : public TerrainFeatHLSL
+{
+public:
+
+   virtual void processPix(Vector<ShaderComponent*>& componentList,
+      const MaterialFeatureData& fd);
+
+   virtual String getName() { return "Terrain Heightmap Blend"; }
+};
+
 #endif // _TERRFEATUREHLSL_H_
 #endif // _TERRFEATUREHLSL_H_

+ 39 - 8
Engine/source/terrain/terrCellMaterial.cpp

@@ -401,16 +401,18 @@ bool TerrainCellMaterial::_initShader(bool deferredMat,
             features.addFeature(MFT_isDeferred, featureIndex);
             features.addFeature(MFT_isDeferred, featureIndex);
          features.addFeature(MFT_TerrainDetailMap, featureIndex);
          features.addFeature(MFT_TerrainDetailMap, featureIndex);
 
 
-         if (!(mat->getORMConfigMap().isEmpty()))
-         {
-            if (deferredMat)
-               features.addFeature(MFT_isDeferred, featureIndex);
-            features.addFeature(MFT_TerrainORMMap, featureIndex);
-         }
-         else
+         if (deferredMat)
          {
          {
-            features.addFeature(MFT_DeferredTerrainBlankInfoMap, featureIndex);
+            if (!(mat->getORMConfigMap().isEmpty()))
+            {
+               features.addFeature(MFT_TerrainORMMap, featureIndex);
+            }
+            else
+            {
+               features.addFeature(MFT_DeferredTerrainBlankInfoMap, featureIndex);
+            }
          }
          }
+         
          if (mat->getInvertRoughness())
          if (mat->getInvertRoughness())
             features.addFeature(MFT_InvertRoughness, featureIndex);
             features.addFeature(MFT_InvertRoughness, featureIndex);
 
 
@@ -444,6 +446,12 @@ bool TerrainCellMaterial::_initShader(bool deferredMat,
          featureIndex++;
          featureIndex++;
       }
       }
 
 
+      // New blending
+      if (matCount > 0 && !Con::getBoolVariable("$Terrain::LerpBlend", false))
+      {
+         features.addFeature(MFT_TerrainHeightBlend);
+      }
+
       MaterialFeatureData featureData;
       MaterialFeatureData featureData;
       featureData.features = features;
       featureData.features = features;
       featureData.materialFeatures = features;
       featureData.materialFeatures = features;
@@ -528,6 +536,7 @@ bool TerrainCellMaterial::_initShader(bool deferredMat,
    mLightMapTexConst = mShader->getShaderConstHandle("$lightMapTex");
    mLightMapTexConst = mShader->getShaderConstHandle("$lightMapTex");
    mOneOverTerrainSizeConst = mShader->getShaderConstHandle("$oneOverTerrainSize");
    mOneOverTerrainSizeConst = mShader->getShaderConstHandle("$oneOverTerrainSize");
    mSquareSizeConst = mShader->getShaderConstHandle("$squareSize");
    mSquareSizeConst = mShader->getShaderConstHandle("$squareSize");
+   mBlendDepthConst = mShader->getShaderConstHandle("$baseBlendDepth");
 
 
    mLightParamsConst = mShader->getShaderConstHandle("$rtParamslightInfoBuffer");
    mLightParamsConst = mShader->getShaderConstHandle("$rtParamslightInfoBuffer");
 
 
@@ -634,6 +643,23 @@ bool TerrainCellMaterial::_initShader(bool deferredMat,
          desc.samplers[sampler].minFilter = GFXTextureFilterLinear;
          desc.samplers[sampler].minFilter = GFXTextureFilterLinear;
    }
    }
 
 
+   for (U32 i = 0; i < matCount && !baseOnly; i++)
+   {
+      TerrainMaterial* mat = mMaterialInfos[i]->mat;
+
+      if (mat == NULL)
+         continue;
+
+      // We only include materials that 
+      // have more than a base texture.
+      if (mat->getDetailSize() <= 0 ||
+         mat->getDetailDistance() <= 0 ||
+         mat->getDetailMap().isEmpty())
+         continue;
+
+      mMaterialInfos[i]->mBlendDepthConst = mShader->getShaderConstHandle(avar("$blendDepth%d", i));
+   }
+
    // If we're doing deferred it requires some 
    // If we're doing deferred it requires some 
    // special stencil settings for it to work.
    // special stencil settings for it to work.
    if ( deferredMat )
    if ( deferredMat )
@@ -701,10 +727,13 @@ void TerrainCellMaterial::_updateMaterialConsts( )
 
 
       detailScaleAndFadeArray[j] = detailScaleAndFade;
       detailScaleAndFadeArray[j] = detailScaleAndFade;
       detailInfoArray[j] = detailIdStrengthParallax;
       detailInfoArray[j] = detailIdStrengthParallax;
+
+      mConsts->setSafe(matInfo->mBlendDepthConst, matInfo->mat->getBlendDepth());
    }
    }
 
 
    mConsts->setSafe(mDetailInfoVArrayConst, detailScaleAndFadeArray);
    mConsts->setSafe(mDetailInfoVArrayConst, detailScaleAndFadeArray);
    mConsts->setSafe(mDetailInfoPArrayConst, detailInfoArray);
    mConsts->setSafe(mDetailInfoPArrayConst, detailInfoArray);
+
 }
 }
 
 
 bool TerrainCellMaterial::setupPass(   const SceneRenderState *state, 
 bool TerrainCellMaterial::setupPass(   const SceneRenderState *state, 
@@ -782,6 +811,8 @@ bool TerrainCellMaterial::setupPass(   const SceneRenderState *state,
       mConsts->set( mFogDataConst, fogData );
       mConsts->set( mFogDataConst, fogData );
    }
    }
 
 
+   mConsts->setSafe( mBlendDepthConst, Con::getFloatVariable("$Terrain::BlendDepth", 0.2f) );
+
    mConsts->setSafe( mFogColorConst, sceneData.fogColor );
    mConsts->setSafe( mFogColorConst, sceneData.fogColor );
 
 
    if (  mLightInfoBufferConst->isValid() &&
    if (  mLightInfoBufferConst->isValid() &&

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

@@ -69,6 +69,7 @@ protected:
 
 
       TerrainMaterial *mat;
       TerrainMaterial *mat;
       U32 layerId;
       U32 layerId;
+      GFXShaderConstHandle* mBlendDepthConst;
    };
    };
 
 
    ///
    ///
@@ -116,6 +117,8 @@ protected:
    GFXShaderConstHandle *mNormalTexArrayConst;
    GFXShaderConstHandle *mNormalTexArrayConst;
    GFXShaderConstHandle *mOrmTexArrayConst;
    GFXShaderConstHandle *mOrmTexArrayConst;
 
 
+   GFXShaderConstHandle* mBlendDepthConst;
+
    TerrainBlock *mTerrain;
    TerrainBlock *mTerrain;
 
 
    U32 mCurrPass;
    U32 mCurrPass;

+ 5 - 0
Engine/source/terrain/terrData.cpp

@@ -1471,6 +1471,11 @@ DefineEngineMethod(TerrainBlock, saveAsset, bool, (), ,
    return static_cast<TerrainBlock*>(object)->saveAsset();
    return static_cast<TerrainBlock*>(object)->saveAsset();
 }
 }
 
 
+DefineEngineMethod( TerrainBlock, setMaterialsDirty, void, (),, "")
+{
+   static_cast<TerrainBlock*>(object)->setMaterialsDirty();
+}
+
 //ConsoleMethod(TerrainBlock, save, bool, 3, 3, "(string fileName) - saves the terrain block's terrain file to the specified file name.")
 //ConsoleMethod(TerrainBlock, save, bool, 3, 3, "(string fileName) - saves the terrain block's terrain file to the specified file name.")
 //{
 //{
 //   char filename[256];
 //   char filename[256];

+ 2 - 0
Engine/source/terrain/terrData.h

@@ -313,6 +313,8 @@ public:
    /// Deletes all the materials on the terrain.
    /// Deletes all the materials on the terrain.
    void deleteAllMaterials();
    void deleteAllMaterials();
 
 
+   void setMaterialsDirty() { mDetailsDirty = true; };
+
    //void setMaterialName( U32 index, const String &name );
    //void setMaterialName( U32 index, const String &name );
 
 
    /// Accessors and mutators for TerrainMaterialUndoAction.
    /// Accessors and mutators for TerrainMaterialUndoAction.

+ 1 - 1
Engine/source/terrain/terrFeatureTypes.cpp

@@ -33,7 +33,7 @@ ImplementFeatureType( MFT_TerrainNormalMap, MFG_Texture, 103.0f, false );
 ImplementFeatureType( MFT_TerrainMacroMap, MFG_Texture, 104.0f, false );
 ImplementFeatureType( MFT_TerrainMacroMap, MFG_Texture, 104.0f, false );
 ImplementFeatureType( MFT_TerrainLightMap, MFG_Texture, 105.0f, false );
 ImplementFeatureType( MFT_TerrainLightMap, MFG_Texture, 105.0f, false );
 ImplementFeatureType( MFT_TerrainSideProject, MFG_Texture, 106.0f, false );
 ImplementFeatureType( MFT_TerrainSideProject, MFG_Texture, 106.0f, false );
-ImplementFeatureType( MFT_TerrainAdditive, MFG_PostProcess, 999.0f, false );
+ImplementFeatureType( MFT_TerrainHeightBlend, MFG_PreLighting, 200.0f, false );
 //Deferred Shading
 //Deferred Shading
 ImplementFeatureType( MFT_DeferredTerrainBlankInfoMap, MFG_Texture, 104.1f, false);
 ImplementFeatureType( MFT_DeferredTerrainBlankInfoMap, MFG_Texture, 104.1f, false);
 ImplementFeatureType( MFT_TerrainORMMap, MFG_Texture, 104.2f, false);
 ImplementFeatureType( MFT_TerrainORMMap, MFG_Texture, 104.2f, false);

+ 1 - 1
Engine/source/terrain/terrFeatureTypes.h

@@ -34,7 +34,7 @@ DeclareFeatureType( MFT_TerrainNormalMap );
 DeclareFeatureType( MFT_TerrainParallaxMap );
 DeclareFeatureType( MFT_TerrainParallaxMap );
 DeclareFeatureType( MFT_TerrainLightMap );
 DeclareFeatureType( MFT_TerrainLightMap );
 DeclareFeatureType( MFT_TerrainSideProject );
 DeclareFeatureType( MFT_TerrainSideProject );
-DeclareFeatureType( MFT_TerrainAdditive );
+DeclareFeatureType( MFT_TerrainHeightBlend );
 //Deferred Shading
 //Deferred Shading
 DeclareFeatureType( MFT_TerrainORMMap );
 DeclareFeatureType( MFT_TerrainORMMap );
 DeclareFeatureType( MFT_DeferredTerrainBlankInfoMap );
 DeclareFeatureType( MFT_DeferredTerrainBlankInfoMap );

+ 4 - 0
Engine/source/terrain/terrMaterial.cpp

@@ -68,6 +68,7 @@ TerrainMaterial::TerrainMaterial()
       mMacroStrength( 0.7f ),
       mMacroStrength( 0.7f ),
       mMacroDistance( 500.0f ),
       mMacroDistance( 500.0f ),
       mParallaxScale( 0.0f ),
       mParallaxScale( 0.0f ),
+      mBlendDepth( 0.5f ),
       mIsSRGB(false),
       mIsSRGB(false),
       mInvertRoughness(false)
       mInvertRoughness(false)
 {
 {
@@ -91,6 +92,9 @@ void TerrainMaterial::initPersistFields()
    addField( "parallaxScale", TypeF32, Offset( mParallaxScale, TerrainMaterial ), "Used to scale the height from the normal map to give some self "
    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" );
 	   "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.");
+
    scriptBindMapSlot(DetailMap, TerrainMaterial, "Raises and lowers the RGB result of the Base Albedo up close.");
    scriptBindMapSlot(DetailMap, TerrainMaterial, "Raises and lowers the RGB result of the Base Albedo up close.");
    addField( "detailSize", TypeF32, Offset( mDetailSize, TerrainMaterial ), "Used to scale the detail map to the material square" );
    addField( "detailSize", TypeF32, Offset( mDetailSize, TerrainMaterial ), "Used to scale the detail map to the material square" );
    addField( "detailStrength", TypeF32, Offset( mDetailStrength, TerrainMaterial ), "Exponentially sharpens or lightens the detail map rendering on the material" );
    addField( "detailStrength", TypeF32, Offset( mDetailStrength, TerrainMaterial ), "Exponentially sharpens or lightens the detail map rendering on the material" );

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

@@ -85,6 +85,11 @@ protected:
    ///
    ///
    F32 mParallaxScale;
    F32 mParallaxScale;
 
 
+   /// Depth for blending the textures using the new
+   /// blending method. Higher numbers = larger blend
+   /// radius.
+   F32 mBlendDepth;
+
 public:
 public:
 
 
    TerrainMaterial();
    TerrainMaterial();
@@ -122,6 +127,8 @@ public:
 
 
    F32 getParallaxScale() const { return mParallaxScale; }
    F32 getParallaxScale() const { return mParallaxScale; }
 
 
+   F32 getBlendDepth() const { return mBlendDepth; }
+
    bool getIsSRGB() const { return mIsSRGB; }
    bool getIsSRGB() const { return mIsSRGB; }
 
 
    bool getInvertRoughness() const { return mInvertRoughness; }
    bool getInvertRoughness() const { return mInvertRoughness; }