public Serializable2DFloatArray GetArrayFromMask(WorldStampCreator parent) { GridSize = WorldStampCreator.GetMinGridSize(parent.Template.Bounds, parent.Template.Terrain); var bounds = parent.Template.Bounds; var width = Mathf.CeilToInt(bounds.size.x / GridSize); var height = Mathf.CeilToInt(bounds.size.z / GridSize); var array = new Serializable2DFloatArray(width, height); for (var u = 0; u < width; u++) { for (var v = 0; v < height; v++) { var pos = new Vector3((u / (float)width) * bounds.size.x, bounds.size.y / 2, (v / (float)height) * bounds.size.z); var cell = GridManager.GetCell(pos); var cellMax = GridManager.GetCellMax(cell).x0z() + bounds.min; var cellMin = GridManager.GetCellCenter(cell).x0z() + bounds.min; if (!bounds.Contains(cellMax) || !bounds.Contains(cellMin)) { continue; } var val = Mask.GetValue(cell); array[u, v] = val; } } return(array); }
public override Serializable2DFloatArray GetHeights(int x, int z, int xSize, int zSize, int hRes) { if (Heights == null || Heights.Width != hRes || Heights.Height != hRes) { return(null); } var h = new Serializable2DFloatArray(xSize, zSize); for (var u = x; u < x + xSize; ++u) { for (var v = z; v < z + zSize; ++v) { var hx = u - x; var hz = v - z; try { var baseHeight = Heights[u, v]; h[hx, hz] = baseHeight; } catch (IndexOutOfRangeException e) { Debug.LogError(string.Format("x {0} y {1}", hx, hz)); throw e; } } } return(h); }
private void FillMaskFromMinY(Bounds bounds, Terrain terrain, Serializable2DFloatArray heights, Vector2 minY) { Mask.Clear(); GridSize = WorldStampCreator.GetMinGridSize(bounds, terrain); for (var u = GridSize / 2f; u < bounds.size.x; u += GridSize) { for (var v = GridSize / 2f; v < bounds.size.z; v += GridSize) { var cell = GridManager.GetCell(new Vector3(u, 0, v)); var cellMax = GridManager.GetCellMax(cell).x0z() + bounds.min; var cellMin = GridManager.GetCellCenter(cell).x0z() + bounds.min; if (!bounds.Contains(cellMax) || !bounds.Contains(cellMin)) { continue; } var h = heights.BilinearSample(new Vector2(u / bounds.size.z, v / bounds.size.x)) * bounds.size.y; if (h < minY.x) { Mask.SetValue(cell, 0); } else if (h <= minY.y) { Mask.SetValue(cell, (h - minY.x) / (minY.y - minY.x)); } else { Mask.SetValue(cell, 1); } } } }
public static void AbsStencil(Serializable2DFloatArray stencil, int stencilKey = 0) { for (var u = 0; u < stencil.Width; ++u) { for (var v = 0; v < stencil.Height; ++v) { int key; float strength; var rawValue = stencil[u, v]; if (rawValue == 0) { continue; } DecompressStencil(rawValue, out key, out strength, false); if (stencilKey != 0 && Mathf.Abs(key) != stencilKey) { continue; } var newValue = CompressStencil(Mathf.Abs(key), strength); stencil[u, v] = newValue; } } }
private void WriteHeightsToTerrain(TerrainWrapper wrapper, Bounds bounds) { var heightmapRes = wrapper.Terrain.terrainData.heightmapResolution; if (Heights == null || Heights.Width != heightmapRes || Heights.Height != heightmapRes) { if (Heights != null && Heights.Width > 0 && Heights.Height > 0) { Debug.LogWarning( string.Format( "Failed to write heights for layer '{0}' as it was the wrong resolution. Expected {1}x{1}, got {2}x{2}", name, heightmapRes, Heights.Width), wrapper); } return; } var terrain = wrapper.Terrain; var existingHeights = wrapper.CompoundTerrainData.Heights; if (existingHeights == null || existingHeights.Width != heightmapRes || existingHeights.Height != heightmapRes) { existingHeights = new Serializable2DFloatArray(heightmapRes, heightmapRes); wrapper.CompoundTerrainData.Heights = existingHeights; } var min = terrain.WorldToHeightmapCoord(bounds.min, TerrainX.RoundType.Floor); var max = terrain.WorldToHeightmapCoord(bounds.max, TerrainX.RoundType.Floor); min = new Common.Coord(Mathf.Clamp(min.x, 0, heightmapRes), Mathf.Clamp(min.z, 0, heightmapRes)); max = new Common.Coord(Mathf.Clamp(max.x, 0, heightmapRes), Mathf.Clamp(max.z, 0, heightmapRes)); BlendMMTerrainLayerUtility.BlendArray(ref existingHeights, Heights, IsValidStencil(Stencil) ? Stencil : null, BlendMode, min, max, new Common.Coord(heightmapRes, heightmapRes)); }
public static void StencilBilinearSample(this Serializable2DFloatArray array, Vector2 normalizedCoord, out float strength, bool ignoreNegativeKeys = true) { if (normalizedCoord.x < 0 || normalizedCoord.x > 1 || normalizedCoord.y < 0 || normalizedCoord.y > 1) { strength = 0; return; } normalizedCoord = new Vector2(normalizedCoord.x * array.Width, normalizedCoord.y * array.Height); int xMin = Mathf.FloorToInt(normalizedCoord.x); int xMax = Mathf.CeilToInt(normalizedCoord.x); int yMin = Mathf.FloorToInt(normalizedCoord.y); int yMax = Mathf.CeilToInt(normalizedCoord.y); xMin = Mathf.Clamp(xMin, 0, array.Width - 1); xMax = Mathf.Clamp(xMax, 0, array.Width - 1); yMin = Mathf.Clamp(yMin, 0, array.Height - 1); yMax = Mathf.Clamp(yMax, 0, array.Height - 1); float v1 = array[xMin, yMin]; float v2 = array[xMin, yMax]; float v3 = array[xMax, yMin]; float v4 = array[xMax, yMax]; int v1Index; int v2Index; int v3Index; int v4Index; MiscUtilities.DecompressStencil(v1, out v1Index, out v1); MiscUtilities.DecompressStencil(v2, out v2Index, out v2); MiscUtilities.DecompressStencil(v3, out v3Index, out v3); MiscUtilities.DecompressStencil(v4, out v4Index, out v4); if (ignoreNegativeKeys) { v1 = v1Index > 0 ? v1 : 0; v2 = v1Index > 0 ? v2 : 0; v3 = v1Index > 0 ? v3 : 0; v4 = v1Index > 0 ? v4 : 0; } if (Math.Abs(v1 + v2 + v3 + v4) < .01f) { strength = 0; return; } float xFrac = normalizedCoord.x.Frac(); float yFrac = normalizedCoord.y.Frac(); v1 *= (1 - xFrac) * (1 - yFrac); v2 *= (1 - xFrac) * (/*1 - */ yFrac); v3 *= (/*1 - */ xFrac) * (1 - yFrac); v4 *= (/*1 - */ xFrac) * (/*1 -*/ yFrac); strength = Mathf.Clamp01(v1 + v2 + v3 + v4); }
public override void Clear(TerrainWrapper wrapper) { Dispose(wrapper, false); Trees.Clear(); TreeRemovals.Clear(); if (Heights != null) { var tRes = wrapper.Terrain.terrainData.heightmapResolution; if (Heights.Width == tRes && Heights.Height == tRes) { Heights.Clear(); } else { Heights = new Serializable2DFloatArray(tRes, tRes); } } SplatData.Clear(); Objects.Clear(); ObjectRemovals.Clear(); if (DetailData != null) { DetailData.Clear(); } if (Stencil != null) { var tRes = wrapper.Terrain.terrainData.heightmapResolution; if (Stencil.Width == tRes && Stencil.Height == tRes) { Stencil.Clear(); } else { Stencil = new Stencil(tRes, tRes); } } #if VEGETATION_STUDIO VSInstances.Clear(); VSRemovals.Clear(); #endif GC.Collect(); #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(this); #endif }
public static float BilinearSample(this Serializable2DFloatArray array, Vector2 normalizedCoord) { if (array == null) { return(0); } if (array.Width == 0 && array.Height == 0) { return(0); } if (normalizedCoord.x < 0 || normalizedCoord.x > 1 || normalizedCoord.y < 0 || normalizedCoord.y > 1) { return(0); } normalizedCoord = new Vector2(normalizedCoord.x * array.Width, normalizedCoord.y * array.Height); int xMin = Mathf.FloorToInt(normalizedCoord.x); int xMax = Mathf.CeilToInt(normalizedCoord.x); int yMin = Mathf.FloorToInt(normalizedCoord.y); int yMax = Mathf.CeilToInt(normalizedCoord.y); xMin = Mathf.Clamp(xMin, 0, array.Width - 1); xMax = Mathf.Clamp(xMax, 0, array.Width - 1); yMin = Mathf.Clamp(yMin, 0, array.Height - 1); yMax = Mathf.Clamp(yMax, 0, array.Height - 1); float v1 = array[xMin, yMin]; float v2 = array[xMin, yMax]; float v3 = array[xMax, yMin]; float v4 = array[xMax, yMax]; if (Math.Abs(v1 + v2 + v3 + v4) < Single.Epsilon) { return(0); } float xFrac = normalizedCoord.x.Frac(); float yFrac = normalizedCoord.y.Frac(); v1 *= (1 - xFrac) * (1 - yFrac); v2 *= (1 - xFrac) * (/*1 - */ yFrac); v3 *= (/*1 - */ xFrac) * (1 - yFrac); v4 *= (/*1 - */ xFrac) * (/*1 -*/ yFrac); return(v1 + v2 + v3 + v4); }
protected override void CaptureInternal(Terrain terrain, Bounds bounds) { var min = terrain.WorldToHeightmapCoord(bounds.min, TerrainX.RoundType.Floor); var max = terrain.WorldToHeightmapCoord(bounds.max, TerrainX.RoundType.Floor); int width = max.x - min.x; int height = max.z - min.z; float avgMarginHeight = 0; // If we want, we can have the stamp try to automatically find a good zero level by averaging the heights around the edge of the stamp int marginCount = 0; Heights = new Serializable2DFloatArray(width, height); float maxHeight = float.MinValue; var sampleHeights = terrain.terrainData.GetHeights(min.x, min.z, width, height); for (var dx = 0; dx < width; ++dx) { for (var dz = 0; dz < height; ++dz) { var sample = sampleHeights[dz, dx]; Heights[dx, dz] = sample; if (sample > maxHeight) { maxHeight = sample; } if (dx == 0 || dx == width - 1 || dz == 0 || dz == height - 1) { avgMarginHeight += sample; marginCount++; } } } if (AutoZeroLevel) { ZeroLevel = avgMarginHeight / marginCount; } for (var dx = 0; dx < width; ++dx) { for (var dz = 0; dz < height; ++dz) { Heights[dx, dz] -= ZeroLevel; } } HeightSize = maxHeight - ZeroLevel; _dirty = true; }
public void SetHeights(int x, int z, Serializable2DFloatArray heights, int heightRes, Serializable2DFloatArray stencil = null) { if (heights == null) { return; } if (Heights == null || Heights.Width != heightRes || Heights.Height != heightRes) { Heights = new Serializable2DFloatArray(heightRes, heightRes); } var width = heights.Width; var height = heights.Height; for (var u = x; u < x + width; ++u) { for (var v = z; v < z + height; ++v) { var hx = u - x; var hz = v - z; try { var heightsSample = heights[hx, hz]; heightsSample = Mathf.Clamp01(heightsSample); if (stencil == null) { Heights[u, v] = heightsSample; } else { float val; int key; MiscUtilities.DecompressStencil(stencil[u, v], out key, out val); Heights[u, v] = Mathf.Lerp(Heights[u, v], heightsSample, val); } } catch (IndexOutOfRangeException e) { Debug.LogError(string.Format("x {0} y {1}", hx, hz)); throw e; } } } #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(this); #endif }
public static void InvertStencil(Serializable2DFloatArray stencil) { for (var u = 0; u < stencil.Width; ++u) { for (var v = 0; v < stencil.Height; ++v) { int key; float strength; var rawValue = stencil[u, v]; if (rawValue == 0) { continue; } DecompressStencil(rawValue, out key, out strength, false); var newValue = CompressStencil(-key, strength); stencil[u, v] = newValue; } } }
public static void ColoriseStencil(Serializable2DFloatArray stencil) { for (var u = 0; u < stencil.Width; ++u) { for (var v = 0; v < stencil.Height; ++v) { int key; float strength; DecompressStencil(stencil[u, v], out key, out strength); if (strength > 0 && key > 0) { stencil[u, v] = MiscUtilities.CompressStencil(key, 1); } else { stencil[u, v] = 0; } } } }
public static void ClampStencil(Serializable2DFloatArray stencil) { if (stencil == null) { return; } for (var u = 0; u < stencil.Width; ++u) { for (var v = 0; v < stencil.Height; ++v) { int key; float strength; DecompressStencil(stencil[u, v], out key, out strength); if (key < 1) { stencil[u, v] = 0; } } } }
public void SetMaskFromArray(WorldStampCreator parent, Serializable2DFloatArray mask) { GridSize = WorldStampCreator.GetMinGridSize(parent.Template.Bounds, parent.Template.Terrain); Mask.Clear(); for (var u = GridSize / 2f; u < parent.Template.Bounds.size.x; u += GridSize) { for (var v = GridSize / 2f; v < parent.Template.Bounds.size.z; v += GridSize) { var cell = GridManager.GetCell(new Vector3(u, 0, v)); var cellMax = GridManager.GetCellMax(cell).x0z() + parent.Template.Bounds.min; var cellMin = GridManager.GetCellCenter(cell).x0z() + parent.Template.Bounds.min; if (!parent.Template.Bounds.Contains(cellMax) || !parent.Template.Bounds.Contains(cellMin)) { continue; } var val = mask.BilinearSample(new Vector2(u / parent.Template.Bounds.size.x, v / parent.Template.Bounds.size.z)); Mask.SetValue(cell, val); } } }
public virtual Serializable2DFloatArray BlendHeights(int x, int z, int width, int height, int heightRes, Serializable2DFloatArray result) { return(result); }
public static void StencilEraseArray( ref Serializable2DByteArray baseData, Serializable2DFloatArray stencil, Common.Coord min, Common.Coord max, Common.Coord size, bool invert, bool absolute) { if (baseData == null || stencil == null) { return; } var width = baseData.Width; var height = baseData.Height; if (TerrainWrapper.ComputeShaders && ShouldCompute(baseData)) { var blendShader = ComputeShaderPool.GetShader("MadMaps/WorldStamp/ComputeShaders/StencilIntArray"); int kernelHandle = blendShader.FindKernel("StencilInts"); var dataAsInt = baseData.Data.ConvertToIntArray(); //var baseBuffer = new ComputeBuffer(baseData.Width * baseData.Height, sizeof(float)); var baseBuffer = ComputeShaderPool.GetBuffer(baseData.Width * baseData.Height, sizeof(float)); baseBuffer.SetData(dataAsInt); blendShader.SetBuffer(kernelHandle, "_Base", baseBuffer); blendShader.SetVector("_BaseSize", new Vector4(baseData.Width, baseData.Height)); blendShader.SetVector("_MinMax", new Vector4(min.x, min.z, max.x, max.z)); blendShader.SetVector("_TotalSize", new Vector4(size.x, size.z)); //ComputeBuffer stencilBuffer= new ComputeBuffer(stencil.Width * stencil.Height, sizeof(float)); var stencilBuffer = ComputeShaderPool.GetBuffer(stencil.Width * stencil.Height, sizeof(float)); stencilBuffer.SetData(stencil.Data); blendShader.SetBuffer(kernelHandle, "_Stencil", stencilBuffer); blendShader.SetVector("_StencilSize", new Vector4(stencil.Width, stencil.Height)); blendShader.SetBool("_Invert", invert); blendShader.SetBool("_Absolute", absolute); blendShader.Dispatch(kernelHandle, baseData.Width, baseData.Height, 1); baseBuffer.GetData(dataAsInt); for (int i = 0; i < dataAsInt.Length; i++) { var val = dataAsInt[i]; baseData.Data[i] = (byte)Mathf.Clamp(val, 0, 255); } /*baseBuffer.Release(); * baseBuffer.Dispose(); * stencilBuffer.Release(); * stencilBuffer.Dispose(); * Resources.UnloadAsset(blendShader);*/ ComputeShaderPool.ReturnBuffer(baseBuffer); ComputeShaderPool.ReturnBuffer(stencilBuffer); UseCount++; } else { for (var u = 0; u < width; ++u) { var uF = (u + min.x) / (float)size.x; for (var v = 0; v < height; ++v) { var vF = (v + min.z) / (float)size.z; float value; stencil.StencilBilinearSample(new Vector2(uF, vF), out value); var existingValue = baseData[u, v]; var newValue = existingValue; if (absolute) { newValue = (byte)(value > 0 ? existingValue : 0); } else { newValue = (byte)Mathf.Clamp(existingValue * (1 - value), 0, 255); if (invert) { newValue = (byte)Mathf.Clamp(existingValue * value, 0, 255); } } baseData[u, v] = newValue; } } } GC.Collect(3, GCCollectionMode.Forced); }
public void Invalidate(Serializable2DFloatArray heights, Func <Vector3> displaySizeGetter, Func <Vector3> positionGetter, Func <Vector3> scaleGetter, Func <Quaternion> rotationGetter, Func <Vector3> dataSizeGetter, bool flipHeights, WorldStampMask mask, Common.Painter.GridManagerInt gridManager, Func <bool> existenceHook, int res) { _dataSize = dataSizeGetter; _displaySize = displaySizeGetter; _existenceHook = existenceHook; _position = positionGetter; _scale = scaleGetter; _rotation = rotationGetter; if (_mesh == null) { _mesh = new Mesh(); } if (_material == null) { _material = Resources.Load <Material>("MadMaps/WorldStamp/WorldStampPreviewMaterial"); } var verts = new Vector3[(res + 1) * (res + 1)]; var uv = new Vector2[verts.Length]; var colors = new Color[verts.Length]; int counter = 0; var dataSize = dataSizeGetter(); for (int u = 0; u <= res; u++) { var uF = u / (float)res; for (int v = 0; v <= res; v++) { var vF = v / (float)res; var samplePoint = flipHeights ? new Vector2(uF, vF) : new Vector2(vF, uF); var height = heights.BilinearSample(samplePoint); var pos = new Vector3(uF * dataSize.x, height * dataSize.y, vF * dataSize.z) - dataSize.xz().x0z() / 2; float val = 1; if (mask != null && gridManager != null) { val = mask.GetBilinear(gridManager, new Vector3(pos.x, 0, pos.z) + dataSize.xz().x0z() / 2); pos.y *= val; } verts[counter] = pos; uv[counter] = new Vector2(uF, vF); colors[counter] = Color.Lerp(Color.clear, Color.white, val); counter++; } } _mesh.vertices = verts; _mesh.uv = uv; _mesh.colors = colors; var tris = new int[((res + 1) * (res)) * 6]; for (var i = 0; i < tris.Length; i += 6) { var vIndex = i / 6; var t1 = vIndex + 0; var t2 = vIndex + 1; var t3 = vIndex + 2 + (res - 1); var t1r = t1 / (res + 1); var t2r = t2 / (res + 1); var t3r = (t3 / (res + 1)) - 1; if (t1r == t2r && t2r == t3r && t3r == t1r) { tris[i + 0] = t1; tris[i + 1] = t2; tris[i + 2] = t3; } var t4 = vIndex + 2 + (res - 1); var t5 = vIndex + 1 + (res - 1); var t6 = vIndex + 0; var t4r = (t4 / (res + 1)) - 1; var t5r = (t5 / (res + 1)) - 1; var t6r = t6 / (res + 1); if (t4r == t5r && t5r == t6r && t6r == t4r) { tris[i + 3] = t4; tris[i + 4] = t5; tris[i + 5] = t6; } } _mesh.triangles = tris; _mesh.RecalculateNormals(0); _mesh.RecalculateBounds(); #if UNITY_EDITOR UnityEditor.SceneView.onSceneGUIDelegate -= OnSceneGUIDelegate; UnityEditor.SceneView.onSceneGUIDelegate += OnSceneGUIDelegate; #endif }
public static void BlendArray(ref Serializable2DFloatArray baseData, Serializable2DFloatArray blendingData, Serializable2DFloatArray stencil, MMTerrainLayer.EMMTerrainLayerBlendMode blendMode, Common.Coord offset, Common.Coord max, Common.Coord originalSize) { if (blendingData == null) { return; } if (baseData == null) { baseData = blendingData; return; } var width = baseData.Width; var height = baseData.Height; if (TerrainWrapper.ComputeShaders && ShouldCompute(baseData)) { var blendShader = ComputeShaderPool.GetShader("MadMaps/WorldStamp/ComputeShaders/BlendFloatArray"); int kernelHandle = blendShader.FindKernel("BlendFloats"); //var baseBuffer = new ComputeBuffer(baseData.Width * baseData.Height, sizeof(float)); var baseBuffer = ComputeShaderPool.GetBuffer(baseData.Width * baseData.Height, sizeof(float)); baseBuffer.SetData(baseData.Data); blendShader.SetBuffer(kernelHandle, "_Base", baseBuffer); blendShader.SetVector("_BaseSize", new Vector4(baseData.Width, baseData.Height)); //var blendBuffer = new ComputeBuffer(blendingData.Width * blendingData.Height, sizeof(float)); var blendBuffer = ComputeShaderPool.GetBuffer(blendingData.Width * blendingData.Height, sizeof(float)); blendBuffer.SetData(blendingData.Data); blendShader.SetBuffer(kernelHandle, "_Blend", blendBuffer); blendShader.SetVector("_BlendSize", new Vector4(blendingData.Width, blendingData.Height)); ComputeBuffer stencilBuffer = null; if (stencil != null && stencil.Width > 0 && stencil.Height > 0) { //stencilBuffer = new ComputeBuffer(stencil.Width * stencil.Height, sizeof(float)); stencilBuffer = ComputeShaderPool.GetBuffer(stencil.Width * stencil.Height, sizeof(float)); stencilBuffer.SetData(stencil.Data); blendShader.SetBuffer(kernelHandle, "_Stencil", stencilBuffer); blendShader.SetVector("_StencilSize", new Vector4(stencil.Width, stencil.Height)); } else { stencilBuffer = ComputeShaderPool.GetBuffer(1, sizeof(float)); blendShader.SetBuffer(kernelHandle, "_Stencil", stencilBuffer); blendShader.SetVector("_StencilSize", new Vector4(0, 0)); } blendShader.SetInt("_BlendMode", (int)blendMode); blendShader.SetVector("_MinMax", new Vector4(offset.x, offset.z, max.x, max.z)); blendShader.SetVector("_TotalSize", new Vector4(originalSize.x, originalSize.z, 0, 0)); blendShader.Dispatch(kernelHandle, baseData.Width, baseData.Height, 1); baseBuffer.GetData(baseData.Data); /*baseBuffer.Release(); * baseBuffer.Dispose(); * blendBuffer.Release(); * blendBuffer.Dispose();*/ ComputeShaderPool.ReturnBuffer(baseBuffer); ComputeShaderPool.ReturnBuffer(blendBuffer); if (stencilBuffer != null) { /*stencilBuffer.Release(); * stencilBuffer.Dispose();*/ ComputeShaderPool.ReturnBuffer(stencilBuffer); } //Resources.UnloadAsset(blendShader); UseCount++; } else { for (int dx = 0; dx < width; dx++) { for (int dz = 0; dz < height; dz++) { var blendingVal = blendingData[dx, dz]; var baseValue = baseData[dx, dz]; switch (blendMode) { case MMTerrainLayer.EMMTerrainLayerBlendMode.Set: baseData[dx, dz] = blendingVal; break; case MMTerrainLayer.EMMTerrainLayerBlendMode.Additive: baseData[dx, dz] += blendingVal; break; case MMTerrainLayer.EMMTerrainLayerBlendMode.Stencil: var xF = (dx + offset.x) / (float)originalSize.x; var zF = (dz + offset.z) / (float)originalSize.z; float strength; stencil.StencilBilinearSample(new Vector2(xF, zF), out strength); //strength = 1; if (strength > 0) { baseData[dx, dz] = Mathf.Lerp(baseValue, blendingVal, strength); } break; } } } } GC.Collect(3, GCCollectionMode.Forced); }
public override void ProcessSplats(TerrainWrapper wrapper, LayerBase baseLayer, int stencilKey) { var layer = baseLayer as MMTerrainLayer; if (layer == null) { Debug.LogWarning(string.Format("Attempted to write {0} to incorrect layer type! Expected Layer {1} to be {2}, but it was {3}", name, baseLayer.name, GetLayerType(), baseLayer.GetType()), this); return; } if (!Network || Configuration == null) { return; } var config = Configuration.GetConfig <Config>(); if (config == null) { Debug.LogError("Invalid configuration! Expected ConnectionTerrainHeightConfiguration"); return; } var terrain = wrapper.Terrain; var splatRes = wrapper.Terrain.terrainData.alphamapResolution; var mainSpline = NodeConnection.GetSpline(); var radius = config.Radius; // Create bounds to encapsulate spline (axis aligned) var bounds = mainSpline.GetApproximateBounds(); bounds.Expand(radius * 2 * Vector3.one); // Create object bounds var objectBounds = mainSpline.GetApproximateXZObjectBounds(); objectBounds.Expand(radius * 2 * Vector3.one); objectBounds.Expand(Vector3.up * 10000); // Early cull var axisBounds = objectBounds.ToAxisBounds(); var terrainBounds = wrapper.Terrain.GetComponent <Collider>().bounds; if (!terrainBounds.Intersects(axisBounds)) { return; } float planeGive = -(wrapper.Terrain.terrainData.size.x / wrapper.Terrain.terrainData.alphamapResolution) * SplatOffset; Plane startPlane, endPlane; GenerateSplinePlanes(planeGive, mainSpline, out startPlane, out endPlane); // Get matrix space min/max var matrixMin = terrain.WorldToSplatCoord(bounds.min, TerrainX.RoundType.Floor); var matrixMax = terrain.WorldToSplatCoord(bounds.max, TerrainX.RoundType.Ceil); matrixMin = new Common.Coord(Mathf.Clamp(matrixMin.x, 0, terrain.terrainData.alphamapResolution), Mathf.Clamp(matrixMin.z, 0, terrain.terrainData.alphamapResolution)); matrixMax = new Common.Coord(Mathf.Clamp(matrixMax.x, 0, terrain.terrainData.alphamapResolution), Mathf.Clamp(matrixMax.z, 0, terrain.terrainData.alphamapResolution)); var floatArraySize = new Common.Coord(matrixMax.x - matrixMin.x, matrixMax.z - matrixMin.z); // Get all the existing compound splats var currentPrototypes = wrapper.GetCompoundSplatPrototypes(layer, true); var baseData = layer.GetSplatMaps(matrixMin.x, matrixMin.z, floatArraySize.x, floatArraySize.z, splatRes); stencilKey = GetStencilKey(); Serializable2DFloatArray thisPatchStencil = new Serializable2DFloatArray(floatArraySize.x, floatArraySize.z); foreach (var splatConfiguration in config.SplatConfigurations) { var splatPrototypeWrapper = splatConfiguration.SplatPrototype; Serializable2DByteArray baseLayerSplat; if (!baseData.TryGetValue(splatPrototypeWrapper, out baseLayerSplat)) { baseLayerSplat = new Serializable2DByteArray(floatArraySize.x, floatArraySize.z); baseData[splatPrototypeWrapper] = baseLayerSplat; } for (var dz = 0; dz < floatArraySize.z; ++dz) { for (var dx = 0; dx < floatArraySize.x; ++dx) { var coordX = matrixMin.x + dx; var coordZ = matrixMin.z + dz; var worldPos = terrain.SplatCoordToWorldPos(new Common.Coord(coordX, coordZ)); worldPos = new Vector3(worldPos.x, objectBounds.center.y, worldPos.z); if (terrain.ContainsPointXZ(worldPos) && objectBounds.Contains(worldPos) && GeometryExtensions.BetweenPlanes(worldPos, startPlane, endPlane)) { var uniformT = mainSpline.GetClosestUniformTimeOnSplineXZ(worldPos.xz()); // Expensive! var closestOnSpline = mainSpline.GetUniformPointOnSpline(uniformT); var normalizedFlatDistToSpline = (worldPos - closestOnSpline).xz().magnitude / (config.Radius); if (normalizedFlatDistToSpline != Mathf.Clamp01(normalizedFlatDistToSpline)) { continue; } var maskValue = config.SplatFalloff.Evaluate(normalizedFlatDistToSpline); var writeFloatValue = splatConfiguration.SplatStrength * maskValue; var writeValue = (byte)Mathf.RoundToInt(Mathf.Clamp(writeFloatValue * 255f, 0, 255)); var mainRead = baseLayerSplat[dx, dz]; var newVal = (byte)Mathf.Clamp(Mathf.Max(writeValue, mainRead), 0, 255); var delta = newVal - mainRead; if (delta < 1 / 255f) { continue; } foreach (var currentPrototype in currentPrototypes) { if (!baseData.ContainsKey(currentPrototype)) { continue; } if (currentPrototype == splatPrototypeWrapper) { continue; } var otherSplatFloatValue = baseData[currentPrototype][dx, dz] / 255f; var otherSplatFloatWriteVal = (otherSplatFloatValue * (1 - (delta / 255f))); var write = (byte)Mathf.Clamp(Mathf.RoundToInt(otherSplatFloatWriteVal * 255), 0, 255); baseData[currentPrototype][dx, dz] = write; } //DebugHelper.DrawPoint(worldPos, 1, Color.red, 10); baseLayerSplat[dx, dz] = newVal; layer.Stencil[coordX, coordZ] = MiscUtilities.CompressStencil(stencilKey, 1); thisPatchStencil[dx, dz] = 1; } else { thisPatchStencil[dx, dz] = 0; } } } } foreach (var existingSplatPrototype in baseData) { var splat = existingSplatPrototype.Key; var data = existingSplatPrototype.Value; layer.SetSplatmap(splat, matrixMin.x, matrixMin.z, data, wrapper.Terrain.terrainData.alphamapResolution, thisPatchStencil); } }
public void SetDetailMap(DetailPrototypeWrapper prototype, int x, int z, Serializable2DByteArray details, int dRes, Serializable2DFloatArray stencil = null) { if (DetailData == null) { DetailData = new CompressedDetailDataLookup(); } Serializable2DByteArray existingDetails; if (!DetailData.TryGetValue(prototype, out existingDetails) || existingDetails.Width != dRes || existingDetails.Height != dRes) { existingDetails = new Serializable2DByteArray(dRes, dRes); DetailData[prototype] = existingDetails; } var width = details.Width; var height = details.Height; for (var u = x; u < x + width; ++u) { for (var v = z; v < z + height; ++v) { var hx = u - x; var hz = v - z; try { var splatSample = details[hx, hz]; if (stencil != null) { existingDetails[u, v] = (byte)Mathf.Clamp(Mathf.Lerp(existingDetails[u, v], splatSample, stencil[hx, hz]), 0, 255); } else { existingDetails[u, v] = (byte)splatSample; } } catch (Exception) { throw; } } } #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(this); #endif }
public void SetSplatmap(SplatPrototypeWrapper prototype, int x, int z, float[,] splats, int splatRes, Serializable2DFloatArray stencil = null) { if (splats == null || prototype == null) { return; } if (SplatData == null) { SplatData = new CompressedSplatDataLookup(); } Serializable2DByteArray existingSplats; if (!SplatData.TryGetValue(prototype, out existingSplats) || existingSplats.Width != splatRes || existingSplats.Height != splatRes) { existingSplats = new Serializable2DByteArray(splatRes, splatRes); SplatData[prototype] = existingSplats; } var width = splats.GetLength(0); var height = splats.GetLength(1); for (var u = x; u < x + width; ++u) { if (u < 0 || u >= splatRes) { continue; } for (var v = z; v < z + height; ++v) { if (v < 0 || v >= splatRes) { continue; } var hx = u - x; var hz = v - z; try { var splatSample = splats[hx, hz] * 255f; if (stencil != null) { var stencilVal = stencil[hx, hz]; splatSample = (byte)Mathf.Clamp(Mathf.Lerp(existingSplats[u, v], splatSample, stencilVal), 0, 255); } existingSplats[u, v] = (byte)Mathf.Clamp(splatSample, 0, 255); } catch (Exception) { throw; } } } #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(this); #endif }
public override void ProcessDetails(TerrainWrapper wrapper, LayerBase baseLayer, int stencilKey) { var layer = baseLayer as MMTerrainLayer; if (layer == null) { Debug.LogWarning(string.Format("Attempted to write {0} to incorrect layer type! Expected Layer {1} to be {2}, but it was {3}", name, baseLayer.name, GetLayerType(), baseLayer.GetType()), this); return; } if (!Network || Configuration == null) { return; } var config = Configuration.GetConfig <Config>(); if (config == null) { Debug.LogError("Invalid configuration! Expected ConnectionTerrainHeightConfiguration"); return; } var terrain = wrapper.Terrain; var dRes = terrain.terrainData.detailResolution; var mainSpline = NodeConnection.GetSpline(); var radius = config.Radius; var falloffCurve = config.GrassFalloff; // Create bounds to encapsulate spline (axis aligned) var bounds = mainSpline.GetApproximateBounds(); bounds.Expand(radius * 2 * Vector3.one); // Create object bounds var objectBounds = mainSpline.GetApproximateXZObjectBounds(); objectBounds.Expand(radius * 2 * Vector3.one); objectBounds.Expand(Vector3.up * 10000); // Early cull var axisBounds = objectBounds.ToAxisBounds(); var terrainBounds = terrain.GetComponent <Collider>().bounds; if (!terrainBounds.Intersects(axisBounds)) { return; } // Get matrix space min/max var matrixMin = terrain.WorldToDetailCoord(bounds.min); matrixMin = new Common.Coord(Mathf.Clamp(matrixMin.x, 0, dRes), Mathf.Clamp(matrixMin.z, 0, dRes)); var matrixMax = terrain.WorldToDetailCoord(bounds.max); matrixMax = new Common.Coord(Mathf.Clamp(matrixMax.x, 0, dRes), Mathf.Clamp(matrixMax.z, 0, dRes)); var floatArraySize = new Common.Coord(matrixMax.x - matrixMin.x, matrixMax.z - matrixMin.z); var layerDetails = layer.GetDetailMaps(matrixMin.x, matrixMin.z, floatArraySize.x, floatArraySize.z, dRes); var writeStencil = new Serializable2DFloatArray(floatArraySize.x, floatArraySize.z); for (var dx = 0; dx < floatArraySize.x; ++dx) { for (var dz = 0; dz < floatArraySize.z; ++dz) { var coordX = matrixMin.x + dx; var coordZ = matrixMin.z + dz; var worldPos = terrain.DetailCoordToWorldPos(new Common.Coord(coordX, coordZ)); worldPos = new Vector3(worldPos.x, objectBounds.center.y, worldPos.z); if (!terrain.ContainsPointXZ(worldPos) || !objectBounds.Contains(worldPos)) { continue; } var uniformT = mainSpline.GetClosestUniformTimeOnSplineXZ(worldPos.xz()); // Expensive! var closestOnSpline = mainSpline.GetUniformPointOnSpline(uniformT); var normalizedFlatDistToSpline = (worldPos - closestOnSpline).xz().magnitude / radius; var maskValue = Mathf.Clamp01(falloffCurve.Evaluate(normalizedFlatDistToSpline)); if (maskValue <= 0 || normalizedFlatDistToSpline < 0 || normalizedFlatDistToSpline > 1) { //DebugHelper.DrawPoint(worldPos, 1, Color.yellow, 20); continue; } //DebugHelper.DrawPoint(worldPos, .2f, Color.green, 20); //Debug.DrawLine(worldPos, worldPos + Vector3.up * maskValue, Color.green, 20); writeStencil[dx, dz] = 1; layer.Stencil[coordX, coordZ] = MiscUtilities.CompressStencil(GetPriority(), 1); foreach (var data in layerDetails) { float readValue = data.Value[dx, dz]; readValue /= 16; var writeValue = readValue * (1 - maskValue); var writeByteValue = (byte)Mathf.Clamp(writeValue * 16, 0, 16); data.Value[dx, dz] = writeByteValue; } } } foreach (var serializable2DByteArray in layerDetails) { layer.SetDetailMap(serializable2DByteArray.Key, matrixMin.x, matrixMin.z, serializable2DByteArray.Value, dRes, writeStencil); } }
public static bool IsValidStencil(Serializable2DFloatArray stencil) { return(stencil != null && stencil.Width > 0 && stencil.Height > 0); }
public override Serializable2DFloatArray BlendHeights(int x, int z, int width, int height, int heightRes, Serializable2DFloatArray result) { var layerHeights = GetHeights(x, z, width, height, heightRes); var stencil = IsValidStencil(Stencil) ? Stencil : null; BlendMMTerrainLayerUtility.BlendArray(ref result, layerHeights, stencil, BlendMode, new Common.Coord(x, z), new Common.Coord(x + width, z + height), new Common.Coord(heightRes, heightRes)); return(result); }