summaryrefslogtreecommitdiff
path: root/Runtime/Terrain/SplatMaterials.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Terrain/SplatMaterials.cpp')
-rw-r--r--Runtime/Terrain/SplatMaterials.cpp228
1 files changed, 228 insertions, 0 deletions
diff --git a/Runtime/Terrain/SplatMaterials.cpp b/Runtime/Terrain/SplatMaterials.cpp
new file mode 100644
index 0000000..3a3acb7
--- /dev/null
+++ b/Runtime/Terrain/SplatMaterials.cpp
@@ -0,0 +1,228 @@
+#include "UnityPrefix.h"
+#include "SplatMaterials.h"
+
+#if ENABLE_TERRAIN
+
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Shaders/ShaderNameRegistry.h"
+#include "Runtime/Utilities/ArrayUtility.h"
+
+namespace SplatMaterials_Static
+{
+static SHADERPROP(MainTex);
+static SHADERPROP(Control);
+} // namespace SplatMaterials_Static
+
+static const char* kSplatNameStrings[4] = {"_Splat0","_Splat1","_Splat2","_Splat3"};
+static const char* kSplatNormalNameStrings[4] = {"_Normal0","_Normal1","_Normal2","_Normal3"};
+
+
+SplatMaterials::SplatMaterials (PPtr<TerrainData> terrain)
+: m_CurrentTemplateMaterial(NULL)
+{
+ m_TerrainData = terrain;
+ for(int i=0;i<kTerrainShaderCount;i++)
+ m_Shaders[i] = NULL;
+ m_BaseMapMaterial = NULL;
+ for(int i = 0; i < 32; i++)
+ m_AllocatedMaterials[i] = NULL;
+}
+
+SplatMaterials::~SplatMaterials ()
+{
+ Cleanup ();
+}
+
+void SplatMaterials::Cleanup ()
+{
+ for (int s = 0; s < ARRAY_SIZE(m_Shaders); ++s)
+ {
+ m_Shaders[s] = NULL;
+ }
+ for (int x = 0; x < ARRAY_SIZE(m_AllocatedMaterials); ++x)
+ {
+ DestroySingleObject (m_AllocatedMaterials[x]);
+ m_AllocatedMaterials[x] = NULL;
+ }
+ DestroySingleObject (m_BaseMapMaterial);
+ m_BaseMapMaterial = NULL;
+ m_CurrentTemplateMaterial = NULL;
+}
+
+
+
+void SplatMaterials::LoadSplatShaders (Material* templateMat)
+{
+ // If material is already created, and our template material is still the same:
+ // nothing to do
+ if ((m_Shaders[0] != NULL) && (templateMat == m_CurrentTemplateMaterial))
+ return;
+
+ // Material template has changed; recreate internal materials
+ if (templateMat != m_CurrentTemplateMaterial)
+ {
+ Cleanup();
+ m_CurrentTemplateMaterial = templateMat;
+ }
+
+ Shader* templateShader = NULL;
+ if (templateMat)
+ templateShader = templateMat->GetShader();
+ if (templateShader)
+ {
+ // Shader in the template is for the first pass;
+ // additive pass and basemap are queried from dependencies
+ m_Shaders[kTerrainShaderFirst] = templateShader;
+ m_Shaders[kTerrainShaderAdd] = templateShader->GetDependency("AddPassShader");
+ m_Shaders[kTerrainShaderBaseMap] = templateShader->GetDependency("BaseMapShader");
+ }
+
+ ScriptMapper& sm = GetScriptMapper ();
+
+ // Note: No good reason to keep "Lightmap-" in the built-in terrain shader names, except
+ // to be able to run Unity 2.x content on regression rig with 3.0 player. The shader
+ // names just have to match _some_ existing built-in shader in Unity 2.x.
+
+ if (m_Shaders[kTerrainShaderBaseMap] == NULL)
+ m_Shaders[kTerrainShaderBaseMap] = sm.FindShader("Diffuse");
+ if (m_Shaders[kTerrainShaderFirst] == NULL)
+ m_Shaders[kTerrainShaderFirst] = sm.FindShader("Hidden/TerrainEngine/Splatmap/Lightmap-FirstPass");
+ if (m_Shaders[kTerrainShaderAdd] == NULL)
+ m_Shaders[kTerrainShaderAdd] = sm.FindShader("Hidden/TerrainEngine/Splatmap/Lightmap-AddPass");
+
+ bool shaderNotFound = false;
+ for (int i = kTerrainShaderFirst; i <= kTerrainShaderAdd; ++i)
+ {
+ if (m_Shaders[i] == NULL)
+ {
+ shaderNotFound = true;
+ m_Shaders[i] = sm.FindShader("Diffuse");
+ }
+ }
+
+ if (shaderNotFound)
+ {
+ ErrorString("Unable to find shaders used for the terrain engine. Please include Nature/Terrain/Diffuse shader in Graphics settings.");
+ }
+}
+
+
+
+
+Material *SplatMaterials::GetSplatBaseMaterial (Material* templateMat)
+{
+ using namespace SplatMaterials_Static;
+
+ LoadSplatShaders (templateMat);
+
+ Material *mat = m_BaseMapMaterial;
+ if (mat && mat->GetTexture(kSLPropMainTex) == NULL)
+ {
+ DestroySingleObject( mat );
+ mat = NULL;
+ }
+ if (!mat) {
+ mat = Material::CreateMaterial (*m_Shaders[kTerrainShaderBaseMap], Object::kHideAndDontSave);
+ mat->SetTexture( kSLPropMainTex, m_TerrainData->GetSplatDatabase().GetBasemap() );
+ m_BaseMapMaterial = mat;
+ }
+ if (templateMat)
+ {
+ mat->CopyPropertiesFromMaterial (*templateMat);
+ mat->SetTexture (kSLPropMainTex, m_TerrainData->GetSplatDatabase().GetBasemap());
+ }
+ return mat;
+}
+
+Material **SplatMaterials::GetMaterials (Material* templateMat, int &materialCount)
+{
+ using namespace SplatMaterials_Static;
+
+ TerrainData *terrainData = m_TerrainData;
+ LoadSplatShaders (templateMat);
+
+ const bool setNormalMaps = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_0_a1);
+
+ int materialIndex = -1;
+ Material *splatMaterial = NULL;
+ int actualNbSplats = terrainData->GetSplatDatabase().GetDepth();
+ int nbSplats = actualNbSplats>1?actualNbSplats: 1;
+ for( int i = 0; i < nbSplats; ++i )
+ {
+ if (i / 4 != materialIndex)
+ {
+ materialIndex = i / 4;
+
+ splatMaterial = m_AllocatedMaterials[materialIndex];
+ if (splatMaterial == NULL)
+ {
+ const int shaderIndex = (materialIndex != 0) ? kTerrainShaderAdd : kTerrainShaderFirst;
+ Shader* shader = m_Shaders[shaderIndex];
+ splatMaterial = Material::CreateMaterial (*shader, Object::kHideAndDontSave);
+ splatMaterial->SetCustomRenderQueue(splatMaterial->GetActualRenderQueue() + materialIndex);
+ m_AllocatedMaterials[materialIndex] = splatMaterial;
+ }
+
+ if (templateMat)
+ splatMaterial->CopyPropertiesFromMaterial (*templateMat);
+
+ if( splatMaterial->HasProperty(kSLPropMainTex) )
+ splatMaterial->SetTexture(kSLPropMainTex, terrainData->GetSplatDatabase().GetBasemap() );
+
+ // As soon as our shader does not support 4 splats per pass,
+ // that means (at least currently) it's a single pass base map.
+ // So stop adding materials.
+ if( splatMaterial->GetTag("SplatCount",false,"") != "4" )
+ break;
+ }
+
+ int localSplatIndex = i - (materialIndex * 4);
+ if (materialIndex < actualNbSplats)
+ {
+ splatMaterial->SetTexture( kSLPropControl, terrainData->GetSplatDatabase().GetAlphaTexture(materialIndex));
+ }
+ else
+ splatMaterial->SetTexture( kSLPropControl, NULL);
+ SetupSplat(*splatMaterial, localSplatIndex, i, setNormalMaps);
+ }
+
+ materialCount = materialIndex + 1;
+
+ return m_AllocatedMaterials;
+}
+
+
+void SplatMaterials::SetupSplat (Material &m, int splatIndex, int index, bool setNormalMap)
+{
+ ShaderLab::FastPropertyName slName = ShaderLab::Property(kSplatNameStrings[splatIndex]);
+ ShaderLab::FastPropertyName slNormalName = ShaderLab::Property(kSplatNormalNameStrings[splatIndex]);
+ const bool hasTex = m.HasProperty(slName);
+ const bool hasNormalMap = setNormalMap && m.HasProperty(slNormalName);
+
+ if (index < m_TerrainData->GetSplatDatabase().GetDepth())
+ {
+ const SplatPrototype& splat = m_TerrainData->GetSplatDatabase().GetSplatPrototypes()[index];
+
+ Vector3f heightmapSize = m_TerrainData->GetHeightmap().GetSize();
+ Vector2f splatScale(heightmapSize.x / splat.tileSize.x, heightmapSize.z / splat.tileSize.y);
+ Vector2f splatOffset(splat.tileOffset.x / heightmapSize.x * splatScale.x, splat.tileOffset.y / heightmapSize.z * splatScale.y);
+
+ if (hasTex)
+ {
+ m.SetTexture (slName, splat.texture);
+ m.SetTextureScale (slName, splatScale);
+ m.SetTextureOffset (slName, splatOffset);
+ }
+ if (hasNormalMap)
+ m.SetTexture (slNormalName, splat.normalMap);
+ }
+ else
+ {
+ if (hasTex)
+ m.SetTexture (slName, NULL);
+ if (hasNormalMap)
+ m.SetTexture (slNormalName, NULL);
+ }
+}
+
+#endif // ENABLE_TERRAIN