public Matrix TestResize (CoordRect newRect) { Matrix result = new Matrix(newRect); Coord min = result.rect.Min; Coord max = result.rect.Max; for (int x=min.x; x<max.x; x++) for (int z=min.z; z<max.z; z++) { float percentX = 1f*(x-result.rect.offset.x)/result.rect.size.x; float origX = percentX*this.rect.size.x + this.rect.offset.x; float percentZ = 1f*(z-result.rect.offset.z)/result.rect.size.z; float origZ = percentZ*this.rect.size.z + this.rect.offset.z; result[x,z] = this.OutdatedGetInterpolated(origX, origZ); } return result; }
public CoordRect Approximate(int val) { CoordRect approx = new CoordRect(); approx.size.x = (size.x / val + 1) * val; approx.size.z = (size.z / val + 1) * val; approx.offset.x = offset.x - (approx.size.x - size.x) / 2; approx.offset.z = offset.z - (approx.size.z - size.z) / 2; approx.offset.x = (int)(approx.offset.x / val + 0.5f) * val; approx.offset.z = (int)(approx.offset.z / val + 0.5f) * val; return(approx); }
public Matrix2(int x, int z, T[] array = null) { rect = new CoordRect(0, 0, x, z); count = x * z; if (array != null && array.Length < count) { Debug.Log("Array length: " + array.Length + " is lower then matrix capacity: " + count); } if (array != null && array.Length >= count) { this.array = array; } else { this.array = new T[count]; } }
public Matrix2(Coord offset, Coord size, T[] array = null) { rect = new CoordRect(offset, size); count = rect.size.x * rect.size.z; if (array != null && array.Length < count) { Debug.Log("Array length: " + array.Length + " is lower then matrix capacity: " + count); } if (array != null && array.Length >= count) { this.array = array; } else { this.array = new T[count]; } }
/*public void FromTexture (Texture2D texture, Coord textureOffset=new Coord(), bool fillBorders=false) { Coord textureSize = new Coord(texture.width, texture.height); CoordRect textureRect = new CoordRect(textureOffset, textureSize); CoordRect intersection = CoordRect.Intersect(textureRect, rect); Color[] colors = texture.GetPixels(intersection.offset.x - textureOffset.x, intersection.offset.z - textureOffset.z, intersection.size.x, intersection.size.z); Coord min = intersection.Min; Coord max = intersection.Max; for (int x=min.x; x<max.x; x++) for (int z=min.z; z<max.z; z++) { int tx = x-min.x; int tz = z-min.z; Color col = colors[tz*(max.x-min.x) + tx]; this[x,z] = (col.r+col.g+col.b)/3; } if (fillBorders) RemoveBorders(intersection); }*/ public void FromTexture (Texture2D texture) { CoordRect textureRect = new CoordRect(0,0, texture.width, texture.height); CoordRect intersection = CoordRect.Intersect(textureRect, rect); Color[] colors = texture.GetPixels(intersection.offset.x, intersection.offset.z, intersection.size.x, intersection.size.z); Coord min = intersection.Min; Coord max = intersection.Max; for (int x=min.x; x<max.x; x++) for (int z=min.z; z<max.z; z++) { int tx = x-min.x; int tz = z-min.z; Color col = colors[tz*(max.x-min.x) + tx]; this[x,z] = (col.r+col.g+col.b)/3; } }
public void Fill(Matrix2 <T> m, bool removeBorders = false) { CoordRect intersection = CoordRect.Intersect(rect, m.rect); Coord min = intersection.Min; Coord max = intersection.Max; for (int x = min.x; x < max.x; x++) { for (int z = min.z; z < max.z; z++) { this[x, z] = m[x, z]; } } if (removeBorders) { RemoveBorders(intersection); } }
public float[,] ReadHeighmap (TerrainData data, float height=1) { CoordRect intersection = CoordRect.Intersect(rect, new CoordRect(0,0,data.heightmapResolution, data.heightmapResolution)); //get heights float[,] array = data.GetHeights(intersection.offset.x, intersection.offset.z, intersection.size.x, intersection.size.z); //returns x and z swapped //reading 2d array Coord min = intersection.Min; Coord max = intersection.Max; for (int x=min.x; x<max.x; x++) for (int z=min.z; z<max.z; z++) this[x,z] = array[z-min.z, x-min.x] * height; //removing borders RemoveBorders(intersection); return array; }
public float[,,] ReadSplatmap (TerrainData data, int channel, float[,,] array=null) { CoordRect intersection = CoordRect.Intersect(rect, new CoordRect(0,0,data.alphamapResolution, data.alphamapResolution)); //get heights if (array==null) array = data.GetAlphamaps(intersection.offset.x, intersection.offset.z, intersection.size.x, intersection.size.z); //returns x and z swapped //reading array Coord min = intersection.Min; Coord max = intersection.Max; for (int x=min.x; x<max.x; x++) for (int z=min.z; z<max.z; z++) this[x,z] = array[z-min.z, x-min.x, channel]; //removing borders RemoveBorders(intersection); return array; }
public Matrix Resize (CoordRect newRect, float smoothness=1, Matrix result=null) { //calculating ratio int upscaleRatio = newRect.size.x / rect.size.x; int downscaleRatio = rect.size.x / newRect.size.x; //checking if rect could be rescaled if (upscaleRatio > 1 && !newRect.Divisible(upscaleRatio)) Debug.LogError("Matrix rect " + rect + " could not be upscaled to " + newRect + " with factor " + upscaleRatio); if (downscaleRatio > 1 && !rect.Divisible(downscaleRatio)) Debug.LogError("Matrix rect " + rect + " could not be downscaled to " + newRect + " with factor " + downscaleRatio); //scaling if (upscaleRatio > 1) result = Upscale(upscaleRatio, result:result); if (downscaleRatio > 1) result = Downscale(downscaleRatio, smoothness:smoothness, result:result); //returning clone if all ratios are 1 if (upscaleRatio <= 1 && downscaleRatio <= 1) return Clone(result); else return result; }
public Stacker (CoordRect smallRect, CoordRect bigRect) { this.smallRect = smallRect; this.bigRect = bigRect; isDownscaled = false; //do not create additional matrices if rect sizes are the same if (bigRect==smallRect) { upscaled = downscaled = new Matrix(bigRect); } else { downscaled = new Matrix(smallRect); upscaled = new Matrix(bigRect); difference = new Matrix(bigRect); //once arrays created they should not be resized } }
public void ClampByRect(CoordRect rect) { if (x < rect.offset.x) { x = rect.offset.x; } if (x >= rect.offset.x + rect.size.x) { x = rect.offset.x + rect.size.x - 1; } if (z < rect.offset.z) { z = rect.offset.z; } if (z >= rect.offset.z + rect.size.z) { z = rect.offset.z + rect.size.z - 1; } }
public IEnumerable <Coord> DistanceArea(CoordRect rect) //same as distance are, but clamped by rect { int maxDist = Mathf.Max(x - rect.offset.x, rect.Max.x - x, z - rect.offset.z, rect.Max.z - z) + 1; if (rect.CheckInRange(this)) { yield return(this); } for (int i = 0; i < maxDist; i++) { foreach (Coord c in DistancePerimeter(i)) { if (rect.CheckInRange(c)) { yield return(c); } } } }
public void WriteHeightmap (TerrainData data, float[,] array=null, float brushFallof=0.5f) { CoordRect intersection = CoordRect.Intersect(rect, new CoordRect(0,0,data.heightmapResolution, data.heightmapResolution)); //checking ref array if (array == null || array.Length != intersection.size.x*intersection.size.z) array = new float[intersection.size.z,intersection.size.x]; //x and z swapped //write to 2d array Coord min = intersection.Min; Coord max = intersection.Max; for (int x=min.x; x<max.x; x++) for (int z=min.z; z<max.z; z++) { float fallofFactor = Fallof(x,z,brushFallof); if (Mathf.Approximately(fallofFactor,0)) continue; array[z-min.z, x-min.x] = this[x,z]*fallofFactor + array[z-min.z, x-min.x]*(1-fallofFactor); //array[z-min.z, x-min.x] += this[x,z]; } data.SetHeights(intersection.offset.x, intersection.offset.z, array); }
public void ToTexture (Texture2D texture=null, Color[] colors=null, float rangeMin=0, float rangeMax=1, bool resizeTexture=false) { //creating or resizing texture if (texture == null) texture = new Texture2D(rect.size.x, rect.size.z); if (resizeTexture) texture.Resize(rect.size.x, rect.size.z); //finding matrix-texture intersection Coord textureSize = new Coord(texture.width, texture.height); CoordRect textureRect = new CoordRect(new Coord(0,0), textureSize); CoordRect intersection = CoordRect.Intersect(textureRect, rect); //checking ref color array if (colors == null || colors.Length != intersection.size.x*intersection.size.z) colors = new Color[intersection.size.x*intersection.size.z]; //filling texture Coord min = intersection.Min; Coord max = intersection.Max; for (int x=min.x; x<max.x; x++) for (int z=min.z; z<max.z; z++) { float val = this[x,z]; //adjusting value to range val -= rangeMin; val /= rangeMax-rangeMin; //making color gradient float byteVal = val * 256; int flooredByteVal = (int)byteVal; float remainder = byteVal - flooredByteVal; float flooredVal = flooredByteVal/256f; float ceiledVal = (flooredByteVal+1)/256f; //saving to colors int tx = x-min.x; int tz = z-min.z; colors[tz*(max.x-min.x) + tx] = new Color(flooredVal, remainder>0.333f ? ceiledVal : flooredVal, remainder>0.666f ? ceiledVal : flooredVal); } texture.SetPixels(intersection.offset.x, intersection.offset.z, intersection.size.x, intersection.size.z, colors); texture.Apply(); }
public void GetHeight(Matrix matrix, Terrain terrain) { CoordRect terrainRect = terrain.GetHeightRect(); CoordRect intersection = CoordRect.Intersect(terrainRect, matrix.rect); if (intersection.size.x <= 0 || intersection.size.z <= 0) { return; } float[,] heights = terrain.terrainData.GetHeights(intersection.offset.x - terrainRect.offset.x, intersection.offset.z - terrainRect.offset.z, intersection.size.x, intersection.size.z); Coord min = intersection.Min; Coord max = intersection.Max; for (int x = min.x; x < max.x; x++) { for (int z = min.z; z < max.z; z++) { matrix[x, z] = heights[z - min.z, x - min.x]; //x and z switched } } }
public void AddSplat(Matrix matrix, Terrain terrain, int channel) { CoordRect terrainRect = terrain.GetSplatRect(); CoordRect intersection = CoordRect.Intersect(terrainRect, matrix.rect); if (intersection.size.x <= 0 || intersection.size.z <= 0) { return; } float[,,] splats = terrain.terrainData.GetAlphamaps(intersection.offset.x - terrainRect.offset.x, intersection.offset.z - terrainRect.offset.z, intersection.size.x, intersection.size.z); int numSplats = splats.GetLength(2); Coord min = intersection.Min; Coord max = intersection.Max; for (int x = min.x; x < max.x; x++) { for (int z = min.z; z < max.z; z++) { int sx = z - min.z; int sz = x - min.x; //x and z switched float val = matrix[x, z]; float invVal = 1 - matrix[x, z]; //multiplying all splats on inverse value for (int s = 0; s < numSplats; s++) { splats[sx, sz, s] *= invVal; } //adding val splats[sx, sz, channel] += val; } } terrain.terrainData.SetAlphamaps(intersection.offset.x - terrainRect.offset.x, intersection.offset.z - terrainRect.offset.z, splats); }
public Matrix(Coord offset, Coord size) { this.rect = new CoordRect(offset, size); array = new float[rect.size.x*rect.size.z]; }
public static void AddSplatmaps(TerrainData data, Matrix[] matrices, int[] channels, float[] opacity, float[,,] array=null, float brushFallof=0.5f) { int numChannels = data.alphamapLayers; bool[] usedChannels = new bool[numChannels]; for (int i=0; i<channels.Length; i++) usedChannels[channels[i]] = true; float[] slice = new float[numChannels]; Coord dataSize = new Coord(data.alphamapResolution, data.alphamapResolution); CoordRect dataRect = new CoordRect(new Coord(0,0), dataSize); CoordRect intersection = CoordRect.Intersect(dataRect, matrices[0].rect); if (array==null) array = data.GetAlphamaps(intersection.offset.x, intersection.offset.z, intersection.size.x, intersection.size.z); Coord min = intersection.Min; Coord max = intersection.Max; for (int x=min.x; x<max.x; x++) for (int z=min.z; z<max.z; z++) { //calculating fallof and opacity float fallofFactor = matrices[0].Fallof(x,z,brushFallof); if (Mathf.Approximately(fallofFactor,0)) continue; //reading slice for (int c=0; c<numChannels; c++) slice[c] = array[z-min.z, x-min.x, c]; //converting matrices to additive for (int i=0; i<matrices.Length; i++) matrices[i][x,z] = Mathf.Max(0, matrices[i][x,z] - slice[channels[i]]); //apply fallof for (int i=0; i<matrices.Length; i++) matrices[i][x,z] *= fallofFactor * opacity[i]; //calculating sum of adding values float addedSum = 0; //the sum of adding channels for (int i=0; i<matrices.Length; i++) addedSum += matrices[i][x,z]; //if (addedSum < 0.00001f) continue; //no need to do anything //if addedsum exceeds 1 - equalizing matrices if (addedSum > 1f) { for (int i=0; i<matrices.Length; i++) matrices[i][x,z] /= addedSum; addedSum=1; } //multiplying all values on a remaining amount float multiplier = 1-addedSum; for (int c=0; c<numChannels; c++) slice[c] *= multiplier; //adding matrices for (int i=0; i<matrices.Length; i++) slice[channels[i]] += matrices[i][x,z]; //saving slice for (int c=0; c<numChannels; c++) array[z-min.z, x-min.x, c] = slice[c]; } data.SetAlphamaps(intersection.offset.x, intersection.offset.z, array); }
//will re-create array only if it is needed public void ChangeRect(CoordRect newRect) { rect.offset.x = newRect.offset.x; rect.offset.z = newRect.offset.z; if (rect.size.x != newRect.size.x || rect.size.z != newRect.size.z) { rect = newRect; array = new float[rect.size.x*rect.size.z]; } }
public void FromTexture(Texture2D texture, bool fillBorders=false) { Coord textureSize = new Coord(texture.width, texture.height); CoordRect textureRect = new CoordRect(new Coord(0,0), textureSize); CoordRect intersection = CoordRect.Intersect(textureRect, rect); Color[] colors = texture.GetPixels(intersection.offset.x, intersection.offset.z, intersection.size.x, intersection.size.z); Coord min = intersection.Min; Coord max = intersection.Max; for (int x=min.x; x<max.x; x++) for (int z=min.z; z<max.z; z++) { int tx = x-min.x; int tz = z-min.z; Color col = colors[tz*(max.x-min.x) + tx]; this[x,z] = (col.r+col.g+col.b)/3; } if (fillBorders) RemoveBorders(intersection); }
public void ApplyBrush(Rect worldRect, bool useFallof=true, bool newUndo=false) { TerrainData data = terrain.terrainData; //if (data.heightmapResolution-1 > 512 || data.alphamapResolution > 512) return; //preparing useful values bool paintSplat = preset.paintSplat; if (data.alphamapLayers==0) paintSplat = false; //finding minimum resolution int smallerRes = Mathf.Min(data.heightmapResolution-1, data.alphamapResolution); int largerRes = Mathf.Max(data.heightmapResolution-1, data.alphamapResolution); int downscaledRes = largerRes / preset.downscale; int minRes = Mathf.Min(smallerRes, downscaledRes); //scale factors (relative to min res) int heightFactor = (data.heightmapResolution-1) / minRes; int splatFactor = data.alphamapResolution / minRes; int downscaledFactor = downscaledRes / minRes; //creating rects CoordRect minRect = new CoordRect(worldRect.x*minRes, worldRect.y*minRes, worldRect.width*minRes, worldRect.height*minRes); CoordRect heightsRect = minRect * heightFactor; CoordRect splatsRect = minRect * splatFactor; CoordRect downscaledRect = minRect * downscaledFactor; //checking stackers if (heights==null || heights.smallRect!=downscaledRect || heights.bigRect!=heightsRect) heights = new Matrix.Stacker(downscaledRect, heightsRect); if (splats==null || splats.smallRect!=downscaledRect || splats.bigRect != splatsRect) splats = new Matrix.Stacker(downscaledRect, splatsRect); if (sediments==null || sediments.smallRect != downscaledRect || sediments.bigRect != splatsRect) sediments = new Matrix.Stacker(downscaledRect, splatsRect); heights.preserveDetail=preserveDetail; splats.preserveDetail=preserveDetail; sediments.preserveDetail = preserveDetail; //creating original arrays heights.matrix.ChangeRect(heightsRect); float[,] heights2d = heights.matrix.ReadHeighmap(data); splats.matrix.ChangeRect(splatsRect); sediments.matrix.ChangeRect(splatsRect); float[,,] splats3d = null; if (paintSplat) { splats3d = splats.matrix.ReadSplatmap(data, preset.foreground.num); sediments.matrix.ReadSplatmap(data, preset.background.num, splats3d); } //downscaling arrays heights.ToSmall(); if (paintSplat) { splats.ToSmall(); sediments.ToSmall(); } //generating #if UNITY_EDITOR if (!preset.isErosion) { Matrix heightsMatrix = heights.matrix; Matrix splatsMatrix = splats.matrix; Matrix sedimentsMatrix = sediments.matrix; Coord min = heightsMatrix.rect.Min; Coord max = heightsMatrix.rect.Max; for (int x=min.x; x<max.x; x++) for (int z=min.z; z<max.z; z++) { float noise = ErosionBrushPlugin.Noise.Fractal(x, z, preset.noise_size); //noise = 1f*(x-min.x)/(max.x-min.x); noise = (noise-(1-preset.noise_uplift)) * preset.noise_amount; heightsMatrix[x,z] += noise / data.size.y; if (paintSplat) { float splatNoise = Mathf.Max(0,noise); splatsMatrix[x,z] = Mathf.Sqrt(splatNoise)*0.3f; float sedimentNoise = Mathf.Max(0,-noise); sedimentsMatrix[x,z] = Mathf.Sqrt(sedimentNoise)*0.3f; } //test //splatsMatrix[x,z] += 0.5f; //1f * (x-min.x) / (max.x-min.x); //sediments.matrix[x,z] += 0.5f;// 1f * (z-min.z) / (max.z-min.z); } } else { ErosionBrushPlugin.Erosion.ErosionIteration (heights.matrix.array, paintSplat? splats.matrix.array:null, paintSplat? sediments.matrix.array:null, heights.matrix.rect.size.x, heights.matrix.rect.size.z, erosionDurability:preset.erosion_durability, erosionAmount:preset.erosion_amount, sedimentAmount:preset.sediment_amount, erosionFluidityIterations:preset.erosion_fluidityIterations); //blurring heights heights.matrix.Blur(intensity:preset.erosion_smooth); //increasing splat splats.matrix.Multiply(1.3f); sediments.matrix.Multiply(1.3f); } #endif //upscaling arrays (+blur) heights.ToBig(); splats.ToBig(); sediments.ToBig(); //record undo. Undo.RecordObject and SetDirty are done in editor if (recordUndo) { if (newUndo) { if (undoList.Count > 10) undoList.RemoveAt(0); undoList.Add(new List<UndoStep>()); } if (undoList.Count == 0) undoList.Add(new List<UndoStep>()); undoList[undoList.Count-1].Add( new UndoStep(heights2d, splats3d, heightsRect.offset.x, heightsRect.offset.z, splatsRect.offset.x, splatsRect.offset.z) ); } //apply heights.matrix.WriteHeightmap(data, heights2d, (useFallof ? preset.brushFallof : -1)); if (paintSplat) Matrix.AddSplatmaps(data, new Matrix[] {splats.matrix, sediments.matrix}, new int[] {preset.foreground.num, preset.background.num}, new float[] {preset.foreground.apply? preset.foreground.opacity:0, preset.background.apply? preset.background.opacity:0}, brushFallof:(useFallof ? preset.brushFallof : -1), array:splats3d); //note that splat and sediments are additive }
public static CoordRect Intersect(CoordRect c1, CoordRect c2) { c1.Clamp(c2.Min, c2.Max); return c1; }
public Matrix(CoordRect rect) { this.rect = rect; array = new float[rect.size.x*rect.size.z]; }
static public void ErosionIteration(Matrix heights, Matrix erosion, Matrix sedimentSum, CoordRect area = new CoordRect(), float erosionDurability = 0.9f, float erosionAmount = 1f, float sedimentAmount = 0.5f, int erosionFluidityIterations = 3, float ruffle = 0.1f, Matrix torrents = null, Matrix sediments = null, int[] stepsArray = null, int[] heightsInt = null, int[] order = null) { if (area.isZero) { area = heights.rect; } int count = heights.count; int seed = 12345; #region Creating order array int steps = 1000000; //creating int-type copy of heights if (heightsInt == null) { heightsInt = new int[count]; } for (int i = 0; i < heights.count; i++) { heightsInt[i] = (int)(Mathf.Clamp01(heights.array[i]) * steps); } //order array if (order == null) { order = new int[count]; } order = ArrayTools.Order(heightsInt, order: order, max: heights.count, stepsArray: stepsArray); //setting out-of-area order num to negative for (int j = 0; j < heights.count; j++) { int pos = order[j]; Coord coord = heights.rect.CoordByNum(pos); if (!area.CheckInRangeAndBounds(coord)) { order[j] = -1; } } #endregion #region Creating torrents if (torrents == null) { torrents = new Matrix(heights.rect); } torrents.ChangeRect(heights.rect); torrents.Fill(1f); //casting initial rain for (int j = count - 1; j >= 0; j--) { //finding column ordered by height int pos = order[j]; if (pos < 0) { continue; } /* * MooreCross height = new MooreCross(heights, pos); //moore * MooreCross torrent = new MooreCross(torrents, pos); //moore * if (torrent.c > 2000000000) torrent.c = 2000000000; * * //creating torrents * MooreCross delta = new MooreCross(height); //moore * delta.SubtractInverse(height.c); * delta.ClampPositive(); * * delta.Percent(); //every side now determines a percent - how many water should go to it * delta.Multiply(torrent.c); * torrent.Add(delta); */ //getting height values float[] m = heights.array; int i = pos; int sizeX = heights.rect.size.x; float h = m[i]; float hx = m[i - 1]; float hX = m[i + 1]; float hz = m[i - sizeX]; float hZ = m[i + sizeX]; float hxz = m[i - 1 - sizeX]; float hXz = m[i + 1 - sizeX]; float hxZ = m[i - 1 + sizeX]; float hXZ = m[i + 1 + sizeX]; //creating delta float d = h - h; float dx = h - hx; float dX = h - hX; float dz = h - hz; float dZ = h - hZ; float dxz = h - hxz; float dXz = h - hXz; float dxZ = h - hxZ; float dXZ = h - hXZ; //ignoring negative delta values d = d > 0? d : 0; dx = dx > 0? dx : 0; dX = dX > 0? dX : 0; dz = dz > 0? dz : 0; dZ = dZ > 0? dZ : 0; dxz = dxz > 0? dxz : 0; dXz = dXz > 0? dXz : 0; dxZ = dxZ > 0? dxZ : 0; dXZ = dXZ > 0? dXZ : 0; //finding percents float p = 0, px = 0, pX = 0, pz = 0, pZ = 0, pxz = 0, pXz = 0, pxZ = 0, pXZ = 0; float s = d + dx + dX + dz + dZ + dxz + dXz + dxZ + dXZ; if (s > 0.00001f) { p = d / s; px = dx / s; pX = dX / s; pz = dz / s; pZ = dZ / s; pxz = dxz / s; pXz = dXz / s; pxZ = dxZ / s; pXZ = dXZ / s; } //getting central torrent (and clamping it) float t = torrents.array[i]; if (t > 2000000000) { t = 2000000000; } //spreading central torrent according percents delta. And adding to torrents array m = torrents.array; m[i] += t * p; m[i - 1] += t * px; m[i + 1] += t * pX; m[i - sizeX] += t * pz; m[i + sizeX] += t * pZ; m[i - 1 - sizeX] += t * pxz; m[i + 1 - sizeX] += t * pXz; m[i - 1 + sizeX] += t * pxZ; m[i + 1 + sizeX] += t * pXZ; } #endregion #region Erosion if (sediments == null) { sediments = new Matrix(heights.rect); } else { sediments.ChangeRect(heights.rect); } sediments.Clear(); for (int j = count - 1; j >= 0; j--) { //finding column ordered by height int pos = order[j]; if (pos < 0) { continue; } /* * Cross height = new Cross(heights, pos); * Cross torrent = new Cross(torrents, pos); * Cross sediment = new Cross(sediments, pos); * * //erosion * float erodeLine = (height.c + height.min)/2f; //halfway between current and maximum height * * if (height.c > erodeLine) //raising soil if column is higher than eroded column * { * float raised = height.c - erodeLine; * raised = Mathf.Min(raised, raised*(torrent.c-1) * (1-erosionDurability)); //could not raise more soil than height-minHeight. //torrents always have 1 or more * raised *= erosionAmount; * * heights.array[pos] -= raised; //raising soil * height.c -= raised; * sediments.array[pos] += raised * sedimentAmount; //and saving raised to sediment * sediment.c += raised * sedimentAmount; * * if (paintErosion != null) paintErosion.array[pos] += raised * mapsFactor; //and writing to ref * } */ //getting height values float[] m = heights.array; int i = pos; int sizeX = heights.rect.size.x; float h = m[i]; float hx = m[i - 1]; float hX = m[i + 1]; float hz = m[i - sizeX]; float hZ = m[i + sizeX]; //height minimum float h_min = h; if (hx < h_min) { h_min = hx; } if (hX < h_min) { h_min = hX; } if (hz < h_min) { h_min = hz; } if (hZ < h_min) { h_min = hZ; } //erosion line float erodeLine = (h + h_min) / 2f; //halfway between current and maximum height if (h < erodeLine) { continue; } //raising soil float raised = h - erodeLine; float maxRaised = raised * (torrents.array[pos] - 1) * (1 - erosionDurability); if (raised > maxRaised) { raised = maxRaised; } raised *= erosionAmount; //saving arrays heights.array[pos] -= raised; sediments.array[pos] += raised * sedimentAmount; if (erosion != null) { erosion.array[pos] += raised; //and writing to ref } } #endregion #region Settling sediment for (int l = 0; l < erosionFluidityIterations; l++) { for (int j = count - 1; j >= 0; j--) { //finding column ordered by height int pos = order[j]; if (pos < 0) { continue; } /* * Cross height = new Cross(heights, pos); * Cross sediment = new Cross(sediments, pos); * float sedimentSum = sediment.c+sediment.px+sediment.nx+sediment.pz+sediment.nz; * if (sedimentSum < 0.00001f) continue; * * //finding columns that sediment will spread to * Cross spread = new Cross(1,1,1,1,1); //actually it'a s bool-cross * * //from top to bottom * foreach (int i in height.Sorted()) * { * //float curMaxLevel = (height*spread).max; * float curMaxLevel = 0; * if (spread.c > 0.1f && height.c > curMaxLevel) curMaxLevel = height.c; * if (spread.px > 0.1f && height.px > curMaxLevel) curMaxLevel = height.px; * if (spread.nx > 0.1f && height.nx > curMaxLevel) curMaxLevel = height.nx; * if (spread.pz > 0.1f && height.pz > curMaxLevel) curMaxLevel = height.pz; * if (spread.nz > 0.1f && height.nz > curMaxLevel) curMaxLevel = height.nz; * * //sum of lack of heights to current max level less then total sediment * float lackSum = 0; //... + Mathf.Max(curMaxLevel-height.px, 0) + ... * if (curMaxLevel-height.c > 0) lackSum += curMaxLevel-height.c; * if (curMaxLevel-height.px > 0) lackSum += curMaxLevel-height.px; * if (curMaxLevel-height.pz > 0) lackSum += curMaxLevel-height.pz; * if (curMaxLevel-height.nx > 0) lackSum += curMaxLevel-height.nx; * if (curMaxLevel-height.nz > 0) lackSum += curMaxLevel-height.nz; * * if (lackSum < sedimentSum) break; * spread[i] = 0; //I find your lack of sediment disturbing! * * //if ((lackSum < sedimentSum)) spread = new Cross(1,1,1,1,1); * //else spread = new Cross(0,0,0,0,0); * * } * * //find sediment-filled level * float columnsRemain = spread.c+spread.px+spread.nx+spread.pz+spread.nz; //aka spread.sum; * float filledLevel = 0; * if (columnsRemain > 0.00001f) filledLevel = // ((height*spread).sum + sediment.sum)/columnsRemain * ((height.c*spread.c)+(height.px*spread.px)+(height.nx*spread.nx)+(height.pz*spread.pz)+(height.nz*spread.nz) + sedimentSum)/columnsRemain; * * //transfering sediment * sediment = new Cross( * filledLevel-height.c>0 ? (filledLevel-height.c)*spread.c : 0, * filledLevel-height.px>0 ? (filledLevel-height.px)*spread.px : 0, * filledLevel-height.nx>0 ? (filledLevel-height.nx)*spread.nx : 0, * filledLevel-height.pz>0 ? (filledLevel-height.pz)*spread.pz : 0, * filledLevel-height.nz>0 ? (filledLevel-height.nz)*spread.nz : 0); * * sediment.ToMatrix(sediments, pos); */ //getting height values float[] m = heights.array; int sizeX = heights.rect.size.x; float h = m[pos]; float hx = m[pos - 1]; float hX = m[pos + 1]; float hz = m[pos - sizeX]; float hZ = m[pos + sizeX]; //getting sediment values m = sediments.array; float s = m[pos]; float sx = m[pos - 1]; float sX = m[pos + 1]; float sz = m[pos - sizeX]; float sZ = m[pos + sizeX]; //sediment sum float sum = s + sx + sX + sz + sZ; if (sum < 0.00001f) { continue; } //pouring sum to all cells float sedimentFifth = sum / 5; s = sedimentFifth; sx = sedimentFifth; sX = sedimentFifth; sz = sedimentFifth; sZ = sedimentFifth; //levelling //for (int i=0; i<2; i++) //{ //x line float avg = (h + s + sx + hx) / 2; if (h + s > hx + sx) { float transfer = s + h - avg; if (transfer > s) { transfer = s; } s -= transfer; sx += transfer; } else { float transfer = sx + hx - avg; if (transfer > sx) { transfer = sx; } sx -= transfer; s += transfer; } avg = (hx + sx + sX + hX) / 2; if (hx + sx > hX + sX) { float transfer = sx + hx - avg; if (transfer > sx) { transfer = sx; } sx -= transfer; sX += transfer; } else { float transfer = sX + hX - avg; if (transfer > sX) { transfer = sX; } sX -= transfer; sx += transfer; } avg = (h + s + sX + hX) / 2; if (h + s > hX + sX) { float transfer = s + h - avg; if (transfer > s) { transfer = s; } s -= transfer; sX += transfer; } else { float transfer = sX + hX - avg; if (transfer > sX) { transfer = sX; } sX -= transfer; s += transfer; } //z line avg = (h + s + sz + hz) / 2; if (h + s > hz + sz) { float transfer = s + h - avg; if (transfer > s) { transfer = s; } s -= transfer; sz += transfer; } else { float transfer = sz + hz - avg; if (transfer > sz) { transfer = sz; } sz -= transfer; s += transfer; } avg = (hZ + sZ + sz + hz) / 2; if (hZ + sZ > hz + sz) { float transfer = sZ + hZ - avg; if (transfer > sZ) { transfer = sZ; } sZ -= transfer; sz += transfer; } else { float transfer = sz + hz - avg; if (transfer > sz) { transfer = sz; } sz -= transfer; sZ += transfer; } avg = (h + s + sz + hz) / 2; if (h + s > hz + sz) { float transfer = s + h - avg; if (transfer > s) { transfer = s; } s -= transfer; sz += transfer; } else { float transfer = sz + hz - avg; if (transfer > sz) { transfer = sz; } sz -= transfer; s += transfer; } //side pairs avg = (hx + sx + sz + hz) / 2; if (hx + sx > hz + sz) { float transfer = sx + hx - avg; if (transfer > sx) { transfer = sx; } sx -= transfer; sz += transfer; } else { float transfer = sz + hz - avg; if (transfer > sz) { transfer = sz; } sz -= transfer; sx += transfer; } avg = (hX + sX + sZ + hZ) / 2; if (hX + sX > hZ + sZ) { float transfer = sX + hX - avg; if (transfer > sX) { transfer = sX; } sX -= transfer; sZ += transfer; } else { float transfer = sZ + hZ - avg; if (transfer > sZ) { transfer = sZ; } sZ -= transfer; sX += transfer; } avg = (hx + sx + sZ + hZ) / 2; if (hx + sx > hZ + sZ) { float transfer = sx + hx - avg; if (transfer > sx) { transfer = sx; } sx -= transfer; sZ += transfer; } else { float transfer = sZ + hZ - avg; if (transfer > sZ) { transfer = sZ; } sZ -= transfer; sx += transfer; } avg = (hX + sX + sz + hz) / 2; if (hX + sX > hz + sz) { float transfer = sX + hX - avg; if (transfer > sX) { transfer = sX; } sX -= transfer; sz += transfer; } else { float transfer = sz + hz - avg; if (transfer > sz) { transfer = sz; } sz -= transfer; sX += transfer; } //} //additionally levelling /*int sNum = 0; //number of cells with sediment * float total = 0; //total height + sediment level * if (s>0.001f) { sNum++; total+=h+s; } * if (sx>0.001f) { sNum++; total+=hx+sx; } if (sX>0.001f) { sNum++; total+=hX+sX; } * if (sz>0.001f) { sNum++; total+=hz+sz; } if (sZ>0.001f) { sNum++; total+=hZ+sZ; } * * float totalAvg = total / sNum; * if (s>0.001f) s=totalAvg-h; * if (sx>0.001f) sx=totalAvg-hx; if (sX>0.001f) sX=totalAvg-hX; * if (sz>0.001f) sz=totalAvg-hz; if (sZ>0.001f) sZ=totalAvg-hZ;*/ //to matrix m = sediments.array; m[pos] = s; m[pos - 1] = sx; m[pos + 1] = sX; m[pos - sizeX] = sz; m[pos + sizeX] = sZ; if (sedimentSum != null) { m = sedimentSum.array; m[pos] += s; m[pos - 1] += sx; m[pos + 1] += sX; m[pos - sizeX] += sz; m[pos + sizeX] += sZ; } } } #endregion #region Writing sediments back to height, adding smooth and ruffle for (int j = count - 1; j >= 0; j--) { //writing heights heights.array[j] += sediments.array[j]; seed = 214013 * seed + 2531011; float random = ((seed >> 16) & 0x7FFF) / 32768f; int pos = order[j]; if (pos < 0) { continue; } float[] m = heights.array; int sizeX = heights.rect.size.x; float h = m[pos]; float hx = m[pos - 1]; float hX = m[pos + 1]; float hz = m[pos - sizeX]; float hZ = m[pos + sizeX]; //smoothing sediments a bit float s = sediments.array[pos]; if (s > 0.0001f) { float smooth = s / 2f; if (smooth > 0.75f) { smooth = 0.75f; } heights.array[pos] = h * (1 - smooth) + (hx + hX + hz + hZ) / 4f * smooth; } else { float maxHeight = hx; if (hX > maxHeight) { maxHeight = hX; } if (hz > maxHeight) { maxHeight = hz; } if (hZ > maxHeight) { maxHeight = hZ; } float minHeight = hx; if (hX < minHeight) { minHeight = hX; } if (hz < minHeight) { minHeight = hz; } if (hZ < minHeight) { minHeight = hZ; } float randomHeight = random * (maxHeight - minHeight) + minHeight; heights.array[pos] = heights.array[pos] * (1 - ruffle) + randomHeight * ruffle; } //Cross height = new Cross(heights, pos); //float maxAround = Mathf.Max(heights.array[pos-1], heights.array[pos+1], heights.array[pos-heights.rect.size.x], heights.array[pos+heights.rect.size.x]); //heights.array[pos] = Mathf.Min(heights.array[pos], maxAround); //heights.array[pos] = Mathf.Min(height.c, height.maxAround); //heights.array[pos] = Mathf.Min(height.c, height.avgAround)*0.5f + height.c*0.5f; //float maxHeight = height.maxAround; //float minHeight = height.minAround; //float randomHeight = random*(maxHeight-minHeight) + minHeight; //heights.array[pos] = heights.array[pos]*(1-ruffle) + randomHeight*ruffle; } #endregion } //erosion iteration
public Matrix (Coord offset, Coord size) { this.rect = new CoordRect(offset, size); array = new float[rect.size.x*rect.size.z]; }
public Matrix (CoordRect rect) { this.rect = rect; array = new float[rect.size.x*rect.size.z]; }
public Stacker(CoordRect smallRect, CoordRect bigRect) { this.smallRect = smallRect; this.bigRect = bigRect; isDownscaled = false; //do not create additional matrices if rect sizes are the same if (bigRect==smallRect) { upscaled = downscaled = new Matrix(bigRect); } else { downscaled = new Matrix(smallRect); upscaled = new Matrix(bigRect); difference = new Matrix(bigRect); //once arrays created they should not be resized } }
public void ApplyBrush(Vector3 pos, float radius, bool useFallof = true) { //preparing height matrix float heightPixelSizeX = terrains[0].terrainData.size.x / terrains[0].terrainData.heightmapResolution; float heightPixelSizeZ = terrains[0].terrainData.size.z / terrains[0].terrainData.heightmapResolution; CoordRect heightRect = pos.ToCoordRect(radius, heightPixelSizeX, heightPixelSizeZ); heightRect = heightRect.Approximate(preset.downscale); Matrix height = new Matrix(heightRect); //filling height matrix for (int t = 0; t < terrains.Length; t++) { GetHeight(height, terrains[t]); } Matrix sourceHeight = height.Copy(); //downscaling if (preset.downscale > 1) { height = height.Downscale(preset.downscale); } //preparing splat matrices Matrix bedrockMatrix = null; Matrix sedimentMatrix = null; if ((preset.foreground.apply && terrains[0].terrainData.alphamapLayers > preset.foreground.num) || (preset.background.apply && terrains[0].terrainData.alphamapLayers > preset.background.num)) { bedrockMatrix = new Matrix(height.rect); sedimentMatrix = new Matrix(height.rect); } //brush if (preset.isNoise) { ApplyNoise(height, bedrockMatrix, sedimentMatrix); } if (preset.isErosion) { ApplyErosion(height, bedrockMatrix, sedimentMatrix); } //upscaling if (preset.downscale > 1) { height = height.Upscale(preset.downscale); if (bedrockMatrix != null) { bedrockMatrix = bedrockMatrix.Upscale(preset.downscale); } if (sedimentMatrix != null) { sedimentMatrix = sedimentMatrix.Upscale(preset.downscale); } } //making height additive if (!preset.preserveDetail || preset.downscale == 1) { height.Subtract(sourceHeight); } else { Matrix smoothHeight = sourceHeight.Downscale(preset.downscale); smoothHeight = smoothHeight.Upscale(preset.downscale); height.Subtract(smoothHeight); } //applying fallof if (useFallof) { Coord center = height.rect.Center; float rad = height.rect.size.x / 2f; Coord min = height.rect.Min; Coord max = height.rect.Max; for (int x = min.x; x < max.x; x++) { for (int z = min.z; z < max.z; z++) { float percent = (rad - Coord.Distance(new Coord(x, z), center)) / (rad - rad * preset.brushFallof); if (percent < 0) { percent = 0; } if (percent > 1) { percent = 1; } percent = 3 * percent * percent - 2 * percent * percent * percent; height[x, z] *= percent; if (bedrockMatrix != null) { bedrockMatrix[x, z] *= percent; } if (sedimentMatrix != null) { sedimentMatrix[x, z] *= percent; } } } } //scaling splat matrices to match terrain splat resolution float splatPixelSize = terrains[0].terrainData.size.x / terrains[0].terrainData.alphamapResolution; CoordRect splatRect = pos.ToCoordRect(radius, splatPixelSize); if (bedrockMatrix != null) { bedrockMatrix = bedrockMatrix.Resize(splatRect); } if (sedimentMatrix != null) { sedimentMatrix = sedimentMatrix.Resize(splatRect); } //backing to terrain for (int t = 0; t < terrains.Length; t++) { AddHeight(height, terrains[t]); if (bedrockMatrix != null && preset.foreground.apply) { AddSplat(bedrockMatrix, terrains[t], preset.foreground.num); } if (sedimentMatrix != null && preset.background.apply) { AddSplat(sedimentMatrix, terrains[t], preset.background.num); } } }
} //inscribed parameter will shrink rect to make it lay inside original rect public void Round(CoordRect r, bool inscribed = false) { offset.Round(r.offset, ceil: inscribed); size.Round(r.size, ceil: !inscribed); }
public void ToTexture(Texture2D texture=null, Color[] colors=null, float rangeMin=0, float rangeMax=1, bool resizeTexture=false) { //creating or resizing texture if (texture == null) texture = new Texture2D(rect.size.x, rect.size.z); if (resizeTexture) texture.Resize(rect.size.x, rect.size.z); //finding matrix-texture intersection Coord textureSize = new Coord(texture.width, texture.height); CoordRect textureRect = new CoordRect(new Coord(0,0), textureSize); CoordRect intersection = CoordRect.Intersect(textureRect, rect); //checking ref color array if (colors == null || colors.Length != intersection.size.x*intersection.size.z) colors = new Color[intersection.size.x*intersection.size.z]; //filling texture Coord min = intersection.Min; Coord max = intersection.Max; for (int x=min.x; x<max.x; x++) for (int z=min.z; z<max.z; z++) { float val = this[x,z]; //adjusting value to range val -= rangeMin; val /= rangeMax-rangeMin; //making color gradient float byteVal = val * 256; int flooredByteVal = (int)byteVal; float remainder = byteVal - flooredByteVal; float flooredVal = flooredByteVal/256f; float ceiledVal = (flooredByteVal+1)/256f; //saving to colors int tx = x-min.x; int tz = z-min.z; colors[tz*(max.x-min.x) + tx] = new Color(flooredVal, remainder>0.333f ? ceiledVal : flooredVal, remainder>0.666f ? ceiledVal : flooredVal); } texture.SetPixels(intersection.offset.x, intersection.offset.z, intersection.size.x, intersection.size.z, colors); texture.Apply(); }
public static CoordRect Intersect(CoordRect c1, CoordRect c2) { c1.Clamp(c2.Min, c2.Max); return(c1); }
public Matrix Resize(CoordRect newRect, float smoothness=1, Matrix result=null) { //calculating ratio int upscaleRatio = newRect.size.x / rect.size.x; int downscaleRatio = rect.size.x / newRect.size.x; //checking if rect could be rescaled if (upscaleRatio > 1 && !newRect.Divisible(upscaleRatio)) Debug.LogError("Matrix rect " + rect + " could not be upscaled to " + newRect + " with factor " + upscaleRatio); if (downscaleRatio > 1 && !rect.Divisible(downscaleRatio)) Debug.LogError("Matrix rect " + rect + " could not be downscaled to " + newRect + " with factor " + downscaleRatio); //scaling if (upscaleRatio > 1) result = Upscale(upscaleRatio, result:result); if (downscaleRatio > 1) result = Downscale(downscaleRatio, smoothness:smoothness, result:result); //returning clone if all ratios are 1 if (upscaleRatio <= 1 && downscaleRatio <= 1) return Clone(result); else return result; }
public void Round(CoordRect r, bool inscribed=false) { offset.Round(r.offset, ceil:inscribed); size.Round(r.size, ceil:!inscribed); }
public void RemoveBorders(CoordRect centerRect) { RemoveBorders( Mathf.Max(0,centerRect.offset.x-rect.offset.x), Mathf.Max(0,centerRect.offset.z-rect.offset.z), Mathf.Max(0,rect.Max.x-centerRect.Max.x+1), Mathf.Max(0,rect.Max.z-centerRect.Max.z+1) ); }
public void ApplyBrush(Rect worldRect, bool useFallof = true, bool newUndo = false) { TerrainData data = terrain.terrainData; //if (data.heightmapResolution-1 > 512 || data.alphamapResolution > 512) return; //preparing useful values bool paintSplat = preset.paintSplat; if (data.alphamapLayers == 0) { paintSplat = false; } //finding minimum resolution int smallerRes = Mathf.Min(data.heightmapResolution - 1, data.alphamapResolution); int largerRes = Mathf.Max(data.heightmapResolution - 1, data.alphamapResolution); int downscaledRes = largerRes / preset.downscale; int minRes = Mathf.Min(smallerRes, downscaledRes); //scale factors (relative to min res) int heightFactor = (data.heightmapResolution - 1) / minRes; int splatFactor = data.alphamapResolution / minRes; int downscaledFactor = downscaledRes / minRes; //creating rects CoordRect minRect = new CoordRect(worldRect.x * minRes, worldRect.y * minRes, worldRect.width * minRes, worldRect.height * minRes); CoordRect heightsRect = minRect * heightFactor; CoordRect splatsRect = minRect * splatFactor; CoordRect downscaledRect = minRect * downscaledFactor; //checking stackers if (heights == null || heights.smallRect != downscaledRect || heights.bigRect != heightsRect) { heights = new Matrix.Stacker(downscaledRect, heightsRect); } if (splats == null || splats.smallRect != downscaledRect || splats.bigRect != splatsRect) { splats = new Matrix.Stacker(downscaledRect, splatsRect); } if (sediments == null || sediments.smallRect != downscaledRect || sediments.bigRect != splatsRect) { sediments = new Matrix.Stacker(downscaledRect, splatsRect); } heights.preserveDetail = preserveDetail; splats.preserveDetail = preserveDetail; sediments.preserveDetail = preserveDetail; //creating original arrays heights.matrix.ChangeRect(heightsRect); float[,] heights2d = heights.matrix.ReadHeighmap(data); splats.matrix.ChangeRect(splatsRect); sediments.matrix.ChangeRect(splatsRect); float[,,] splats3d = null; if (paintSplat) { splats3d = splats.matrix.ReadSplatmap(data, preset.foreground.num); sediments.matrix.ReadSplatmap(data, preset.background.num, splats3d); } //downscaling arrays heights.ToSmall(); if (paintSplat) { splats.ToSmall(); sediments.ToSmall(); } //generating #if UNITY_EDITOR if (!preset.isErosion) { Matrix heightsMatrix = heights.matrix; Matrix splatsMatrix = splats.matrix; Matrix sedimentsMatrix = sediments.matrix; Coord min = heightsMatrix.rect.Min; Coord max = heightsMatrix.rect.Max; for (int x = min.x; x < max.x; x++) { for (int z = min.z; z < max.z; z++) { float noise = ErosionBrushPlugin.Noise.Fractal(x, z, preset.noise_size); //noise = 1f*(x-min.x)/(max.x-min.x); noise = (noise - (1 - preset.noise_uplift)) * preset.noise_amount; heightsMatrix[x, z] += noise / data.size.y; if (paintSplat) { float splatNoise = Mathf.Max(0, noise); splatsMatrix[x, z] = Mathf.Sqrt(splatNoise) * 0.3f; float sedimentNoise = Mathf.Max(0, -noise); sedimentsMatrix[x, z] = Mathf.Sqrt(sedimentNoise) * 0.3f; } //test //splatsMatrix[x,z] += 0.5f; //1f * (x-min.x) / (max.x-min.x); //sediments.matrix[x,z] += 0.5f;// 1f * (z-min.z) / (max.z-min.z); } } } else { ErosionBrushPlugin.Erosion.ErosionIteration(heights.matrix.array, paintSplat? splats.matrix.array:null, paintSplat? sediments.matrix.array:null, heights.matrix.rect.size.x, heights.matrix.rect.size.z, erosionDurability: preset.erosion_durability, erosionAmount: preset.erosion_amount, sedimentAmount: preset.sediment_amount, erosionFluidityIterations: preset.erosion_fluidityIterations); //blurring heights heights.matrix.Blur(intensity: preset.erosion_smooth); //increasing splat splats.matrix.Multiply(1.3f); sediments.matrix.Multiply(1.3f); } #endif //upscaling arrays (+blur) heights.ToBig(); splats.ToBig(); sediments.ToBig(); //record undo. Undo.RecordObject and SetDirty are done in editor if (recordUndo) { if (newUndo) { if (undoList.Count > 10) { undoList.RemoveAt(0); } undoList.Add(new List <UndoStep>()); } if (undoList.Count == 0) { undoList.Add(new List <UndoStep>()); } undoList[undoList.Count - 1].Add(new UndoStep(heights2d, splats3d, heightsRect.offset.x, heightsRect.offset.z, splatsRect.offset.x, splatsRect.offset.z)); } //apply heights.matrix.WriteHeightmap(data, heights2d, (useFallof ? preset.brushFallof : -1)); if (paintSplat) { Matrix.AddSplatmaps(data, new Matrix[] { splats.matrix, sediments.matrix }, new int[] { preset.foreground.num, preset.background.num }, new float[] { preset.foreground.apply?preset.foreground.opacity: 0, preset.background.apply? preset.background.opacity:0 }, brushFallof: (useFallof ? preset.brushFallof : -1), array: splats3d); //note that splat and sediments are additive } }
public Matrix () { array = new float[0]; rect = new CoordRect(0,0,0,0); count = 0; } //for serializer