static public 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); }
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 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 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 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 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 ApplyBrush(Rect worldRect, bool useFallof = true, bool newUndo = false) { TerrainData data = terrain.terrainData; //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 (!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 = 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); } //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 void Round (int val, bool inscribed=false) { offset.Round(val, ceil:inscribed); size.Round(val, ceil:!inscribed); } //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 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; //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 (!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 = 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); } //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 void ChangeRect (CoordRect newRect) //will re-create array only if it is needed { 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 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 static CoordRect Intersect (CoordRect c1, CoordRect c2) { c1.Clamp(c2.Min, c2.Max); return c1; }
//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]; } }