Files
2024-11-20 15:21:28 +01:00

1199 lines
63 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
#if UNITY_EDITOR
using UnityEditorInternal;
using UnityEditor;
#endif
using UnityEngine;
using System.Linq;
namespace Gaia
{
public enum ImageMaskOperation { CollisionMask = 5, DistanceMask = 1, HeightMask = 2, ImageMask = 0, NoiseMask = 4, SlopeMask = 3, Smooth = 8, StrengthTransform = 6, TerrainTexture = 9, WorldBiomeMask = 11, PROConcaveConvex = 10, PROHydraulicErosion = 7 }
public enum ImageMaskBlendMode { Multiply, GreaterThan, SmallerThan, Add, Subtract }
public enum ImageMaskDistanceMaskAxes {[Description("XZ Circular")] XZ, [Description("X only")] X, [Description("Z Only")] Z }
public enum ImageMaskInfluence { Local, Global }
/// <summary>
/// Toggle between two different ways of handling height masks
/// Absolute will store the minimum and maximum value for the mask as absolute values relative to the sea levele
/// Relative will store the minimum and maximum value for the mask as absolute values relative to the sea levele
/// </summary>
public enum HeightMaskType { WorldSpace, RelativeToSeaLevel }
public enum HeightMaskUnit { Meter, Percent}
[System.Serializable]
public class ImageMask
{
public bool m_active = true;
public bool m_invert = false;
public bool m_hasErrors = false;
public ImageMaskInfluence m_influence = ImageMaskInfluence.Local;
public ImageMaskOperation m_operation;
public ImageMaskBlendMode m_blendMode;
public float m_strength = 1f;
public float m_seaLevel = 0f;
//The maximum terrain height, NOT the theoretical maximum height, but the highest measured physical point on the terrain
public float m_maxWorldHeight = 0f;
//The minimum terrain height, NOT the theoretical minimum height, but the lowest measured physical point on the terrain
public float m_minWorldHeight = 0f;
public float m_xOffSet = 0f;
public float m_zOffSet = 0f;
public float m_xOffSetScalar = 0f;
public float m_zOffSetScalar = 0f;
//The current multi-terrain op we are working on - used to get heightmap, normalmap etc. for the affected area
[NonSerialized]
public GaiaMultiTerrainOperation m_multiTerrainOperation;
//Image Mask specific
public Texture2D ImageMaskTexture
{
get{
if (m_imageMaskTexture == null)
{
if (!string.IsNullOrEmpty(m_imageMaskTextureGUID))
{
#if UNITY_EDITOR
m_imageMaskTexture = (Texture2D)AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(m_imageMaskTextureGUID), typeof(Texture2D));
#endif
}
}
return m_imageMaskTexture;
}
set{
if (value != m_imageMaskTexture)
{
m_imageMaskTexture = value;
if (value != null)
{
#if UNITY_EDITOR
m_imageMaskTextureGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(value));
#endif
}
else
{
m_imageMaskTextureGUID = "";
}
}
}
}
private Texture2D m_imageMaskTexture;
[SerializeField]
private string m_imageMaskTextureGUID;
public GaiaConstants.ImageMaskFilterMode m_imageMaskFilterMode;
public Color m_imageMaskColorSelectionColor = Color.white;
public float m_imageMaskColorSelectionAccuracy = 0.5f;
//distance Mask specific
public AnimationCurve m_distanceMaskCurve = new AnimationCurve(new Keyframe[2] { new Keyframe() { time = 0, value = 1, weightedMode = WeightedMode.None }, new Keyframe() { time = 1, value = 0, weightedMode = WeightedMode.None } });
//height Mask specific
public AnimationCurve m_heightMaskCurve = new AnimationCurve(new Keyframe[2] { new Keyframe() { time = 0, value = 0, weightedMode = WeightedMode.None }, new Keyframe() { time = 1, value = 1, weightedMode = WeightedMode.None } });
//strength Transform specific
public AnimationCurve m_strengthTransformCurve = NewAnimCurveStraightUpwards();
public HeightMaskType m_heightMaskType = HeightMaskType.RelativeToSeaLevel;
[SerializeField]
private HeightMaskUnit m_heightMaskUnit = HeightMaskUnit.Percent;
public float m_relativeHeightMin = 25f;
public float m_relativeHeightMax = 75f;
public HeightMaskUnit HeightMaskUnit
{
get
{
return m_heightMaskUnit;
}
set
{
if (value != m_heightMaskUnit)
{
//Get the meter value for 1% of the terrain height - makes for an easy conversion.
float worldHeightDifference = m_maxWorldHeight - m_minWorldHeight;
float worldHeightPercentInMeter = worldHeightDifference / 100f;
if (value == HeightMaskUnit.Meter)
{
//Value was in % before - convert all values to meter accordingly
m_absoluteHeightMin *= worldHeightPercentInMeter;
m_absoluteHeightMax *= worldHeightPercentInMeter;
m_relativeHeightMin *= worldHeightPercentInMeter;
m_relativeHeightMax *= worldHeightPercentInMeter;
}
else
{
//Value was in Meter before - convert all values to % accordingly
m_absoluteHeightMin /= worldHeightPercentInMeter;
m_absoluteHeightMax /= worldHeightPercentInMeter;
m_relativeHeightMin /= worldHeightPercentInMeter;
m_relativeHeightMax /= worldHeightPercentInMeter;
}
}
m_heightMaskUnit = value;
}
}
//The absolute minimum height for the heightmask selection, e.g. "the selection starts at 50 meters"
public float m_absoluteHeightMin = 50;
//The absolute maximum height for the heightmask selection, e.g. "the selection ends at 150 meters"
public float m_absoluteHeightMax = 150;
//This is a legacy field that is not really used in the height mask anymore, but serves as a flag to detect whether
//a height mask has been created under an older data structure - it will then automatically be migrated when the mask is used.
public float m_seaLevelRelativeHeightMin = -109876.54321f;
//This is a legacy field that is not really used in the height mask anymore, but serves as a flag to detect whether
//a height mask has been created under an older data structure - it will then automatically be migrated when the mask is used.
public float m_seaLevelRelativeHeightMax = 109876.54321f;
public bool tree1active = false;
public bool tree2active = false;
public AnimationCurve m_slopeMaskCurve = NewAnimCurveStraightUpwards();
public float m_slopeMin = 0.0f;
public float m_slopeMax = 0.1f;
public ImageMaskDistanceMaskAxes m_distanceMaskAxes;
public GaiaNoiseSettings m_gaiaNoiseSettings = new GaiaNoiseSettings();
#if UNITY_EDITOR
public NoiseSettings m_noiseSettings;
public NoiseToolSettings m_noiseToolSettings = new NoiseToolSettings();
public NoiseSettingsGUI noiseSettingsGUI;
#endif
public bool m_ShowNoiseTransformSettings = false;
public bool m_ShowNoisePreviewTexture = true;
public bool m_noisePreviewTextureLocked = false;
public bool m_ShowNoiseTypeSettings = false;
public bool m_scaleNoiseToTerrainSize = false;
private Texture2D m_distanceMaskCurveTexture;
private Texture2D distanceMaskCurveTexture
{
get
{
return ImageProcessing.CreateMaskCurveTexture(ref m_distanceMaskCurveTexture);
}
}
private Texture2D m_heightMaskCurveTexture;
private Texture2D heightMaskCurveTexture
{
get
{
return ImageProcessing.CreateMaskCurveTexture(ref m_heightMaskCurveTexture);
}
}
private Texture2D m_slopeMaskCurveTexture;
private Texture2D slopeMaskCurveTexture
{
get
{
return ImageProcessing.CreateMaskCurveTexture(ref m_slopeMaskCurveTexture);
}
}
private Texture2D m_strengthTransformCurveTexture;
private Texture2D strengthTransformCurveTexture
{
get
{
return ImageProcessing.CreateMaskCurveTexture(ref m_strengthTransformCurveTexture);
}
}
//collision mask specific
public bool m_collisionMaskExpanded = true;
public CollisionMask[] m_collisionMasks = new CollisionMask[0];
#if UNITY_EDITOR
public ReorderableList m_reorderableCollisionMaskList;
#endif
#region Erosion Settings
//Eroder class reference for the erosion feature
#if UNITY_EDITOR && GAIA_PRO_PRESENT
private HydraulicEroder m_Eroder = null;
#endif
public GaiaConstants.ErosionMaskOutput m_erosionMaskOutput = GaiaConstants.ErosionMaskOutput.Sediment;
public float m_erosionSimScale = 9f;
public float m_erosionHydroTimeDelta = 0.05f;
public int m_erosionHydroIterations = 15;
public float m_erosionThermalTimeDelta = 0.01f;
public int m_erosionThermalIterations = 80;
public int m_erosionThermalReposeAngle = 80;
public float m_erosionPrecipRate = 0.5f;
public float m_erosionEvaporationRate = 0.5f;
public float m_erosionFlowRate = 0.5f;
public float m_erosionSedimentCapacity = 0.5f;
public float m_erosionSedimentDepositRate = 0.8f;
public float m_erosionSedimentDissolveRate = 0.5f;
public float m_erosionRiverBankDepositRate = 7.0f;
public float m_erosionRiverBankDissolveRate = 5.0f;
public float m_erosionRiverBedDepositRate = 5.0f;
public float m_erosionRiverBedDissolveRate = 5.0f;
public bool m_erosionShowAdvancedUI;
public bool m_erosionShowThermalUI;
public bool m_erosionShowWaterUI;
public bool m_erosionShowSedimentUI;
public bool m_erosionShowRiverBankUI;
#endregion
//smooth settings
public float m_smoothVerticality = 0f;
public float m_smoothBlurRadius = 1f;
//Texture mask settings
//public int m_textureLayerId = 0;
public string m_textureMaskSpawnRuleGUID = "";
public static SpawnRule[] m_allTextureSpawnRules;
public static Spawner[] m_allTextureSpawners;
public static string[] m_allTextureSpawnRuleNames;
public static int[] m_allTextureSpawnRuleIndices;
//Convex Concave settings
public float m_concavity = 1f;
public float m_concavityFeatureSize = 10f;
private ComputeShader m_concavityShader;
public bool m_foldedOut = true;
public string m_selectedWorldBiomeMaskGUID;
public void FreeTextureReferences()
{
m_imageMaskTexture = null;
}
public void CheckHeightMaskMigration()
{
if (m_seaLevelRelativeHeightMax != 109876.54321f || m_seaLevelRelativeHeightMin != -109876.54321f)
{
//This is a height mask created under the old height mask model, needs to be migrated
switch (m_heightMaskType)
{
case HeightMaskType.WorldSpace:
//This height mask was using the old "Absolute" setting before
//This equals "Relative to Sea Level in Meters" in the new logic
m_heightMaskType = HeightMaskType.RelativeToSeaLevel;
m_heightMaskUnit = HeightMaskUnit.Meter;
m_relativeHeightMin = m_seaLevelRelativeHeightMin;
m_relativeHeightMax = m_seaLevelRelativeHeightMax;
m_absoluteHeightMin = m_relativeHeightMin + m_seaLevel;
m_absoluteHeightMax = m_relativeHeightMax + m_seaLevel;
break;
case HeightMaskType.RelativeToSeaLevel:
//This height mask was using the old "Relative" setting before
//This equals "World Space in Percent" in the new logic
m_heightMaskType = HeightMaskType.WorldSpace;
m_heightMaskUnit = HeightMaskUnit.Percent;
m_absoluteHeightMin = m_relativeHeightMin;
m_absoluteHeightMax = m_relativeHeightMax;
float worldHeightDifference = Mathf.Max(1f,m_maxWorldHeight - m_minWorldHeight);
float worldHeightPercentInMeter = worldHeightDifference / 100f;
float seaLevelInPercent = m_seaLevel / worldHeightPercentInMeter;
m_relativeHeightMin = m_absoluteHeightMin - seaLevelInPercent;
m_relativeHeightMax = m_absoluteHeightMax - seaLevelInPercent;
break;
}
//fill in the magic numbers to mark the migration as complete
m_seaLevelRelativeHeightMin = -109876.54321f;
m_seaLevelRelativeHeightMax = 109876.54321f;
}
}
/// <summary>
/// Applies the mask to an input render texture and returns the result as render texture.
/// </summary>
/// <param name="inputTexture">The input texture</param>
/// <returns>The processed output in a render texture.</returns>
public RenderTexture Apply(RenderTexture inputTexture, RenderTexture outputTexture)
{
RenderTexture currentRT = RenderTexture.active;
#if UNITY_EDITOR
#if GAIA_PRO_PRESENT
//clean up eroder if not in use anymore
if (m_Eroder != null && m_operation != ImageMaskOperation.PROHydraulicErosion)
{
ClearEroder();
}
#endif
Material filterMat = GetCurrentFXFilterMaterial();
if (filterMat == null)
{
Debug.LogWarning("Could not find a filter material for operation " + m_operation.ToString());
return inputTexture;
}
filterMat.SetTexture("_InputTex", inputTexture);
filterMat.SetFloat("_Strength", m_strength);
if (m_operation != ImageMaskOperation.PROHydraulicErosion)
{
filterMat.SetInt("_Invert", m_invert ? 1 : 0);
}
else
{
//Special treatement for the hydraulic erosion mask: Flip the invert flag because the erosion map data seems to be inverted already
filterMat.SetInt("_Invert", m_invert ? 0 : 1);
}
if (m_operation == ImageMaskOperation.NoiseMask && IsDefaultStrenghtCurve())
{
m_strengthTransformCurve = NewAnimCurveDefaultNoise();
}
ImageProcessing.BakeCurveTexture(m_strengthTransformCurve, strengthTransformCurveTexture);
filterMat.SetTexture("_HeightTransformTex", strengthTransformCurveTexture);
switch (m_operation)
{
case ImageMaskOperation.ImageMask:
#if !GAIA_PRO_PRESENT
if (m_imageMaskFilterMode != GaiaConstants.ImageMaskFilterMode.PROColorSelection)
{
#endif
filterMat.SetTexture("_ImageMaskTex", ImageMaskTexture);
filterMat.SetInt("_FilterMode", (int)m_imageMaskFilterMode);
filterMat.SetColor("_Color", m_imageMaskColorSelectionColor);
filterMat.SetFloat("_ColorAccuracy", m_imageMaskColorSelectionAccuracy);
filterMat.SetFloat("_XOffset", m_xOffSetScalar);
filterMat.SetFloat("_ZOffset", m_zOffSetScalar);
Graphics.Blit(inputTexture, outputTexture, filterMat, (int)m_blendMode);
filterMat.SetTexture("_ImageMaskTex", null);
#if !GAIA_PRO_PRESENT
}
else
{
Graphics.Blit(inputTexture, outputTexture);
}
#endif
break;
case ImageMaskOperation.DistanceMask:
ImageProcessing.BakeCurveTexture(m_distanceMaskCurve, distanceMaskCurveTexture);
filterMat.SetTexture("_DistanceMaskTex", distanceMaskCurveTexture);
filterMat.SetFloat("_XOffset", m_xOffSetScalar);
filterMat.SetFloat("_ZOffset", m_zOffSetScalar);
filterMat.SetFloat("_AxisMode", (int)m_distanceMaskAxes);
Graphics.Blit(inputTexture, outputTexture, filterMat, (int)m_blendMode);
filterMat.SetTexture("_DistanceMaskTex", null);
break;
case ImageMaskOperation.HeightMask:
ImageProcessing.BakeCurveTexture(m_heightMaskCurve, heightMaskCurveTexture);
filterMat.SetTexture("_HeightMapTex", m_multiTerrainOperation.RTheightmap);
filterMat.SetTexture("_HeightMaskTex", heightMaskCurveTexture);
//calculate the correct scalar min max height values according to the current terrain and the sea level
Terrain currentTerrain = m_multiTerrainOperation.m_originTerrain;
float scalarSeaLevel = Mathf.InverseLerp(0, currentTerrain.terrainData.size.y, m_seaLevel);
float m_scalarMaxHeight = 0.5f;
float m_scalarMinHeight = 0f;
float minHeightInMeters = m_absoluteHeightMin;
float maxHeightInMeters = m_absoluteHeightMax;
float worldHeightDifference = m_maxWorldHeight - m_minWorldHeight;
float worldHeightPercentInMeter = worldHeightDifference / 100f;
if (m_heightMaskType == HeightMaskType.WorldSpace)
{
if (m_heightMaskUnit == HeightMaskUnit.Percent)
{
minHeightInMeters *= worldHeightPercentInMeter;
maxHeightInMeters *= worldHeightPercentInMeter;
}
}
else
{
if (m_heightMaskUnit == HeightMaskUnit.Percent)
{
minHeightInMeters = (m_relativeHeightMin * worldHeightPercentInMeter) + m_seaLevel;
maxHeightInMeters = (m_relativeHeightMax * worldHeightPercentInMeter) + m_seaLevel;
}
else
{
minHeightInMeters = m_relativeHeightMin + m_seaLevel;
maxHeightInMeters = m_relativeHeightMax + m_seaLevel;
}
}
m_scalarMaxHeight = Mathf.InverseLerp(0, currentTerrain.terrainData.size.y, maxHeightInMeters);
m_scalarMinHeight = Mathf.InverseLerp(0, currentTerrain.terrainData.size.y, minHeightInMeters);
//transfer the scalar 0..1 value to -0.5..0.5 as this is how it is used in the shader
m_scalarMaxHeight = Mathf.Lerp(0, 0.5f, m_scalarMaxHeight);
m_scalarMinHeight = Mathf.Lerp(0, 0.5f, m_scalarMinHeight);
filterMat.SetFloat("_MinHeight", m_scalarMinHeight);
filterMat.SetFloat("_MaxHeight", m_scalarMaxHeight);
Graphics.Blit(inputTexture, outputTexture, filterMat, (int)m_blendMode);
filterMat.SetTexture("_HeightMapTex", null);
filterMat.SetTexture("_HeightMaskTex", null);
break;
case ImageMaskOperation.SlopeMask:
ImageProcessing.BakeCurveTexture(m_slopeMaskCurve, slopeMaskCurveTexture);
filterMat.SetTexture("_NormalMapTex", m_multiTerrainOperation.RTnormalmap);
filterMat.SetTexture("_SlopeMaskTex", slopeMaskCurveTexture);
filterMat.SetFloat("_MinSlope", m_slopeMin);
filterMat.SetFloat("_MaxSlope", m_slopeMax);
Graphics.Blit(inputTexture, outputTexture, filterMat, (int)m_blendMode);
filterMat.SetTexture("_NormalMapTex", null);
filterMat.SetTexture("_SlopeMaskTex", null);
break;
case ImageMaskOperation.NoiseMask:
//noise settings can be null when the mask was never viewed in the inspector, e.g. from autospawning
if (m_noiseSettings == null)
{
m_noiseSettings = (NoiseSettings)ScriptableObject.CreateInstance(typeof(NoiseSettings));
//Try to initialize from our own Gaia Noise Settings
m_noiseSettings.transformSettings.translation = m_gaiaNoiseSettings.m_translation;
m_noiseSettings.transformSettings.rotation = m_gaiaNoiseSettings.m_rotation;
m_noiseSettings.transformSettings.scale = m_gaiaNoiseSettings.m_scale;
m_noiseSettings.domainSettings.noiseTypeName = m_gaiaNoiseSettings.m_noiseTypeName;
m_noiseSettings.domainSettings.noiseTypeParams = m_gaiaNoiseSettings.m_noiseTypeParams;
m_noiseSettings.domainSettings.fractalTypeName = m_gaiaNoiseSettings.m_fractalTypeName;
m_noiseSettings.domainSettings.fractalTypeParams = m_gaiaNoiseSettings.m_fractalTypeParams;
}
float previewSize = 1 / m_multiTerrainOperation.m_originTerrain.terrainData.size.x;
// get proper noise material from current noise settings
NoiseSettings noiseSettings = m_noiseSettings;
Material matNoise = NoiseUtils.GetDefaultBlitMaterial(m_noiseSettings);
// setup the noise material with values in noise settings
m_noiseSettings.SetupMaterial(matNoise);
// convert brushRotation to radians
float brushRotation = 0;
//brushRotation *= Mathf.PI / 180;
Vector3 brushPosWS = m_multiTerrainOperation.m_originTransform.position + (Vector3)TerrainLoaderManager.Instance.GetOrigin();
float brushSize = m_multiTerrainOperation.m_range;
//Adjust scaling depending on whether we want to scale up with terrain size
if (m_scaleNoiseToTerrainSize)
{
bool isWorldSpace = (m_noiseToolSettings.coordSpace == CoordinateSpace.World);
brushSize = isWorldSpace ? brushSize * previewSize : 1;
brushPosWS = isWorldSpace ? brushPosWS * previewSize : Vector3.zero;
}
else
{
brushSize = m_multiTerrainOperation.m_range / 512;
brushPosWS = (brushPosWS / m_multiTerrainOperation.m_originTerrain.terrainData.size.x) * ( m_multiTerrainOperation.m_originTerrain.terrainData.size.x / m_multiTerrainOperation.m_range) * brushSize ;
}
// // override noise transform
Quaternion rotQ = Quaternion.AngleAxis(-brushRotation, Vector3.up);
Matrix4x4 translation = Matrix4x4.Translate(brushPosWS);
Matrix4x4 rotation = Matrix4x4.Rotate(rotQ);
Matrix4x4 scale = Matrix4x4.Scale(Vector3.one * brushSize);
Matrix4x4 noiseToWorld = translation * scale;
matNoise.SetMatrix(NoiseSettings.ShaderStrings.transform,
m_noiseSettings.trs * noiseToWorld);
RenderTexture noiseRT = RenderTexture.GetTemporary(m_multiTerrainOperation.RTheightmap.descriptor);
int noisePass = NoiseUtils.kNumBlitPasses * NoiseLib.GetNoiseIndex(m_noiseSettings.domainSettings.noiseTypeName);
Graphics.Blit(inputTexture, noiseRT, matNoise, noisePass);
//now that we have the noise, put it in a simple image mask operation to get the final result
filterMat.SetTexture("_ImageMaskTex", noiseRT);
Graphics.Blit(inputTexture, outputTexture, filterMat, (int)m_blendMode);
RenderTexture.ReleaseTemporary(noiseRT);
filterMat.SetTexture("_ImageMaskTex", null);
noiseRT = null;
break;
case ImageMaskOperation.CollisionMask:
RenderTexture.active = currentRT;
m_multiTerrainOperation.GetCollisionMask(m_collisionMasks);
filterMat.SetTexture("_ImageMaskTex", m_multiTerrainOperation.RTbakedMask);
Graphics.Blit(inputTexture, outputTexture, filterMat, (int)m_blendMode);
filterMat.SetTexture("_ImageMaskTex", null);
break;
case ImageMaskOperation.StrengthTransform:
Graphics.Blit(inputTexture, outputTexture, filterMat, (int)m_blendMode);
break;
case ImageMaskOperation.PROHydraulicErosion:
#if GAIA_PRO_PRESENT
m_multiTerrainOperation.RTheightmap.filterMode = FilterMode.Bilinear;
Material erosionMat = new Material(Shader.Find("Hidden/GaiaPro/SimpleHeightBlend"));
if (m_Eroder == null)
{
m_Eroder = new HydraulicEroder();
m_Eroder.OnEnable();
}
//ImageProcessing.WriteRenderTexture("D:\\ErosionHeightInput.png", m_multiTerrainOperation.RTheightmap);
m_Eroder.inputTextures["Height"] = m_multiTerrainOperation.RTheightmap;
Vector2 texelSize = new Vector2(m_multiTerrainOperation.m_originTerrain.terrainData.size.x / m_multiTerrainOperation.m_originTerrain.terrainData.heightmapResolution,
m_multiTerrainOperation.m_originTerrain.terrainData.size.z / m_multiTerrainOperation.m_originTerrain.terrainData.heightmapResolution);
//apply Erosion settings
m_Eroder.m_ErosionSettings.m_SimScale.value = m_erosionSimScale;
m_Eroder.m_ErosionSettings.m_HydroTimeDelta.value = m_erosionHydroTimeDelta;
m_Eroder.m_ErosionSettings.m_HydroIterations.value = m_erosionHydroIterations;
m_Eroder.m_ErosionSettings.m_ThermalTimeDelta = m_erosionThermalTimeDelta;
m_Eroder.m_ErosionSettings.m_ThermalIterations = m_erosionThermalIterations;
m_Eroder.m_ErosionSettings.m_ThermalReposeAngle = m_erosionThermalReposeAngle;
m_Eroder.m_ErosionSettings.m_PrecipRate.value = m_erosionPrecipRate;
m_Eroder.m_ErosionSettings.m_EvaporationRate.value = m_erosionEvaporationRate;
m_Eroder.m_ErosionSettings.m_FlowRate.value = m_erosionFlowRate;
m_Eroder.m_ErosionSettings.m_SedimentCapacity.value = m_erosionSedimentCapacity;
m_Eroder.m_ErosionSettings.m_SedimentDepositRate.value = m_erosionSedimentDepositRate;
m_Eroder.m_ErosionSettings.m_SedimentDissolveRate.value = m_erosionSedimentDissolveRate;
m_Eroder.m_ErosionSettings.m_RiverBankDepositRate.value = m_erosionRiverBankDepositRate;
m_Eroder.m_ErosionSettings.m_RiverBankDissolveRate.value = m_erosionRiverBankDissolveRate;
m_Eroder.m_ErosionSettings.m_RiverBedDepositRate.value = m_erosionRiverBedDepositRate;
m_Eroder.m_ErosionSettings.m_RiverBedDissolveRate.value = m_erosionRiverBedDissolveRate;
//and erode
m_Eroder.ErodeHeightmap(m_multiTerrainOperation.m_originTerrain.terrainData.size, m_multiTerrainOperation.m_terrainDetailBrushTransform.GetBrushXYBounds(), texelSize, false);
Vector4 erosionBrushParams = new Vector4(1f, 0.0f, 0.0f, 0.0f);
//if (activeLocalFilters)
erosionMat.SetTexture("_BrushTex", inputTexture);
//else
// erosionMat.SetTexture("_BrushTex", localinputTexture);
switch (m_erosionMaskOutput)
{
//case GaiaConstants.ErosionMaskOutput.ErodedSediment:
// erosionMat.SetTexture("_NewHeightTex", m_Eroder.outputTextures["Eroded Sediment"]);
// break;
//case GaiaConstants.ErosionMaskOutput.Height:
// erosionMat.SetTexture("_NewHeightTex", m_Eroder.outputTextures["Height"]);
// break;
case GaiaConstants.ErosionMaskOutput.Sediment:
erosionMat.SetTexture("_NewHeightTex", m_Eroder.outputTextures["Sediment"]);
break;
case GaiaConstants.ErosionMaskOutput.WaterFlux:
erosionMat.SetTexture("_NewHeightTex", m_Eroder.outputTextures["Water Flux"]);
break;
//case GaiaConstants.ErosionMaskOutput.WaterLevel:
// erosionMat.SetTexture("_NewHeightTex", m_Eroder.outputTextures["Water Level"]);
// break;
case GaiaConstants.ErosionMaskOutput.WaterVelocity:
erosionMat.SetTexture("_NewHeightTex", m_Eroder.outputTextures["Water Velocity"]);
break;
}
erosionMat.SetVector("_BrushParams", erosionBrushParams);
RenderTexture eroderTempRT = RenderTexture.GetTemporary(m_Eroder.outputTextures["Height"].descriptor);
m_multiTerrainOperation.SetupMaterialProperties(erosionMat, MultiTerrainOperationType.Heightmap);
Graphics.Blit(m_multiTerrainOperation.RTheightmap, eroderTempRT, erosionMat, 0);
filterMat.SetTexture("_InputTex", eroderTempRT);
Graphics.Blit(eroderTempRT, outputTexture, filterMat, (int)m_blendMode);
filterMat.SetTexture("_InputTex", null);
erosionMat.SetTexture("_NewHeightTex", null);
m_Eroder.ReleaseRenderTextures();
RenderTexture.ReleaseTemporary(eroderTempRT);
//ImageProcessing.WriteRenderTexture("D:\\ErosionOutput.png", outputTexture);
#else
Graphics.Blit(inputTexture, outputTexture);
#endif
break;
case ImageMaskOperation.Smooth:
Vector4 brushParams = new Vector4(1f, 0.0f, 0.0f, 0.0f);
//ImageProcessing.WriteRenderTexture("D:\\inputTexture.png", inputTexture);
filterMat.SetTexture("_MainTex", inputTexture);
filterMat.SetTexture("_BrushTex", Texture2D.whiteTexture);
filterMat.SetTexture("_HeightTransformTex", strengthTransformCurveTexture);
filterMat.SetVector("_BrushParams", brushParams);
Vector4 smoothWeights = new Vector4(
Mathf.Clamp01(1.0f - Mathf.Abs(m_smoothVerticality)), // centered
Mathf.Clamp01(-m_smoothVerticality), // min
Mathf.Clamp01(m_smoothVerticality), // max
m_smoothBlurRadius); // kernel size
filterMat.SetVector("_SmoothWeights", smoothWeights);
//Do not set up the UV properties according to the operation. In this case, this would lead to the "smoothness brush"
//being rotated inside our existing mask stack.
filterMat.SetVector("_PCUVToBrushUVScales", new Vector4(1, 0, 0, 1));
filterMat.SetVector("_PCUVToBrushUVOffset", new Vector4(0, 0, 0.0f, 0.0f));
// Two pass blur (first horizontal, then vertical)
//RenderTexture workaround1 = RenderTexture.GetTemporary(m_multiTerrainOperation.RTheightmap.descriptor);
RenderTexture tmpsmoothRT = new RenderTexture(m_multiTerrainOperation.RTheightmap.descriptor);
//tmpsmoothRT = RenderTexture.GetTemporary(m_multiTerrainOperation.RTheightmap.descriptor);
Graphics.Blit(inputTexture, tmpsmoothRT, filterMat, 0);
Graphics.Blit(tmpsmoothRT, outputTexture, filterMat, 1);
filterMat.SetTexture("_MainTex", null);
filterMat.SetTexture("_BrushTex", null);
filterMat.SetTexture("_HeightTransformTex", null);
tmpsmoothRT.Release();
GameObject.DestroyImmediate(tmpsmoothRT);
//RenderTexture.ReleaseTemporary(tmpsmoothRT);
//RenderTexture.ReleaseTemporary(workaround1);
break;
case ImageMaskOperation.TerrainTexture:
SpawnRule sr = m_allTextureSpawnRules.FirstOrDefault(x => x.GUID == m_textureMaskSpawnRuleGUID);
if (sr != null)
{
Spawner spawner = m_allTextureSpawners.FirstOrDefault(x => x.m_settings.m_spawnerRules.Contains(sr));
if (spawner != null)
{
ResourceProtoTexture proto = spawner.m_settings.m_resources.m_texturePrototypes[sr.m_resourceIdx];
TerrainLayer layer = TerrainHelper.GetLayerFromPrototype(proto);
m_multiTerrainOperation.GetSplatmap(layer);
filterMat.SetTexture("_ImageMaskTex", m_multiTerrainOperation.RTtextureSplatmap);
Graphics.Blit(inputTexture, outputTexture, filterMat, (int)m_blendMode);
filterMat.SetTexture("_ImageMaskTex", null);
}
}
break;
case ImageMaskOperation.PROConcaveConvex:
#if GAIA_PRO_PRESENT
m_concavityShader = (ComputeShader)(Resources.Load("GaiaConcavity"));
Graphics.Blit(inputTexture, outputTexture, filterMat, (int)m_blendMode);
int kidx = m_concavityShader.FindKernel("ConcavityMultiply");
switch (m_blendMode)
{
case ImageMaskBlendMode.GreaterThan:
kidx = m_concavityShader.FindKernel("ConcavityGreaterThan");
break;
case ImageMaskBlendMode.SmallerThan:
kidx = m_concavityShader.FindKernel("ConcavitySmallerThan");
break;
case ImageMaskBlendMode.Add:
kidx = m_concavityShader.FindKernel("ConcavityAdd");
break;
case ImageMaskBlendMode.Subtract:
kidx = m_concavityShader.FindKernel("ConcavitySubtract");
break;
}
m_concavityShader.SetTexture(kidx, "In_BaseMaskTex", inputTexture);
//ImageProcessing.WriteRenderTexture("D:\\tempOutput.png", m_multiTerrainOperation.RTheightmap);
m_concavityShader.SetTexture(kidx, "In_HeightTex", m_multiTerrainOperation.RTheightmap);
m_concavityShader.SetTexture(kidx, "In_HeightTransformTex", strengthTransformCurveTexture);
m_concavityShader.SetInt("HeightTransformTexResolution", strengthTransformCurveTexture.width - 1);
m_concavityShader.SetTexture(kidx, "OutputTex", outputTexture);
//cs.SetTexture(kidx, "RemapTex", remapTex);
m_concavityShader.SetVector("HeightmapResolution", new Vector2(m_multiTerrainOperation.RTheightmap.width, m_multiTerrainOperation.RTheightmap.height));
m_concavityShader.SetVector("TextureResolution", new Vector4(inputTexture.width, inputTexture.height, m_concavityFeatureSize, m_concavity));
m_concavityShader.Dispatch(kidx, outputTexture.width, outputTexture.height, 1);
//Workaround - the output texture of the compute shader can turn up empty sometimes when used directly in the image mask afterwards
//filterMat.SetTexture("_InputTexture", concavityTemp2);
//filterMat.SetTexture("_ImageMaskTex", inputTexture);
//ImageProcessing.WriteRenderTexture("D:\\tempOutput.png", outputTexture);
//Graphics.Blit(concavityTemp2, outputTexture);//, filterMat, (int)m_blendMode);
//RenderTexture.ReleaseTemporary(concavityTemp1);
//RenderTexture.ReleaseTemporary(concavityTemp2);
m_concavityShader = null;
#else
Graphics.Blit(inputTexture, outputTexture);
#endif
break;
case ImageMaskOperation.WorldBiomeMask:
//fetch the world biome mask
//RenderTexture.active = currentRT;
m_multiTerrainOperation.GetWorldBiomeMask(m_selectedWorldBiomeMaskGUID);
//ImageProcessing.WriteRenderTexture("D:\\input.png", inputTexture);
//ImageProcessing.WriteRenderTexture("D:\\RTcollision.png", m_multiTerrainOperation.RTcollision);
filterMat.SetTexture("_ImageMaskTex", m_multiTerrainOperation.RTbakedMask);
Graphics.Blit(inputTexture, outputTexture, filterMat, (int)m_blendMode);
filterMat.SetTexture("_ImageMaskTex", null);
//ImageProcessing.WriteRenderTexture("D:\\output.png", outputTexture);
break;
default:
break;
}
filterMat.SetTexture("_InputTex", null);
filterMat.SetTexture("_HeightTransformTex", null);
GameObject.DestroyImmediate(filterMat);
//release input texture
//if (inputTexture != null)
//{
// inputTexture.Release();
// GameObject.DestroyImmediate(inputTexture);
// inputTexture = null;
//}
#endif
return outputTexture;
}
public void ClearEroder()
{
#if UNITY_EDITOR && GAIA_PRO_PRESENT
if (m_Eroder != null)
{
m_Eroder.ReleaseRenderTextures();
m_Eroder = null;
}
#endif
}
private float CalculateScalarHeightRelativeToSeaLevel(float heightValue, float scalarSeaLevel)
{
if (heightValue < 0.25f)
{
//The position is below the marked sea level on the slider -> lerp accordingly
heightValue = Mathf.Lerp(0f, scalarSeaLevel, Mathf.InverseLerp(0, 0.25f, heightValue));
}
else
{
//The position is above the marked sea level on the slider -> lerp accordingly
heightValue = Mathf.Lerp(scalarSeaLevel, 1f, Mathf.InverseLerp(0.25f, 1f, heightValue));
}
return heightValue;
}
/// <summary>
/// sets up the default linear strenght transform curve that maps the input 1:1 to the output (equals no transformation at all)
/// </summary>
/// <param name="max">The maximum value at which the curve ends.</param>
/// <returns></returns>
public static AnimationCurve NewAnimCurveStraightUpwards(float max = 1f)
{
return new AnimationCurve(new Keyframe[2] { new Keyframe() {
inTangent = 1,
inWeight = 0,
outTangent = 1,
outWeight = 0,
time = 0,
value = 0,
weightedMode = WeightedMode.None
},
new Keyframe() {
inTangent = 1,
inWeight = 0,
outTangent = 1,
outWeight = 0,
time = 1,
value = max,
weightedMode = WeightedMode.None
} }); ;
}
/// <summary>
/// Checks if the current strenght transform curve is still the default linear curve set at initialization
/// </summary>
/// <returns>true if original curve, false if the user altered it</returns>
private bool IsDefaultStrenghtCurve()
{
//Get a default anim curve for comparison
AnimationCurve defaultCurve = NewAnimCurveStraightUpwards();
//different number of keys? => it is a different curve
if (m_strengthTransformCurve.keys.Length != defaultCurve.keys.Length)
{
return false;
}
//keyframe data different from original? => it is a different curve
for (int i = 0; i < m_strengthTransformCurve.keys.Length; i++)
{
Keyframe current = m_strengthTransformCurve.keys[i];
Keyframe original = defaultCurve.keys[i];
if (current.inTangent != original.inTangent ||
current.inWeight != original.inWeight ||
current.outTangent != original.outTangent ||
current.outWeight != original.outWeight ||
current.time != original.time ||
current.value != original.value ||
current.weightedMode != original.weightedMode)
{
return false;
}
}
return true;
}
/// <summary>
/// Sets up a distance map curve suitable for the "water border" style for the base map creation in the random terrain generator.
/// <returns></returns>
public static AnimationCurve NewAnimCurveWaterBorder()
{
AnimationCurve returnCurve = new AnimationCurve(new Keyframe[2] { new Keyframe() {
inTangent = 0,
inWeight = 0,
outTangent = 0,
outWeight = 0,
time = 0,
value = 1,
weightedMode = WeightedMode.None
},
new Keyframe() {
inTangent = -3.183739f,
inWeight = 0.02412868f,
outTangent = -3.183739f,
outWeight = 0,
time = 1f,
value = 0,
weightedMode = WeightedMode.None
}
}); ;
return returnCurve;
}
/// <summary>
/// Sets up a distance map curve suitable for the "mountain border" style for the base map creation in the random terrain generator.
/// <returns></returns>
public static AnimationCurve NewAnimCurveMountainBorderDistance()
{
AnimationCurve returnCurve = new AnimationCurve(new Keyframe[2] { new Keyframe() {
inTangent = 0,
inWeight = 0,
outTangent = 0,
outWeight = 0,
time = 0,
value = 0.5f,
weightedMode = WeightedMode.None
},
new Keyframe() {
inTangent = 1.301094f,
inWeight = 0.04557639f,
outTangent = 1.301094f,
outWeight = 0,
time = 1f,
value = 1f,
weightedMode = WeightedMode.None
},
}); ;
return returnCurve;
}
public static AnimationCurve NewAnimCurveMountainBorderStrength()
{
AnimationCurve returnCurve = new AnimationCurve(new Keyframe[3] { new Keyframe() {
inTangent = 1.102063f,
inWeight = 0,
outTangent = 1.102063f,
outWeight = 0.2820168f,
time = 0,
value = 0f,
weightedMode = WeightedMode.None
},
new Keyframe() {
inTangent = 2.96729f,
inWeight = 0.08592079f,
outTangent = 2.96729f,
outWeight = 0.2352394f,
time = 0.6f,
value = 1f,
weightedMode = WeightedMode.None
},
new Keyframe() {
inTangent = 2.606707f,
inWeight = 0.1011895f,
outTangent = 2.606707f,
outWeight = 0f,
time = 1f,
value = 2f,
weightedMode = WeightedMode.None
},
}); ;
return returnCurve;
}
/// <summary>
/// Sets up a better strenght curve for noise that has a steeper cutoff in strength.
/// This curve is better suited to get small islands or patches of noise.
/// </summary>
/// <param name="distanceFromCenter">How far from the center of the curve the cutoff should take place. The smaller this value is, the "sharper" the cutoff will be for the noise pattern</param>
/// <returns></returns>
public static AnimationCurve NewAnimCurveDefaultNoise(float distanceFromCenter = 0.2f)
{
AnimationCurve returnCurve = new AnimationCurve(new Keyframe[4] { new Keyframe() {
inTangent = 0,
inWeight = 0,
outTangent = 0,
outWeight = 0,
time = 0,
value = 0,
weightedMode = WeightedMode.None
},
new Keyframe() {
inTangent = 0,
inWeight = 0f,
outTangent = 2.5f,
outWeight = 0.3333333f,
time = 0.5f - distanceFromCenter,
value = 0,
weightedMode = WeightedMode.None
},
new Keyframe() {
inTangent = 2.5f,
inWeight = 0f,
outTangent = 0,
outWeight = 0.3333333f,
time = 0.5f + distanceFromCenter,
value = 1,
weightedMode = WeightedMode.None
},
new Keyframe() {
inTangent = 0,
inWeight = 0,
outTangent = 0,
outWeight = 0,
time = 1,
value = 1,
weightedMode = WeightedMode.None
} }); ;
return returnCurve;
}
private Material GetCurrentFXFilterMaterial()
{
string shaderName = "";
switch (m_operation)
{
case ImageMaskOperation.ImageMask:
shaderName = "Hidden/Gaia/FilterImageMask";
break;
case ImageMaskOperation.DistanceMask:
shaderName = "Hidden/Gaia/FilterDistanceMask";
break;
case ImageMaskOperation.HeightMask:
shaderName = "Hidden/Gaia/FilterHeightMask";
break;
case ImageMaskOperation.SlopeMask:
shaderName = "Hidden/Gaia/FilterSlopeMask";
break;
case ImageMaskOperation.NoiseMask:
//We need the FilterImageMask as material for the final operation AFTER the noise has been calculated
//For the shader that creates the noise itself, see the implementation of the noise mask operation
//in Apply()
shaderName = "Hidden/Gaia/FilterImageMask";
break;
case ImageMaskOperation.CollisionMask:
//We need the FilterImageMask as material for the final operation AFTER the Collision mask has been gathered from the operation.
//For collection & assembly of the collision mask data, see the implementation of the collision mask operation
//in Apply()
shaderName = "Hidden/Gaia/FilterImageMask";
break;
case ImageMaskOperation.StrengthTransform:
shaderName = "Hidden/GaiaPro/StrengthTransform";
break;
case ImageMaskOperation.PROHydraulicErosion:
//We need the Strength Transform as material for the final operation AFTER the Erosion mask has been gathered from the Eroder.
//For collection & assembly of the collision mask data, see the implementation of the erosion mask operation
//in Apply()
shaderName = "Hidden/GaiaPro/StrengthTransform";
break;
case ImageMaskOperation.Smooth:
shaderName = "Hidden/Gaia/SmoothHeight";
break;
case ImageMaskOperation.TerrainTexture:
//Here we just load the splatmap input into an image mask
shaderName = "Hidden/Gaia/FilterImageMask";
break;
case ImageMaskOperation.PROConcaveConvex:
//Concave / Convex is calculated in compute shader, we use the strength transform as a workaround in this case
shaderName = "Hidden/GaiaPro/StrengthTransform";
break;
case ImageMaskOperation.WorldBiomeMask:
//We need the FilterImageMask as material for the final operation AFTER the World Biome mask has been gathered from the operation.
//For collection & assembly of the world biome mask data, see the implementation of the world biome mask operation
//in Apply()
shaderName = "Hidden/Gaia/FilterImageMask";
break;
default:
break;
}
if (shaderName == "")
{
return null;
}
return new Material(Shader.Find(shaderName));
}
public static void CheckMaskStackForInvalidTextureRules(string objectDescription, string objectName, ImageMask[] maskStack)
{
if (ImageMask.m_allTextureSpawners == null || ImageMask.m_allTextureSpawnRules == null)
{
ImageMask.RefreshSpawnRuleGUIDs();
}
foreach (ImageMask mask in maskStack)
{
//Is this a texture mask? If yes, check if the associated texture spawn rule exists, if not, put out a warning
if (mask.m_operation == ImageMaskOperation.TerrainTexture)
{
//only perform the check if there is actually a GUID selected in the mask
if (!string.IsNullOrEmpty(mask.m_textureMaskSpawnRuleGUID))
{
SpawnRule sr = ImageMask.m_allTextureSpawnRules.FirstOrDefault(x => x.GUID == mask.m_textureMaskSpawnRuleGUID);
if (sr == null)
{
mask.m_active = false;
mask.m_hasErrors = true;
Debug.LogWarning("The " + objectDescription + " '" + objectName + "' uses a Texture Mask that links to a non-existent texture spawn rule. The spawn might not work as intended. Please select the spawner, and assign the correct texture in the texture mask, or remove the texture mask altogether.");
}
else
{
mask.m_hasErrors = false;
}
}
else
{
mask.m_hasErrors = false;
}
}
}
}
public static void RefreshSpawnRuleGUIDs()
{
List<SpawnRule> tempTextureSpawnRules = new List<SpawnRule>();
List<Spawner> tempTextureSpawner = new List<Spawner>();
List<string> tempTextureSpawnRuleNames = new List<string>();
List<SpawnRule> tempTreeSpawnRules = new List<SpawnRule>();
List<Spawner> tempTreeSpawner = new List<Spawner>();
List<string> tempTreeSpawnRuleNames = new List<string>();
Spawner[] allSpawner = Resources.FindObjectsOfTypeAll<Spawner>();
foreach (Spawner spawner in allSpawner)
{
foreach (SpawnRule sr in spawner.m_settings.m_spawnerRules)
{
if (sr.m_resourceType == GaiaConstants.SpawnerResourceType.TerrainTexture)
{
tempTextureSpawnRules.Add(sr);
tempTextureSpawnRuleNames.Add(sr.m_name);
if (!tempTextureSpawner.Contains(spawner))
{
tempTextureSpawner.Add(spawner);
}
}
if (sr.m_resourceType == GaiaConstants.SpawnerResourceType.TerrainTree)
{
tempTreeSpawnRules.Add(sr);
tempTreeSpawnRuleNames.Add(sr.m_name);
if (!tempTreeSpawner.Contains(spawner))
{
tempTreeSpawner.Add(spawner);
}
}
}
}
m_allTextureSpawnRuleIndices = Enumerable
.Repeat(0, (int)((tempTextureSpawnRules.Count - 0) / 1) + 1)
.Select((tr, ti) => tr + (1 * ti))
.ToArray();
CollisionMask.m_allTreeSpawnRuleIndices = Enumerable
.Repeat(0, (int)((tempTreeSpawnRules.Count - 0) / 1) + 1)
.Select((tr, ti) => tr + (1 * ti))
.ToArray();
CollisionMask.m_allTreeSpawnRules = tempTreeSpawnRules.ToArray();
CollisionMask.m_allTreeSpawners = tempTreeSpawner.ToArray();
CollisionMask.m_allTreeSpawnRuleNames = tempTreeSpawnRuleNames.ToArray();
m_allTextureSpawnRules = tempTextureSpawnRules.ToArray();
m_allTextureSpawners = tempTextureSpawner.ToArray();
m_allTextureSpawnRuleNames = tempTextureSpawnRuleNames.ToArray();
}
public static ImageMask Clone(ImageMask source)
{
ImageMask target = new ImageMask();
#if UNITY_EDITOR
GaiaUtils.CopyFields(source, target);
//special treatment for the heightmask min max fields - those are properites which will not be copied by the field copy above
target.m_absoluteHeightMax = source.m_absoluteHeightMax;
target.m_absoluteHeightMin = source.m_absoluteHeightMin;
//special treatment for all object fields
target.m_distanceMaskCurve = new AnimationCurve(source.m_distanceMaskCurve.keys);
target.m_heightMaskCurve = new AnimationCurve(source.m_heightMaskCurve.keys);
target.m_slopeMaskCurve = new AnimationCurve(source.m_slopeMaskCurve.keys);
target.m_strengthTransformCurve = new AnimationCurve(source.m_strengthTransformCurve.keys);
if (source.m_gaiaNoiseSettings != null)
{
target.m_gaiaNoiseSettings = new GaiaNoiseSettings();
GaiaUtils.CopyFields(source.m_gaiaNoiseSettings, target.m_gaiaNoiseSettings);
}
if (source.m_noiseSettings != null)
{
target.m_noiseSettings = (NoiseSettings)ScriptableObject.CreateInstance(typeof(NoiseSettings));
GaiaUtils.CopyFields(source.m_noiseSettings, target.m_noiseSettings);
}
target.m_noiseToolSettings = new NoiseToolSettings();
GaiaUtils.CopyFields(source.m_noiseToolSettings, target.m_noiseToolSettings);
target.noiseSettingsGUI = null;
target.m_collisionMasks = new CollisionMask[source.m_collisionMasks.Length];
//Clone all collision masks as well
for (int i = 0; i < target.m_collisionMasks.Length; i++)
{
target.m_collisionMasks[i] = new CollisionMask();
GaiaUtils.CopyFields(source.m_collisionMasks[i], target.m_collisionMasks[i]);
}
//GaiaUtils.CopyFields(source.m_distanceMaskCurve, target.m_distanceMaskCurve);
#endif
return target;
}
}
}