/// <summary> /// Fix or repair holes in GeoTIFF data. Typically used when GeoTIFF data /// has values > 65000 (which in metres is very unlikely) /// Also used with Include Below Sea Level with OpenTopo GMRT data. /// See notes in ImportHeightmapTIFF() under 32bit SampleFormat.IEEEFP. /// </summary> /// <param name="landscape"></param> /// <param name="lbLayer"></param> /// <param name="showErrors"></param> public static void FixGeoTIFFHeightmap(LBLandscape landscape, LBLayer lbLayer, bool showErrors) { string methodName = "LBImportTIFF.FixGeoTIFFHeightmap"; if (landscape == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - landscape is null. Please report"); } } else if (landscape.topographyLayersList == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - topographyLayers is not defined. Please report"); } } else if (lbLayer == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - lbLayer is not defined. Please report"); } } else if (lbLayer.type != LBLayer.LayerType.UnityTerrains) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - lbLayer is not of type: UnityTerrains. Please report"); } } else if (lbLayer.lbTerrainDataList == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - lbLayer.lbTerrainDataList is not defined. Please report"); } } else if (lbLayer.lbTerrainDataList.Count < 1) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - lbLayer.lbTerrainDataList does not contain any data. Please report"); } } { int numTerrainData = (lbLayer.lbTerrainDataList == null ? 0 : lbLayer.lbTerrainDataList.Count); LBTerrainData lbTerrainData = null; ushort upperClip = 65000; // GRMT data adjustment variables. New sea-level to be 10,000 ushort landCeiling = 65335 - 9999; // The maximum assumed height land values can be // We may need to use LBTerrainData.GetLandscapeScopedHeightmap() to fix holes // across terrain boundaries for (int lbTDataIdx = 0; lbTDataIdx < numTerrainData; lbTDataIdx++) { lbTerrainData = lbLayer.lbTerrainDataList[lbTDataIdx]; if (lbTerrainData != null && lbTerrainData.HasRAWHeightData()) { // Raw data is stored as a 1-dimensional USHORT byte array // To fix holes we int heightmapResolution = lbTerrainData.RawHeightResolution; int byteIndex, byteIndexNearby; ushort heightUShort, heightUShortNearby; ushort sampleMinValue = ushort.MaxValue; ushort sampleMaxValue = ushort.MinValue; for (int z = 0; z < heightmapResolution; z++) { for (int x = 0; x < heightmapResolution; x++) { byteIndex = z * (heightmapResolution * 2) + (x * 2); // LB stores RAW data in little endian (Windows) format, which is more suited to Intel processors. heightUShort = (ushort)((lbTerrainData.rawHeightData[byteIndex + 1] << 8) | lbTerrainData.rawHeightData[byteIndex]); // Global Multi-Resolution Topography (GMRT) data include +ve and -ve data. // As we only import into ushort, the -ve values become values close to 64K if (lbLayer.isBelowSeaLevelDataIncluded) { // WORKAROUD - z==0 and z = max have incorrect values. // Attempt to fix bottom and top edges with some GMRT datasets (not sure why this happens yet...) //if ((z == 0 || z == (heightmapResolution - 1)) && heightUShort == 0u) if ((z == 0) && heightUShort == 0) { ulong total = 0; int pixels = 0; ushort pixelAvg = 0; // Get matrix of nearby pixels for (int pz = -3; pz < 4; pz++) { // Ignore pixels we're trying to fix if (pz == 0) { continue; } // Ensure the pixel is within the data area if (z + pz < 0 || z + pz > heightmapResolution - 1) { continue; } for (int px = -3; px < 4; px++) { // Ignore original value and ensure the pixel is within the data area if ((pz == 0 && px == 0) || (x + px < 0 || x + px > heightmapResolution - 1)) { continue; } byteIndexNearby = (z + pz) * (heightmapResolution * 2) + ((x + px) * 2); heightUShortNearby = (ushort)((lbTerrainData.rawHeightData[byteIndexNearby + 1] << 8) | lbTerrainData.rawHeightData[byteIndexNearby]); // Only include pixels that are not being adjusted if (heightUShortNearby > 0) { pixels++; total += heightUShortNearby; } } } if (pixels > 0) { pixelAvg = Convert.ToUInt16((total / (ulong)pixels)); if (pixelAvg > 0) { heightUShort = pixelAvg; } } } // Raise land values by 10,000 if (heightUShort < landCeiling) { heightUShort += 10000; } else { // The -ve (below sea level) values have become height +ve numbers during the import // process. Move these original -ve number back under the new artifical sea level. heightUShort = (ushort)(10000u - (65535u - (uint)heightUShort)); } // Convert ushort 0-65535 back to 2-bytes // LB stores RAW data in little endian (Windows) format, which is more suited to Intel processors. lbTerrainData.rawHeightData[byteIndex] = System.Convert.ToByte(heightUShort & 255); lbTerrainData.rawHeightData[byteIndex + 1] = System.Convert.ToByte(heightUShort >> 8); // keep track of the min/max values in the input TIFF data if (heightUShort > sampleMaxValue) { sampleMaxValue = heightUShort; } if (heightUShort < sampleMinValue) { sampleMinValue = heightUShort; } } else { // Is this an extreme value? if (heightUShort >= upperClip) { ulong total = 0; int pixels = 0; ushort pixelAvg = 0; // Get matrix of nearby pixels for (int pz = -3; pz < 4; pz++) { // Ensure the pixel is within the data area if (z + pz < 0 || z + pz > heightmapResolution - 1) { continue; } for (int px = -3; px < 4; px++) { // Ignore original value and ensure the pixel is within the data area if ((pz == 0 && px == 0) || (x + px < 0 || x + px > heightmapResolution - 1)) { continue; } // Ensure the pixel is within the data area //if (x + px < 0 || x + px > heightmapResolution - 1) { continue; } byteIndexNearby = (z + pz) * (heightmapResolution * 2) + ((x + px) * 2); heightUShortNearby = (ushort)((lbTerrainData.rawHeightData[byteIndexNearby + 1] << 8) | lbTerrainData.rawHeightData[byteIndexNearby]); // Only include pixels that are not being clipped. Adjacent pixels may also need to be clipped, so we need // to not include them for averaging purposes, else the new pixel updated below will be much higher than the surrounding terrain. if (heightUShortNearby < upperClip) { pixels++; total += heightUShortNearby; } } } if (pixels > 0) { pixelAvg = Convert.ToUInt16((total / (ulong)pixels)); if (pixelAvg > 0) { // Convert ushort 0-65535 back to 2-bytes // LB stores RAW data in little endian (Windows) format, which is more suited to Intel processors. lbTerrainData.rawHeightData[byteIndex] = System.Convert.ToByte(pixelAvg & 255); lbTerrainData.rawHeightData[byteIndex + 1] = System.Convert.ToByte(pixelAvg >> 8); //Debug.Log("x,z " + x + "," + z + " avg: " + pixelAvg); } } else { // keep track of the min/max values in the input TIFF data if (heightUShort > sampleMaxValue) { sampleMaxValue = heightUShort; } if (heightUShort < sampleMinValue) { sampleMinValue = heightUShort; } } } else // Value is not extreme so update min/max values { // keep track of the min/max values in the input TIFF data if (heightUShort > sampleMaxValue) { sampleMaxValue = heightUShort; } if (heightUShort < sampleMinValue) { sampleMinValue = heightUShort; } } } } } // Need to update max height lbTerrainData.rawMinHeight = sampleMinValue; lbTerrainData.rawMaxHeight = sampleMaxValue; } } } }
/// <summary> /// Import a GeoTIFF file from disk into a Topography Layer /// </summary> /// <param name="landscape"></param> /// <param name="rawFileList"></param> /// <param name="isMacRAWFormat"></param> /// <param name="showErrors"></param> /// <returns></returns> public static bool ImportGeoTIFFHeightmap(LBLandscape landscape, string filePath, bool showErrors) { bool isSuccessful = false; string methodName = "LBImportTIFF.ImportGeoTIFFHeightmap"; if (landscape == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - landscape is null. Please report"); } } else if (landscape.topographyLayersList == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - topographyLayers is not defined. Please report"); } } else if (string.IsNullOrEmpty(filePath)) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - filePath is not defined. Please report."); } } else if (landscape.landscapeTerrains == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - landscapeTerrains is not defined. Please report"); } } else if (landscape.landscapeTerrains.Length < 1) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - No terrains in landscape"); } } else if (landscape.topographyLayersList.Exists(lyr => lyr.type == LBLayer.LayerType.UnityTerrains)) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " - Multiple Unity Terrain Layers are not supported in the same landscape"); } } else { float generationStartTime = Time.realtimeSinceStartup; int numTerrains = landscape.landscapeTerrains.Length; List <LBTerrainData> lbTerrainDataList = new List <LBTerrainData>(); // Track the min/max heights for all the terrains ushort sampleMinHeight = ushort.MaxValue; ushort sampleMaxHeight = ushort.MinValue; for (int index = 0; index < numTerrains; index++) { //Debug.Log("INFO: " + methodName + " importing TIFF for terrain: " + landscape.landscapeTerrains[index].name + " using file: " + filePath); // Import TIFF region for the current terrain LBTerrainData lbTerrainData = ImportHeightmapTIFF(landscape, landscape.landscapeTerrains[index], filePath, ref sampleMinHeight, ref sampleMaxHeight, showErrors); // Don't continue if at least one terrain cannot be imported if (lbTerrainData == null) { break; } else { lbTerrainDataList.Add(lbTerrainData); } // Only create a new layer if all terrains were imported if (lbTerrainDataList.Count == numTerrains) { LBLayer lbLayer = new LBLayer(); if (lbLayer != null) { lbLayer.type = LBLayer.LayerType.UnityTerrains; lbLayer.lbTerrainDataList = lbTerrainDataList; lbLayer.isCheckHeightRangeDiff = true; lbLayer.heightScale = 1.0f; lbLayer.normaliseImage = false; // Insert at the top of the list landscape.topographyLayersList.Insert(0, lbLayer); } if (landscape.showTiming) { Debug.Log("Time taken to import TIFF heightmaps: " + (Time.realtimeSinceStartup - generationStartTime).ToString("00.00") + " seconds."); } isSuccessful = true; } } } return(isSuccessful); }
// Use this for initialization void Awake() { #region Initialise // This line just gets the starting time of the generation so that the total generation time // can be recorded and displayed float generationStartTime = Time.realtimeSinceStartup; RuntimeSampleHelper.RemoveDefaultCamera(); RuntimeSampleHelper.RemoveDefaultLight(); // Get a link to the LBLandscape script landscape = this.GetComponent <LBLandscape>(); if (landscape == null) { Debug.Log("Cannot find LBLandscape script attached to Runtime gameobject"); return; } // Check to see if Universal Render Pipeline is installed in the project bool isURP = LBLandscape.IsURP(false); // Check to see if Light Weight Render Pipeline is installed in this project bool isLWRP = !isURP && LBLandscape.IsLWRP(false); // Check to see if High Definition Render Pipeline is installed in this project bool isHDRP = !isURP && !isLWRP && LBLandscape.IsHDRP(false); #if UNITY_2019_2_OR_NEWER bool is201920Plus = true; #else bool is201920Plus = false; #endif #endregion #region Create the terrains and store references to them terrainsList = new List <Terrain>(); int terrainNumber = 0; for (float tx = 0f; tx < landscapeSize.x - 1f; tx += 2000f) { for (float ty = 0f; ty < landscapeSize.y - 1f; ty += 2000f) { // Create a new gameobject GameObject terrainObj = new GameObject("Runtime Terrain " + (terrainNumber++).ToString("000")); // Correctly parent and position the terrain terrainObj.transform.parent = this.transform; terrainObj.transform.localPosition = new Vector3(tx, 0f, ty); // Add a terrain component Terrain newTerrain = terrainObj.AddComponent <Terrain>(); // Set terrain settings (depending on your situtation, you may need to set more or less than I have in this example) newTerrain.heightmapPixelError = 1; newTerrain.basemapDistance = 5000f; newTerrain.treeDistance = 5000f; newTerrain.treeBillboardDistance = 100f; newTerrain.detailObjectDistance = 150f; newTerrain.treeCrossFadeLength = 25f; // Set terrain data settings (same as above comment) TerrainData newTerrainData = new TerrainData(); // One thing to note here is that modfiying the heightmap resolution not only clears all terrai height data, // it also scales up or down the size of the terrain. So you should always set the heightmap resolution // BEFORE you set the terrain size newTerrainData.heightmapResolution = 513; newTerrainData.size = Vector3.one * 2000f; newTerrainData.SetDetailResolution(1024, 16); newTerrain.terrainData = newTerrainData; // Set up the terrain collider TerrainCollider newTerrainCol = terrainObj.AddComponent <TerrainCollider>(); newTerrainCol.terrainData = newTerrainData; // Add the terrain to the list of terrains terrainsList.Add(newTerrain); } } #endregion landscape.SetLandscapeTerrains(true); landscape.SetTerrainNeighbours(false); #region Set the terrain material int numTerrains = landscape.landscapeTerrains == null ? 0 : landscape.landscapeTerrains.Length; // Check for URP/LWRP/HDRP or do we need to create a default material for U2019.2.0 or newer if (isURP || isLWRP || isHDRP || is201920Plus) { float pixelError = 0f; Terrain terrain = null; LBLandscape.TerrainMaterialType terrainMaterialType = isURP ? LBLandscape.TerrainMaterialType.URP : (isLWRP ? LBLandscape.TerrainMaterialType.LWRP : (terrainMaterialType = isHDRP ? LBLandscape.TerrainMaterialType.HDRP : LBLandscape.TerrainMaterialType.BuiltInStandard)); for (int tIdx = 0; tIdx < numTerrains; tIdx++) { terrain = landscape.landscapeTerrains[tIdx]; landscape.SetTerrainMaterial(terrain, tIdx, (tIdx == numTerrains - 1), terrain.terrainData.size.x, ref pixelError, terrainMaterialType); } } #endregion // Set the topography noise variables float maskWarpAmount = 0f; float maskNoiseTileSize = 10000f; float maskNoiseOffsetX = 0f; float maskNoiseOffsetY = 0f; AnimationCurve distanceToCentreMask = new AnimationCurve(); int keyInt = distanceToCentreMask.AddKey(0f, 1f); keyInt = distanceToCentreMask.AddKey(0.529f, 0.959f); keyInt = distanceToCentreMask.AddKey(1f, 0f); Keyframe[] curveKeys = distanceToCentreMask.keys; curveKeys[0].inTangent = 0f; curveKeys[0].outTangent = 0f; curveKeys[1].inTangent = -0.25f; curveKeys[1].outTangent = -0.25f; curveKeys[2].inTangent = 0f; curveKeys[2].outTangent = 0f; distanceToCentreMask = new AnimationCurve(curveKeys); AnimationCurve maskNoiseCurveModifier = AnimationCurve.Linear(0f, 0.5f, 1f, 1f); // Avoid warning of keyInt not being used. if (keyInt == 0) { } // Create the Topography Layers // You can mix and match Perlin and Image layers landscape.topographyLayersList = new List <LBLayer>(); if (landscape.topographyLayersList != null) { // Add one or more Base layers LBLayer lbBaseLayer1 = new LBLayer(); if (lbBaseLayer1 != null) { lbBaseLayer1 = LBLayer.SetLayerFromPreset(LBLayer.LayerPreset.DesertFloorBase); landscape.topographyLayersList.Add(lbBaseLayer1); } // Add one or more Additive layers LBLayer lbAdditiveLayer1 = new LBLayer(); if (lbAdditiveLayer1 != null) { // You can manually configure a layer, or use a preset then modify it. lbAdditiveLayer1 = LBLayer.SetLayerFromPreset(LBLayer.LayerPreset.MountainRangeComplexBase); // If using using a different type of preset, must set the type after applying preset lbAdditiveLayer1.type = LBLayer.LayerType.PerlinAdditive; // Optionally override the preset settings lbAdditiveLayer1.noiseTileSize = 5000f; lbAdditiveLayer1.octaves = 8; lbAdditiveLayer1.lacunarity = 1.92f; lbAdditiveLayer1.gain = 0.45f; lbAdditiveLayer1.warpAmount = 0; lbAdditiveLayer1.removeBaseNoise = true; lbAdditiveLayer1.heightScale = 1f; lbAdditiveLayer1.additiveAmount = 0.75f; lbAdditiveLayer1.additiveCurve = LBLayer.CreateAdditiveCurve(lbAdditiveLayer1.additiveAmount); lbAdditiveLayer1.perOctaveCurveModifierPresets = new List <LBCurve.CurvePreset>(); lbAdditiveLayer1.perOctaveCurveModifierPresets.Add(LBCurve.CurvePreset.DoubleRidged); landscape.topographyLayersList.Add(lbAdditiveLayer1); } // Add a detail layer LBLayer lbDetailLayer1 = new LBLayer(); if (lbDetailLayer1 != null) { lbDetailLayer1 = LBLayer.SetLayerFromPreset(LBLayer.LayerPreset.HillsDetail); landscape.topographyLayersList.Add(lbDetailLayer1); } } // Create the terrain topographies for (int t = 0; t < terrainsList.Count && landscape.topographyLayersList != null; t++) { // Add the topography layers terrainsList[t].terrainData = LBLandscapeTerrain.HeightmapFromLayers(landscape, terrainsList[t].terrainData, terrainsList[t].transform.position, landscapeSize, landscape.transform.position, landscape.topographyLayersList); if (IsMaskingOn) { // Example of applying a mask to the terrain topography terrainsList[t].terrainData = LBLandscapeTerrain.MaskedHeightmap(terrainsList[t].terrainData, terrainsList[t].transform.position, landscapeSize, transform.position, 1, distanceToCentreMask, maskWarpAmount, maskNoiseTileSize, new Vector2(maskNoiseOffsetX, maskNoiseOffsetY), maskNoiseCurveModifier); } } // Create a list of LBTerrainTexture objects // These contain the textures and normal maps but also the rules for applying them to the terrain landscape.terrainTexturesList = new List <LBTerrainTexture>(); // Populate the list by creating temporary LBTerrainTexture objects and adjusting their settings, // then adding each one into the list // Grass Hill texture LBTerrainTexture tempTerrainTexture = new LBTerrainTexture(); tempTerrainTexture.texture = grassHillTexture; tempTerrainTexture.normalMap = grassHillNormalMap; tempTerrainTexture.tileSize = Vector2.one * 25f; tempTerrainTexture.minInclination = 0f; tempTerrainTexture.maxInclination = 45f; tempTerrainTexture.useNoise = true; tempTerrainTexture.noiseTileSize = 100f; tempTerrainTexture.texturingMode = LBTerrainTexture.TexturingMode.Inclination; landscape.terrainTexturesList.Add(tempTerrainTexture); // Rock Layered texture tempTerrainTexture = new LBTerrainTexture(); tempTerrainTexture.texture = rockLayeredTexture; tempTerrainTexture.normalMap = rockLayeredNormalMap; tempTerrainTexture.tileSize = Vector2.one * 100f; tempTerrainTexture.minInclination = 30f; tempTerrainTexture.maxInclination = 90f; tempTerrainTexture.useNoise = true; tempTerrainTexture.noiseTileSize = 100f; tempTerrainTexture.texturingMode = LBTerrainTexture.TexturingMode.Inclination; landscape.terrainTexturesList.Add(tempTerrainTexture); // Texture the terrains for (int t = 0; t < terrainsList.Count; t++) { // Use the LBLandscapeTerrain.TextureTerrain function for texturing the terrain terrainsList[t].terrainData = LBLandscapeTerrain.TextureTerrain(terrainsList[t].terrainData, landscape.terrainTexturesList, terrainsList[t].transform.position, landscapeSize, this.transform.position, false, landscape); } // Display the total time taken to generate the landscape (usually for debugging purposes) Debug.Log("Time taken to generate landscape: " + (Time.realtimeSinceStartup - generationStartTime).ToString("00.00") + " seconds."); }
// Use this for initialization void Awake() { #region Initialise // This line just gets the starting time of the generation so that the total generation time // can be recorded and displayed float generationStartTime = Time.realtimeSinceStartup; RuntimeSampleHelper.RemoveDefaultCamera(); RuntimeSampleHelper.RemoveDefaultLight(); // We're using some old trees in this demo, which don't like anti-aliasing QualitySettings.antiAliasing = 0; // Get a link to the LBLandscape script landscape = this.GetComponent <LBLandscape>(); if (landscape == null) { Debug.Log("Cannot find LBLandscape script attached to Runtime gameobject"); return; } // Update the size landscape.size = landscapeSize; // Create the terrains int terrainNumber = 0; #endregion #region Create Terrains for (float tx = 0f; tx < landscapeSize.x - 1f; tx += terrainWidth) { for (float ty = 0f; ty < landscapeSize.y - 1f; ty += terrainWidth) { // Create a new gameobject GameObject terrainObj = new GameObject("RuntimeTerrain" + (terrainNumber++).ToString("000")); // Create a new gameobject // Correctly parent and position the terrain terrainObj.transform.parent = this.transform; terrainObj.transform.localPosition = new Vector3(tx, 0f, ty); // Add a terrain component Terrain newTerrain = terrainObj.AddComponent <Terrain>(); // Set terrain settings (depending on your situtation, you may need to set more or less than I have in this example) newTerrain.heightmapPixelError = 5f; newTerrain.basemapDistance = 1000f; newTerrain.treeDistance = 750f; newTerrain.treeBillboardDistance = 100f; newTerrain.detailObjectDistance = 200f; newTerrain.treeCrossFadeLength = 5f; // Set terrain data settings (same as above comment) TerrainData newTerrainData = new TerrainData(); newTerrainData.heightmapResolution = 513; newTerrainData.size = new Vector3(terrainWidth, terrainHeight, terrainWidth); newTerrainData.SetDetailResolution(1024, 16); newTerrain.terrainData = newTerrainData; // Set up the terrain collider TerrainCollider newTerrainCol = terrainObj.AddComponent <TerrainCollider>(); newTerrainCol.terrainData = newTerrainData; } } landscape.SetLandscapeTerrains(true); landscape.SetDefaultTerrainMaterial(); #endregion #region Create a Stencil // Add a new Stencil to the (empty) landscape LBStencil lbStencil = LBStencil.CreateStencilInScene(landscape, landscape.gameObject); if (lbStencil != null) { lbStencil.name += "01"; /// Import PNG files into stencil layers (at runtime) and add as filters. lbStencil.layerImportMethod = LBStencil.LayerImportMethod.Grayscale; if (lbStencil.ImportTexture("Hills Layer 1", stencilLayerTex1, true)) { // Remove the default first layer if (lbStencil.stencilLayerList.Count > 1) { lbStencil.stencilLayerList.RemoveAt(0); } } } #endregion #region Define the Topography // NOTE: This sample uses a topography mask. This is not a requirement // Create the distance to centre mask using an animation curve AnimationCurve distanceToCentreMask = new AnimationCurve(); distanceToCentreMask.AddKey(0.00f, 0.04f); distanceToCentreMask.AddKey(0.30f, 0.03f); distanceToCentreMask.AddKey(0.78f, 1.00f); distanceToCentreMask.AddKey(1.00f, 1.00f); Keyframe[] distanceToCentreMaskKeys = distanceToCentreMask.keys; distanceToCentreMaskKeys[0].inTangent = 0.00f; distanceToCentreMaskKeys[0].outTangent = 0.00f; distanceToCentreMaskKeys[1].inTangent = -0.02f; distanceToCentreMaskKeys[1].outTangent = -0.02f; distanceToCentreMaskKeys[2].inTangent = 0.01f; distanceToCentreMaskKeys[2].outTangent = 0.01f; distanceToCentreMaskKeys[3].inTangent = 0.00f; distanceToCentreMaskKeys[3].outTangent = 0.00f; distanceToCentreMask = new AnimationCurve(distanceToCentreMaskKeys); // Assign the topography mask to the LBLandscape instance landscape.distanceToCentreMask = distanceToCentreMask; landscape.topographyMaskMode = LBLandscape.MaskMode.DistanceToCentre; // Create the Topography Layers // You can mix and match Perlin and Image layers landscape.topographyLayersList = new List <LBLayer>(); if (landscape.topographyLayersList != null) { // NOTE // If you have used the LB Editor to first build a test landscape, the Topography layer // can be scripted out and pasted into your runtime script here. // Layer Code generated from Landscape Builder at 7:59 AM on .... AnimationCurve additiveCurve = new AnimationCurve(); AnimationCurve subtractiveCurve = new AnimationCurve(); List <LBLayerFilter> filters = new List <LBLayerFilter>(); // Add LayerFilter code here List <AnimationCurve> imageCurveModifiers = new List <AnimationCurve>(); List <AnimationCurve> outputCurveModifiers = new List <AnimationCurve>(); AnimationCurve OutputCurve1 = new AnimationCurve(); OutputCurve1.AddKey(0.00f, 0.00f); OutputCurve1.AddKey(0.50f, 0.06f); OutputCurve1.AddKey(1.00f, 1.00f); Keyframe[] OutputCurve1Keys = OutputCurve1.keys; OutputCurve1Keys[0].inTangent = 0.00f; OutputCurve1Keys[0].outTangent = 0.00f; OutputCurve1Keys[1].inTangent = 0.50f; OutputCurve1Keys[1].outTangent = 0.50f; OutputCurve1Keys[2].inTangent = 4.00f; OutputCurve1Keys[2].outTangent = 4.00f; OutputCurve1 = new AnimationCurve(OutputCurve1Keys); outputCurveModifiers.Add(OutputCurve1); List <AnimationCurve> perOctaveCurveModifiers = new List <AnimationCurve>(); AnimationCurve PerOctaveCurve1 = new AnimationCurve(); PerOctaveCurve1.AddKey(0.00f, 0.00f); PerOctaveCurve1.AddKey(0.05f, 0.10f); PerOctaveCurve1.AddKey(0.45f, 0.90f); PerOctaveCurve1.AddKey(0.50f, 1.00f); PerOctaveCurve1.AddKey(0.55f, 0.90f); PerOctaveCurve1.AddKey(0.95f, 0.10f); PerOctaveCurve1.AddKey(1.00f, 0.00f); Keyframe[] PerOctaveCurve1Keys = PerOctaveCurve1.keys; PerOctaveCurve1Keys[0].inTangent = 0.00f; PerOctaveCurve1Keys[0].outTangent = 0.00f; PerOctaveCurve1Keys[1].inTangent = 2.00f; PerOctaveCurve1Keys[1].outTangent = 2.00f; PerOctaveCurve1Keys[2].inTangent = 2.00f; PerOctaveCurve1Keys[2].outTangent = 2.00f; PerOctaveCurve1Keys[3].inTangent = 0.00f; PerOctaveCurve1Keys[3].outTangent = 0.00f; PerOctaveCurve1Keys[4].inTangent = -2.00f; PerOctaveCurve1Keys[4].outTangent = -2.00f; PerOctaveCurve1Keys[5].inTangent = -2.00f; PerOctaveCurve1Keys[5].outTangent = -2.00f; PerOctaveCurve1Keys[6].inTangent = 0.00f; PerOctaveCurve1Keys[6].outTangent = 0.00f; PerOctaveCurve1 = new AnimationCurve(PerOctaveCurve1Keys); perOctaveCurveModifiers.Add(PerOctaveCurve1); AnimationCurve mapPathBlendCurve = new AnimationCurve(); mapPathBlendCurve.AddKey(0.00f, 0.00f); mapPathBlendCurve.AddKey(1.00f, 1.00f); Keyframe[] mapPathBlendCurveKeys = mapPathBlendCurve.keys; mapPathBlendCurveKeys[0].inTangent = 0.00f; mapPathBlendCurveKeys[0].outTangent = 0.00f; mapPathBlendCurveKeys[1].inTangent = 0.00f; mapPathBlendCurveKeys[1].outTangent = 0.00f; mapPathBlendCurve = new AnimationCurve(mapPathBlendCurveKeys); AnimationCurve mapPathHeightCurve = new AnimationCurve(); mapPathHeightCurve.AddKey(0.00f, 1.00f); mapPathHeightCurve.AddKey(1.00f, 1.00f); Keyframe[] mapPathHeightCurveKeys = mapPathHeightCurve.keys; mapPathHeightCurveKeys[0].inTangent = 0.00f; mapPathHeightCurveKeys[0].outTangent = 0.00f; mapPathHeightCurveKeys[1].inTangent = 0.00f; mapPathHeightCurveKeys[1].outTangent = 0.00f; mapPathHeightCurve = new AnimationCurve(mapPathHeightCurveKeys); LBLayer lbBaseLayer01 = new LBLayer(); if (lbBaseLayer01 != null) { lbBaseLayer01.type = LBLayer.LayerType.PerlinBase; lbBaseLayer01.preset = LBLayer.LayerPreset.MountainPeaksBase; lbBaseLayer01.layerTypeMode = LBLayer.LayerTypeMode.Add; lbBaseLayer01.noiseTileSize = 1000; lbBaseLayer01.noiseOffsetX = 0; lbBaseLayer01.noiseOffsetZ = 0; lbBaseLayer01.octaves = 7; lbBaseLayer01.downscaling = 1; lbBaseLayer01.lacunarity = 1.75f; lbBaseLayer01.gain = 0.475f; lbBaseLayer01.additiveAmount = 0.5f; lbBaseLayer01.subtractiveAmount = 0.2f; lbBaseLayer01.additiveCurve = additiveCurve; lbBaseLayer01.subtractiveCurve = subtractiveCurve; lbBaseLayer01.removeBaseNoise = true; lbBaseLayer01.addMinHeight = false; lbBaseLayer01.addHeight = 0f; lbBaseLayer01.restrictArea = false; lbBaseLayer01.areaRect = new Rect(0, 0, 1000, 1000); lbBaseLayer01.interpolationSmoothing = 0; //lbBaseLayer01.heightmapImage = heightmapImage; lbBaseLayer01.imageHeightScale = 1f; lbBaseLayer01.imageCurveModifiers = imageCurveModifiers; lbBaseLayer01.filters = filters; lbBaseLayer01.isDisabled = false; lbBaseLayer01.showLayer = false; lbBaseLayer01.showAdvancedSettings = false; lbBaseLayer01.showCurvesAndFilters = true; lbBaseLayer01.showAreaHighlighter = false; lbBaseLayer01.detailSmoothRate = 0f; lbBaseLayer01.areaBlendRate = 0.5f; lbBaseLayer01.downscaling = 1; lbBaseLayer01.warpAmount = 0f; lbBaseLayer01.warpOctaves = 1; lbBaseLayer01.outputCurveModifiers = outputCurveModifiers; lbBaseLayer01.perOctaveCurveModifiers = perOctaveCurveModifiers; lbBaseLayer01.heightScale = 0.75f; lbBaseLayer01.minHeight = 0f; lbBaseLayer01.imageSource = LBLayer.LayerImageSource.Default; lbBaseLayer01.imageRepairHoles = false; lbBaseLayer01.threshholdRepairHoles = 0f; lbBaseLayer01.mapPathBlendCurve = mapPathBlendCurve; lbBaseLayer01.mapPathHeightCurve = mapPathHeightCurve; lbBaseLayer01.mapPathAddInvert = false; lbBaseLayer01.floorOffsetY = 0f; // Add LBMapPath code here // lbBaseLayer01.lbPath = lbPath; // lbBaseLayer01.lbMapPath = lbMapPath; // NOTE Add the new layer to the landscape meta-data landscape.topographyLayersList.Add(lbBaseLayer01); } } #endregion // Create the terrain topographies landscape.ApplyTopography(false, true); // NOTE // If you have used the LB Editor to first build a test path in a landscape, the path points can be scripted // out and pasted into your runtime script #region LBMapPath Map Path // LBMapPath generated from Landscape Builder at 3:10 PM on Sunday, October 01, 2017 LBMapPath lbMapPath = LBMapPath.CreateMapPath(landscape, landscape.gameObject); if (lbMapPath != null) { lbMapPath.name = "Map Path"; if (lbMapPath.lbPath != null) { lbMapPath.lbPath.pathName = "Map Path"; lbMapPath.mapResolution = 1; lbMapPath.lbPath.blendStart = true; lbMapPath.lbPath.blendEnd = true; lbMapPath.lbPath.pathResolution = 2f; lbMapPath.lbPath.closedCircuit = false; lbMapPath.lbPath.edgeBlendWidth = 0f; lbMapPath.lbPath.removeCentre = false; lbMapPath.lbPath.leftBorderWidth = 0f; lbMapPath.lbPath.rightBorderWidth = 0f; lbMapPath.lbPath.snapToTerrain = true; lbMapPath.lbPath.heightAboveTerrain = 0f; lbMapPath.lbPath.zoomOnFind = true; lbMapPath.lbPath.findZoomDistance = 50f; // Mesh options lbMapPath.lbPath.isMeshLandscapeUV = false; lbMapPath.lbPath.meshUVTileScale = new Vector2(1, 1); lbMapPath.lbPath.meshYOffset = 0.08f; lbMapPath.lbPath.meshEdgeSnapToTerrain = true; lbMapPath.lbPath.meshSnapType = LBPath.MeshSnapType.BothEdges; lbMapPath.lbPath.meshIsDoubleSided = false; lbMapPath.lbPath.meshIncludeEdges = false; lbMapPath.lbPath.meshIncludeWater = false; lbMapPath.lbPath.meshSaveToProjectFolder = false; lbMapPath.meshMaterial = path1meshMaterial; // Path Points lbMapPath.lbPath.positionList.Add(new Vector3(631.6624f, 23.52699f, 825.2574f)); lbMapPath.lbPath.positionList.Add(new Vector3(615.8184f, 9.507171f, 763.9471f)); lbMapPath.lbPath.positionList.Add(new Vector3(592.5421f, 4.510101f, 721.7316f)); lbMapPath.lbPath.positionList.Add(new Vector3(600.6003f, 2.589149f, 679.3673f)); lbMapPath.lbPath.positionList.Add(new Vector3(620.8919f, 2.475228f, 658.526f)); lbMapPath.lbPath.positionList.Add(new Vector3(599.6022f, 2.838308f, 605.2884f)); lbMapPath.lbPath.positionList.Add(new Vector3(575.0403f, 2.714737f, 555.6046f)); lbMapPath.lbPath.positionList.Add(new Vector3(560.4745f, 2.838308f, 472.1874f)); lbMapPath.lbPath.positionList.Add(new Vector3(570.2177f, 3.09073f, 444.6886f)); lbMapPath.lbPath.positionList.Add(new Vector3(570.2177f, 3.299063f, 387.1046f)); lbMapPath.lbPath.positionList.Add(new Vector3(600.3734f, 4.613719f, 311.1418f)); lbMapPath.lbPath.positionList.Add(new Vector3(610.5876f, 6.345493f, 293.1662f)); lbMapPath.lbPath.widthList.Add(14f); lbMapPath.lbPath.widthList.Add(14f); lbMapPath.lbPath.widthList.Add(14f); lbMapPath.lbPath.widthList.Add(14f); lbMapPath.lbPath.widthList.Add(14f); lbMapPath.lbPath.widthList.Add(11.32066f); lbMapPath.lbPath.widthList.Add(14f); lbMapPath.lbPath.widthList.Add(14f); lbMapPath.lbPath.widthList.Add(14f); lbMapPath.lbPath.widthList.Add(14f); lbMapPath.lbPath.widthList.Add(14f); lbMapPath.lbPath.widthList.Add(14f); lbMapPath.lbPath.positionListLeftEdge.Add(new Vector3(638.2852f, 23.52699f, 823.5834f)); lbMapPath.lbPath.positionListLeftEdge.Add(new Vector3(622.2536f, 9.507171f, 761.4589f)); lbMapPath.lbPath.positionListLeftEdge.Add(new Vector3(599.409f, 4.510101f, 720.4888f)); lbMapPath.lbPath.positionListLeftEdge.Add(new Vector3(606.8383f, 2.589149f, 682.5374f)); lbMapPath.lbPath.positionListLeftEdge.Add(new Vector3(627.8883f, 2.475228f, 658.7487f)); lbMapPath.lbPath.positionListLeftEdge.Add(new Vector3(604.7557f, 2.838308f, 602.9471f)); lbMapPath.lbPath.positionListLeftEdge.Add(new Vector3(581.7473f, 2.714737f, 553.6004f)); lbMapPath.lbPath.positionListLeftEdge.Add(new Vector3(567.473f, 2.838308f, 472.0406f)); lbMapPath.lbPath.positionListLeftEdge.Add(new Vector3(577.1524f, 3.09073f, 445.6413f)); lbMapPath.lbPath.positionListLeftEdge.Add(new Vector3(577.0537f, 3.299063f, 388.6086f)); lbMapPath.lbPath.positionListLeftEdge.Add(new Vector3(606.7752f, 4.613719f, 313.9633f)); lbMapPath.lbPath.positionListLeftEdge.Add(new Vector3(616.454f, 6.345493f, 296.91f)); lbMapPath.lbPath.positionListRightEdge.Add(new Vector3(625.0396f, 23.52699f, 826.9315f)); lbMapPath.lbPath.positionListRightEdge.Add(new Vector3(609.3831f, 9.507171f, 766.4353f)); lbMapPath.lbPath.positionListRightEdge.Add(new Vector3(585.6751f, 4.510101f, 722.9744f)); lbMapPath.lbPath.positionListRightEdge.Add(new Vector3(594.3624f, 2.589149f, 676.1973f)); lbMapPath.lbPath.positionListRightEdge.Add(new Vector3(613.8955f, 2.475228f, 658.3033f)); lbMapPath.lbPath.positionListRightEdge.Add(new Vector3(594.4488f, 2.838308f, 607.6296f)); lbMapPath.lbPath.positionListRightEdge.Add(new Vector3(568.3334f, 2.714737f, 557.6088f)); lbMapPath.lbPath.positionListRightEdge.Add(new Vector3(553.4761f, 2.838308f, 472.3342f)); lbMapPath.lbPath.positionListRightEdge.Add(new Vector3(563.2829f, 3.09073f, 443.736f)); lbMapPath.lbPath.positionListRightEdge.Add(new Vector3(563.3816f, 3.299063f, 385.6005f)); lbMapPath.lbPath.positionListRightEdge.Add(new Vector3(593.9716f, 4.613719f, 308.3203f)); lbMapPath.lbPath.positionListRightEdge.Add(new Vector3(604.7213f, 6.345493f, 289.4224f)); lbMapPath.minPathWidth = lbMapPath.lbPath.GetMinWidth(); lbMapPath.lbPath.RefreshPathHeights(landscape); // Create Mesh for Path if (lbMapPath.lbPath.CreateMeshFromPath(landscape)) { Vector3 meshPosition = new Vector3(0f, lbMapPath.lbPath.meshYOffset, 0f); Transform meshTransform = LBMeshOperations.AddMeshToScene(lbMapPath.lbPath.lbMesh, meshPosition, lbMapPath.lbPath.pathName + " Mesh", lbMapPath.transform, lbMapPath.meshMaterial, true, true); if (meshTransform != null) { } } } } #endregion // Create a list of LBTerrainTexture objects // These contain the textures and normal maps but also the rules for applying them to the terrain landscape.terrainTexturesList = new List <LBTerrainTexture>(); // Populate the list by creating temporary LBTerrainTexture objects and adjusting their settings, // then adding each one into the list // Update the textures LBTerrainTexture tempTerrainTexture = new LBTerrainTexture(); if (tempTerrainTexture != null) { // Forest Floor tempTerrainTexture.texture = texture1; tempTerrainTexture.normalMap = texture1NM; tempTerrainTexture.tileSize = Vector2.one * 15f; tempTerrainTexture.useNoise = false; tempTerrainTexture.texturingMode = LBTerrainTexture.TexturingMode.ConstantInfluence; tempTerrainTexture.strength = 0.04f; landscape.terrainTexturesList.Add(tempTerrainTexture); // Rock Layered tempTerrainTexture = new LBTerrainTexture(); tempTerrainTexture.texture = texture2; tempTerrainTexture.normalMap = texture2NM; tempTerrainTexture.tileSize = Vector2.one * 100f; tempTerrainTexture.texturingMode = LBTerrainTexture.TexturingMode.HeightAndInclination; tempTerrainTexture.minHeight = 400f / terrainHeight; tempTerrainTexture.maxHeight = 1000f / terrainHeight; tempTerrainTexture.minInclination = 30f; tempTerrainTexture.maxInclination = 90f; tempTerrainTexture.useNoise = true; tempTerrainTexture.noiseTileSize = 100f; tempTerrainTexture.strength = 1f; landscape.terrainTexturesList.Add(tempTerrainTexture); // Rock1 tempTerrainTexture = new LBTerrainTexture(); tempTerrainTexture.texture = texture3; tempTerrainTexture.normalMap = texture3NM; tempTerrainTexture.tileSize = Vector2.one * 100f; tempTerrainTexture.texturingMode = LBTerrainTexture.TexturingMode.HeightAndInclination; tempTerrainTexture.minHeight = 10f / terrainHeight; tempTerrainTexture.maxHeight = 1000f / terrainHeight; tempTerrainTexture.minInclination = 30f; tempTerrainTexture.maxInclination = 60f; tempTerrainTexture.useNoise = true; tempTerrainTexture.noiseTileSize = 100f; tempTerrainTexture.strength = 1f; landscape.terrainTexturesList.Add(tempTerrainTexture); // CliffBlue tempTerrainTexture = new LBTerrainTexture(); tempTerrainTexture.texture = texture4; tempTerrainTexture.normalMap = texture4NM; tempTerrainTexture.tileSize = Vector2.one * 100f; tempTerrainTexture.texturingMode = LBTerrainTexture.TexturingMode.Height; tempTerrainTexture.minHeight = 100f / terrainHeight; tempTerrainTexture.maxHeight = 1000f / terrainHeight; tempTerrainTexture.useNoise = true; tempTerrainTexture.noiseTileSize = 10f; tempTerrainTexture.strength = 0.5f; landscape.terrainTexturesList.Add(tempTerrainTexture); // Good Dirt - FUTURE generate Map at runtime from slightly wider path and texture edges //tempTerrainTexture = new LBTerrainTexture(); //tempTerrainTexture.texture = texture5; //tempTerrainTexture.normalMap = texture5NM; //tempTerrainTexture.tileSize = Vector2.one * 10f; //tempTerrainTexture.texturingMode = LBTerrainTexture.TexturingMode.Map; //tempTerrainTexture.map = .... //tempTerrainTexture.mapIsPath = true; //tempTerrainTexture.useAdvancedMapTolerance = false; //tempTerrainTexture.mapInverse = false; //tempTerrainTexture.useNoise = false; //tempTerrainTexture.strength = 1f; //landscape.terrainTexturesList.Add(tempTerrainTexture); // Texture the terrains landscape.ApplyTextures(true, true); } // Add a tree type LBTerrainTree terrainTree1 = new LBTerrainTree(); if (terrainTree1 != null && treePrefab1 != null) { // Set tree type options as required. See LBTerrainTree class constructor for default settings terrainTree1.prefab = treePrefab1.gameObject; terrainTree1.maxTreesPerSqrKm = 140; terrainTree1.minScale = 2f; terrainTree1.maxScale = 3f; terrainTree1.treeScalingMode = LBTerrainTree.TreeScalingMode.RandomScaling; terrainTree1.lockWidthToHeight = true; terrainTree1.treePlacingMode = LBTerrainTree.TreePlacingMode.HeightAndInclination; terrainTree1.minInclination = 0f; terrainTree1.maxInclination = 30f; terrainTree1.minHeight = 0f; terrainTree1.maxHeight = 200f / terrainHeight; terrainTree1.useNoise = false; //// Create a new filter, which will be used for the Stencil Layer //// This demonstrates how to apply a stencil layer to the placement of trees //LBFilter lbFilterTree = LBFilter.CreateFilter(lbStencil, "Hills Layer 1", true); //if (lbFilterTree != null) //{ // // If the list of filters isn't defined, create an empty list // if (terrainTree1.filterList == null) { terrainTree1.filterList = new List<LBFilter>(); } // // Add the Stencil Layer to the list of Tree Filters // terrainTree1.filterList.Add(lbFilterTree); //} // Add the tree type configuration to the landscape landscape.terrainTreesList.Add(terrainTree1); } LBTerrainTree terrainTree2 = new LBTerrainTree(); if (terrainTree2 != null && treePrefab1 != null) { // Set tree type options as required. See LBTerrainTree class constructor for default settings terrainTree2.prefab = treePrefab2.gameObject; terrainTree2.maxTreesPerSqrKm = 140; terrainTree2.minScale = 2f; terrainTree2.maxScale = 3f; terrainTree2.treeScalingMode = LBTerrainTree.TreeScalingMode.RandomScaling; terrainTree2.lockWidthToHeight = true; terrainTree2.treePlacingMode = LBTerrainTree.TreePlacingMode.HeightAndInclination; terrainTree2.minInclination = 0f; terrainTree2.maxInclination = 30f; terrainTree2.minHeight = 0f; terrainTree2.maxHeight = 200f / terrainHeight; terrainTree2.useNoise = false; // Add the tree type configuration to the landscape landscape.terrainTreesList.Add(terrainTree2); } // Populate the landscape with the trees landscape.treesHaveColliders = true; landscape.treePlacementSpeed = LBTerrainTree.TreePlacementSpeed.FastPlacement; landscape.ApplyTrees(true, true); // Add some rock prefabs to the landscape LBLandscapeMesh lbLandscapeMesh = new LBLandscapeMesh(); if (lbLandscapeMesh != null && rockPrefab != null) { lbLandscapeMesh.usePrefab = true; lbLandscapeMesh.prefab = rockPrefab; // Set mesh options as required. See LBLandscapeMesh class constructor for default settings lbLandscapeMesh.maxMeshes = 50; lbLandscapeMesh.meshPlacingMode = LBLandscapeMesh.MeshPlacingMode.ConstantInfluence; // Sink the rocks into the ground by 0.5m lbLandscapeMesh.offset = new Vector3(0f, -0.5f, 0f); // Group the rocks together //lbLandscapeMesh.isClustered = true; //lbLandscapeMesh.clusterDensity = 0.1f; //lbLandscapeMesh.clusterDistance = 10f; landscape.landscapeMeshList.Add(lbLandscapeMesh); landscape.meshPlacementSpeed = LBLandscapeMesh.MeshPlacementSpeed.FastPlacement; //landscape.ApplyMeshes(true, true); } // Get the first Camera Animator in the scene, snap the camera path to the new terrain, // and start moving the camera along the camera path. LBCameraAnimator lbCameraAnimator = LBCameraAnimator.GetFirstCameraAnimatorInLandscape(landscape); if (lbCameraAnimator == null) { Debug.LogWarning("GetFirstCameraAnimatorInLandscape returned null"); } else { if (lbMapPath != null) { if (!lbCameraAnimator.cameraPath.ImportMapPathPoints(lbMapPath)) { Debug.LogWarning("Could not make camera animator travel along new Map Path."); } } // Get the LBPath instance which contains the points along the camera path LBPath lbPath = lbCameraAnimator.cameraPath.lbPath; if (lbPath == null) { Debug.LogWarning("Could not find the camera path instance for the animator"); } else { // Optionally update the path points to match the terrain lbPath.heightAboveTerrain = 5f; lbPath.snapToTerrain = true; lbPath.RefreshPathHeights(landscape); // Start the camera moving from the start of the path. lbCameraAnimator.BeginAnimation(true, 0f); } } // Display the total time taken to generate the landscape (usually for debugging purposes) Debug.Log("Time taken to generate landscape: " + (Time.realtimeSinceStartup - generationStartTime).ToString("00.00") + " seconds."); }
/// <summary> /// Create an LBMesh object to hold the water mesh for a Topography Image Modifier Layer. /// </summary> /// <param name="landscape"></param> /// <param name="lbLayer"></param> /// <param name="layerIdx"></param> /// <param name="meshTitle"></param> /// <param name="showErrors"></param> /// <returns></returns> public static bool CreateMeshForWaterFromLayer(LBLandscape landscape, LBLayer lbLayer, int layerIdx, string meshTitle, bool showErrors) { bool isSuccessful = false; string methodName = "LBMeshOperations.CreateMeshForWaterFromLayer"; if (landscape == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " landscape is null. Please Report"); } } else if (lbLayer == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " lbLayer is null. Please Report"); } } else if (lbLayer.type != LBLayer.LayerType.ImageModifier) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " LayerType must be ImageModifier. Please Report"); } } { // TODO get the minium area below zero for the RAW modifier file to use for meshWidth/Length // Get the absolute width and length Vector2 meshSize = Vector2.zero; meshSize.x = lbLayer.areaRect.width < 0 ? -lbLayer.areaRect.width : lbLayer.areaRect.width; meshSize.y = lbLayer.areaRect.height < 0 ? -lbLayer.areaRect.height : lbLayer.areaRect.height; // Get the bottom-left point of mesh with landscape (it might be outside the landscape) Vector2 meshBottomLeft = Vector2.zero; meshBottomLeft.x = lbLayer.areaRect.x - (meshSize.x / 2f); meshBottomLeft.y = lbLayer.areaRect.y - (meshSize.y / 2f); if (meshBottomLeft.x < 0) { meshBottomLeft.x += meshSize.x; } else if (meshBottomLeft.x == meshSize.x) { meshBottomLeft.x = 0f; } if (meshBottomLeft.y < 0) { meshBottomLeft.y += meshSize.y; } else if (meshBottomLeft.y == meshSize.y) { meshBottomLeft.y = 0f; } //Debug.Log("INFO: " + methodName + " areaRect xy: " + lbLayer.areaRect.x + "," + lbLayer.areaRect.y + " b-l:" + meshBottomLeft); // If there is an existing LBMesh instance, remove all the mesh data if (lbLayer.modifierWaterLBMesh != null) { LBMesh.DeleteMesh(lbLayer.modifierWaterLBMesh); } else { lbLayer.modifierWaterLBMesh = new LBMesh(); } if (lbLayer.modifierWaterLBMesh == null) { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " Could not create LBMesh for the water in Layer " + (layerIdx + 1) + ". Please Report"); } } else if (meshSize.x < 0.1f || meshSize.y < 0.1f) { { if (showErrors) { Debug.LogWarning("ERROR: " + methodName + " LayerType " + (layerIdx + 1) + " has an invalid water mesh size " + meshSize.x + "," + meshSize.y); } } } { lbLayer.modifierWaterLBMesh.title = meshTitle; // Initialise mesh lists List <Vector3> verts = new List <Vector3>(); List <Vector2> uvs = new List <Vector2>(); List <int> triangles = new List <int>(); List <Vector3> normals = new List <Vector3>(); List <Vector4> tangents = new List <Vector4>(); List <Vector4> colours = new List <Vector4>(); // Store as Vector4s rather than Color so they are serializable // Declare outside loops for less garbage collection // We are creating a flat plane, so can use Vector2. Vector2 vertPositionN = Vector2.zero; // Normalised position within mesh Vector2 vertLandscapePosN = Vector2.zero; // Normalised postion within the landscape int vertCount = 0; // Default colour of each vert (stored in LB as a Vector4) Vector4 defaultVertColour = new Vector4(1f, 1f, 1f, 1f); // The number of cells wide and long in the mesh int meshWidth = 10; int meshLength = 10; for (int x = 0; x < meshWidth; x++) { for (int z = 0; z < meshLength; z++) { // Create the vert as a 0-1 position // Normalise the RAW Pixel - converting it to a range of 0 to 1 vertPositionN.x = (float)x / (float)(meshWidth - 1); vertPositionN.y = (float)z / (float)(meshLength - 1); verts.Add(new Vector3(vertPositionN.x * meshSize.x, 0f, vertPositionN.y * meshSize.y)); normals.Add(Vector3.up); if (lbLayer.modifierWaterIsMeshLandscapeUV) { // UVs normalised to the landscape vertLandscapePosN.x = ((vertPositionN.x * meshSize.x) + meshBottomLeft.x) / landscape.size.x; vertLandscapePosN.y = ((vertPositionN.y * meshSize.y) + meshBottomLeft.y) / landscape.size.y; uvs.Add(new Vector2(vertLandscapePosN.x / lbLayer.modifierWaterMeshUVTileScale.x, vertLandscapePosN.y / lbLayer.modifierWaterMeshUVTileScale.y)); } else { // Generic uvs (simply 0-1 coordinates of vert position) uvs.Add(new Vector2(vertPositionN.x / lbLayer.modifierWaterMeshUVTileScale.x, vertPositionN.y / lbLayer.modifierWaterMeshUVTileScale.y)); } // Create tangent tangents.Add(new Vector4(1f, 0f, 0f, 1f)); // Add the two triangles for the quad // Not required if on left or bottom edges of the mesh if (x < meshWidth - 1 && z < meshLength - 1) { // Bottom (left) triangle // Bottom left of quad triangles.Add(vertCount); // Bottom right of quad triangles.Add(vertCount + 1); // Top right of quad triangles.Add(vertCount + meshWidth + 1); // Top (right) triangle // Top left of quad triangles.Add(vertCount + meshWidth); // Bottom left of quad triangles.Add(vertCount); // Top right of quad triangles.Add(vertCount + meshWidth + 1); } // Set default vert colour colours.Add(defaultVertColour); // Increment the vert count vertCount++; } } lbLayer.modifierWaterLBMesh.verts = verts; lbLayer.modifierWaterLBMesh.triangles = triangles; lbLayer.modifierWaterLBMesh.normals = normals; lbLayer.modifierWaterLBMesh.uvs = uvs; lbLayer.modifierWaterLBMesh.tangents = tangents; //Debug.Log("INFO " + methodName + " - created Mesh (verts " + verts.Count + " tris " + (triangles.Count / 3) + ")"); isSuccessful = true; } } return(isSuccessful); }
// Use this for initialization void Awake() { #region Initialise // This line just gets the starting time of the generation so that the total generation time // can be recorded and displayed float generationStartTime = Time.realtimeSinceStartup; RuntimeSampleHelper.RemoveDefaultCamera(); RuntimeSampleHelper.RemoveDefaultLight(); // Get a link to the LBLandscape script landscape = this.GetComponent <LBLandscape>(); if (landscape == null) { Debug.Log("Cannot find LBLandscape script attached to Runtime gameobject"); return; } // Update the size landscape.size = landscapeSize; #endregion #region Create the terrains int terrainNumber = 0; for (float tx = 0f; tx < landscapeSize.x - 1f; tx += 2000f) { for (float ty = 0f; ty < landscapeSize.y - 1f; ty += 2000f) { // Create a new gameobject GameObject terrainObj = new GameObject("Runtime Terrain " + (terrainNumber++).ToString("000")); // Correctly parent and position the terrain terrainObj.transform.parent = this.transform; terrainObj.transform.localPosition = new Vector3(tx, 0f, ty); // Add a terrain component Terrain newTerrain = terrainObj.AddComponent <Terrain>(); // Set terrain settings (depending on your situtation, you may need to set more or less than I have in this example) newTerrain.heightmapPixelError = 1; newTerrain.basemapDistance = 5000f; newTerrain.treeDistance = 5000f; newTerrain.treeBillboardDistance = 100f; newTerrain.detailObjectDistance = 150f; newTerrain.treeCrossFadeLength = 25f; newTerrain.groupingID = 100; // default is 0 newTerrain.allowAutoConnect = true; // Set terrain data settings (same as above comment) TerrainData newTerrainData = new TerrainData(); // One thing to note here is that modfiying the heightmap resolution not only clears all terrain height data, // it also scales up or down the size of the terrain. So you should always set the heightmap resolution // BEFORE you set the terrain size newTerrainData.heightmapResolution = 513; newTerrainData.size = Vector3.one * 2000f; newTerrainData.SetDetailResolution(1024, 16); newTerrain.terrainData = newTerrainData; // Set up the terrain collider TerrainCollider newTerrainCol = terrainObj.AddComponent <TerrainCollider>(); newTerrainCol.terrainData = newTerrainData; } } #endregion landscape.SetLandscapeTerrains(true); terrainHeight = landscape.GetLandscapeTerrainHeight(); landscape.SetDefaultTerrainMaterial(); #region Add a new Stencil to the (empty) landscape LBStencil lbStencil = LBStencil.CreateStencilInScene(landscape, landscape.gameObject); if (lbStencil != null) { lbStencil.name += "01"; /// Import PNG files into stencil layers (at runtime) and add as filters. lbStencil.layerImportMethod = LBStencil.LayerImportMethod.Grayscale; if (lbStencil.ImportTexture("Hills Layer 1", stencilLayerTex1, true)) { // Remove the default first layer if (lbStencil.stencilLayerList.Count > 1) { lbStencil.stencilLayerList.RemoveAt(0); } } } #endregion // NOTE: This sample uses a topography mask. This is not a requirement // Create the distance to centre mask using an animation curve AnimationCurve distanceToCentreMask = new AnimationCurve(); int keyInt = distanceToCentreMask.AddKey(0f, 1f); keyInt = distanceToCentreMask.AddKey(0.529f, 0.959f); keyInt = distanceToCentreMask.AddKey(1f, 0f); Keyframe[] curveKeys = distanceToCentreMask.keys; curveKeys[0].inTangent = 0f; curveKeys[0].outTangent = 0f; curveKeys[1].inTangent = -0.25f; curveKeys[1].outTangent = -0.25f; curveKeys[2].inTangent = 0f; curveKeys[2].outTangent = 0f; distanceToCentreMask = new AnimationCurve(curveKeys); // Assign the topography mask to the LBLandscape instance landscape.distanceToCentreMask = distanceToCentreMask; if (IsMaskingOn) { landscape.topographyMaskMode = LBLandscape.MaskMode.DistanceToCentre; } else { landscape.topographyMaskMode = LBLandscape.MaskMode.None; } landscape.maskWarpAmount = 0f; landscape.maskNoiseTileSize = 10000f; landscape.maskNoiseCurveModifier = AnimationCurve.Linear(0f, 0.5f, 1f, 1f); landscape.maskNoiseOffsetX = 0f; landscape.maskNoiseOffsetY = 0f; // Avoid warning of keyInt not being used. if (keyInt == 0) { } // Create the Topography Layers // You can mix and match Perlin and Image layers landscape.topographyLayersList = new List <LBLayer>(); if (landscape.topographyLayersList != null) { // Add one or more Base layers LBLayer lbBaseLayer1 = new LBLayer(); if (lbBaseLayer1 != null) { lbBaseLayer1 = LBLayer.SetLayerFromPreset(LBLayer.LayerPreset.DesertFloorBase); landscape.topographyLayersList.Add(lbBaseLayer1); } // Add one or more Additive layers LBLayer lbAdditiveLayer1 = new LBLayer(); if (lbAdditiveLayer1 != null) { // You can manually configure a layer, or use a preset then modify it. lbAdditiveLayer1 = LBLayer.SetLayerFromPreset(LBLayer.LayerPreset.RollingHillsBase); // If using using a different type of preset, must set the type after applying preset lbAdditiveLayer1.type = LBLayer.LayerType.PerlinAdditive; // Optionally override the preset settings //lbAdditiveLayer1.noiseTileSize = 5000f; //lbAdditiveLayer1.octaves = 8; //lbAdditiveLayer1.lacunarity = 1.92f; //lbAdditiveLayer1.gain = 0.45f; //lbAdditiveLayer1.warpAmount = 0; lbAdditiveLayer1.removeBaseNoise = true; lbAdditiveLayer1.heightScale = 0.35f; lbAdditiveLayer1.additiveAmount = 0.95f; lbAdditiveLayer1.additiveCurve = LBLayer.CreateAdditiveCurve(lbAdditiveLayer1.additiveAmount); //lbAdditiveLayer1.perOctaveCurveModifierPresets = new List<LBCurve.CurvePreset>(); //lbAdditiveLayer1.perOctaveCurveModifierPresets.Add(LBCurve.CurvePreset.DoubleRidged); // Create a new topography layer filter, which will be used for the Stencil Layer LBLayerFilter lbLayerFilter = LBLayerFilter.CreateLayerFilter(lbStencil, "Hills Layer 1", true); if (lbLayerFilter != null) { // If the list of filters isn't defined, create an empty list if (lbAdditiveLayer1.filters == null) { lbAdditiveLayer1.filters = new List <LBLayerFilter>(); } // Add the Stencil Layer to the list of Topography LayerFilters lbAdditiveLayer1.filters.Add(lbLayerFilter); } landscape.topographyLayersList.Add(lbAdditiveLayer1); } // Add a detail layer LBLayer lbDetailLayer1 = new LBLayer(); if (lbDetailLayer1 != null) { lbDetailLayer1 = LBLayer.SetLayerFromPreset(LBLayer.LayerPreset.HillsDetail); landscape.topographyLayersList.Add(lbDetailLayer1); } } // Create the terrain topographies landscape.ApplyTopography(false, true); // Get the first Camera Animator in the scene, snap the camera path to the new terrain, // and start moving the camera along the camera path. LBCameraAnimator lbCameraAnimator = LBCameraAnimator.GetFirstCameraAnimatorInLandscape(landscape); if (lbCameraAnimator == null) { Debug.LogWarning("GetFirstCameraAnimatorInLandscape returned null"); } else { // Get the LBPath instance which contains the points along the camera path LBPath lbPath = lbCameraAnimator.cameraPath.lbPath; if (lbPath == null) { Debug.LogWarning("Could not find the camera path instance for the animator"); } else { // Optionally update the path points to match the terrain lbPath.heightAboveTerrain = 15f; lbPath.snapToTerrain = true; lbPath.RefreshPathHeights(landscape); // Start the camera moving from the start of the path. lbCameraAnimator.BeginAnimation(true, 0f); } } // Add some water the scene int numberOfMeshes = 0; Vector2 waterSize = new Vector2(landscape.size.x * 2f, landscape.size.y * 2f); // The primary water body is placed in the centre of the landscape Vector3 waterPosition = landscape.start + (0.5f * new Vector3(landscape.size.x, 0f, landscape.size.y)); // Populate the paramaters to pass to AddWaterToScene() LBWaterParameters lbWaterParms = new LBWaterParameters(); lbWaterParms.landscape = landscape; lbWaterParms.landscapeGameObject = landscape.gameObject; lbWaterParms.waterPosition = waterPosition; lbWaterParms.waterSize = waterSize; lbWaterParms.waterIsPrimary = true; lbWaterParms.waterHeight = waterHeight; lbWaterParms.waterPrefab = waterPrefab; lbWaterParms.keepPrefabAspectRatio = true; lbWaterParms.waterResizingMode = LBWater.WaterResizingMode.StandardAssets; lbWaterParms.waterMaxMeshThreshold = 5000; lbWaterParms.waterMainCamera = Camera.main; lbWaterParms.waterCausticsPrefabList = null; lbWaterParms.isRiver = false; lbWaterParms.lbLighting = GameObject.FindObjectOfType <LBLighting>(); LBWater addedWater = LBWaterOperations.AddWaterToScene(lbWaterParms, ref numberOfMeshes); if (addedWater == null) { Debug.LogWarning("Could not add water to scene"); } // Create a list of LBTerrainTexture objects // These contain the textures and normal maps but also the rules for applying them to the terrain landscape.terrainTexturesList = new List <LBTerrainTexture>(); // Populate the list by creating temporary LBTerrainTexture objects and adjusting their settings, // then adding each one into the list // Grass Hill texture LBTerrainTexture tempTerrainTexture = new LBTerrainTexture(); if (tempTerrainTexture != null) { tempTerrainTexture.texture = grassHillTexture; tempTerrainTexture.normalMap = grassHillNormalMap; tempTerrainTexture.tileSize = Vector2.one * 25f; tempTerrainTexture.minInclination = 0f; tempTerrainTexture.maxInclination = 45f; tempTerrainTexture.useNoise = true; tempTerrainTexture.noiseTileSize = 100f; tempTerrainTexture.texturingMode = LBTerrainTexture.TexturingMode.Inclination; landscape.terrainTexturesList.Add(tempTerrainTexture); // Rock Layered texture tempTerrainTexture = new LBTerrainTexture(); tempTerrainTexture.texture = rockLayeredTexture; tempTerrainTexture.normalMap = rockLayeredNormalMap; tempTerrainTexture.tileSize = Vector2.one * 100f; tempTerrainTexture.minInclination = 30f; tempTerrainTexture.maxInclination = 90f; tempTerrainTexture.useNoise = true; tempTerrainTexture.noiseTileSize = 100f; tempTerrainTexture.texturingMode = LBTerrainTexture.TexturingMode.Inclination; landscape.terrainTexturesList.Add(tempTerrainTexture); // Texture the terrains landscape.ApplyTextures(true, true); } // Add a tree type LBTerrainTree tempTerrainTree = new LBTerrainTree(); if (tempTerrainTree != null && treePrefab != null) { // Set tree type options as required. See LBTerrainTree class constructor for default settings tempTerrainTree.prefab = treePrefab.gameObject; tempTerrainTree.maxTreesPerSqrKm = 100; tempTerrainTree.treePlacingMode = LBTerrainTree.TreePlacingMode.HeightAndInclination; tempTerrainTree.minInclination = 0f; tempTerrainTree.maxInclination = 30f; tempTerrainTree.minHeight = (waterHeight + 2f) / terrainHeight; // Create a new filter, which will be used for the Stencil Layer // This demonstrates how to apply a stencil layer to the placement of trees LBFilter lbFilterTree = LBFilter.CreateFilter(lbStencil, "Hills Layer 1", true); if (lbFilterTree != null) { // If the list of filters isn't defined, create an empty list if (tempTerrainTree.filterList == null) { tempTerrainTree.filterList = new List <LBFilter>(); } // Add the Stencil Layer to the list of Tree Filters tempTerrainTree.filterList.Add(lbFilterTree); } // Add the tree type configuration to the landscape landscape.terrainTreesList.Add(tempTerrainTree); // Populate the landscape with the trees landscape.ApplyTrees(true, true); } // Add some rock prefabs to the landscape LBLandscapeMesh lbLandscapeMesh = new LBLandscapeMesh(); if (lbLandscapeMesh != null && rockPrefab != null) { lbLandscapeMesh.usePrefab = true; lbLandscapeMesh.prefab = rockPrefab; // Set mesh options as required. See LBLandscapeMesh class constructor for default settings lbLandscapeMesh.maxMeshes = 100; // Sink the rocks into the ground by 0.5m lbLandscapeMesh.offset = new Vector3(0f, -0.5f, 0f); // Place the rocks near the water's edge lbLandscapeMesh.meshPlacingMode = LBLandscapeMesh.MeshPlacingMode.HeightAndInclination; lbLandscapeMesh.minHeight = (waterHeight + 1f) / terrainHeight; lbLandscapeMesh.maxHeight = lbLandscapeMesh.minHeight + (10f / terrainHeight); lbLandscapeMesh.maxInclination = 12.5f; lbLandscapeMesh.useNoise = true; // Group the rocks together lbLandscapeMesh.isClustered = true; lbLandscapeMesh.clusterDensity = 0.1f; lbLandscapeMesh.clusterDistance = 10f; //lbLandscapeMesh.minProximity = 10f; landscape.landscapeMeshList.Add(lbLandscapeMesh); landscape.ApplyMeshes(true, true); } // Display the total time taken to generate the landscape (usually for debugging purposes) Debug.Log("Time taken to generate landscape: " + (Time.realtimeSinceStartup - generationStartTime).ToString("00.00") + " seconds."); }